[
  {
    "path": ".gitignore",
    "content": "# This file is used to ignore files which are generated\n# ----------------------------------------------------------------------------\ndeps/*\nrdeployed/*\nbrelease/*\nbdebug/*\nrdeployed2/*\nrdepmin/*\n\n*.wav\n*~\n*.autosave\n*.a\n*.core\n*.moc\n*.o\n*.obj\n*.orig\n*.rej\n*.so\n*.so.*\n*_pch.h.cpp\n*_resource.rc\n*.qm\n.#*\n*.*#\ncore\n!core/\ntags\n.DS_Store\n.directory\n*.debug\nMakefile*\n*.prl\n*.app\nmoc_*.cpp\nui_*.h\nqrc_*.cpp\nThumbs.db\n*.res\n*.rc\n*.qmake.cache\n*.qmake.stash\nrelease/*\n\n# qtcreator generated files\n*.pro.user*\n\n# xemacs temporary files\n*.flc\n\n# Vim temporary files\n.*.swp\n\n# Visual Studio generated files\n*.ib_pdb_index\n*.idb\n*.ilk\n*.pdb\n*.sln\n*.suo\n*.vcproj\n*vcproj.*.*.user\n*.ncb\n*.sdf\n*.opensdf\n*.vcxproj\n*vcxproj.*\n\n# MinGW generated files\n*.Debug\n*.Release\n\n# Python byte code\n*.pyc\n\n# Binaries\n# --------\n*.dll\n*.exe\n\ndeps.zip\nREADME.md.backup\n/.qtc_clangd\n/build\n"
  },
  {
    "path": "EnglishPhoneticProcessor.cpp",
    "content": "#include \"EnglishPhoneticProcessor.h\"\n#include \"VoxCommon.hpp\"\n#include <Windows.h>\n#include <string>\n\n\nusing namespace std;\n\nconst std::string SPECIAL_SPACE = \"@\\\\_\";\n\nbool EnglishPhoneticProcessor::Initialize(Phonemizer* InPhn, ESpeakPhonemizer *InENGPh)\n{\n\n\n    Phoner = InPhn;\n    Tokenizer.SetAllowedChars(Phoner->GetGraphemeChars());\n    ENG_Phonemizer = InENGPh;\n\n\n\n\n\treturn true;\n}\n\n\nstd::string EnglishPhoneticProcessor::ProcessTextPhonetic(const std::string& InText, const std::vector<u32string> &InPhonemes, const std::vector<DictEntry>& InDict, ETTSLanguageType::Enum InLanguageType, bool IsTac)\n{\n    if (!Phoner)\n\t\treturn \"ERROR\";\n\n\n\n    vector<string> Words = Tokenizer.Tokenize(InText,IsTac);\n\n\tstring Assemble = \"\";\n\n\n    if (InLanguageType == ETTSLanguageType::Char)\n    {\n        for (size_t w = 0; w < Words.size();w++)\n        {\n            Assemble.append(Words[w]);\n\n            if (w > 0)\n                Assemble.append(\" \");\n\n        }\n\n        if (Assemble[Assemble.size() - 1] == ' ')\n            Assemble.pop_back();\n\n        return Assemble;\n\n\n\n    }\n\n    // Make a copy of the dict passed.\n    std::vector<DictEntry> CurrentDict = InDict;\n\n\n\tfor (size_t w = 0; w < Words.size();w++) \n\t{\n\t\tconst string& Word = Words[w];\n        bool NextIsPunct = false;\n\n        if (w < Words.size() - 1)\n            NextIsPunct = Words[w + 1].find(\"@@\") != std::string::npos;\n\n\n\n        if (Word.size() > 22)\n            continue;\n\n\n        // Double email symbol indicates Tacotron punctuation handling\n        if (Word.find(\"@@\") != std::string::npos)\n        {\n            std::string AddPonct = Word.substr(2); // Remove the @@\n            Assemble.append(\" \");\n            Assemble.append(AddPonct);\n            Assemble.append(\" \");\n            if (InLanguageType == ETTSLanguageType::ARPA && w != Words.size() - 1){\n                Assemble.append(SPECIAL_SPACE);\n                Assemble.append(\" \");\n            }\n\n\n            continue;\n\n\n        }\n\n        if (Word.find(\"@\") != std::string::npos){\n            std::u32string AddPh = VoxUtil::StrToU32(Word.substr(1)); // Remove the @\n            size_t OutId = 0;\n            if (VoxUtil::FindInVec(AddPh,InPhonemes,OutId))\n            {\n                Assemble.append(VoxUtil::U32ToStr(InPhonemes[OutId]));\n                Assemble.append(\" \");\n\n\n            }\n\n            continue;\n\n        }\n\n\n\n\n        size_t OverrideIdx = 0;\n        if (!ENG_Phonemizer && VoxUtil::FindInVec2<std::string,DictEntry>(Word,InDict,OverrideIdx))\n        {\n             Assemble.append(InDict[OverrideIdx].PhSpelling);\n             Assemble.append(\" \");\n             continue;\n        }\n\n\n\n        std::string Res = Word;\n        if (!ENG_Phonemizer){\n            Res = Phoner->ProcessWord(Word,0.001f);\n            CurrentDict.push_back({Word,Res,\"\"});\n        }\n\n\n        // Cache the word in the override dict so next time we don't have to research it\n\n\n        Assemble.append(Res);\n        Assemble.append(\" \");\n        if (InLanguageType == ETTSLanguageType::ARPA && !NextIsPunct && w != Words.size() - 1)\n        {\n            Assemble.append(SPECIAL_SPACE);\n            Assemble.append(\" \");\n        }\n\n\n\n\n\n\n\n\n\t}\n\t\n\n    // eSpeak phonemizer takes in whole thing\n    if (ENG_Phonemizer){\n\n        Assemble = ENG_Phonemizer->Phonemize(Assemble);\n    }\n\n\n    // Delete last space if there is\n\tif (Assemble[Assemble.size() - 1] == ' ')\n\t\tAssemble.pop_back();\n\n\n\n\treturn Assemble;\n}\n\nEnglishPhoneticProcessor::EnglishPhoneticProcessor()\n{\n    Phoner = nullptr;\n    ENG_Phonemizer = nullptr;\n}\n\nEnglishPhoneticProcessor::EnglishPhoneticProcessor(Phonemizer *InPhn, ESpeakPhonemizer *InENGPh)\n{\n    Initialize(InPhn,InENGPh);\n\n}\n\n\n\nEnglishPhoneticProcessor::~EnglishPhoneticProcessor()\n{\n    // Causes annoying crash on exit. It's also irrelevant because the OS frees what little memory this had.\n    /*\n    if (Phoner)\n       delete Phoner;\n\n   */\n}\n"
  },
  {
    "path": "EnglishPhoneticProcessor.h",
    "content": "#pragma once\n#include \"TextTokenizer.h\"\n\n\n#include \"phoneticdict.h\"\n#include \"phonemizer.h\"\n#include \"espeakphonemizer.h\"\n\nclass EnglishPhoneticProcessor\n{\nprivate:\n\tTextTokenizer Tokenizer;\n    Phonemizer* Phoner;\n\n    ESpeakPhonemizer* ENG_Phonemizer;\n\n\tinline bool FileExists(const std::string& name) {\n        std::ifstream f(name.c_str());\n\t\treturn f.good();\n\t}\n\npublic:\n    bool Initialize(Phonemizer *InPhn,ESpeakPhonemizer* InENGPh = nullptr);\n    std::string ProcessTextPhonetic(const std::string& InText, const std::vector<std::u32string> &InPhonemes, const std::vector<DictEntry>& InDict, ETTSLanguageType::Enum InLanguageType, bool IsTac);\n\tEnglishPhoneticProcessor();\n    EnglishPhoneticProcessor(Phonemizer *InPhn,ESpeakPhonemizer* InENGPh = nullptr);\n\t~EnglishPhoneticProcessor();\n\n    inline TextTokenizer& GetTokenizer() {return Tokenizer;}\n};\n\n"
  },
  {
    "path": "FastSpeech2.cpp",
    "content": "#include \"FastSpeech2.h\"\n\n\n\nFastSpeech2::FastSpeech2()\n{\n}\n\n\nTFTensor<float> FastSpeech2::DoInference(const std::vector<int32_t>& InputIDs,const std::vector<float>& ArgsFloat,const std::vector<int32_t> ArgsInt, int32_t SpeakerID , int32_t EmotionID)\n{\n    if (!CurrentMdl)\n        throw std::exception(\"Tried to do inference on unloaded or invalid model!\");\n\n    // Convenience reference so that we don't have to constantly derefer pointers.\n    cppflow::model& Mdl = *CurrentMdl;\n\n    // This is the shape of the input IDs, our equivalent to tf.expand_dims.\n\n    std::vector<int64_t> InputIDShape = { 1, (int64_t)InputIDs.size() };\n\n    // Define the tensors\n    cppflow::tensor input_ids{InputIDs, InputIDShape };\n    cppflow::tensor energy_ratios{ ArgsFloat[1] };\n    cppflow::tensor f0_ratios{ArgsFloat[2]};\n    cppflow::tensor speaker_ids{ SpeakerID };\n    cppflow::tensor speed_ratios{ ArgsFloat[0] };\n    cppflow::tensor* emotion_ids = nullptr;\n\n\n\n\n\n\n    // Vector of input tensors\n    TensorVec Inputs = {{\"serving_default_input_ids:0\",input_ids},\n                        {\"serving_default_speaker_ids:0\",speaker_ids},\n                        {\"serving_default_energy_ratios:0\",energy_ratios},\n                        {\"serving_default_f0_ratios:0\",f0_ratios},\n                        {\"serving_default_speed_ratios:0\",speed_ratios}};\n\n    // This is a multi-emotion model\n    if (EmotionID != -1)\n    {\n        emotion_ids = new cppflow::tensor{EmotionID};\n        Inputs.push_back({\"serving_default_emotion_ids:0\",*emotion_ids});\n\n\n    }\n\n\n\n\n\n    // Do inference\n    // If we don't extract every single output it crashes.\n    auto Outputs = Mdl(Inputs,{\"StatefulPartitionedCall:0\",\"StatefulPartitionedCall:1\",\"StatefulPartitionedCall:2\",\"StatefulPartitionedCall:3\",\"StatefulPartitionedCall:4\"});\n\n    // Define output and return it\n    TFTensor<float> Output = VoxUtil::CopyTensor<float>(Outputs[1]);\n\n    // We allocated the emotion_ids cppflow::tensor dynamically, delete it\n    if (emotion_ids)\n        delete emotion_ids;\n\n    // We could just straight out define it in the return statement, but I like it more this way\n\n    return Output;\n}\n\nFastSpeech2::~FastSpeech2()\n{\n\n}\n"
  },
  {
    "path": "FastSpeech2.h",
    "content": "#pragma once\n\n#include \"melgen.h\"\n\n\nclass FastSpeech2 : public MelGen\n{\n\npublic:\n\tFastSpeech2();\n\n\n\n\t/*\n\tDo inference on a FastSpeech2 model.\n\n\t-> InputIDs: Input IDs of tokens for inference\n\t-> SpeakerID: ID of the speaker in the model to do inference on. If single speaker, always leave at 0. If multispeaker, refer to your model.\n    -> (In ArgsFloat)Speed, Energy, F0: Parameters for FS2 inference. Leave at 1.f for defaults\n\n\t<- Returns: TFTensor<float> with shape {1,<len of mel in frames>,80} containing contents of mel spectrogram. \n\t*/\n    TFTensor<float> DoInference(const std::vector<int32_t>& InputIDs,const std::vector<float>& ArgsFloat,const std::vector<int32_t> ArgsInt, int32_t SpeakerID = 0, int32_t EmotionID = -1);\n\n\n\n\t~FastSpeech2();\n};\n\n"
  },
  {
    "path": "LICENSE.md",
    "content": "MIT License\n\nCopyright (c) 2020 ZDisket\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": "MultiBandMelGAN.cpp",
    "content": "#include \"MultiBandMelGAN.h\"\n#define IF_EXCEPT(cond,ex) if (cond){throw std::exception(ex);}\n\n\n\nbool MultiBandMelGAN::Initialize(const std::string & VocoderPath)\n{\n\ttry {\n        MelGAN = std::make_unique<cppflow::model>(VocoderPath);\n\t}\n\tcatch (...) {\n\t\treturn false;\n\n\t}\n\treturn true;\n\n\n}\n\nTFTensor<float> MultiBandMelGAN::DoInference(const TFTensor<float>& InMel)\n{\n    IF_EXCEPT(!MelGAN, \"Tried to infer MB-MelGAN on uninitialized model!!!!\")\n\n    // Convenience reference so that we don't have to constantly derefer pointers.\n    cppflow::model& Mdl = *MelGAN;\n\n\n    cppflow::tensor input_mels{ InMel.Data, InMel.Shape};\n\n\n    auto out_audio = Mdl({{\"serving_default_mels:0\",input_mels}}, {\"StatefulPartitionedCall:0\"})[0];\n    TFTensor<float> RetTensor = VoxUtil::CopyTensor<float>(out_audio);\n\n    return RetTensor;\n\n\n\n\n\n}\n\nMultiBandMelGAN::MultiBandMelGAN()\n{\n\tMelGAN = nullptr;\n}\n\n\nMultiBandMelGAN::~MultiBandMelGAN()\n{\n\n\n}\n"
  },
  {
    "path": "MultiBandMelGAN.h",
    "content": "#pragma once\n\n#include \"VoxCommon.hpp\"\n#include <memory>\nclass MultiBandMelGAN\n{\nprivate:\n    std::unique_ptr<cppflow::model> MelGAN;\n\n\npublic:\n    virtual bool Initialize(const std::string& VocoderPath);\n\n\n\t// Do MultiBand MelGAN inference including PQMF\n\t// -> InMel:  Mel spectrogram (shape [1, xx, 80])\n\t// <- Returns: Tensor data [4, xx, 1]\n    virtual TFTensor<float> DoInference(const TFTensor<float>& InMel);\n\n\tMultiBandMelGAN();\n\t~MultiBandMelGAN();\n};\n\n"
  },
  {
    "path": "ONNXModel.cpp",
    "content": "#include \"ONNXModel.h\"\n#include <dml_provider_factory.h>\n#include <onnxruntime_c_api.h>\n// We can't include this (or embed in the ONNXModel header) because the linker thinks they're already defined in\n// subclasses like Crepe (???)\n#include \"ONNXUtil.hpp\"\n\n#include <iostream>\n#include <filesystem>\n\n\n#include <windows.h>\n#include <string>\n#include <dxgi1_6.h>\n#include <wrl/client.h>\n\n\nbool DoesFileExist(const std::wstring& path) {\n    DWORD fileAttributes = GetFileAttributesW(path.c_str());\n    if (fileAttributes == INVALID_FILE_ATTRIBUTES) {\n        return false; // The file does not exist or there is an error.\n    }\n    return true; // The file exists.\n}\n\nstd::wstring GetDefaultAdapterName()\n{\n    Microsoft::WRL::ComPtr<IDXGIFactory6> factory;\n    if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&factory)))) {\n        return L\"GPU\";\n    }\n\n    Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;\n    if (FAILED(factory->EnumAdapterByGpuPreference(0, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IID_PPV_ARGS(&adapter)))) {\n        return L\"GPU\";\n    }\n\n    DXGI_ADAPTER_DESC1 desc = {};\n    if (FAILED(adapter->GetDesc1(&desc))) {\n        return L\"GPU\";\n    }\n\n    return std::wstring(desc.Description);\n}\n\n\nbool ONNXModel::SetUpDirectML(Ort::SessionOptions& SessionOptions)\n{\n    // This functions tells us it's deprecated but the alternative causes a crash, plus it's not mentioned in documentation (????)\n    OrtStatusPtr Stat = OrtSessionOptionsAppendExecutionProvider_DML(SessionOptions, 0);\n\n    OrtStatus* Status = (Stat);\n    if (Status)\n        return false; // Status NOT nullptr indicates failure\n\n    // https://onnxruntime.ai/docs/execution-providers/DirectML-ExecutionProvider.html#configuration-options\n    /*\n    The DirectML execution provider does not support the use of memory pattern optimizations or parallel execution in onnxruntime\n    When supplying session options during InferenceSession creation, these options must be disabled or an error will be returned.\n    */\n    \n    SessionOptions.DisableMemPattern();\n    SessionOptions.SetExecutionMode(ExecutionMode::ORT_SEQUENTIAL);\n    \n\n\n    return true;\n}\n\nvoid ONNXModel::DisableMemReuse(OrtSessionOptions* SessPtr)\n{\n}\n\nstd::pair<std::vector<const char*>, std::vector<const char*>> ONNXModel::GetInputOutputNamesChar(const std::vector<std::string>& InputNames, const std::vector<std::string>& OutputNames)\n{\n    return std::pair< std::vector<const char*>, std::vector<const char*>>{\n        ONNXUtil::StringVectoCharVec(InputNames),\n        ONNXUtil::StringVectoCharVec(OutputNames),\n    };\n\n}\n\nONNXModel::ONNXModel()\n{\n    ModelLoaded = false;\n    IsGPU = false;\n    DeviceName = L\"CPU\";\n}\n\nvoid ONNXModel::_overrideDevices(bool is_gpu, std::wstring device_name)\n{\n    IsGPU = is_gpu;\n    DeviceName = device_name;\n\n}\n\nbool ONNXModel::Load(const std::wstring& ModelPath, const std::string& EnvName)\n{\n\n    if (!DoesFileExist(ModelPath))\n         return false;\n\n    Environment = std::make_unique<Ort::Env>(ORT_LOGGING_LEVEL_WARNING, EnvName.c_str());\n\n    // Set up DirectML\n    \n    Ort::SessionOptions session_options;\n    bool DML_Succ = SetUpDirectML(session_options);\n\n    Sess = std::make_unique<Ort::Session>(*Environment, ModelPath.c_str(), session_options);\n    Alloc = std::make_unique<Ort::AllocatorWithDefaultOptions>();\n\n    if (!Sess || !Alloc)\n        return false;\n\n    IsGPU = DML_Succ;\n    DeviceName = DML_Succ ? GetDefaultAdapterName() : L\"CPU\";\n    ModelLoaded = true;\n\n\n\n    return true;\n}\n\n\nstd::vector<std::string> ONNXModel::GetOutputNames()\n{\n    std::vector<std::string> OutputNames;\n    for (std::size_t i = 0; i < Sess->GetOutputCount(); i++) {\n        OutputNames.emplace_back(Sess->GetOutputNameAllocated(i, *Alloc).get());\n    }\n    return OutputNames;\n}\n\nstd::vector<std::string> ONNXModel::GetInputNames()\n{\n    std::vector<std::string> InputNames;\n    for (std::size_t i = 0; i < Sess->GetInputCount(); i++) {\n        InputNames.emplace_back(Sess->GetInputNameAllocated(i, *Alloc).get());\n    }\n    return InputNames;\n}\n\n\nstd::vector<ONNXTensorElementDataType> ONNXModel::GetInputTypes()\n{\n    std::vector<ONNXTensorElementDataType> InputTypes;\n    for (std::size_t i = 0; i < Sess->GetInputCount(); i++) {\n        InputTypes.emplace_back(Sess->GetInputTypeInfo(i).GetTensorTypeAndShapeInfo().GetElementType());\n    }\n\n\n    return InputTypes;\n}\n\nstd::pair<std::vector<std::vector<int64_t>>, std::vector<std::string>> ONNXModel::GetInputs()\n{\n \n    std::vector<std::vector<int64_t>> InputShapes;\n    std::vector<std::string> InputNames;\n    for (std::size_t i = 0; i < Sess->GetInputCount(); i++) {\n        InputNames.emplace_back(Sess->GetInputNameAllocated(i, *Alloc).get());\n\n        InputShapes.emplace_back(Sess->GetInputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape());\n    }\n\n    std::pair<std::vector<std::vector<int64_t>>, std::vector<std::string>> Ret(InputShapes, InputNames);\n    return Ret;\n}\n"
  },
  {
    "path": "ONNXModel.h",
    "content": "#pragma once\n\n#include <iostream>\n#include <string>\n#include <vector>\n#include <onnxruntime_cxx_api.h>\n#include <memory>\n#include <sstream>\n#include <algorithm>\n\n\n\ntemplate<typename T>\nstruct ONXTensor {\n\tstd::vector<T> Data;\n\tstd::vector<int64_t> Shape;\n\n\tONXTensor(Ort::Value& Tens)\n\t{\n\t\tShape = Tens.GetTensorTypeAndShapeInfo().GetShape();\n\n\t\t\n\t\tint64_t product = 1;\n\t\tfor (int64_t dim : Shape)\n\t\t\tproduct *= dim;\n\n\t\tData = std::vector<T>(Tens.GetTensorData<float>(), Tens.GetTensorData<float>() + product);\n\n\t}\n\tONXTensor(const ONXTensor& CpyT)\n\t{\n\t\tData = CpyT.Data;\n\t\tShape = CpyT.Shape;\n\t}\n\tONXTensor() {\n\t\n\n\t}\n};\n\n\n/*\nONNXModel: Base class for ONNX models, running on DirectML.\n*/\nclass ONNXModel\n{\nprivate:\n\tbool ModelLoaded;\n\tbool IsGPU;\n\tstd::wstring DeviceName;\n\n\tstd::unique_ptr<Ort::Env> Environment;\n\n\n\tstd::unique_ptr<Ort::Session> Sess;\n\tstd::unique_ptr<Ort::AllocatorWithDefaultOptions> Alloc;\n\t\n\t/*\n\t Sets up DirectML backend\n\t Takes in NON-CONST (direct operation on) reference to session options, and returns success\n\t If succeeds, use the session options to create the session\n\t*/\n\tbool SetUpDirectML(Ort::SessionOptions& SessionOptions);\n\n\tvoid DisableMemReuse(OrtSessionOptions* SessPtr);\n\n\tstd::pair<std::vector<const char*>, std::vector<const char*>> GetInputOutputNamesChar(const std::vector<std::string>& InputNames, const std::vector<std::string>& OutputNames);\n\npublic:\n\tONNXModel();\n\tinline ONNXModel(const std::wstring& InitModelPath) { Load(InitModelPath); };\n\n\tinline bool IsLoaded() const { return ModelLoaded; }\n\tinline bool IsGPUDevice() const { return IsGPU; }\n\tinline const std::wstring& GetDeviceName() const { return DeviceName; }\n\n    void _overrideDevices(bool is_gpu, std::wstring device_name);\n\n\n\t/*\n\tLoad model in DirectML mode.\n\tInputs:\n\n\tModelPath: Path of model \n\n\tReturns:\n\tSuccess (true), or failure (false)\n\t*/\n\tvirtual bool Load(const std::wstring& ModelPath, const std::string& EnvName = \"defaultenv\");\n\n\t/*\n\tForward pass through the model, for simple calling when all tensors are of one type.\n\n\tInputs:\n\n\t- Inputs: Tensor datas\n\t- InputShapes: Shapes of each tensor\n\t- InputNames: Names of the input tensors. Optional; pass an empty one and they will be auto-fetched\n\t- OutputNames: Names of the output tensors. Optional; pass an empty one and they will be auto-fetched\n\n\t*/\n\ttemplate<typename D>          // NOTE: Inputs should be a const reference, but vec_to_tensor requires a non-const one.\n\tstd::vector<Ort::Value> Forward(std::vector<std::vector<D>>& Inputs, const std::vector<std::vector<int64_t>>& InputShapes,\n\t\t\t\t\t\t\t\t\tstd::vector<std::string> InputNames, std::vector<std::string> OutputNames) \n\t{\n\t\t// Make input tensors\n\t\tstd::vector<Ort::Value> InputTensors;\n\t\tOrt::MemoryInfo mem_info = Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault);\n\t\tfor (size_t i = 0; i < Inputs.size(); i++) \n\t\t{\n\t\t\t// Ort::Value::Value(const Ort::Value &) is deleted, so we have to directly push back the output of the fun instead of using a variable.\n\t\t\tInputTensors.emplace_back(Ort::Value::CreateTensor<D>(mem_info, Inputs[i].data(), Inputs[i].size(), InputShapes[i].data(), InputShapes[i].size())\n\t\t\t);\n\t\t\t\n\t\t\n\t\t}\n\n\t\treturn Forward(InputTensors, InputNames, OutputNames);\n\t}\n\n\n\t/*\n\tForward pass through the model\n\n\tInputs:\n\n\t- InputTensors: Tensors.\n\t- InputNames: Names of the input tensors. Optional; pass an empty one and they will be auto-fetched\n\t- OutputNames: Names of the output tensors. Optional; pass an empty one and they will be auto-fetched\n\n\t*/\n\tstd::vector<Ort::Value> Forward(std::vector<Ort::Value>& InputTensors,\n\t\tstd::vector<std::string> InputNames = std::vector<std::string>{}, std::vector<std::string> OutputNames = std::vector<std::string>{})\n\t{\n\n\t\t// Autogen output names if we don't have them\n\t\tif (!OutputNames.size())\n\t\t\tOutputNames = GetOutputNames();\n\n\t\t// Ditto for input names\n\t\tif (!InputNames.size())\n\t\t\tInputNames = GetInputs().second;\n\n\n\t\t// Create input names\n\t\tauto InputOutNames = GetInputOutputNamesChar(InputNames, OutputNames);\n\t\tstd::vector<const char*> InputNamesChar = InputOutNames.first;\n\t\tstd::vector<const char*> OutputNamesChar = InputOutNames.second;\n\n\t\tif (!Sess)\n\t\t\tthrow std::runtime_error(\"Session is NULL!\");\n\n\t\tauto OutputTensors = Sess->Run(Ort::RunOptions{ nullptr }, InputOutNames.first.data(), InputTensors.data(),\n\t\t\tInputOutNames.first.size(), InputOutNames.second.data(), InputOutNames.second.size());\n\n\n\t\treturn OutputTensors;\n\t}\n\n\t// Get a reference to the unique ptr of the session.\n\tstd::unique_ptr<Ort::Session>& GetSession() { return Sess; }\n\n\tstd::vector<std::string> GetOutputNames();\n\tstd::vector<std::string> GetInputNames();\n\n\tstd::vector<ONNXTensorElementDataType> GetInputTypes();\n\t// Returns the input shapes and names\n\tstd::pair<std::vector<std::vector<int64_t>>, std::vector<std::string>> GetInputs();\n};\n\n"
  },
  {
    "path": "ONNXUtil.hpp",
    "content": "#pragma once\n#include <iostream>\n#include <string>\n#include <vector>\n#include <onnxruntime_cxx_api.h>\n#include <memory>\n#include <sstream>\n#include <algorithm>\n\n\n// Util functions for ONNX stuff\nnamespace ONNXUtil {\n\t// pretty prints a shape dimension vector\n\tstd::string print_shape(const std::vector<std::int64_t>& v) {\n\t\tstd::stringstream ss(\"\");\n\t\tfor (std::size_t i = 0; i < v.size() - 1; i++) ss << v[i] << \"x\";\n\t\tss << v[v.size() - 1];\n\t\treturn ss.str();\n\t}\n\n\ttemplate <typename T>\n\tOrt::Value vec_to_tensor(std::vector<T>& data, const std::vector<std::int64_t>& shape) {\n\n\t\tOrt::MemoryInfo mem_info =\n\t\t\tOrt::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault);\n\t\tauto tensor = Ort::Value::CreateTensor<T>(mem_info, data.data(), data.size(), shape.data(), shape.size());\n\t\treturn tensor;\n\t}\n\n\tstd::vector<const char*> StringVectoCharVec(const std::vector<std::string>& inputStrings) {\n\t\tstd::vector<const char*> outputChars(inputStrings.size(), nullptr);\n\t\tstd::transform(std::begin(inputStrings), std::end(inputStrings), std::begin(outputChars),\n\t\t\t[](const std::string& str) { return str.c_str(); });\n\n\t\treturn outputChars;\n\t}\n\n}\n"
  },
  {
    "path": "README.md",
    "content": "# TensorVox\n\n\nTensorVox is an application designed to enable user-friendly and lightweight neural speech synthesis in the desktop, aimed at increasing accessibility to such technology. \n\nBeing able to load a variety of AI TTS models, it is written in pure C++/Qt, using the ONNX Runtime, and supporting TensorFlow and LibTorch as legacy backends.\n\n![Interface with Tac2 model loaded](https://i.imgur.com/wtPzzNh.png)\n\n\n### Try it out\n**System requirements:** Windows 10 64-bit and a CPU that supports the AVX instruction set (pretty much anything made after 2010). As for GPU, to use it you need one that supports DirectX 12 (only with ONNX models)\n\n\n[Detailed guide in Google Docs](https://docs.google.com/document/d/1OS1kfb19bvpPPkF71Vbak_b735mi7epjUanIfPG671M/edit?usp=sharing)\n\nGrab a copy from the releases, extract the .zip and check [the Google Drive folder](https://drive.google.com/drive/folders/1atUyxBbstKZpMqQEZMdNmRF2AKrlahKy?usp=sharing) for models and installation instructions\n\nIf you're interested in using your own model, first you need to train then export it. \n\n\n## Supported architectures\n\nTensorVox supports several models by various authors\n\n### Current-gen models\nThese are models under active development by their authors and support by me. These use the ONNX backend, with GPU support under DirectML\n - **ZDisket/VITS Evolution:** This is my fully upgraded version of VITS\n - **Supertone/[Supertonic](https://github.com/supertone-inc/supertonic) 2:** Supertone's small, fast model made for fast on-device inference\n\nMore models are to be added soon.\n### Legacy models\nCompatibility for these models is kept but not maintained, as they're obsolete and have been superseded by newer ones. These run under the LibTorch and Tensorflow backends, supporting only CPU.\n - **jaywalnut310/VITS:** VITS, which is a fully E2E model. (Stressed IPA as phonemes) Export notebook: [<img src=\"https://colab.research.google.com/assets/colab-badge.svg\">](https://colab.research.google.com/drive/1BSGE5DQYweXBWrwPOmb6CRPUU8H5mBvb?usp=sharing)\n - **TensorFlowTTS**: FastSpeech2, Tacotron2, both char and phoneme based and Multi-Band MelGAN. Here's a Colab notebook demonstrating how to export the LJSpeech pretrained, char-based Tacotron2 model: [<img src=\"https://colab.research.google.com/assets/colab-badge.svg\">](https://colab.research.google.com/drive/1KLqZ1rkD4Enw7zpTgXGL6if7e5s0UeWa?usp=sharing) \n - **Coqui-TTS:** Tacotron2 (phoneme-based IPA) and Multi-Band MelGAN, after converting from PyTorch to Tensorflow. Here's a notebook showing how to export the LJSpeech DDC model: [<img src=\"https://colab.research.google.com/assets/colab-badge.svg\">](https://colab.research.google.com/drive/15CdGEAu_-KezV1XxwzVfQiFSm0tveBkC?usp=sharing)\n\nMore support of modern TTS models is being actively worked on!\n\nThese examples should provide you with enough guidance to understand what is needed. If you're looking to train a model specifically for this purpose, then stay tuned... \n*Or if you’d rather skip the training and export work, you can also get a TensorVox-ready model directly from me. (see contact details at the bottom of this)*\n\nAs for languages, out-of-the-box support is provided for English, German and Spanish (only TensorFlowTTS); that is, you won't have to do anything. You can add languages without modifying code, as long as the phoneme set are IPA (stressed or nonstressed), ARPA, or GlobalPhone, (open an issue and I'll explain it to you)\n\n## Backends\nTensorVox currently supports multiple inference backends.\n\nLibTorch (TorchScript) and TensorFlow backends are maintained for compatibility with older models and projects created before ONNX export was refined enough.\n\nNew development and active support are focused on ONNX Runtime, with DirectML used for GPU acceleration on Windows. This backend provides the best portability, long-term stability, and hardware coverage.\n\n\n## Build instructions\nCurrently, only Windows 10 x64 (although I've heard reports of it running on 8.1) is supported.\n\n**Requirements:**\n 1. Qt Creator\n 2. MSVC 2017 (v141) compiler\n\n**Primed build (with all provided libraries):**\n\n 1. Download [precompiled binary dependencies and includes](https://drive.google.com/file/d/1N6IxSpsgemS94z_v82toXhiNs2tLXkz6/view?usp=sharing)\n 2. Unzip it so that the `deps` folder is in the same place as the .pro and main source files.\n 3. Open the project with Qt Creator, add your compiler and compile\n\nNote that to try your shiny new executable you'll need to download a release of program as described above and replace the executable in that release with your new one, so you have all the DLLs in place.\n\nTODO: Add instructions for compile from scratch.\n\n## Externals (and thanks)\n\n - **ONNX Runtime** :https://onnxruntime.ai/\n\n - **Tensorflow C API**: [https://www.tensorflow.org/install/lang_c](https://www.tensorflow.org/install/lang_c)\n - **CppFlow** (TF C API -> C++ wrapper): [https://github.com/serizba/cppflow](https://github.com/serizba/cppflow) \n - **AudioFile** (for WAV export): [https://github.com/adamstark/AudioFile](https://github.com/adamstark/AudioFile)\n - **Frameless Dark Style Window**: https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle\n - **JSON for modern C++**: https://github.com/nlohmann/json\n - **r8brain-free-src** (Resampling): https://github.com/avaneev/r8brain-free-src\n - **rnnoise** (CMake version, denoising output): https://github.com/almogh52/rnnoise-cmake\n - **Logitech LED Illumination SDK** (Mouse RGB integration): https://www.logitechg.com/en-us/innovation/developer-lab.html\n - **QCustomPlot** : https://www.qcustomplot.com/index.php/introduction\n - **libnumbertext** : https://github.com/Numbertext/libnumbertext\n\n\n## Contact\nYou can open an issue here or join the [Discord server](https://discord.gg/B9fGwXgz) and discuss/ask anything there\n\nCustom model training, fine-tuning, and compatible exports are available on request (not free). Use email or DM me on Xitter\n\nFollow me on X (formerly Twitter): [ZD1908 (@ZDi____) / X](https://x.com/ZDi____)\nFor any formal inquiries, send to this email: nika109021@gmail.com\n\n## Note about licensing\n\nThis program itself is MIT licensed, but for the models you use, their license terms apply. For example, if you're in Vietnam and using TensorFlowTTS models, you'll have to check [here](https://github.com/TensorSpeech/TensorFlowTTS#license) for some details\n"
  },
  {
    "path": "Supertonic.cpp",
    "content": "#include \"Supertonic.h\"\n#include \"ext/json.hpp\"\n\n#include <algorithm>\n#include <fstream>\n#include <random>\n#include <filesystem>\n\nusing json = nlohmann::json;\n\nnamespace {\nstd::wstring ToWide(const std::string& value)\n{\n    return std::wstring(value.begin(), value.end());\n}\n\ntemplate<typename T>\nOrt::Value CreateTensor(Ort::MemoryInfo& mem_info, std::vector<T>& data, const std::vector<int64_t>& shape)\n{\n    return Ort::Value::CreateTensor<T>(mem_info, data.data(), data.size(), shape.data(), shape.size());\n}\n}\n\nbool Supertonic::Initialize(const std::string& onnxDir, ETTSRepo::Enum InTTSRepo)\n{\n    CurrentRepo = InTTSRepo;\n    config_loaded_ = LoadConfig(onnxDir);\n    models_loaded_ = LoadModels(onnxDir);\n\n    std::filesystem::path p(onnxDir);\n    BasePath = p.parent_path().generic_string(); // generic_string always returns in forward slash style.\n\n    CurrentLoadedVoice = \"\";\n\n    return config_loaded_ && models_loaded_;\n}\n\nbool Supertonic::LoadConfig(const std::string& onnxDir)\n{\n    std::ifstream file(onnxDir + \"/tts.json\");\n    if (!file.is_open()) {\n        return false;\n    }\n\n    json cfg;\n    file >> cfg;\n\n    sample_rate_ = cfg[\"ae\"][\"sample_rate\"].get<int>();\n    base_chunk_size_ = cfg[\"ae\"][\"base_chunk_size\"].get<int>();\n    chunk_compress_factor_ = cfg[\"ttl\"][\"chunk_compress_factor\"].get<int>();\n    latent_dim_ = cfg[\"ttl\"][\"latent_dim\"].get<int>();\n\n\n\n\n\n    return true;\n}\n\nbool Supertonic::LoadModels(const std::string& onnxDir)\n{\n    duration_predictor_ = std::make_unique<ONNXModel>();\n    text_encoder_ = std::make_unique<ONNXModel>();\n    vector_estimator_ = std::make_unique<ONNXModel>();\n\n    const std::wstring dp_path = ToWide(onnxDir + \"/duration_predictor.onnx\");\n    const std::wstring text_enc_path = ToWide(onnxDir + \"/text_encoder.onnx\");\n    const std::wstring vector_est_path = ToWide(onnxDir + \"/vector_estimator.onnx\");\n\n    const bool dp_loaded = duration_predictor_->Load(dp_path, \"supertonic_dp\");\n    const bool text_loaded = text_encoder_->Load(text_enc_path, \"supertonic_text_enc\");\n    const bool vector_loaded = vector_estimator_->Load(vector_est_path, \"supertonic_vector_est\");\n\n    _overrideDevices(duration_predictor_->IsGPUDevice(), duration_predictor_->GetDeviceName());\n\n    return dp_loaded && text_loaded && vector_loaded;\n}\n\nbool Supertonic::LoadStyle(const std::string& stylePath)\n{\n    std::ifstream file(stylePath);\n    if (!file.is_open()) {\n        return false;\n    }\n\n    json style_json;\n    file >> style_json;\n\n    style_.ttl_shape = style_json[\"style_ttl\"][\"dims\"].get<std::vector<int64_t>>();\n    style_.dp_shape = style_json[\"style_dp\"][\"dims\"].get<std::vector<int64_t>>();\n\n    auto ttl_data_nested = style_json[\"style_ttl\"][\"data\"].get<std::vector<std::vector<std::vector<float>>>>();\n    style_.ttl_data.clear();\n    for (const auto& batch : ttl_data_nested) {\n        for (const auto& row : batch) {\n            style_.ttl_data.insert(style_.ttl_data.end(), row.begin(), row.end());\n        }\n    }\n\n    auto dp_data_nested = style_json[\"style_dp\"][\"data\"].get<std::vector<std::vector<std::vector<float>>>>();\n    style_.dp_data.clear();\n    for (const auto& batch : dp_data_nested) {\n        for (const auto& row : batch) {\n            style_.dp_data.insert(style_.dp_data.end(), row.begin(), row.end());\n        }\n    }\n\n    style_.loaded = true;\n    return true;\n}\n\nTFTensor<float> Supertonic::DoInference(const std::vector<int32_t>& InputIDs, const std::vector<float>& ArgsFloat,\n                                       const std::vector<int32_t> ArgsInt, int32_t SpeakerID, int32_t EmotionID)\n{\n    (void)EmotionID;\n    EnsureSpeaker(SpeakerID);\n\n    TFTensor<float> result;\n    if (InputIDs.empty()) {\n        return result;\n    }\n    if (!models_loaded_ || !config_loaded_ || !style_.loaded) {\n        throw std::runtime_error(\"Supertonic not initialized or style not loaded\");\n    }\n\n    int total_step = ArgsInt.empty() ? 6 : ArgsInt[0];\n    float speed = ArgsFloat.empty() ? 1.05f : ArgsFloat[0];\n    if (total_step < 1) {\n        total_step = 1;\n    }\n    if (speed <= 0.0f) {\n        speed = 1.0f;\n    }\n\n    std::vector<int64_t> text_ids;\n    text_ids.reserve(InputIDs.size());\n    for (auto id : InputIDs) {\n        text_ids.push_back(static_cast<int64_t>(id));\n    }\n\n    const int64_t batch = 1;\n    const int64_t seq_len = static_cast<int64_t>(text_ids.size());\n\n    std::vector<int64_t> text_shape{batch, seq_len};\n    std::vector<int64_t> text_mask_shape{batch, 1, seq_len};\n    std::vector<float> text_mask(seq_len, 1.0f);\n\n    Ort::MemoryInfo mem_info = Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault);\n\n    std::vector<Ort::Value> dp_inputs;\n    dp_inputs.emplace_back(CreateTensor(mem_info, text_ids, text_shape));\n    dp_inputs.emplace_back(CreateTensor(mem_info, style_.dp_data, style_.dp_shape));\n    dp_inputs.emplace_back(CreateTensor(mem_info, text_mask, text_mask_shape));\n\n    auto dp_outputs = duration_predictor_->Forward(dp_inputs, {\"text_ids\", \"style_dp\", \"text_mask\"}, {\"duration\"});\n    if (dp_outputs.empty() || !dp_outputs[0].IsTensor()) {\n        return result;\n    }\n\n    auto duration_info = dp_outputs[0].GetTensorTypeAndShapeInfo();\n    const float* duration_ptr = dp_outputs[0].GetTensorData<float>();\n    float duration = duration_info.GetElementCount() ? duration_ptr[0] : 0.0f;\n    duration /= speed;\n\n    std::vector<Ort::Value> text_inputs;\n    text_inputs.emplace_back(CreateTensor(mem_info, text_ids, text_shape));\n    text_inputs.emplace_back(CreateTensor(mem_info, style_.ttl_data, style_.ttl_shape));\n    text_inputs.emplace_back(CreateTensor(mem_info, text_mask, text_mask_shape));\n\n    auto text_outputs = text_encoder_->Forward(text_inputs, {\"text_ids\", \"style_ttl\", \"text_mask\"}, {\"text_emb\"});\n    if (text_outputs.empty() || !text_outputs[0].IsTensor()) {\n        return result;\n    }\n\n    auto text_emb_info = text_outputs[0].GetTensorTypeAndShapeInfo();\n    std::vector<int64_t> text_emb_shape = text_emb_info.GetShape();\n    size_t text_emb_count = text_emb_info.GetElementCount();\n    const float* text_emb_ptr = text_outputs[0].GetTensorData<float>();\n    std::vector<float> text_emb_vec(text_emb_ptr, text_emb_ptr + text_emb_count);\n\n    int64_t wav_len = static_cast<int64_t>(duration * sample_rate_);\n    wav_len = std::max<int64_t>(wav_len, 1);\n    int64_t chunk_size = static_cast<int64_t>(base_chunk_size_) * chunk_compress_factor_;\n    int64_t latent_len = std::max<int64_t>(1, (wav_len + chunk_size - 1) / chunk_size);\n    int64_t latent_dim = static_cast<int64_t>(latent_dim_) * chunk_compress_factor_;\n\n    std::vector<int64_t> latent_shape{batch, latent_dim, latent_len};\n    std::vector<int64_t> latent_mask_shape{batch, 1, latent_len};\n    std::vector<float> latent_mask(latent_len, 1.0f);\n\n    std::vector<float> xt(static_cast<std::size_t>(latent_dim * latent_len));\n    std::random_device rd;\n    std::mt19937 gen(rd());\n    std::normal_distribution<float> dist(0.0f, 1.0f);\n\n    for (int64_t d = 0; d < latent_dim; ++d) {\n        for (int64_t t = 0; t < latent_len; ++t) {\n            xt[static_cast<std::size_t>(d * latent_len + t)] = dist(gen) * latent_mask[static_cast<std::size_t>(t)];\n        }\n    }\n\n    std::vector<int64_t> scalar_shape{batch};\n    std::vector<float> total_step_vec(batch, static_cast<float>(total_step));\n\n    for (int step = 0; step < total_step; ++step) {\n        std::vector<float> current_step_vec(batch, static_cast<float>(step));\n        std::vector<Ort::Value> vector_inputs;\n        vector_inputs.emplace_back(CreateTensor(mem_info, xt, latent_shape));\n        vector_inputs.emplace_back(CreateTensor(mem_info, text_emb_vec, text_emb_shape));\n        vector_inputs.emplace_back(CreateTensor(mem_info, style_.ttl_data, style_.ttl_shape));\n        vector_inputs.emplace_back(CreateTensor(mem_info, text_mask, text_mask_shape));\n        vector_inputs.emplace_back(CreateTensor(mem_info, latent_mask, latent_mask_shape));\n        vector_inputs.emplace_back(CreateTensor(mem_info, total_step_vec, scalar_shape));\n        vector_inputs.emplace_back(CreateTensor(mem_info, current_step_vec, scalar_shape));\n\n        auto vector_outputs = vector_estimator_->Forward(\n            vector_inputs,\n            {\"noisy_latent\", \"text_emb\", \"style_ttl\", \"text_mask\", \"latent_mask\", \"total_step\", \"current_step\"},\n            {\"denoised_latent\"}\n        );\n\n        if (vector_outputs.empty() || !vector_outputs[0].IsTensor()) {\n            return result;\n        }\n\n        auto denoised_info = vector_outputs[0].GetTensorTypeAndShapeInfo();\n        const float* denoised_ptr = vector_outputs[0].GetTensorData<float>();\n        size_t denoised_count = denoised_info.GetElementCount();\n        xt.assign(denoised_ptr, denoised_ptr + denoised_count);\n    }\n\n    result.Data = xt;\n    result.Shape = latent_shape;\n    result.TotalSize = xt.size();\n    return result;\n}\n\nvoid Supertonic::EnsureSpeaker(int32_t SpeakerID)\n{\n    std::string RequestedSpeakerJSON = std::to_string(SpeakerID) + \".json\";\n\n    if (RequestedSpeakerJSON != CurrentLoadedVoice){\n        LoadStyle(BasePath + \"/styles/\" + RequestedSpeakerJSON);\n        CurrentLoadedVoice = RequestedSpeakerJSON;\n    }\n\n\n}\n"
  },
  {
    "path": "Supertonic.h",
    "content": "#pragma once\n\n#include \"ONNXModel.h\"\n#include \"melgen.h\"\n#include <cstdint>\n#include <memory>\n#include <string>\n#include <vector>\n\nclass Supertonic : public ONNXModel, public MelGen\n{\npublic:\n    bool Initialize(const std::string& onnxDir, ETTSRepo::Enum InTTSRepo) override;\n    bool LoadStyle(const std::string& stylePath);\n    TFTensor<float> DoInference(const std::vector<int32_t>& InputIDs, const std::vector<float>& ArgsFloat,\n                                const std::vector<int32_t> ArgsInt, int32_t SpeakerID = 0, int32_t EmotionID = -1) override;\n\n    int GetSampleRate() const { return sample_rate_; }\n\nprivate:\n    struct StyleData {\n        std::vector<float> ttl_data;\n        std::vector<int64_t> ttl_shape;\n        std::vector<float> dp_data;\n        std::vector<int64_t> dp_shape;\n        bool loaded = false;\n    };\n\n    std::string BasePath;\n    std::string CurrentLoadedVoice;\n\n    void EnsureSpeaker(int32_t SpeakerID);\n\n    bool LoadConfig(const std::string& onnxDir);\n    bool LoadModels(const std::string& onnxDir);\n\n    std::unique_ptr<ONNXModel> duration_predictor_;\n    std::unique_ptr<ONNXModel> text_encoder_;\n    std::unique_ptr<ONNXModel> vector_estimator_;\n    bool models_loaded_ = false;\n    bool config_loaded_ = false;\n\n    int sample_rate_ = 0;\n    int base_chunk_size_ = 0;\n    int chunk_compress_factor_ = 0;\n    int latent_dim_ = 0;\n\n    StyleData style_;\n};\n"
  },
  {
    "path": "SupertonicTextProcessor.cpp",
    "content": "#include \"SupertonicTextProcessor.h\"\n#include \"ext/json.hpp\"\n\n#include <algorithm>\n#include <cctype>\n#include <fstream>\n#include <stdexcept>\n#include <unordered_map>\n\nusing json = nlohmann::json;\n\n// Available languages for multilingual TTS\nconst std::vector<std::string> AVAILABLE_LANGS = {\"en\", \"ko\", \"es\", \"pt\", \"fr\"};\n\nnamespace {\nbool IsAsciiSpace(char32_t cp)\n{\n    if (cp > 0x7F) {\n        return false;\n    }\n    return std::isspace(static_cast<unsigned char>(cp)) != 0;\n}\n\nbool IsEmoji(char32_t cp)\n{\n    return cp >= 0x1F000 && cp <= 0x1FFFF;\n}\n\nbool IsTerminalPunct(char32_t cp)\n{\n    switch (cp) {\n    case U'.':\n    case U'!':\n    case U'?':\n    case U';':\n    case U':':\n    case U',':\n    case U'\\'':\n    case U'\"':\n    case U')':\n    case U']':\n    case U'}':\n    case U'>':\n    case U'\\u2026': // …\n    case U'\\u3002': // 。\n    case U'\\u300D': // 」\n    case U'\\u300F': // 』\n    case U'\\u3011': // 】\n    case U'\\u3009': // 〉\n    case U'\\u300B': // 》\n    case U'\\u203A': // ›\n    case U'\\u00BB': // »\n    case U'\\u201C': // “\n    case U'\\u201D': // ”\n    case U'\\u2018': // ‘\n    case U'\\u2019': // ’\n        return true;\n    default:\n        return false;\n    }\n}\n\nvoid ReplaceAll(std::u32string& text, const std::u32string& from, const std::u32string& to)\n{\n    if (from.empty()) {\n        return;\n    }\n    size_t pos = 0;\n    while ((pos = text.find(from, pos)) != std::u32string::npos) {\n        text.replace(pos, from.size(), to);\n        pos += to.size();\n    }\n}\n\nstd::u32string TrimSpaces(const std::u32string& text)\n{\n    size_t start = 0;\n    while (start < text.size() && text[start] == U' ') {\n        ++start;\n    }\n    size_t end = text.size();\n    while (end > start && text[end - 1] == U' ') {\n        --end;\n    }\n    return text.substr(start, end - start);\n}\n\nstd::u32string NormalizeSpaces(const std::u32string& text)\n{\n    std::u32string result;\n    result.reserve(text.size());\n    bool prev_space = false;\n    for (char32_t cp : text) {\n        if (IsAsciiSpace(cp)) {\n            if (!prev_space) {\n                result.push_back(U' ');\n                prev_space = true;\n            }\n            continue;\n        }\n        prev_space = false;\n        result.push_back(cp);\n    }\n    return result;\n}\n\nstd::u32string AsciiToU32(const std::string& text)\n{\n    std::u32string result;\n    result.reserve(text.size());\n    for (unsigned char ch : text) {\n        result.push_back(static_cast<char32_t>(ch));\n    }\n    return result;\n}\n}\n\nSupertonicTextProcessor::SupertonicTextProcessor(const std::string& unicode_indexer_json_path)\n{\n    std::ifstream file(unicode_indexer_json_path);\n    if (!file.is_open()) {\n        throw std::runtime_error(\"Failed to open file: \" + unicode_indexer_json_path);\n    }\n    json j;\n    file >> j;\n    indexer_ = j.get<std::vector<int64_t>>();\n}\n\nSupertonicTextProcessor::Result SupertonicTextProcessor::Process(\n    const std::vector<std::string>& text_list,\n    const std::vector<std::string>& lang_list\n    )\n{\n    std::vector<std::u32string> processed_texts;\n    processed_texts.reserve(text_list.size());\n    for (size_t i = 0; i < text_list.size(); ++i) {\n        processed_texts.push_back(preprocessText(text_list[i], lang_list[i]));\n    }\n    return buildResult(processed_texts);\n}\n\nSupertonicTextProcessor::Result SupertonicTextProcessor::Process(\n    const std::vector<std::u32string>& text_list,\n    const std::vector<std::string>& lang_list\n    )\n{\n    std::vector<std::u32string> processed_texts;\n    processed_texts.reserve(text_list.size());\n    for (size_t i = 0; i < text_list.size(); ++i) {\n        processed_texts.push_back(preprocessText(text_list[i], lang_list[i]));\n    }\n    return buildResult(processed_texts);\n}\n\nstd::u32string SupertonicTextProcessor::preprocessText(const std::string& text, const std::string& lang)\n{\n    return preprocessText(ansiToU32(text), lang);\n}\n\nstd::u32string SupertonicTextProcessor::preprocessText(const std::u32string& text, const std::string& lang)\n{\n    std::u32string result = normalizeText(text);\n\n    if (!result.empty()) {\n        char32_t last_char = result.back();\n        if (!IsTerminalPunct(last_char)) {\n            result.push_back(U'.');\n        }\n    }\n\n    bool valid_lang = false;\n    for (const auto& available_lang : AVAILABLE_LANGS) {\n        if (lang == available_lang) {\n            valid_lang = true;\n            break;\n        }\n    }\n    if (!valid_lang) {\n        throw std::runtime_error(\"Invalid language: \" + lang + \". Available: en, ko, es, pt, fr\");\n    }\n\n    std::u32string tag_lang = AsciiToU32(lang);\n    std::u32string result_with_tags;\n    result_with_tags.reserve(result.size() + tag_lang.size() * 2 + 5);\n    result_with_tags += U\"<\";\n    result_with_tags += tag_lang;\n    result_with_tags += U\">\";\n    result_with_tags += result;\n    result_with_tags += U\"</\";\n    result_with_tags += tag_lang;\n    result_with_tags += U\">\";\n    return result_with_tags;\n}\n\nstd::u32string SupertonicTextProcessor::normalizeText(const std::u32string& text)\n{\n    std::u32string result;\n    result.reserve(text.size());\n\n    for (char32_t cp : text) {\n        switch (cp) {\n        case U'\\u2013': // –\n        case U'\\u2011': // ‑\n        case U'\\u2014': // —\n            cp = U'-';\n            break;\n        case U'_':\n        case U'[':\n        case U']':\n        case U'|':\n        case U'/':\n        case U'#':\n        case U'\\u2192': // →\n        case U'\\u2190': // ←\n            cp = U' ';\n            break;\n        case U'\\u201C': // “\n        case U'\\u201D': // ”\n            cp = U'\"';\n            break;\n        case U'\\u2018': // ‘\n        case U'\\u2019': // ’\n        case U'\\u00B4': // ´\n        case U'`':\n            cp = U'\\'';\n            break;\n        default:\n            break;\n        }\n        result.push_back(cp);\n    }\n\n    std::u32string emoji_filtered;\n    emoji_filtered.reserve(result.size());\n    for (char32_t cp : result) {\n        if (!IsEmoji(cp)) {\n            emoji_filtered.push_back(cp);\n        }\n    }\n\n    std::u32string symbol_filtered;\n    symbol_filtered.reserve(emoji_filtered.size());\n    for (char32_t cp : emoji_filtered) {\n        if (cp == U'\\u2665' || cp == U'\\u2606' || cp == U'\\u2661' || cp == U'\\u00A9' || cp == U'\\\\') {\n            continue;\n        }\n        symbol_filtered.push_back(cp);\n    }\n\n    ReplaceAll(symbol_filtered, U\"@\", U\" at \");\n    ReplaceAll(symbol_filtered, U\"e.g.,\", U\"for example, \");\n    ReplaceAll(symbol_filtered, U\"i.e.,\", U\"that is, \");\n\n    ReplaceAll(symbol_filtered, U\" ,\", U\",\");\n    ReplaceAll(symbol_filtered, U\" .\", U\".\");\n    ReplaceAll(symbol_filtered, U\" !\", U\"!\");\n    ReplaceAll(symbol_filtered, U\" ?\", U\"?\");\n    ReplaceAll(symbol_filtered, U\" ;\", U\";\");\n    ReplaceAll(symbol_filtered, U\" :\", U\":\");\n    ReplaceAll(symbol_filtered, U\" '\", U\"'\");\n\n    while (symbol_filtered.find(U\"\\\"\\\"\") != std::u32string::npos) {\n        ReplaceAll(symbol_filtered, U\"\\\"\\\"\", U\"\\\"\");\n    }\n    while (symbol_filtered.find(U\"''\") != std::u32string::npos) {\n        ReplaceAll(symbol_filtered, U\"''\", U\"'\");\n    }\n    while (symbol_filtered.find(U\"``\") != std::u32string::npos) {\n        ReplaceAll(symbol_filtered, U\"``\", U\"`\");\n    }\n\n    std::u32string normalized = NormalizeSpaces(symbol_filtered);\n    normalized = TrimSpaces(normalized);\n    return normalized;\n}\n\nstd::u32string SupertonicTextProcessor::ansiToU32(const std::string& text)\n{\n    std::u32string result;\n    size_t i = 0;\n    while (i < text.size()) {\n        uint32_t codepoint = 0;\n        unsigned char c = static_cast<unsigned char>(text[i]);\n        codepoint = c;\n        i += 1;\n        result.push_back(static_cast<char32_t>(codepoint));\n    }\n    return result;\n}\n\n// Hangul syllable decomposition constants (Unicode Standard Annex #15)\nstatic const uint32_t HANGUL_SBASE = 0xAC00;\nstatic const uint32_t HANGUL_LBASE = 0x1100;\nstatic const uint32_t HANGUL_VBASE = 0x1161;\nstatic const uint32_t HANGUL_TBASE = 0x11A7;\nstatic const int HANGUL_LCOUNT = 19;\nstatic const int HANGUL_VCOUNT = 21;\nstatic const int HANGUL_TCOUNT = 28;\nstatic const int HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT;\nstatic const int HANGUL_SCOUNT = HANGUL_LCOUNT * HANGUL_NCOUNT;\n\n// Latin character NFKD decompositions for Spanish, Portuguese, French\nstatic const std::unordered_map<uint32_t, std::vector<char32_t>> LATIN_DECOMPOSITIONS = {\n    {0x00C1, {0x0041, 0x0301}},\n    {0x00C9, {0x0045, 0x0301}},\n    {0x00CD, {0x0049, 0x0301}},\n    {0x00D3, {0x004F, 0x0301}},\n    {0x00DA, {0x0055, 0x0301}},\n    {0x00E1, {0x0061, 0x0301}},\n    {0x00E9, {0x0065, 0x0301}},\n    {0x00ED, {0x0069, 0x0301}},\n    {0x00F3, {0x006F, 0x0301}},\n    {0x00FA, {0x0075, 0x0301}},\n    {0x00C0, {0x0041, 0x0300}},\n    {0x00C8, {0x0045, 0x0300}},\n    {0x00CC, {0x0049, 0x0300}},\n    {0x00D2, {0x004F, 0x0300}},\n    {0x00D9, {0x0055, 0x0300}},\n    {0x00E0, {0x0061, 0x0300}},\n    {0x00E8, {0x0065, 0x0300}},\n    {0x00EC, {0x0069, 0x0300}},\n    {0x00F2, {0x006F, 0x0300}},\n    {0x00F9, {0x0075, 0x0300}},\n    {0x00C2, {0x0041, 0x0302}},\n    {0x00CA, {0x0045, 0x0302}},\n    {0x00CE, {0x0049, 0x0302}},\n    {0x00D4, {0x004F, 0x0302}},\n    {0x00DB, {0x0055, 0x0302}},\n    {0x00E2, {0x0061, 0x0302}},\n    {0x00EA, {0x0065, 0x0302}},\n    {0x00EE, {0x0069, 0x0302}},\n    {0x00F4, {0x006F, 0x0302}},\n    {0x00FB, {0x0075, 0x0302}},\n    {0x00C3, {0x0041, 0x0303}},\n    {0x00D1, {0x004E, 0x0303}},\n    {0x00D5, {0x004F, 0x0303}},\n    {0x00E3, {0x0061, 0x0303}},\n    {0x00F1, {0x006E, 0x0303}},\n    {0x00F5, {0x006F, 0x0303}},\n    {0x00C4, {0x0041, 0x0308}},\n    {0x00CB, {0x0045, 0x0308}},\n    {0x00CF, {0x0049, 0x0308}},\n    {0x00D6, {0x004F, 0x0308}},\n    {0x00DC, {0x0055, 0x0308}},\n    {0x00E4, {0x0061, 0x0308}},\n    {0x00EB, {0x0065, 0x0308}},\n    {0x00EF, {0x0069, 0x0308}},\n    {0x00F6, {0x006F, 0x0308}},\n    {0x00FC, {0x0075, 0x0308}},\n    {0x00C7, {0x0043, 0x0327}},\n    {0x00E7, {0x0063, 0x0327}},\n    };\n\nstatic void DecomposeCharacter(char32_t codepoint, std::u32string& output)\n{\n    if (codepoint >= HANGUL_SBASE && codepoint < HANGUL_SBASE + HANGUL_SCOUNT) {\n        uint32_t sIndex = static_cast<uint32_t>(codepoint) - HANGUL_SBASE;\n        uint32_t lIndex = sIndex / HANGUL_NCOUNT;\n        uint32_t vIndex = (sIndex % HANGUL_NCOUNT) / HANGUL_TCOUNT;\n        uint32_t tIndex = sIndex % HANGUL_TCOUNT;\n\n        output.push_back(static_cast<char32_t>(HANGUL_LBASE + lIndex));\n        output.push_back(static_cast<char32_t>(HANGUL_VBASE + vIndex));\n        if (tIndex > 0) {\n            output.push_back(static_cast<char32_t>(HANGUL_TBASE + tIndex));\n        }\n        return;\n    }\n\n    auto it = LATIN_DECOMPOSITIONS.find(static_cast<uint32_t>(codepoint));\n    if (it != LATIN_DECOMPOSITIONS.end()) {\n        for (char32_t cp : it->second) {\n            output.push_back(cp);\n        }\n        return;\n    }\n\n    output.push_back(codepoint);\n}\n\nstd::vector<int64_t> SupertonicTextProcessor::codepointsToIds(const std::u32string& text)\n{\n    std::vector<int64_t> ids;\n    ids.reserve(text.size());\n\n    std::u32string decomposed;\n    decomposed.reserve(text.size());\n    for (char32_t cp : text) {\n        DecomposeCharacter(cp, decomposed);\n    }\n\n    for (char32_t cp : decomposed) {\n        if (cp < indexer_.size()) {\n            int64_t id = indexer_[static_cast<size_t>(cp)];\n            if (id >= 0) {\n                ids.push_back(id);\n            }\n        }\n    }\n\n    return ids;\n}\n\nstd::vector<std::vector<std::vector<float>>> SupertonicTextProcessor::getTextMask(\n    const std::vector<int64_t>& text_ids_lengths\n    )\n{\n    int64_t max_len = 0;\n    for (auto len : text_ids_lengths) {\n        if (len > max_len) {\n            max_len = len;\n        }\n    }\n\n    std::vector<std::vector<std::vector<float>>> mask;\n    mask.reserve(text_ids_lengths.size());\n    for (auto len : text_ids_lengths) {\n        std::vector<std::vector<float>> batch_mask(1);\n        batch_mask[0].resize(static_cast<size_t>(max_len));\n        for (int64_t i = 0; i < max_len; ++i) {\n            batch_mask[0][static_cast<size_t>(i)] = (i < len) ? 1.0f : 0.0f;\n        }\n        mask.push_back(std::move(batch_mask));\n    }\n    return mask;\n}\n\nSupertonicTextProcessor::Result SupertonicTextProcessor::buildResult(\n    const std::vector<std::u32string>& text_list\n    )\n{\n    Result result;\n    std::vector<std::vector<int64_t>> all_ids;\n    all_ids.reserve(text_list.size());\n    result.lengths.reserve(text_list.size());\n\n    for (size_t i = 0; i < text_list.size(); ++i) {\n        std::vector<int64_t> ids = codepointsToIds(text_list[i]);\n        result.lengths.push_back(static_cast<int64_t>(ids.size()));\n        all_ids.push_back(std::move(ids));\n    }\n\n    int64_t max_len = 0;\n    for (auto len : result.lengths) {\n        if (len > max_len) {\n            max_len = len;\n        }\n    }\n\n    result.text_ids.resize(text_list.size());\n    for (size_t i = 0; i < all_ids.size(); ++i) {\n        result.text_ids[i].assign(static_cast<size_t>(max_len), 0);\n        for (size_t j = 0; j < all_ids[i].size(); ++j) {\n            result.text_ids[i][j] = all_ids[i][j];\n        }\n    }\n\n    result.text_mask = getTextMask(result.lengths);\n    return result;\n}\n"
  },
  {
    "path": "SupertonicTextProcessor.h",
    "content": "#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n\n// Available languages for multilingual TTS\nextern const std::vector<std::string> AVAILABLE_LANGS;\n\nclass SupertonicTextProcessor {\npublic:\n    explicit SupertonicTextProcessor(const std::string& unicode_indexer_json_path);\n\n    struct Result {\n        std::vector<std::vector<int64_t>> text_ids;\n        std::vector<std::vector<std::vector<float>>> text_mask;\n        std::vector<int64_t> lengths;\n    };\n\n    Result Process(\n        const std::vector<std::string>& text_list,\n        const std::vector<std::string>& lang_list\n        );\n\n    Result Process(\n        const std::vector<std::u32string>& text_list,\n        const std::vector<std::string>& lang_list\n        );\n\nprivate:\n    std::vector<int64_t> indexer_;\n\n    std::u32string preprocessText(const std::string& text, const std::string& lang);\n    std::u32string preprocessText(const std::u32string& text, const std::string& lang);\n    std::u32string normalizeText(const std::u32string& text);\n    std::u32string ansiToU32(const std::string& text);\n    std::vector<int64_t> codepointsToIds(const std::u32string& text);\n    std::vector<std::vector<std::vector<float>>> getTextMask(\n        const std::vector<int64_t>& text_ids_lengths\n        );\n\n    Result buildResult(const std::vector<std::u32string>& text_list);\n};\n"
  },
  {
    "path": "SupertonicVocoder.cpp",
    "content": "#include \"SupertonicVocoder.h\"\n\n#include <cstddef>\n#include <string>\n#include <vector>\n\nnamespace {\nstd::wstring ToWide(const std::string& value)\n{\n    return std::wstring(value.begin(), value.end());\n}\n\nstd::size_t CountElements(const std::vector<std::int64_t>& shape)\n{\n    std::size_t total = 1;\n    for (auto dim : shape) {\n        total *= static_cast<std::size_t>(dim);\n    }\n    return total;\n}\n}\n\nbool SupertonicVocoder::Initialize(const std::string& vocoderPath)\n{\n    std::string model_path = vocoderPath;\n    const std::string suffix = \".onnx\";\n\n    if (model_path.size() < suffix.size() ||\n        model_path.compare(model_path.size() - suffix.size(), suffix.size(), suffix) != 0) {\n        if (!model_path.empty() && model_path.back() != '/' && model_path.back() != '\\\\') {\n            model_path += '/';\n        }\n        model_path += \"vocoder.onnx\";\n    }\n\n    std::wstring wide_path = ToWide(model_path);\n    return static_cast<ONNXModel&>(*this).Load(wide_path, \"supertonic_vocoder\");\n}\n\nTFTensor<float> SupertonicVocoder::DoInference(const TFTensor<float>& InMel)\n{\n    TFTensor<float> result;\n    if (InMel.Data.empty() || InMel.Shape.empty()) {\n        return result;\n    }\n\n    Ort::MemoryInfo mem_info = Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault);\n    std::vector<float> input_data = InMel.Data;\n    std::vector<int64_t> input_shape = InMel.Shape;\n\n    std::vector<Ort::Value> input_tensors;\n    input_tensors.emplace_back(Ort::Value::CreateTensor<float>(mem_info, input_data.data(), input_data.size(), input_shape.data(), input_shape.size()));\n\n    auto output_tensors = Forward(input_tensors);\n    if (output_tensors.empty() || !output_tensors[0].IsTensor()) {\n        return result;\n    }\n\n    auto output_shape = output_tensors[0].GetTensorTypeAndShapeInfo().GetShape();\n    std::size_t output_count = CountElements(output_shape);\n    const float* output_ptr = output_tensors[0].GetTensorData<float>();\n\n    result.Data.assign(output_ptr, output_ptr + output_count);\n\n    // Return shape: [1, N]\n    // Squeeze extra dim\n    result.Shape = {output_shape[1]};\n\n    result.TotalSize = output_count;\n\n\n    return result;\n}\n"
  },
  {
    "path": "SupertonicVocoder.h",
    "content": "#pragma once\n\n#include \"ONNXModel.h\"\n#include \"MultiBandMelGAN.h\"\n#include <string>\n\nclass SupertonicVocoder : public ONNXModel, public MultiBandMelGAN\n{\npublic:\n    bool Initialize(const std::string& vocoderPath);\n    TFTensor<float> DoInference(const TFTensor<float>& InMel);\n};\n"
  },
  {
    "path": "TensorVox.pro",
    "content": "QT += core gui\nQT += multimedia\nQT += winextras\ngreaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport\n\nCONFIG += c++17\n\n# The following define makes your compiler emit warnings if you use\n# any Qt feature that has been marked deprecated (the exact warnings\n# depend on your compiler). Please consult the documentation of the\n# deprecated API in order to know how to port your code away from it.\nDEFINES += QT_DEPRECATED_WARNINGS _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING\n# You can also make your code fail to compile if it uses deprecated APIs.\n# In order to do so, uncomment the following line.\n# You can also select to disable deprecated APIs only up to a certain version of Qt.\n#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0\n\nSOURCES += \\\n    EnglishPhoneticProcessor.cpp \\\n    FastSpeech2.cpp \\\n    MultiBandMelGAN.cpp \\\n    ONNXModel.cpp \\\n    Supertonic.cpp \\\n    SupertonicTextProcessor.cpp \\\n    SupertonicVocoder.cpp \\\n    TextTokenizer.cpp \\\n    VITSEvo.cpp \\\n    Voice.cpp \\\n    VoxCommon.cpp \\\n    attention.cpp \\\n    batchdenoisedlg.cpp \\\n    bert.cpp \\\n    berttokenizer.cpp \\\n    devits.cpp \\\n    espeakphonemizer.cpp \\\n    ext/ByteArr.cpp \\\n    ext/Qt-Frameless-Window-DarkStyle-master/DarkStyle.cpp \\\n    ext/Qt-Frameless-Window-DarkStyle-master/framelesswindow/framelesswindow.cpp \\\n    ext/Qt-Frameless-Window-DarkStyle-master/framelesswindow/windowdragger.cpp \\\n    ext/ZCharScanner.cpp \\\n    ext/ZCharScannerWide.cpp \\\n    ext/ZFile.cpp \\\n    ext/qcustomplot.cpp \\\n    istftnettorch.cpp \\\n    main.cpp \\\n    mainwindow.cpp \\\n    melgen.cpp \\\n    modelinfodlg.cpp \\\n    phddialog.cpp \\\n    phonemizer.cpp \\\n    phoneticdict.cpp \\\n    phonetichighlighter.cpp \\\n    spectrogram.cpp \\\n    tacotron2.cpp \\\n    tacotron2torch.cpp \\\n    tfg2p.cpp \\\n    torchmoji.cpp \\\n    track.cpp \\\n    vits.cpp \\\n    voicemanager.cpp \\\n    voxer.cpp\n\nHEADERS += \\\n    EnglishPhoneticProcessor.h \\\n    FastSpeech2.h \\\n    MultiBandMelGAN.h \\\n    ONNXModel.h \\\n    ONNXUtil.hpp \\\n    Supertonic.h \\\n    SupertonicTextProcessor.h \\\n    SupertonicVocoder.h \\\n    TextTokenizer.h \\\n    VITSEvo.h \\\n    Voice.h \\\n    VoxCommon.hpp \\\n    attention.h \\\n    batchdenoisedlg.h \\\n    bert.h \\\n    berttokenizer.h \\\n    devits.h \\\n    espeakphonemizer.h \\\n    ext/AudioFile.hpp \\\n    ext/ByteArr.h \\\n    ext/CppFlow/context.h \\\n    ext/CppFlow/cppflow.h \\\n    ext/CppFlow/datatype.h \\\n    ext/CppFlow/defer.h \\\n    ext/CppFlow/model.h \\\n    ext/CppFlow/ops.h \\\n    ext/CppFlow/raw_ops.h \\\n    ext/CppFlow/tensor.h \\\n    ext/Qt-Frameless-Window-DarkStyle-master/DarkStyle.h \\\n    ext/Qt-Frameless-Window-DarkStyle-master/framelesswindow/framelesswindow.h \\\n    ext/Qt-Frameless-Window-DarkStyle-master/framelesswindow/windowdragger.h \\\n    ext/ZCharScanner.h \\\n    ext/ZCharScannerWide.h \\\n    ext/ZFile.h \\\n    ext/json.hpp \\\n    ext/qcustomplot.h \\\n    istftnettorch.h \\\n    mainwindow.h \\\n    melgen.h \\\n    modelinfodlg.h \\\n    phddialog.h \\\n    phonemizer.h \\\n    phoneticdict.h \\\n    phonetichighlighter.h \\\n    spectrogram.h \\\n    tacotron2.h \\\n    tacotron2torch.h \\\n    tfg2p.h \\\n    torchmoji.h \\\n    track.h \\\n    vits.h \\\n    voicemanager.h \\\n    voxer.h\n\nFORMS += \\\n    batchdenoisedlg.ui \\\n    ext/Qt-Frameless-Window-DarkStyle-master/framelesswindow/framelesswindow.ui \\\n    mainwindow.ui \\\n    modelinfodlg.ui \\\n    phddialog.ui\n\n# Default rules for deployment.\nqnx: target.path = /tmp/$${TARGET}/bin\nelse: unix:!android: target.path = /opt/$${TARGET}/bin\n!isEmpty(target.path): INSTALLS += target\n\n\nDEFINES += _CRT_SECURE_NO_WARNINGS\n\nINCLUDEPATH += $$PWD/deps/include\nINCLUDEPATH += $$PWD/deps/include/libtorch\nINCLUDEPATH += $$PWD/ext/Qt-Frameless-Window-DarkStyle-master/framelesswindow\nwin32: LIBS += -L$$PWD/deps/lib/ DirectML.lib onnxruntime.lib tensorflow.lib r8bsrc64.lib rnnoise64.lib LogitechLEDLib.lib LibNumberText64.lib c10.lib torch.lib torch_cpu.lib libespeak-ng.lib Utf8Proc.lib\nwin32: LIBS += Advapi32.lib User32.lib Psapi.lib dxgi.lib\n\n\nRESOURCES += \\\n    ext/Qt-Frameless-Window-DarkStyle-master/darkstyle.qrc \\\n    ext/Qt-Frameless-Window-DarkStyle-master/framelesswindow.qrc \\\n    stdres.qrc\n\nwin32:RC_ICONS += winicon.ico\n\nVERSION = 1.2.0.0\nCONFIG += force_debug_info\n\nQMAKE_CXXFLAGS += /std:c++17 /utf-8 -DPSAPI_VERSION=1\n\nDISTFILES += \\\n    res/defaultim.png\n"
  },
  {
    "path": "TextTokenizer.cpp",
    "content": "#include \"TextTokenizer.h\"\n#include \"ext/ZCharScanner.h\"\n#include <algorithm>\n#include <cassert>\n#include <cctype>\n#include <iostream>\n#include <algorithm>\n\n\n\n\n// Punctuation, this gets auto-converted to SIL\nconst std::u32string punctuation_f = U\",.-;\";\n\n// For Tacotron2, including question and other marks\nconst std::u32string punctuation_tac = U\",.;¡!¿?:-\";\n\n\nconst std::u32string digits = U\"1234567890\";\n\nusing namespace std;\n\nvoid TextTokenizer::SetAllowedChars(const std::string &value)\n{\n    AllowedChars = VoxUtil::StrToU32(value);\n}\n\nvector<string> TextTokenizer::ExpandNumbers(const std::vector<std::string>& SpaceTokens)\n{\n\tvector<string> RetVec;\n\tRetVec.reserve(SpaceTokens.size());\n\n\tfor (auto& Token : SpaceTokens) {\n\t\tchar* p;\n        strtol(Token.c_str(), &p, 10);\n\t\tif (*p) {\n\t\t\tRetVec.push_back(Token);\n\t\t}\n\t\telse {\n            std::string ModTk = Token;\n            CuNumber->numbertext(ModTk,NumLang);\n\n            std::replace(ModTk.begin(),ModTk.end(),'-',' ');\n\n            // If the number has spaces we must sep again and add one by one otherwise all the words are merged together due to the\n            // nature of it\n            ZStringDelimiter DelSp(ModTk);\n            DelSp.AddDelimiter(\" \");\n\n            if (DelSp.szTokens())\n            {\n                for (const auto& Ttk : DelSp.GetTokens())\n                    RetVec.push_back(Ttk);\n\n            }else{\n                RetVec.push_back(ModTk);\n            }\n\n\n\n\n\n\t\t}\n\t}\n\n\treturn RetVec;\n\n}\n\nstring TextTokenizer::SpaceChars(const string &InStr)\n{\n    std::u32string AsmStr = U\"\";\n    std::u32string Stry = VoxUtil::StrToU32(InStr);\n\n    bool InNumChain = false;\n    bool InPhn = false;\n\n    for (size_t i = 0; i < Stry.size();i++)\n    {\n        auto uChar = Stry[i];\n\n        if (uChar == U'@')\n            InPhn = true;\n\n        if (uChar == U' ')\n            InPhn = false;\n\n\n        if (InPhn)\n        {\n            AsmStr += uChar;\n            continue;\n\n        }\n\n\n        if (digits.find(uChar) != std::u32string::npos && !InNumChain)\n        {\n            AsmStr += U\" \";\n            AsmStr += uChar;\n            InNumChain = true;\n            continue;\n        }\n\n        if (digits.find(uChar) == std::u32string::npos && InNumChain )\n        {\n            AsmStr += U\" \";\n            AsmStr += uChar;\n\n            InNumChain = false;\n            continue;\n\n        }\n\n        AsmStr += uChar;\n\n\n\n    }\n\n    return VoxUtil::U32ToStr(AsmStr);\n\n\n}\n\nTextTokenizer::TextTokenizer()\n{\n}\n\nTextTokenizer::~TextTokenizer()\n{\n}\n\nvoid TextTokenizer::SetNumberText(Numbertext &INum, const string &Lang)\n{\n    CuNumber = &INum;\n    NumLang = Lang;\n\n}\n\n\n\nvector<string> TextTokenizer::Tokenize(const std::string & InTxt,bool IsTacotron, bool IsTorchMoji)\n{\n\tvector<string> ProcessedTokens;\n\n\n\n    std::string TxtPreProc = SpaceChars(InTxt);\n\n    ZStringDelimiter Delim(TxtPreProc);\n\tDelim.AddDelimiter(\" \");\n\n    vector<string> DelimitedTokens = Delim.GetTokens();\n\n\n\n\t// Single word handler\n    if (!Delim.szTokens())\n        DelimitedTokens.push_back(TxtPreProc);\n\n    DelimitedTokens = ExpandNumbers(DelimitedTokens);\n\n    std::u32string punctuation = punctuation_f;\n\n    if (IsTacotron)\n        punctuation = punctuation_tac;\n\n\n\n\n\t// We know that the new vector is going to be at least this size so we reserve\n\tProcessedTokens.reserve(DelimitedTokens.size());\n\n\t/*\n\tIn this step we go through the string and only allow qualified character to pass through.\n\t*/\n    for (size_t TokCtr = 0; TokCtr < DelimitedTokens.size();TokCtr++)\n    {\n        // We are now using U32string because it's guaranteed to be 1 character = 1 element\n        const auto& tok = VoxUtil::StrToU32(DelimitedTokens[TokCtr]);\n        std::u32string AppTok = U\"\";\n\n\n        if (tok.find(U\"@\") != string::npos)\n        {\n\n            ProcessedTokens.push_back(VoxUtil::U32ToStr(tok));\n            continue;\n\n        }\n\n\t\tfor (size_t s = 0;s < tok.size();s++)\n\t\t{\n\n\n            if (AllowedChars.find(tok[s]) != std::u32string::npos)\n                AppTok += tok[s];\n\n\n\t\t\t// Punctuation handler\n            // This time we explicitly add a token to the vector\n            if (punctuation.find(tok[s]) != std::u32string::npos) {\n\n\n\t\t\t\t// First, if the assembled string isn't empty, we add it in its current state\n\t\t\t\t// Otherwise, the SIL could end up appearing before the word.\n\n                if (!AppTok.empty()) {\n                    ProcessedTokens.push_back(VoxUtil::U32ToStr(AppTok));\n\n                    AppTok = U\"\";\n\t\t\t\t}\n\n                if (IsTacotron){\n\n                    // Double at-symbol is handled later\n                    if (!IsTorchMoji)\n                        AppTok += U\"@@\";\n\n                    AppTok += tok[s];\n\n                }\n                else{\n                    AppTok = U\"@SIL\";\n                }\n\n                ProcessedTokens.push_back(VoxUtil::U32ToStr(AppTok));\n                AppTok = U\"\";\n                continue;\n\n\t\t\t}\n\n\n\n\n\n\n\t\t}\n        if (!AppTok.empty())\n        {\n            ProcessedTokens.push_back(VoxUtil::U32ToStr(AppTok));\n            AppTok = U\"\";\n\n\n        }\n\n\t}\n\t// Prevent out of range error if the user inputs one word\n\tif (ProcessedTokens.size() > 1) \n\t{\n\t\tif (ProcessedTokens[ProcessedTokens.size() - 1] == \"SIL\")\n\t\t\tProcessedTokens.pop_back();\n\t}\n\n\n\treturn ProcessedTokens;\n}\n"
  },
  {
    "path": "TextTokenizer.h",
    "content": "#pragma once\n#include <vector>\n#include <string>\n#include \"VoxCommon.hpp\"\n#include \"Numbertext.hxx\"\n\nclass TextTokenizer\n{\nprivate:\n    std::u32string AllowedChars;\n\n\tstd::vector<std::string> ExpandNumbers(const std::vector<std::string>& SpaceTokens);\n\n    Numbertext* CuNumber;\n\n    std::string NumLang;\n\n\n    // Go through the string and add spaces before and after punctuation.\n    // This is because ExpandNumbers won't recognize numbers if they've got punctuation like 500, or .9000\n    std::string SpaceChars(const std::string& InStr);\n\n\n\npublic:\n\tTextTokenizer();\n\t~TextTokenizer();\n\n    void SetNumberText(Numbertext& INum,const std::string& Lang);\n\n    std::vector<std::string> Tokenize(const std::string& InTxt, bool IsTacotron = false, bool IsTorchMoji = false);\n    void SetAllowedChars(const std::string &value);\n};\n\n"
  },
  {
    "path": "VITSEvo.cpp",
    "content": "#include \"VITSEvo.h\"\n#include <cstddef>\n#include <string>\n\n\nstd::vector<int64_t> ZeroPadVec(const std::vector<int32_t> &InIDs)\n{\n    std::vector<int64_t> NewIDs;\n    NewIDs.reserve(InIDs.size() * 2);\n\n    NewIDs.push_back(0);\n\n    for (auto CharID : InIDs)\n    {\n\n        NewIDs.push_back((int64_t)CharID);\n        NewIDs.push_back(0);\n\n\n    }\n\n    return NewIDs;\n\n}\n\n\nnamespace {\nstd::size_t CountElements(const std::vector<std::int64_t>& shape)\n{\n    std::size_t total = 1;\n    for (auto dim : shape) {\n        total *= static_cast<std::size_t>(dim);\n    }\n    return total;\n}\n}\n\nbool VITSEvo::Initialize(const std::string &SavedModelFolder, ETTSRepo::Enum InTTSRepo)\n{\n\n    std::wstring WidePath( SavedModelFolder.begin(), SavedModelFolder.end() );\n\n    CurrentRepo = InTTSRepo;\n\n    return Load(WidePath);\n\n}\n\n\n\n\nTFTensor<float> VITSEvo::DoInference(const std::vector<int32_t> &InputIDs, const std::vector<float> &ArgsFloat, const std::vector<int32_t> ArgsInt, int32_t SpeakerID, int32_t EmotionID)\n{\n    // VITS EVO uses zero interspersion\n\n    std::vector<int64_t> RealIDs = ZeroPadVec(InputIDs);\n\n\n    // Call the ONNX inference fun\n\n    std::vector<float> AudioFrames = Predict(RealIDs, (int64_t)SpeakerID);\n    const int64_t output_count = (int64_t)AudioFrames.size();\n\n\n    TFTensor<float> RetTensor;\n\n    RetTensor.Shape = {output_count};\n    RetTensor.TotalSize = output_count;\n\n\n    RetTensor.Data = AudioFrames;\n\n    return RetTensor;\n\n}\n\nstd::vector<float> VITSEvo::Predict(std::vector<std::int64_t>& text_ids, int64_t SpeakerID)\n{\n    if (text_ids.empty()) {\n        return {};\n    }\n\n    const std::int64_t batch = 1;\n    const std::int64_t seq_len = static_cast<std::int64_t>(text_ids.size());\n\n    std::vector<std::int64_t> text_shape{ batch, seq_len };\n    std::vector<std::int64_t> text_lengths_shape{ batch };\n    std::vector<std::int64_t> text_lengths_data{ seq_len };\n\n    Ort::MemoryInfo mem_info = Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault);\n    std::vector<Ort::Value> input_tensors;\n\n    input_tensors.emplace_back(Ort::Value::CreateTensor<std::int64_t>(mem_info,\n                                                                      text_ids.data(), text_ids.size(), text_shape.data(), text_shape.size()));\n    input_tensors.emplace_back(Ort::Value::CreateTensor<std::int64_t>(mem_info,\n                                                                      text_lengths_data.data(), text_lengths_data.size(), text_lengths_shape.data(), text_lengths_shape.size()));\n\n    std::vector<std::string> InputNames = { \"text\", \"text_lengths\" };\n\n    if (SpeakerID != -1)\n    {\n        // This is a multi speaker model.\n\n        std::vector<int64_t> speaker_id_data = {SpeakerID};\n        std::vector<int64_t> speaker_id_shape = {batch};\n\n        input_tensors.emplace_back(\n            Ort::Value::CreateTensor<std::int64_t>(mem_info,\n                                                   speaker_id_data.data(), speaker_id_data.size(), speaker_id_shape.data(), speaker_id_shape.size())\n            );\n\n        InputNames.push_back(\"sid\");\n\n    }\n\n    auto output_tensors = Forward(input_tensors, InputNames);\n    if (output_tensors.empty() || !output_tensors[0].IsTensor()) {\n        return {};\n    }\n\n    auto output_shape = output_tensors[0].GetTensorTypeAndShapeInfo().GetShape();\n    std::size_t output_count = CountElements(output_shape);\n    const float* output_ptr = output_tensors[0].GetTensorData<float>();\n\n\n    return std::vector<float>(output_ptr, output_ptr + output_count);\n}\n"
  },
  {
    "path": "VITSEvo.h",
    "content": "#pragma once\n\n#include \"ONNXModel.h\"\n#include \"melgen.h\"\n#include <cstdint>\n#include <vector>\n\nclass VITSEvo : public ONNXModel, public MelGen\n{\npublic:\n    // Since VITS EVO runs on ONNX we override the loader\n    /*\n    Initialize and load the model\n\n    -> SavedModelFolder: Not a folder, but path to the .onnx file\n    <- Returns: (bool)Success\n    */\n    virtual bool Initialize(const std::string& SavedModelFolder, ETTSRepo::Enum InTTSRepo);\n\n\n    /*\n    Do inference on a VITS model.\n\n    -> InputIDs: Input IDs of tokens for inference\n    -> SpeakerID: ID of the speaker in the model to do inference on. If single speaker, always leave at 0. If multispeaker, refer to your model.\n    -> ArgsFloat[0]: Length scale. (not yet)\n\n    <- Returns: TFTensor<float> with shape {frames} of audio data\n    */\n    TFTensor<float> DoInference(const std::vector<int32_t>& InputIDs,const std::vector<float>& ArgsFloat,const std::vector<int32_t> ArgsInt, int32_t SpeakerID = 0, int32_t EmotionID = -1);\n\n    std::vector<float> Predict(std::vector<std::int64_t>& text_ids, int64_t SpeakerID);\n};\n"
  },
  {
    "path": "Voice.cpp",
    "content": "#include \"Voice.h\"\n#include \"ext/ZCharScanner.h\"\n\n\nstd::vector<int32_t> Voice::CharsToID(const std::string & RawInTxt)\n{\n\n    std::cout << \"CharsToID: \" << RawInTxt << \"\\n\";\n    std::vector<int32_t> VecPhones;\n\n    std::u32string InTxt = VoxUtil::StrToU32(RawInTxt);\n\n    for (const auto& Char : InTxt)\n    {\n        size_t ArrID = 0;\n        std::u32string CharAs;\n        CharAs += Char;\n\n        if (VoxUtil::FindInVec<std::u32string>(CharAs, Phonemes, ArrID))\n            VecPhones.push_back(PhonemeIDs[ArrID]);\n        else\n            std::cout << \"Voice::PhonemesToID() WARNING: Unknown phoneme \" << Char << std::endl;\n\n\n\n    }\n\n\n    return VecPhones;\n\n}\n\nstd::vector<int32_t> Voice::PhonemesToID(const std::string & RawInTxt)\n{\n    std::cout << \"PhonemesToID: \" << RawInTxt << \"\\n\";\n    ZStringDelimiter Delim(RawInTxt);\n\tDelim.AddDelimiter(\" \");\n    std::u32string InTxt = VoxUtil::StrToU32(RawInTxt);\n\n\n\tstd::vector<int32_t> VecPhones;\n\tVecPhones.reserve(Delim.szTokens());\n\n\n\tfor (const auto& Pho : Delim.GetTokens()) \n\t{\n\t\tsize_t ArrID = 0;\n        std::u32string PhnU = VoxUtil::StrToU32(Pho);\n\n        if (VoxUtil::FindInVec<std::u32string>(PhnU, Phonemes, ArrID))\n            VecPhones.push_back(PhonemeIDs[ArrID]);\n\t\telse\n            std::cout << \"Voice::PhonemesToID() WARNING: Unknown phoneme \" << Pho << std::endl;\n\n\n\n\t}\n\n\n\treturn VecPhones;\n\n}\n\nvoid Voice::ReadPhonemes(const std::string &PhonemePath)\n{\n    std::ifstream Phone(PhonemePath);\n\n    std::string Line;\n    while (std::getline(Phone, Line))\n    {\n        if (Line.find(\"\\t\") == std::string::npos)\n            continue;\n\n\n        ZStringDelimiter Deline(Line);\n        Deline.AddDelimiter(\"\\t\");\n\n        Phonemes.push_back(VoxUtil::StrToU32(Deline[0]));\n        PhonemeIDs.push_back(stoi(Deline[1]));\n\n\n\n\n    }\n\n}\n\nvoid Voice::ReadSpeakers(const std::string &SpeakerPath)\n{\n    Speakers = VoxUtil::GetLinedFile(SpeakerPath);\n\n}\n\nvoid Voice::ReadEmotions(const std::string &EmotionPath)\n{\n    Emotions = VoxUtil::GetLinedFile(EmotionPath);\n\n}\n\n\nvoid Voice::ReadModelInfo(const std::string &ModelInfoPath)\n{\n\n    ModelInfo = \"\";\n    std::vector<std::string> MiLines = VoxUtil::GetLinedFile(ModelInfoPath);\n\n    for (const std::string& ss : MiLines)\n        ModelInfo += ss + \"\\n\";\n\n\n}\n\n\nVoice::Voice(const std::string & VoxPath, const std::string &inName, Phonemizer *InPhn)\n{\n    MelPredictorIsGPU = false;\n    MelPredictorDeviceName = L\"CPU\";\n\n    ReadModelInfo(VoxPath + \"/info.txt\");\n\n\n\n\n    VoxInfo = VoxUtil::ReadModelJSON(VoxPath + \"/info.json\");\n\n    const int32_t Tex2MelArch = VoxInfo.Architecture.Text2Mel;\n\n    const bool IsVITS = Tex2MelArch == EText2MelModel::VITS || Tex2MelArch == EText2MelModel::DEVITS || Tex2MelArch == EText2MelModel::VITSEvo;\n\n    if (Tex2MelArch == EText2MelModel::Tacotron2)\n        MelPredictor = std::make_unique<Tacotron2>();\n    else if (Tex2MelArch == EText2MelModel::FastSpeech2)\n        MelPredictor = std::make_unique<FastSpeech2>();\n    else if (Tex2MelArch == EText2MelModel::VITS)\n        MelPredictor = std::make_unique<VITS>();\n    else if (Tex2MelArch == EText2MelModel::DEVITS)\n         MelPredictor = std::make_unique<DEVITS>();\n    else if (Tex2MelArch == EText2MelModel::VITSEvo)\n        MelPredictor = std::make_unique<VITSEvo>();\n    else if (Tex2MelArch == EText2MelModel::Supertonic)\n        MelPredictor = std::make_unique<Supertonic>();\n    else\n        MelPredictor = std::make_unique<Tacotron2Torch>();\n\n\n    std::string MelPredInit = VoxPath + \"/melgen\";\n\n    if (IsVITS)\n        MelPredInit = VoxPath + \"/vits.pt\";\n\n    if (Tex2MelArch == EText2MelModel::Tacotron2Torch)\n        MelPredInit = VoxPath + \"/tacotron2.pt\";\n    else if (Tex2MelArch == EText2MelModel::VITSEvo)\n        MelPredInit = VoxPath + \"/vits.onnx\";\n    else if (Tex2MelArch == EText2MelModel::Supertonic)\n        MelPredInit = VoxPath + \"/onnx\";\n\n\n    MelPredictor->Initialize(MelPredInit,(ETTSRepo::Enum)VoxInfo.Architecture.Repo);\n\n    if (auto* OnnxMelPredictor = dynamic_cast<ONNXModel*>(MelPredictor.get())) {\n        MelPredictorIsGPU = OnnxMelPredictor->IsGPUDevice();\n        MelPredictorDeviceName = OnnxMelPredictor->GetDeviceName();\n    } else {\n        MelPredictorIsGPU = false;\n        MelPredictorDeviceName = L\"CPU\";\n    }\n\n\n\n\n    if (Tex2MelArch == EText2MelModel::DEVITS){\n        Moji.Initialize(VoxPath + \"/moji.pt\", VoxPath + \"/tm_dict.txt\");\n        BertFE.Initialize(VoxPath + \"/bert.pt\", VoxPath + \"/bert_vocab.txt\");\n\n    }\n\n\n\n    const int32_t VocoderArch = VoxInfo.Architecture.Vocoder;\n\n\n    if (VocoderArch == EVocoderModel::iSTFTNet)\n        Vocoder = std::make_unique<iSTFTNetTorch>();\n    else if (VocoderArch == EVocoderModel::NullVocoder)\n        Vocoder = nullptr;\n    else if (VocoderArch == EVocoderModel::SupertonicVocoder)\n        Vocoder = std::make_unique<SupertonicVocoder>();\n    else\n        Vocoder = std::make_unique<MultiBandMelGAN>();\n\n    if (Vocoder.get())\n    {\n\n\n        Vocoder.get()->Initialize(VoxPath + \"/vocoder\");\n\n\n    }\n\n\n\n\n\n\n\n\n\n    if (InPhn)\n        Processor.Initialize(InPhn);\n\n    if (Tex2MelArch == EText2MelModel::Supertonic){\n        Supertonic_Processor = std::make_unique<SupertonicTextProcessor>(VoxPath + \"/unicode_indexer.json\");\n    }\n\n\n    Name = inName;\n    ReadPhonemes(VoxPath + \"/phonemes.txt\");\n    ReadSpeakers(VoxPath + \"/speakers.txt\");\n    ReadEmotions(VoxPath + \"/emotions.txt\");\n\n\n\n\n\n\n\n\n\n}\n\nvoid Voice::AddPhonemizer(Phonemizer *InPhn,ESpeakPhonemizer* InENGPhn)\n{\n    Processor.Initialize(InPhn,InENGPhn);\n    Processor.GetTokenizer().SetNumberText(NumTxt,VoxCommon::CommonLangConst);\n\n\n}\n\nvoid Voice::LoadNumberText(const std::string &NumTxtPath)\n{\n    NumTxt.load(VoxCommon::CommonLangConst,NumTxtPath);\n}\n\nstd::string Voice::PhonemizeStr(const std::string &Prompt)\n{\n\n\n    return Processor.ProcessTextPhonetic(Prompt,Phonemes,CurrentDict,\n                                                           (ETTSLanguageType::Enum)VoxInfo.LangType,\n                                                           true); // default voxistac to true to preserve punctuation.\n\n}\n\nstd::vector<int32_t> Voice::GetText(const int32_t &Text2MelN, bool &VoxIsTac,\n                      std::string &PromptToFeed) {\n\n    std::vector<int32_t> InputIDs;\n    std::string PhoneticTxt = Processor.ProcessTextPhonetic(\n        PromptToFeed, Phonemes, CurrentDict,\n        (ETTSLanguageType::Enum)VoxInfo.LangType, VoxIsTac);\n\n    if (Text2MelN == EText2MelModel::Tacotron2Torch)\n        PhoneticTxt += VoxInfo.EndPadding;\n\n    if (VoxInfo.LangType == ETTSLanguageType::Char) {\n        InputIDs = CharsToID(PhoneticTxt);\n        if (VoxInfo.EndPadding.size())\n            InputIDs.push_back(std::stoi(VoxInfo.EndPadding));\n\n    } else {\n        if (VoxInfo.LangType == ETTSLanguageType::IPA)\n            InputIDs = CharsToID(PhoneticTxt);\n        else\n            InputIDs = PhonemesToID(PhoneticTxt);\n    }\n\n    return InputIDs;\n}\n\nVoxResults Voice::Vocalize(const std::string &Prompt, float Speed,\n                           int32_t SpeakerID, float Energy, float F0,\n                           int32_t EmotionID, const std::string &EmotionOvr) {\n\n    const int32_t Text2MelN = VoxInfo.Architecture.Text2Mel;\n\n    bool VoxIsTac = Text2MelN != EText2MelModel::FastSpeech2;\n\n    std::string PromptToFeed = Prompt;\n    if (VoxInfo.LangType != ETTSLanguageType::Char &&\n        Text2MelN != EText2MelModel::Tacotron2Torch)\n        PromptToFeed += VoxInfo.EndPadding;\n\n  //  if (Text2MelN == EText2MelModel::Supertonic)\n    //    PromptToFeed = \"<en>\" + PromptToFeed + \"</en>\";\n\n    std::vector<int32_t> InputIDs;\n    TFTensor<float> Mel;\n    TFTensor<float> Attention;\n\n    if (Text2MelN == EText2MelModel::Supertonic)\n    {\n        std::vector<std::u32string> Texts = {VoxUtil::StrToU32(Prompt)};\n        std::vector<std::string> Langs = {\"en\"};\n\n        auto Process_Result = Supertonic_Processor->Process(Texts, Langs);\n\n        std::vector<int64_t>& Ids_Pre = Process_Result.text_ids[0];\n        InputIDs.reserve(Ids_Pre.size());\n\n        for (auto id : Ids_Pre){\n            InputIDs.push_back((int32_t)id);\n        }\n\n\n    }\n\n\n    if (!InputIDs.size())\n        InputIDs = GetText(Text2MelN, VoxIsTac, PromptToFeed);\n\n    std::vector<float> FloatArgs;\n    std::vector<int32_t> IntArgs;\n\n\n\n    if (Text2MelN == EText2MelModel::Tacotron2)\n    {\n\n        Mel = ((Tacotron2*)MelPredictor.get())->DoInference(InputIDs,FloatArgs,IntArgs,SpeakerID, EmotionID);\n        Attention = ((Tacotron2*)MelPredictor.get())->Attention;\n\n    }\n    else if (Text2MelN == EText2MelModel::Tacotron2Torch)\n    {\n        Mel = MelPredictor.get()->DoInference(InputIDs,FloatArgs,IntArgs,SpeakerID, EmotionID);\n        Attention = ((Tacotron2Torch*)MelPredictor.get())->Attention;\n    }\n    else if (Text2MelN == EText2MelModel::FastSpeech2)\n    {\n\n        FloatArgs = {Speed,Energy,F0};\n\n        Mel = ((FastSpeech2*)MelPredictor.get())->DoInference(InputIDs,FloatArgs,IntArgs,SpeakerID, EmotionID);\n\n    }else if (Text2MelN == EText2MelModel::VITS)\n    {\n        FloatArgs = {Speed};\n\n\n        TFTensor<float> Audio = MelPredictor.get()->DoInference(InputIDs,FloatArgs,IntArgs,SpeakerID,EmotionID);\n        Attention = ((VITS*)MelPredictor.get())->Attention;\n\n        std::vector<float> AudioData = Audio.Data;\n\n        Mel.Shape.push_back(-1); // Tell the plotter that we have no mel to plot\n\n        // As VITS is fully E2E, we return here\n\n        return {AudioData,Attention,Mel};\n\n    }else if (Text2MelN == EText2MelModel::VITSEvo)\n    {\n        TFTensor<float> Audio = MelPredictor.get()->DoInference(InputIDs,FloatArgs,IntArgs,SpeakerID,EmotionID);\n\n        std::vector<float> AudioData = Audio.Data;\n\n        Mel.Shape.push_back(-1); // Tell the plotter that we have no mel to plot\n\n        // End 2 end. Return here.\n\n        return {AudioData,Attention,Mel};\n\n    }\n    else if (Text2MelN == EText2MelModel::Supertonic)\n    {\n        Mel = MelPredictor.get()->DoInference(InputIDs,{Speed},IntArgs,SpeakerID, EmotionID);\n    }\n    else // DE-VITS\n    {\n        FloatArgs = {Speed};\n        std::vector<std::string> MojiInput = Processor.GetTokenizer().Tokenize(EmotionOvr,true,true);\n        TFTensor<float> MojiStates = Moji.Infer(MojiInput);\n\n        auto BERTOutputs = BertFE.Infer(Prompt);\n\n\n\n\n        TFTensor<float> Audio = ((DEVITS*)MelPredictor.get())->DoInferenceDE(InputIDs, MojiStates,\n                                                                             BERTOutputs.first,FloatArgs,\n                                                                             IntArgs,SpeakerID,EmotionID);\n        Attention = ((VITS*)MelPredictor.get())->Attention;\n\n        std::vector<float> AudioData = Audio.Data;\n\n        Mel.Shape.push_back(-1); // Tell the plotter that we have no mel to plot\n\n        // As VITS is fully E2E, we return here\n\n        return {AudioData,Attention,Mel};\n    }\n\n    // Vocoder inference\n\n\n    TFTensor<float> AuData = Vocoder.get()->DoInference(Mel);\n    std::vector<float> AudioData;\n\n    if (AuData.Shape.size() > 1)\n    {\n\n        int64_t Width = AuData.Shape[0];\n        int64_t Height = AuData.Shape[1];\n        int64_t Depth = AuData.Shape[2];\n        //int z = 0;\n\n\n        AudioData.resize(Height);\n\n        // Code to access 1D array as if it were 3D\n        for (int64_t x = 0; x < Width;x++)\n        {\n            for (int64_t z = 0;z < Depth;z++)\n            {\n                for (int64_t y = 0; y < Height;y++) {\n                    int64_t Index = x * Height * Depth + y * Depth + z;\n                    AudioData[(size_t)y] = AuData.Data[(size_t)Index];\n\n                }\n\n            }\n        }\n\n    }\n    else\n    {\n        // Pre-flattened vocoder output\n        AudioData = AuData.Data;\n\n    }\n\n\n\n    if (!AudioData.size())\n        QMessageBox::critical(nullptr,\"f\",\"ss\");\n\n    if (Text2MelN == EText2MelModel::Supertonic){\n        /*\n         *  Supertonic doesn't return a mel spectrogram, but a latent\n         *  We were using its return as Mel for convinience's sake so that it is fed right into the vocoder.\n         *  But now we have to reset the Mel tensor, otherwise the rest of the program\n         *  will see a non-empty tensor, think it's a spectrogram, and plot it (bad!)\n         *\n         */\n        Mel = TFTensor<float>();\n        Mel.Shape.push_back(-1);\n    }\n\n    return {AudioData,Attention,Mel};\n}\n\nvoid Voice::SetDictEntries(const std::vector<DictEntry> &InEntries)\n{\n    for (const DictEntry& Entr : InEntries)\n    {\n        if (Entr.Language != VoxInfo.s_Language_Fullname)\n            continue;\n\n        CurrentDict.push_back(Entr);\n\n    }\n\n}\n\nVoice::~Voice()\n{\n}\n"
  },
  {
    "path": "Voice.h",
    "content": "#pragma once\n\n#include \"FastSpeech2.h\"\n#include \"tacotron2.h\"\n#include \"MultiBandMelGAN.h\"\n#include \"EnglishPhoneticProcessor.h\"\n#include \"vits.h\"\n#include \"Numbertext.hxx\"\n#include \"torchmoji.h\"\n#include \"phoneticdict.h\"\n#include \"tacotron2torch.h\"\n#include \"istftnettorch.h\"\n#include \"devits.h\"\n#include \"bert.h\"\n#include \"VITSEvo.h\"\n#include \"Supertonic.h\"\n#include \"SupertonicVocoder.h\"\n\n#include \"SupertonicTextProcessor.h\"\n\nstruct VoxResults{\n  std::vector<float> Audio;\n  TFTensor<float> Alignment;\n  TFTensor<float> Mel;\n};\n\nclass Voice\n{\nprivate:\n    std::unique_ptr<MelGen> MelPredictor;\n    std::unique_ptr<MultiBandMelGAN> Vocoder;\n    std::unique_ptr<SupertonicTextProcessor> Supertonic_Processor;\n\tEnglishPhoneticProcessor Processor;\n    VoiceInfo VoxInfo;\n    bool MelPredictorIsGPU;\n    std::wstring MelPredictorDeviceName;\n\n    TorchMoji Moji;\n    BERT BertFE;\n\n\n\n    std::vector<std::u32string> Phonemes;\n    std::vector<int32_t> PhonemeIDs;\n\n\n\n    std::vector<int32_t> PhonemesToID(const std::string& RawInTxt);\n\n    std::vector<std::string> Speakers;\n    std::vector<std::string> Emotions;\n\n    void ReadPhonemes(const std::string& PhonemePath);\n\n    void ReadSpeakers(const std::string& SpeakerPath);\n\n    void ReadEmotions(const std::string& EmotionPath);\n\n\n\n    void ReadModelInfo(const std::string& ModelInfoPath);\n\n\n\n    std::vector<DictEntry> CurrentDict;\n\n    std::string ModelInfo;\n\n    std::vector<int32_t> CharsToID(const std::string &RawInTxt);\n\n    Numbertext NumTxt;\npublic:\n\t/* Voice constructor, arguments obligatory.\n\t -> VoxPath: Path of folder where models are contained. \n\t --  Must be a folder without an ending slash with UNIX slashes, can be relative or absolute (eg: MyVoices/Karen)\n\t --  The folder must contain the following elements:\n\t ---  melgen: Folder generated where a FastSpeech2 model was saved as SavedModel, with .pb, variables, etc\n\t ---  vocoder: Folder where a Multi-Band MelGAN model was saved as SavedModel.\n     ---  info.json: Model information\n     ---  phonemes.txt: Tab delimited file containing PHONEME \\t ID, for inputting to the FS2 model.\n\n     --- If multispeaker, a lined .txt file called speakers.txt\n     --- If multi-emotion, a lined .txt file called emotions.txt\n\n\t*/\n\n\n    Voice(const std::string& VoxPath, const std::string& inName,Phonemizer* InPhn);\n\n    void AddPhonemizer(Phonemizer* InPhn, ESpeakPhonemizer *InENGPhn);\n    void LoadNumberText(const std::string& NumTxtPath);\n\n\n    std::string PhonemizeStr(const std::string& Prompt);\n    std::vector<int32_t> GetText(const int32_t &Text2MelN, bool &VoxIsTac,\n                   std::string &PromptToFeed);\n    VoxResults Vocalize(const std::string &Prompt, float Speed = 1.f,\n                        int32_t SpeakerID = 0, float Energy = 1.f,\n                        float F0 = 1.f, int32_t EmotionID = -1,\n                        const std::string &EmotionOvr = \"\");\n\n    std::string Name;\n    inline const VoiceInfo& GetInfo(){return VoxInfo;}\n    inline bool IsMelPredictorGPU() const { return MelPredictorIsGPU; }\n    inline const std::wstring& GetMelPredictorDeviceName() const { return MelPredictorDeviceName; }\n\n    inline const std::vector<std::string>& GetSpeakers(){return Speakers;}\n    inline const std::vector<std::string>& GetEmotions(){return Emotions;}\n\n\n    void SetDictEntries(const std::vector<DictEntry>& InEntries);\n    inline const std::string& GetModelInfo(){return ModelInfo;}\n\n\t~Voice();\n};\n\n"
  },
  {
    "path": "VoxCommon.cpp",
    "content": "#include \"VoxCommon.hpp\"\n#include \"ext/json.hpp\"\nusing namespace nlohmann;\n#include <codecvt>\n#include <locale>         // std::wstring_convert\n\nconst std::vector<std::string> Text2MelNames = {\"FastSpeech2\",\"Tacotron2 (TF)\",\"VITS\",\"DE-VITS\",\"Tacotron2 (Torch)\", \"VITS EVO\", \"Supertonic\"};\nconst std::vector<std::string> VocoderNames = {\"Multi-Band MelGAN\",\"MelGAN-STFT\",\"\",\"iSTFTNet\", \"Supertonic Vocoder\"};\nconst std::vector<std::string> RepoNames = {\"TensorflowTTS\",\"Coqui-TTS\",\"jaywalnut310\",\"keonlee9420\", \"ZDisket\", \"Supertone\"};\n\nconst std::vector<std::string> LanguageNames = {\"English\",\"Spanish\", \"German\", \"EnglishIPA\"};\nconst std::vector<std::string> LangaugeNamesNumToWords = {\"en\", \"es\",\"de\",\"en\"};\n\n\n\n\n#include \"ext/ZCharScanner.h\"\n\nconst std::map<int32_t,std::string> LegacyToV1Lang = {\n    {-3,\"German-Char\"},\n    {0,\"English-ARPA\"},\n    {-1,\"English-Char\"},\n    {3,\"English-IPA\"},\n    {1,\"Spanish-GlobalPhone\"}\n                                                           };\n\nconst std::map<std::string,int32_t> V1LangTypes ={\n  {\"IPA\",ETTSLanguageType::IPA},\n  {\"IPAStressed\",ETTSLanguageType::IPA},\n  {\"ARPA\",ETTSLanguageType::ARPA},\n  {\"Char\",ETTSLanguageType::Char},\n  {\"GlobalPhone\",ETTSLanguageType::GlobalPhone}\n};\n\nvoid VoxUtil::ExportWAV(const std::string & Filename, const std::vector<float>& Data, unsigned SampleRate) {\n\tAudioFile<float>::AudioBuffer Buffer;\n\tBuffer.resize(1);\n\n\n\tBuffer[0] = Data;\n\tsize_t BufSz = Data.size();\n\n\n\tAudioFile<float> File;\n\n    File.setAudioBuffer(Buffer);\n\tFile.setAudioBufferSize(1, (int)BufSz);\n\tFile.setNumSamplesPerChannel((int)BufSz);\n\tFile.setNumChannels(1);\n\tFile.setBitDepth(32);\n\tFile.setSampleRate(SampleRate);\n\n\tFile.save(Filename, AudioFileFormat::Wave);\n\n\n\n}\n\n// Process language value for vector indexes. Language value must adhere to standard.\nuint32_t ProcessLanguageValue(int32_t LangVal)\n{\n    if (LangVal > -1)\n        return LangVal;\n\n    if (LangVal == -1)\n        return 0;\n\n    if (LangVal < 0)\n        return (LangVal * -1) - 1;\n\n    return LangVal;\n\n}\n\nVoiceInfo VoxUtil::ReadModelJSON(const std::string &InfoFilename)\n{\n    const size_t MaxNoteSize = 80;\n\n\n    std::ifstream JFile(InfoFilename);\n    json JS;\n\n\n    try {\n        JFile >> JS;\n    } catch(json::parse_error Err) {\n        QMessageBox::critical(nullptr,\"JSON parse error\",QString::fromUtf8(Err.what()));\n    }\n\n\n    JFile.close();\n\n    auto Arch = JS[\"architecture\"];\n\n    ArchitectureInfo CuArch;\n    CuArch.Repo = Arch[\"repo\"].get<int>();\n    CuArch.Text2Mel = Arch[\"text2mel\"].get<int>();\n    CuArch.Vocoder = Arch[\"vocoder\"].get<int>();\n\n    // Now fill the strings\n    CuArch.s_Repo = RepoNames[CuArch.Repo];\n    CuArch.s_Text2Mel = Text2MelNames[CuArch.Text2Mel];\n    CuArch.s_Vocoder = VocoderNames[CuArch.Vocoder];\n\n    // Language value for the info\n\n    auto LangVal = JS[\"language\"];\n\n    \n    std::string LanguageFullName;\n\n    if (LangVal.is_string()){  // V1 Language type standard model; see ETTSLanguageType enum desc on header\n        LanguageFullName = LangVal.get<std::string>();\n\n    }else{\n        // Convert legacy language to V1\n       int32_t LegacyLang = JS[\"language\"].get<int32_t>();\n       LanguageFullName = LegacyToV1Lang.find(LegacyLang)->second;\n\n\n    }\n\n     ZStringDelimiter LangDel(LanguageFullName);\n     LangDel.AddDelimiter(\"-\");\n\n     std::string LangName = LangDel[0];\n     std::string LangTypeStr = LangDel[1];\n     std::string eSpeakLangStr = \"\";\n     if (LangDel.szTokens() > 2)\n     {\n         eSpeakLangStr = LangDel[2];\n         LanguageFullName = LangDel[0] + \"-\" + LangDel[1];\n\n     }\n\n     int32_t LangType = V1LangTypes.find(LangTypeStr)->second;\n\n\n\n    // If the voice is char then the pad value must be a string of the EOS token ID (like \"148\").\n    std::string EndToken = JS[\"pad\"].get<std::string>();\n\n    // If it's phonetic then it's the token str, like \"@EOS\"\n    if (LangType != ETTSLanguageType::Char && EndToken.size() && CuArch.Text2Mel != EText2MelModel::Tacotron2Torch)\n        EndToken =  \" \" + EndToken; // In this case we add a space for separation since we directly append the value to the prompt\n\n\n\n    VoiceInfo Inf{JS[\"name\"].get<std::string>(),\n                 JS[\"author\"].get<std::string>(),\n                 JS[\"version\"].get<int>(),\n                 JS[\"description\"].get<std::string>(),\n                 CuArch,\n                 JS[\"note\"].get<std::string>(),\n                 JS[\"sarate\"].get<uint32_t>(),\n                LangName,\n                LanguageFullName,\n                eSpeakLangStr,\n                 EndToken,\n                LangType\n                 };\n\n    if (Inf.Note.size() > MaxNoteSize)\n        Inf.Note = Inf.Note.substr(0,MaxNoteSize);\n\n    return Inf;\n\n\n\n\n\n\n\n}\n\nstd::vector<std::string> VoxUtil::GetLinedFile(const std::string &Path)\n{\n    std::vector<std::string> RetLines;\n    std::ifstream Fi(Path);\n\n    if (!Fi.good()) // File not exists, ret empty vec\n        return RetLines;\n\n    std::string Line;\n    while (std::getline(Fi, Line))\n    {\n        if (Line.size() > 1)\n            RetLines.push_back(Line);\n\n\n    }\n\n    return RetLines;\n}\n\nstd::string VoxUtil::U32ToStr(const std::u32string &InU32)\n{\n    std::wstring_convert<std::codecvt_utf8<char32_t>,char32_t> Converter;\n    return Converter.to_bytes(InU32);\n\n\n\n}\n\nstd::u32string VoxUtil::StrToU32(const std::string &InStr)\n{\n    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> Converter;\n    return Converter.from_bytes(InStr);\n\n}\n"
  },
  {
    "path": "VoxCommon.hpp",
    "content": "#pragma once\n/*\n VoxCommon.hpp : Defines common data structures and constants to be used with TensorVox \n*/\n#include <iostream>\n\n#undef slots // https://github.com/pytorch/pytorch/issues/19405\n\n\n#pragma warning(push, 0) // LibTorch spams us with warnings\n#include <torch/script.h> // One-stop header.\n#pragma warning(pop)\n\n#define slots Q_SLOTS\n\n#include <vector>\n#include \"ext/AudioFile.hpp\"\n#include \"ext/CppFlow/ops.h\"\n#include \"ext/CppFlow/model.h\"\n\n\n\n#include <QMessageBox>\n\n\n\n#define IF_RETURN(cond,ret) if (cond){return ret;}\n\nconst uint32_t CommonSampleRate = 48000;\n\nnamespace VoxCommon{\nconst std::string CommonLangConst = \"_std\";\nconst int32_t TorchMojiLen = 120;\nconst int32_t TorchMojiEmbSize = 2304;\n\n\n}\n\n// https://github.com/almogh52/rnnoise-cmake/blob/d981adb2e797216f456cfcf158f73761a29981f8/examples/rnnoise_demo.c#L31\nconst uint32_t RNNoiseFrameSize = 480;\ntypedef std::vector<std::tuple<std::string,cppflow::tensor>> TensorVec;\n\ntemplate<typename T>\nstruct TFTensor {\n\tstd::vector<T> Data;\n\tstd::vector<int64_t> Shape;\n\tsize_t TotalSize;\n\n};\n\n\nnamespace ETTSRepo {\nenum Enum{\n    TensorflowTTS = 0,\n    CoquiTTS,\n    jaywalnut310, // OG VITS repo\n    keonlee9420,\n    ZDisket,\n    Supertone\n};\n\n}\nnamespace EText2MelModel {\nenum Enum{\n    FastSpeech2 = 0,\n    Tacotron2,\n    VITS,\n    DEVITS,\n    Tacotron2Torch,\n    VITSEvo,\n    Supertonic\n};\n\n}\n\nnamespace EVocoderModel{\nenum Enum{\n    MultiBandMelGAN = 0,\n    MelGANSTFT, // there is no architectural changes so we can use mb-melgan class for melgan-stft\n    NullVocoder, // For fully E2E models\n    iSTFTNet,\n    SupertonicVocoder\n};\n}\n\n// ===========DEPRECATED===============\n// Negative numbers denote character-based language, positive for phoneme based. Standard is char-equivalent language idx = negative(phn-based)\n// In case of English, since -0 doesn't exist, we use -1.\n// For example, German phonetic would be 3, and character based would be -3\n// IPA-phn-based are mainly for Coqui\n// ===========DEPRECATED===============\nnamespace ETTSLanguage{\nenum Enum{\n  GermanChar = -3,\n  SpanishChar,\n  EnglishChar,\n  EnglishPhn,\n  SpanishPhn,\n  GermanPhn,\n  EnglishIPA,\n};\n\n}\n\n/* Language Spec Standard V1:\n- Language is specified with a string from the JSON and the type is saved instead of relying\non ETTSLanguage enum.\n-- The string is LanguageName-Method; for example English-StressedIPA, English-ARPA, German-Char\n- Both pre-V1 standard and current are supported\n- V1 Standard does not require changes in code to add new languages\n-- For eSpeak phonemizers, an additional entry is added with the language name: English-StressedIPA-English (America)\n\n\n\n*/\n\nnamespace ETTSLanguageType{\nenum Enum{\n    ARPA = 0,\n    Char,\n    IPA,\n    GlobalPhone\n};\n}\n\n\nstruct ArchitectureInfo{\n    int Repo;\n    int Text2Mel;\n    int Vocoder;\n\n    // String versions of the info, for displaying.\n    // We want boilerplate int index to str conversion code to be low.\n    std::string s_Repo;\n    std::string s_Text2Mel;\n    std::string s_Vocoder;\n\n};\nstruct VoiceInfo{\n  std::string Name;\n  std::string Author;\n  int32_t Version;\n  std::string Description;\n  ArchitectureInfo Architecture;\n  std::string Note;\n\n  uint32_t SampleRate;\n\n  std::string s_Language; // Language name = English-ARPA -> \"English\"\n  std::string s_Language_Fullname; // Full language name = \"English-ARPA\"\n  std::string s_eSpeakLang; // eSpeak voice name: \"English (America)\"\n\n  std::string EndPadding;\n  int32_t LangType;\n\n\n\n};\n\nnamespace VoxUtil {\n\n\n    std::string U32ToStr(const std::u32string& InU32);\n    std::u32string StrToU32(const std::string& InStr);\n\n    std::vector<std::string> GetLinedFile(const std::string& Path);\n\n    VoiceInfo ReadModelJSON(const std::string& InfoFilename);\n\n\n\n    // Copy PyTorch tensor\n\n    template<typename D>\n    TFTensor<D> CopyTensor(const at::Tensor& InTens){\n        D* Data = InTens.data<D>();\n        std::vector<int64_t> Shape = InTens.sizes().vec();\n\n        size_t TotalSize = 1;\n\n        for (const int64_t& Dim : Shape)\n            TotalSize *= Dim;\n\n        std::vector<D> DataVec = std::vector<D>(Data,Data + TotalSize);\n\n        return TFTensor<D>{DataVec,Shape,TotalSize};\n\n\n    }\n\n\n    // Copy CppFlow (TF) tensor\n\ttemplate<typename F>\n    TFTensor<F> CopyTensor(cppflow::tensor& InTens)\n\t{\n\t\tstd::vector<F> Data = InTens.get_data<F>();\n        std::vector<int64_t> Shape = InTens.shape().get_data<int64_t>();\n\t\tsize_t TotalSize = 1;\n\t\tfor (const int64_t& Dim : Shape)\n\t\t\tTotalSize *= Dim;\n\n\t\treturn TFTensor<F>{Data, Shape, TotalSize};\n\n\n\t}\n\n    template<typename VXVec1>\n    bool FindInVec(VXVec1 In, const std::vector<VXVec1>& Vec, size_t& OutIdx, size_t start = 0) {\n\t\tfor (size_t xx = start;xx < Vec.size();xx++)\n\t\t{\n\t\t\tif (Vec[xx] == In) {\n\t\t\t\tOutIdx = xx;\n\t\t\t\treturn true;\n\n\t\t\t}\n\n\t\t}\n\n\n\t\treturn false;\n\n\t}\n    template<typename VXVec1, typename X>\n    bool FindInVec2(VXVec1 In, const std::vector<X>& Vec, size_t& OutIdx, size_t start = 0) {\n        for (size_t xx = start;xx < Vec.size();xx++)\n        {\n            if (Vec[xx] == In) {\n                OutIdx = xx;\n                return true;\n\n            }\n\n        }\n\n\n        return false;\n\n    }\n\n\tvoid ExportWAV(const std::string& Filename, const std::vector<float>& Data, unsigned SampleRate);\n}\n"
  },
  {
    "path": "attention.cpp",
    "content": "#include \"attention.h\"\n\n\nAttention::Attention(QWidget *parent) : QCustomPlot(parent)\n{\n\n    QBrush FillBrush(QColor(100,100,100));\n    this->setBackground(FillBrush);\n    QColor White(255,255,255);\n    QPen AxisPen(QColor(150,150,150));\n    xAxis->setTickLabelColor(White);\n    yAxis->setTickLabelColor(White);\n\n    xAxis->setBasePen(AxisPen);\n    yAxis->setBasePen(AxisPen);\n\n    xAxis->setLabel(\"Decoder timestep\");\n    yAxis->setLabel(\"Encoder timestep\");\n\n    xAxis->setLabelColor(White);\n    yAxis->setLabelColor(White);\n    QFont Fnt = QFont(font().family(), 10);\n\n    xAxis->setLabelFont(QFont(font().family(), 9));\n    yAxis->setLabelFont(QFont(font().family(), 9));\n\n    yAxis->setTickPen(AxisPen);\n    xAxis->setTickPen(AxisPen);\n\n    yAxis->setSubTickPen(AxisPen);\n    xAxis->setSubTickPen(AxisPen);\n\n\n\n}\n\nvoid Attention::DoPlot(const TFTensor<float> &Alignment)\n{\n    const auto& Shp = Alignment.Shape;\n\n\n\n\n    Map->data()->setSize((int32_t)Shp[2],(int32_t)Shp[1]);\n\n    Map->data()->setRange(QCPRange(0.0,(double)Shp[2]),QCPRange(0.0,(double)Shp[1]));\n    for (int64_t x = 0; x < Shp[2];x++)\n    {\n        for (int64_t y = 0;y < Shp[1];y++)\n        {\n            size_t i = x + Shp[2]*y;\n            Map->data()->setCell(x,y,(double)Alignment.Data[i]);\n\n        }\n\n\n    }\n    Map->setDataRange(QCPRange(0.0,1.0));\n    xAxis->setRange(QCPRange(0.0,(double)Shp[2]));\n\n    yAxis->setRange(QCPRange(0.0,(double)Shp[1]));\n\n    rescaleAxes();\n\n    replot();\n\n\n}\n\n"
  },
  {
    "path": "attention.h",
    "content": "#ifndef ATTENTION_H\n#define ATTENTION_H\n\n#include \"ext/qcustomplot.h\"\n#include \"VoxCommon.hpp\"\n\nclass Attention : public QCustomPlot\n{\npublic:\n    Attention(QWidget *parent = nullptr);\n\n    void DoPlot(const TFTensor<float>& Alignment);\n\n    QCPColorMap* Map;\n\n};\n\n#endif // ATTENTION_H\n"
  },
  {
    "path": "batchdenoisedlg.cpp",
    "content": "#include \"batchdenoisedlg.h\"\n#include \"ui_batchdenoisedlg.h\"\n\n#include <QFileDialog>\n#include <QDir>\n#include <QDirIterator>\n#include \"mainwindow.h\"\n\n#define ManWi ((MainWindow*)pMainWindow)\n\nBatchDenoiseDlg::BatchDenoiseDlg(QWidget *parent) :\n    QDialog(parent),\n    ui(new Ui::BatchDenoiseDlg)\n{\n    ui->setupUi(this);\n    ProcessedFiles = 0;\n    CurrentIndex = 0;\n    Failures = 0;\n\n}\n\n\n// can't define in header because InferDetails belongs to mainwindow.h and including it in this dlg's .h would case circular dependency error\nInferDetails MakeInferDetails(const std::vector<float>& InAudat,const QString& FilePath,unsigned InSampleRate,int32_t OutSampleRate)\n{\n    InferDetails Dets;\n    Dets.F0 = 0.0f;\n    Dets.Speed = 0.0f;\n    Dets.Energy = 0.0f;\n    Dets.pItem = nullptr; // the mainwindow's function will make an item for us.\n    Dets.Prompt = \"\";\n    Dets.SpeakerID = OutSampleRate; // SpeakerID will double as resample when a denoise only job is requested.\n    Dets.EmotionID = -1;\n    Dets.Denoise = true;\n    Dets.Amplification = 1.f;\n    Dets.ExportFileName = FilePath;\n\n\n    Dets.VoiceName = \"\";\n    Dets.ForcedAudio = InAudat;\n    Dets.SampleRate = InSampleRate;\n\n    return Dets;\n\n}\n\n\nBatchDenoiseDlg::~BatchDenoiseDlg()\n{\n    delete ui;\n}\n\nvoid BatchDenoiseDlg::IterateDo()\n{\n\n    if (ProcessedFiles == Files.size() && ManWi->GetCountItems() == 0)\n    {\n        // It's done!\n        delete timIter;\n        SetControls(true);\n\n        return;\n\n    }\n\n    if (ManWi->GetCountItems() != 0)\n        return;\n\n\n    ManWi->DenBatchSize = ui->spbBatchSz->value();\n    ManWi->DenDone = 0;\n    if (CurrentIndex + ui->spbBatchSz->value() > Files.size())\n        ManWi->DenBatchSize = Files.size() - CurrentIndex;\n\n    for (int32_t i = 0;i < ui->spbBatchSz->value();i++)\n    {\n\n\n\n        QString CurrentFn = Files[CurrentIndex];\n\n        AudioFile<float> AudFile;\n        InferDetails CurrentDets;\n        bool SkipCurrent = false;\n        try {\n            AudFile.load(CurrentFn.toStdString());\n\n            if ( (ui->chkExcludeAlreadySR->isChecked() && AudFile.getSampleRate() == (uint32_t)ui->spbOutSR->value()) || AudFile.getLengthInSeconds() < 1.0)\n                SkipCurrent = true;\n            else\n                CurrentDets = MakeInferDetails(AudFile.samples[0],CurrentFn,AudFile.getSampleRate(),ui->spbOutSR->value());\n\n\n\n\n        }  catch (...) {\n\n           CurrentIndex += 1; // NOT i !!!!!!!\n           ProcessedFiles += 1;\n           ++Failures;\n\n           if (CurrentIndex > Files.size() - 1)\n               break;\n\n           continue;\n        }\n\n        if (!SkipCurrent)\n            ManWi->PushToInfers(CurrentDets);\n        else\n            ManWi->DenBatchSize -= 1; // Indicate to the auto clear that we will send less files\n\n\n\n\n        CurrentIndex += 1; // NOT i !!!!!!!\n        ProcessedFiles += 1;\n\n        if (CurrentIndex > Files.size() - 1)\n            break;\n\n\n    }\n    SetLabel();\n\n\n\n\n\n\n\n\n}\n\nvoid BatchDenoiseDlg::on_btnFindFolder_clicked()\n{\n\n    QString Dir = QFileDialog::getExistingDirectory(this, tr(\"Find base folder of your WAVs\"),\n                                                \"\",\n                                                QFileDialog::ShowDirsOnly\n                                                | QFileDialog::DontResolveSymlinks);\n\n    ui->edtFolPath->setText(Dir);\n\n    UpdateDirectory();\n\n}\n\nvoid BatchDenoiseDlg::on_edtFolPath_editingFinished()\n{\n    UpdateDirectory();\n\n}\n\nvoid BatchDenoiseDlg::SetLabel()\n{\n    ui->lblFiles->setText(QString(QString::number(ProcessedFiles) + \" / \" + QString::number(Files.size()) + \" files, \" + QString::number(Failures) + \" failures.\") );\n\n    ui->pgFiles->setValue(ProcessedFiles);\n    ui->pgFiles->update();\n}\n\nvoid BatchDenoiseDlg::UpdateDirectory()\n{\n    if (ui->edtFolPath->text().isEmpty())\n        return;\n\n    if (Files.size())\n        Files.clear();\n\n    QDirIterator DirIt(ui->edtFolPath->text(),QDirIterator::Subdirectories);\n    while (DirIt.hasNext())\n    {\n        DirIt.next();\n        if (QFileInfo(DirIt.filePath()).isFile() && QFileInfo(DirIt.filePath()).suffix() == \"wav\")\n            Files.push_back(DirIt.filePath());\n    }\n    CurrentIndex = 0;\n    ProcessedFiles = 0;\n    Failures = 0;\n\n    ui->pgFiles->setRange(0,Files.size());\n\n\n    SetLabel();\n\n\n}\n\nvoid BatchDenoiseDlg::on_btnStart_clicked()\n{\n\n\n\n    CurrentIndex = 0;\n    ProcessedFiles = 0;\n    Failures = 0;\n    ManWi->DenBatchSize = ui->spbBatchSz->value();\n\n    timIter = new QTimer(this);\n    timIter->setSingleShot(false);\n    timIter->setInterval(1000);\n\n    connect(timIter,&QTimer::timeout,this,&BatchDenoiseDlg::IterateDo);\n\n    timIter->start();\n\n    SetControls(false);\n}\n\nvoid BatchDenoiseDlg::SetControls(bool En)\n{\n    ui->edtFolPath->setEnabled(En);\n    ui->spbBatchSz->setEnabled(En);\n    ui->btnStart->setEnabled(En);\n    ui->btnFindFolder->setEnabled(En);\n\n}\n"
  },
  {
    "path": "batchdenoisedlg.h",
    "content": "#ifndef BATCHDENOISEDLG_H\n#define BATCHDENOISEDLG_H\n\n#include <QDialog>\n#include <QTimer>\nnamespace Ui {\nclass BatchDenoiseDlg;\n}\n\nclass BatchDenoiseDlg : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit BatchDenoiseDlg(QWidget *parent = nullptr);\n    ~BatchDenoiseDlg();\n\n\n\n    // if we included mainwindow.h in here it would result in circular dependency problem so we include it in the .cpp\n    // and make it a void* here\n    void* pMainWindow;\n\nprivate slots:\n\n\n    void IterateDo();\n    void on_btnFindFolder_clicked();\n\n    void on_edtFolPath_editingFinished();\n\n    void on_btnStart_clicked();\n\nprivate:\n\n    void SetControls(bool En);\n\n    QStringList Files;\n    QTimer* timIter;\n    int32_t ProcessedFiles;\n    int32_t CurrentIndex;\n    int32_t Failures;\n\n\n\n    void SetLabel();\n    void UpdateDirectory();\n    Ui::BatchDenoiseDlg *ui;\n};\n\n#endif // BATCHDENOISEDLG_H\n"
  },
  {
    "path": "batchdenoisedlg.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>BatchDenoiseDlg</class>\n <widget class=\"QDialog\" name=\"BatchDenoiseDlg\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>737</width>\n    <height>403</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Dialog</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <widget class=\"QLabel\" name=\"label\">\n       <property name=\"text\">\n        <string>Folder Path</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLineEdit\" name=\"edtFolPath\"/>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"btnFindFolder\">\n       <property name=\"text\">\n        <string>Browse</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n     <item>\n      <widget class=\"QLabel\" name=\"label_3\">\n       <property name=\"text\">\n        <string>Batch size:</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QSpinBox\" name=\"spbBatchSz\">\n       <property name=\"maximum\">\n        <number>16384</number>\n       </property>\n       <property name=\"singleStep\">\n        <number>32</number>\n       </property>\n       <property name=\"value\">\n        <number>4096</number>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"label_4\">\n       <property name=\"text\">\n        <string>Output sampling rate (Hz): </string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QSpinBox\" name=\"spbOutSR\">\n       <property name=\"maximum\">\n        <number>96000</number>\n       </property>\n       <property name=\"singleStep\">\n        <number>8000</number>\n       </property>\n       <property name=\"value\">\n        <number>48000</number>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QCheckBox\" name=\"chkExcludeAlreadySR\">\n       <property name=\"text\">\n        <string>Exclude already SR matching</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"label_2\">\n     <property name=\"text\">\n      <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Note: Will find all WAVs, recursive (folders and subfolders), and REPLACE FILES. If you don't want it to do that, make a copy first. Treats all files as mono.&lt;/p&gt;&lt;p&gt;Note 2: Files are resampled on input and output accordingly&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"lblFiles\">\n     <property name=\"text\">\n      <string>Files: 0 / 0</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QProgressBar\" name=\"pgFiles\">\n     <property name=\"value\">\n      <number>0</number>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QPushButton\" name=\"btnStart\">\n     <property name=\"text\">\n      <string>Start</string>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "bert.cpp",
    "content": "#include \"bert.h\"\n#include <windows.h>\n\nBERT::BERT()\n{\n\n}\n\nBERT::BERT(const std::string &Path, const std::string &DictPath)\n{\n    Initialize(Path, DictPath);\n}\n\nvoid BERT::Initialize(const std::string &Path, const std::string &DictPath)\n{\n    Model = torch::jit::load(Path);\n    Tokenizer = std::make_unique<FullTokenizer>(DictPath,true);\n\n\n}\n\nstd::pair<TFTensor<float>, TFTensor<float> > BERT::Infer(const std::string &InText)\n{\n    torch::NoGradGuard no_grad;\n\n    auto Tokens = Tokenizer->tokenize(InText + \"\\n\");\n    auto Ids = Tokenizer->convertTokensToIds(Tokens);\n\n    std::vector<int32_t> InTokens(Ids.begin(),Ids.end());\n\n\n\n    auto InIDS = torch::tensor(InTokens).unsqueeze(0); // (1, tokens)\n    std::pair<TFTensor<float>,TFTensor<float>> BERTOutputs;\n\n    try{\n        auto Output = Model({InIDS}).toTuple(); // (hidden states, pooled)\n        BERTOutputs.first = VoxUtil::CopyTensor<float>(Output.get()->elements()[0].toTensor());\n        BERTOutputs.second = VoxUtil::CopyTensor<float>(Output.get()->elements()[1].toTensor());\n\n\n\n    }\n\n    catch (const std::exception& e) {\n        int msgboxID = MessageBox(\n                    NULL,\n                    (LPCWSTR)QString::fromStdString(e.what()).toStdWString().c_str(),\n                    (LPCWSTR)L\"Error1!!\",\n                    MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2\n                    );\n\n\n        return BERTOutputs;\n\n    }\n\n\n\n\n\n\n    return BERTOutputs;\n\n\n\n}\n"
  },
  {
    "path": "bert.h",
    "content": "#ifndef BERT_H\n#define BERT_H\n\n#include \"VoxCommon.hpp\"\n\n#include \"berttokenizer.h\"\n\n// BERT: Class for inference of TorchScript-exported BERT.\nclass BERT\n{\nprivate:\n    torch::jit::script::Module Model;\n    std::unique_ptr<FullTokenizer> Tokenizer;\n\npublic:\n    BERT();\n    BERT(const std::string& Path,const std::string& DictPath);\n    void Initialize(const std::string& Path,const std::string& DictPath);\n\n\n    // Do inference on BERT model.\n    // Returns 2 tensors:\n    // [1, tokens, channels] : Hidden states\n    // [1, channels] : Pooled embeddings\n    std::pair<TFTensor<float>,TFTensor<float>> Infer(const std::string& InText);\n};\n\n#endif // BERT_H\n"
  },
  {
    "path": "berttokenizer.cpp",
    "content": "#include \"berttokenizer.h\"\nconst std::wstring stripChar = L\" \\t\\n\\r\\v\\f\";\n#include \"utf8proc.h\"\n#include \"ext/ZCharScannerWide.h\"\n\nconst std::vector<std::wstring> sDelimChar = {L\" \",L\"\\t\",L\"\\n\",L\"\\r\",L\"\\v\",L\"\\f\"};\n\nstatic std::string normalize_nfd(const std::string& s) {\n    std::string ret;\n    char *result = (char *) utf8proc_NFD((unsigned char *)s.c_str());\n    if (result) {\n        ret = std::string(result);\n        free(result);\n        result = NULL;\n    }\n    return ret;\n}\n\nstatic bool isStripChar(const wchar_t& ch) {\n    return stripChar.find(ch) != std::wstring::npos;\n}\n\nstatic std::wstring strip(const std::wstring& text) {\n    std::wstring ret =  text;\n    if (ret.empty()) return ret;\n    size_t pos = 0;\n    while (pos < ret.size() && isStripChar(ret[pos])) pos++;\n    if (pos != 0) ret = ret.substr(pos, ret.size() - pos);\n    pos = ret.size() - 1;\n    while (pos != (size_t)-1 && isStripChar(ret[pos])) pos--;\n    return ret.substr(0, pos + 1);\n}\n\nstatic std::vector<std::wstring> split(const std::wstring& text) {\n\n\n\n    std::vector<std::wstring>  result;\n    ZStringDelimiterWide Del(text);\n\n    Del.AddDelimiter(L\" \");\n\n    result = Del.GetTokens();\n    if (!result.size())\n        result.push_back(text); //\n\n\n\n    return result;\n}\n\nstatic std::vector<std::wstring> whitespaceTokenize(const std::wstring& text) {\n    std::wstring rtext = strip(text);\n    if (rtext.empty()) return std::vector<std::wstring>();\n    return split(text);\n}\n\nstatic std::wstring convertToUnicode(const std::string& text) {\n    size_t i = 0;\n    std::wstring ret;\n    while (i < text.size()) {\n        wchar_t codepoint;\n        utf8proc_ssize_t forward = utf8proc_iterate((utf8proc_uint8_t *)&text[i], text.size() - i, (utf8proc_int32_t*)&codepoint);\n        if (forward < 0) return L\"\";\n        ret += codepoint;\n        i += forward;\n    }\n    return ret;\n}\n\nstatic std::string convertFromUnicode(const std::wstring& wText) {\n    char dst[64];\n    std::string ret;\n    for (auto ch : wText) {\n        utf8proc_ssize_t num = utf8proc_encode_char(ch, (utf8proc_uint8_t *)dst);\n        if (num <= 0) return \"\";\n        ret += std::string(dst, dst+num);\n    }\n    return ret;\n}\n\nstatic std::wstring tolower(const std::wstring& s) {\n    std::wstring ret(s.size(), L' ');\n    for (size_t i = 0; i < s.size(); i++) {\n        ret[i] = utf8proc_tolower(s[i]);\n    }\n    return ret;\n}\n\nstatic std::shared_ptr<Vocab> loadVocab(const std::string& vocabFile) {\n    std::shared_ptr<Vocab> vocab(new Vocab);\n    size_t index = 0;\n    std::ifstream ifs(vocabFile, std::ifstream::in);\n    std::string line;\n    while (getline(ifs, line)) {\n        std::wstring token = convertToUnicode(line);\n        if (token.empty()) break;\n        token = strip(token);\n        (*vocab)[token] = index;\n        index++;\n    }\n\n    return vocab;\n}\n\nBasicTokenizer::BasicTokenizer(bool doLowerCase)\n    : mDoLowerCase(doLowerCase) {\n}\n\nstd::wstring BasicTokenizer::cleanText(const std::wstring& text) const {\n    std::wstring output;\n    for (const wchar_t& cp : text)  {\n        if (cp == 0 || cp == 0xfffd || isControol(cp)) continue;\n        if (isWhitespace(cp)) output += L\" \";\n        else output += cp;\n    }\n    return output;\n}\n\nbool BasicTokenizer::isControol(const wchar_t& ch) const {\n    if (ch== L'\\t' || ch== L'\\n' || ch== L'\\r') return false;\n    auto cat = utf8proc_category(ch);\n    if (cat == UTF8PROC_CATEGORY_CC || cat == UTF8PROC_CATEGORY_CF) return true;\n    return false;\n}\n\nbool BasicTokenizer::isWhitespace(const wchar_t& ch) const {\n    if (ch== L' ' || ch== L'\\t' || ch== L'\\n' || ch== L'\\r') return true;\n    auto cat = utf8proc_category(ch);\n    if (cat == UTF8PROC_CATEGORY_ZS) return true;\n    return false;\n}\n\nbool BasicTokenizer::isPunctuation(const wchar_t& ch) const {\n    if ((ch >= 33 && ch <= 47) || (ch >= 58 && ch <= 64) ||\n        (ch >= 91 && ch <= 96) || (ch >= 123 && ch <= 126)) return true;\n    auto cat = utf8proc_category(ch);\n    if (cat == UTF8PROC_CATEGORY_PD || cat == UTF8PROC_CATEGORY_PS\n            || cat == UTF8PROC_CATEGORY_PE || cat == UTF8PROC_CATEGORY_PC\n            || cat == UTF8PROC_CATEGORY_PO //sometimes ¶ belong SO\n            || cat == UTF8PROC_CATEGORY_PI\n            || cat == UTF8PROC_CATEGORY_PF) return true;\n    return false;\n}\n\nbool BasicTokenizer::isChineseChar(const wchar_t& ch) const {\n    if ((ch >= 0x4E00 && ch <= 0x9FFF) ||\n        (ch >= 0x3400 && ch <= 0x4DBF) ||\n        (ch >= 0x20000 && ch <= 0x2A6DF) ||\n        (ch >= 0x2A700 && ch <= 0x2B73F) ||\n        (ch >= 0x2B740 && ch <= 0x2B81F) ||\n        (ch >= 0x2B820 && ch <= 0x2CEAF) ||\n        (ch >= 0xF900 && ch <= 0xFAFF) ||\n        (ch >= 0x2F800 && ch <= 0x2FA1F))\n        return true;\n    return false;\n}\n\nstd::wstring BasicTokenizer::tokenizeChineseChars(const std::wstring& text) const {\n    std::wstring output;\n    for (auto& ch : text) {\n        if (isChineseChar(ch)) {\n            output += L' ';\n            output += ch;\n            output += L' ';\n        }\n        else\n            output += ch;\n    }\n    return output;\n}\n\nstd::wstring BasicTokenizer::runStripAccents(const std::wstring& text) const {\n    //Strips accents from a piece of text.\n    std::wstring nText;\n    try {\n        nText = convertToUnicode(normalize_nfd(convertFromUnicode(text)));\n    } catch (std::bad_cast& e) {\n        std::cerr << \"bad_cast\" << std::endl;\n        return L\"\";\n    }\n\n    std::wstring output;\n    for (auto& ch : nText) {\n        auto cat = utf8proc_category(ch);\n        if (cat == UTF8PROC_CATEGORY_MN) continue;\n        output += ch;\n    }\n    return output;\n}\n\nstd::vector<std::wstring> BasicTokenizer::runSplitOnPunc(const std::wstring& text) const {\n    size_t i = 0;\n    bool startNewWord = true;\n    std::vector<std::wstring> output;\n    while (i < text.size()) {\n        wchar_t ch = text[i];\n        if (isPunctuation(ch)) {\n            output.push_back(std::wstring(&ch, 1));\n            startNewWord = true;\n        }\n        else {\n            if (startNewWord) output.push_back(std::wstring());\n            startNewWord = false;\n            output[output.size() - 1] += ch;\n        }\n        i++;\n    }\n    return output;\n}\n\nstd::vector<std::wstring> BasicTokenizer::tokenize(const std::string& text) const {\n    std::wstring nText = convertToUnicode(text);\n    nText = cleanText(nText);\n\n    nText = tokenizeChineseChars(nText);\n\n    const std::vector<std::wstring>& origTokens = whitespaceTokenize(nText);\n    std::vector<std::wstring> splitTokens;\n    for (std::wstring token : origTokens) {\n        if (mDoLowerCase) {\n            token = tolower(token);\n            token = runStripAccents(token);\n        }\n        const auto& tokens = runSplitOnPunc(token);\n        splitTokens.insert(splitTokens.end(), tokens.begin(), tokens.end());\n    }\n    ZStringDelimiterWide DelRs;\n    std::wstring WSP = DelRs.Reassemble(L\" \",splitTokens);\n\n\n\n\n    return whitespaceTokenize(WSP);\n}\n\nWordpieceTokenizer::WordpieceTokenizer(const std::shared_ptr<Vocab> vocab, const std::wstring& unkToken, size_t maxInputCharsPerWord)\n    : mVocab(vocab),\n    mUnkToken(unkToken),\n    mMaxInputCharsPerWord(maxInputCharsPerWord) {\n}\n\nstd::vector<std::wstring> WordpieceTokenizer::tokenize(const std::wstring& text) const {\n    std::vector<std::wstring> outputTokens;\n\n    for (auto& token : whitespaceTokenize(text)) {\n        if (token.size() > mMaxInputCharsPerWord) {\n            outputTokens.push_back(mUnkToken);\n        }\n        bool isBad = false;\n        size_t start = 0;\n        std::vector<std::wstring> subTokens;\n        while (start < token.size()) {\n            size_t end = token.size();\n            std::wstring curSubstr;\n            bool hasCurSubstr = false;\n            while (start < end) {\n                std::wstring substr = token.substr(start, end - start);\n                if (start > 0) substr = L\"##\" + substr;\n                if (mVocab->find(substr) != mVocab->end()) {\n                    curSubstr = substr;\n                    hasCurSubstr = true;\n                    break;\n                }\n                end--;\n            }\n            if (!hasCurSubstr) {\n                isBad = true;\n                break;\n            }\n            subTokens.push_back(curSubstr);\n            start = end;\n        }\n        if (isBad) outputTokens.push_back(mUnkToken);\n        else outputTokens.insert(outputTokens.end(), subTokens.begin(), subTokens.end());\n    }\n    return outputTokens;\n}\n\nFullTokenizer::FullTokenizer(const std::string& vocabFile, bool doLowerCase) :\n    mVocab(loadVocab(vocabFile)),\n    mBasicTokenizer(BasicTokenizer(doLowerCase)),\n    mWordpieceTokenizer(WordpieceTokenizer(mVocab)) {\n    for (auto& v : *mVocab) mInvVocab[v.second] = v.first;\n}\n\nstd::vector<std::wstring> FullTokenizer::tokenize(const std::string& text) const {\n    std::vector<std::wstring> splitTokens;\n    for (auto& token : mBasicTokenizer.tokenize(text))\n        for (auto& subToken : mWordpieceTokenizer.tokenize(token))\n            splitTokens.push_back(subToken);\n    return splitTokens;\n}\n\nstd::vector<size_t> FullTokenizer::convertTokensToIds(const std::vector<std::wstring>& text) const {\n    std::vector<size_t> ret(text.size());\n    for (size_t i = 0; i < text.size(); i++) {\n        ret[i] = (*mVocab)[text[i]];\n    }\n    return ret;\n}\n"
  },
  {
    "path": "berttokenizer.h",
    "content": "#ifndef BERTTOKENIZER_H\n#define BERTTOKENIZER_H\n// https://gist.github.com/luistung/ace4888cf5fd1bad07844021cb2c7ecf\n\n#include <iostream>\n#include <fstream>\n#include <string>\n#include <vector>\n#include <unordered_map>\n\nusing Vocab = std::unordered_map<std::wstring, size_t>;\nusing InvVocab = std::unordered_map<size_t, std::wstring>;\n\nclass BasicTokenizer {\npublic:\n    BasicTokenizer(bool doLowerCase);\n    std::vector<std::wstring> tokenize(const std::string& text) const;\n\nprivate:\n    std::wstring cleanText(const std::wstring& text) const;\n    bool isControol(const wchar_t& ch) const;\n    bool isWhitespace(const wchar_t& ch) const;\n    bool isPunctuation(const wchar_t& ch) const;\n    bool isChineseChar(const wchar_t& ch) const;\n    std::wstring tokenizeChineseChars(const std::wstring& text) const;\n    bool isStripChar(const wchar_t& ch) const;\n    std::wstring strip(const std::wstring& text) const;\n    std::vector<std::wstring> split(const std::wstring& text) const;\n    std::wstring runStripAccents(const std::wstring& text) const;\n    std::vector<std::wstring> runSplitOnPunc(const std::wstring& text) const;\n\n    bool mDoLowerCase;\n};\n\nclass WordpieceTokenizer {\npublic:\n    WordpieceTokenizer(std::shared_ptr<Vocab> vocab, const std::wstring& unkToken = L\"[UNK]\", size_t maxInputCharsPerWord=200);\n    std::vector<std::wstring> tokenize(const std::wstring& text) const;\n\nprivate:\n    std::shared_ptr<Vocab> mVocab;\n    std::wstring mUnkToken;\n    size_t mMaxInputCharsPerWord;\n};\n\nclass FullTokenizer {\npublic:\n    FullTokenizer(const std::string& vocabFile, bool doLowerCase = true);\n    std::vector<std::wstring> tokenize(const std::string& text) const;\n    std::vector<size_t> convertTokensToIds(const std::vector<std::wstring>& text) const;\n\nprivate:\n    std::shared_ptr<Vocab> mVocab;\n    InvVocab mInvVocab;\n    std::string mVocabFile;\n    bool mDoLowerCase;\n    BasicTokenizer mBasicTokenizer;\n    WordpieceTokenizer mWordpieceTokenizer;\n};\n\n\n\n\n#endif // BERTTOKENIZER_H\n"
  },
  {
    "path": "devits.cpp",
    "content": "#include \"devits.h\"\n\n\nDEVITS::DEVITS()\n{\n\n}\n\n#include <windows.h>\n#include <string>\n#include <vector>\n#include <sstream>\n\nvoid displayVectorInMessageBox(const std::vector<int>& numbers) {\n    // Convert vector to string\n    std::stringstream ss;\n    for (int num : numbers) {\n        ss << num << \" \";\n    }\n\n    std::string strNumbers = ss.str();\n\n    // Display in a MessageBox\n    MessageBoxA(NULL, strNumbers.c_str(), \"Vector Elements\", MB_OK);\n}\n\nTFTensor<float> DEVITS::DoInferenceDE(const std::vector<int32_t> &InputIDs, const TFTensor<float> &MojiIn, const TFTensor<float> &BERTIn, const std::vector<float> &ArgsFloat, const std::vector<int32_t> ArgsInt, int32_t SpeakerID, int32_t EmotionID)\n{\n    // without this memory consumption is 4x\n    torch::NoGradGuard no_grad;\n\n\n\n    std::vector<int64_t> PaddedIDs;\n\n\n    PaddedIDs = ZeroPadVec(InputIDs);\n   // displayVectorInMessageBox(InputIDs);\n\n\n    std::vector<int64_t> inLen = { (int64_t)PaddedIDs.size() };\n\n\n    // ZDisket: Is this really necessary?\n    torch::TensorOptions Opts = torch::TensorOptions().requires_grad(false);\n\n    auto InIDS = torch::tensor(PaddedIDs, Opts).unsqueeze(0);\n    auto InLens = torch::tensor(inLen, Opts);\n    auto MojiHidden = torch::tensor(MojiIn.Data).unsqueeze(0);\n    auto BERTHidden = torch::tensor(BERTIn.Data).reshape(BERTIn.Shape);\n\n    std::vector<int64_t> BERTSz = {BERTIn.Shape[1]};\n    auto BERTLens = torch::tensor(BERTSz);\n\n    auto InLenScale = torch::tensor({ ArgsFloat[0]}, Opts);\n\n\n\n    std::vector<torch::jit::IValue> inputs{ InIDS,InLens, MojiHidden, BERTHidden, BERTLens, InLenScale };\n\n    if (SpeakerID != -1){\n        auto InSpkid = torch::tensor({SpeakerID},Opts);\n        inputs.push_back(InSpkid);\n    }\n\n\n\n\n\n    // Infer\n\n    c10::IValue Output = Model.get_method(\"infer_ts\")(inputs);\n\n    // Output = tuple (audio,att)\n\n    auto OutputT = Output.toTuple();\n\n    // Grab audio\n    // [1, frames] -> [frames]\n    auto AuTens = OutputT.get()->elements()[0].toTensor().squeeze();\n\n    // Grab Attention\n    // [1, 1, x, y] -> [x, y] -> [y,x] -> [1, y, x]\n    auto AttTens = OutputT.get()->elements()[1].toTensor().squeeze().transpose(0,1).unsqueeze(0);\n\n    Attention = VoxUtil::CopyTensor<float>(AttTens);\n\n    return VoxUtil::CopyTensor<float>(AuTens);\n\n}\n"
  },
  {
    "path": "devits.h",
    "content": "#ifndef DEVITS_H\n#define DEVITS_H\n#include \"vits.h\"\n\nclass DEVITS : public VITS\n{\npublic:\n    DEVITS();\n\n    /*\n    Do inference on a DE-VITS model.\n\n    -> InputIDs: Input IDs of tokens for inference\n    -> SpeakerID: ID of the speaker in the model to do inference on. If single speaker, always leave at 0. If multispeaker, refer to your model.\n    -> MojiIn: TorchMoji hidden states size [tm]\n    -> BERTIn: BERT hidden states size [1, n_tokens, channels]\n    -> ArgsFloat[0]: Length scale.\n\n    <- Returns: TFTensor<float> with shape {frames} of audio data\n    */\n    TFTensor<float> DoInferenceDE(const std::vector<int32_t>& InputIDs, const TFTensor<float>& MojiIn, const TFTensor<float>& BERTIn,const std::vector<float>& ArgsFloat,const std::vector<int32_t> ArgsInt, int32_t SpeakerID = 0, int32_t EmotionID = -1);\n};\n\n#endif // DEVITS_H\n"
  },
  {
    "path": "espeakphonemizer.cpp",
    "content": "#include \"espeakphonemizer.h\"\n#include <espeak/speak_lib.h>\n\n\nstatic const std::u32string Punctuation_t = U\",.;¡!¿?:-~\";\nstatic const std::u32string Punctuation_ns = U\"¿-~\";\n\nusing namespace ESP;\n\nstd::string ESpeakPhonemizer::ToPhon(const std::string &InTxt)\n{\n    const char* TextPtr = InTxt.c_str();\n    const void** OurPtr = (const void**)&TextPtr;\n    const char* Phon = espeak_TextToPhonemes(OurPtr, espeakCHARS_AUTO, (int)PhonemePars.to_ulong());\n\n\n    return std::string(Phon);\n}\n\n\nvoid ESpeakPhonemizer::Initialize(const std::string &DataPath, const std::string &VoiceName)\n{\n    // these are irrelevant because we don't play any audio, we just use the phonemizer\n    espeak_AUDIO_OUTPUT output = AUDIO_OUTPUT_SYNCH_PLAYBACK;\n    int buflength = 500, options = 0;\n\n\n    auto Err1 = espeak_Initialize(output, buflength, DataPath.c_str(), options);\n    auto Err = espeak_SetVoiceByName(VoiceName.c_str());\n    EVoiceName = VoiceName;\n\n\n    PhonemePars[1] = 1; // set IPA\n\n\n}\n\nstd::string ESpeakPhonemizer::Phonemize(const std::string &Input)\n{\n    std::u32string In = VoxUtil::StrToU32(Input);\n\n    // ESpeak's phonemize function stops at punctuation, so we split it up into chunks, phonemize, then put them back together\n    PunctSplitVec SplitVec = IterativePunctuationSplit(In, Punctuation_t);\n\n    std::string Assembled = \"\";\n    bool Space = false;\n    for (const auto& Spli : SplitVec)\n    {\n\n\n        std::string Pibber = VoxUtil::U32ToStr(Spli.second);\n        if (!Spli.first)\n        {\n            Pibber = ToPhon(Pibber);\n            if (Space)\n                Assembled += \" \";\n\n\n        }else\n        {\n            Space = true;\n            for (const auto& PCh : Punctuation_ns){\n                if (Spli.second.find(PCh) != std::u32string::npos)\n                    Space = false;\n\n            }\n\n\n\n\n\n        }\n        Assembled += Pibber;\n\n\n    }\n\n    return Assembled;\n\n}\n\nESpeakPhonemizer::ESpeakPhonemizer()\n{\n\n}\n\nESP::PunctSplitVec ESP::IterativePunctuationSplit(const std::u32string &Input, const std::u32string &Punct)\n{\n    PunctSplitVec Ret;\n\n    std::u32string CuStr = U\"\";\n    for (const auto& Ch : Input) {\n\n        if (Punct.find(Ch) != std::u32string::npos) {\n            if (CuStr.size())\n                Ret.push_back({ false,CuStr });\n\n            std::u32string PunctOnly(1,Ch);\n            Ret.push_back({ true, PunctOnly });\n            CuStr = U\"\";\n\n        }\n        else {\n            CuStr += Ch;\n        }\n\n\n    }\n    Ret.push_back({ false,CuStr });\n    return Ret;\n\n}\n\n"
  },
  {
    "path": "espeakphonemizer.h",
    "content": "#ifndef ESPEAKPHONEMIZER_H\n#define ESPEAKPHONEMIZER_H\n\n/*\n\n  ESpeakPhonemizer: Tool for IPA Text2Phon using ESpeak NG as backend.\n\n*/\n#include <iostream>\n#include <string>\n#include <bitset>\n#include \"VoxCommon.hpp\"\n#include <vector>\n\nnamespace ESP{\ntypedef std::pair<bool, std::u32string> PunctSplit;\ntypedef std::vector<PunctSplit> PunctSplitVec;\n\n\n// Returns vector<pair<IS_PUNCTUATION,String>>\nPunctSplitVec IterativePunctuationSplit(const std::u32string& Input, const std::u32string& Punct);\n\n}\n\nclass ESpeakPhonemizer\n{\nprivate:\n    std::bitset<sizeof(int) * 8> PhonemePars;\n    std::string ToPhon(const std::string& InTxt);\n\n    std::string EVoiceName;\npublic:\n\n    // DataPath: Path to ESpeak NG data dir\n    // VoiceName: Name of voice to use for phonemizing (like \"Spanish (Latin America)\")\n    void Initialize(const std::string& DataPath,const std::string& VoiceName);\n\n\n    // Phonemize text using ESpeak phonemizer\n    // Unlike regular phonemizer, feed complete texts at once instead of just words.\n    std::string Phonemize(const std::string& Input);\n\n    ESpeakPhonemizer();\n    const std::string& GetVoiceName() const {return EVoiceName;};\n};\n\n#endif // ESPEAKPHONEMIZER_H\n"
  },
  {
    "path": "ext/AudioFile.hpp",
    "content": "//=======================================================================\n/** @file AudioFile.h\n *  @author Adam Stark\n *  @copyright Copyright (C) 2017  Adam Stark\n *\n * This file is part of the 'AudioFile' library\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n//=======================================================================\n\n#ifndef _AS_AudioFile_h\n#define _AS_AudioFile_h\n\n#include <iostream>\n#include <vector>\n#include <assert.h>\n#include <string>\n#include <fstream>\n#include <unordered_map>\n#include <iterator>\n#include <algorithm>\n\n// disable some warnings on Windows\n#if defined (_MSC_VER)\n    __pragma(warning (push))\n    __pragma(warning (disable : 4244))\n    __pragma(warning (disable : 4457))\n    __pragma(warning (disable : 4458))\n    __pragma(warning (disable : 4389))\n    __pragma(warning (disable : 4996))\n#elif defined (__GNUC__)\n    _Pragma(\"GCC diagnostic push\")\n    _Pragma(\"GCC diagnostic ignored \\\"-Wconversion\\\"\")\n    _Pragma(\"GCC diagnostic ignored \\\"-Wsign-compare\\\"\")\n    _Pragma(\"GCC diagnostic ignored \\\"-Wshadow\\\"\")\n#endif\n\n//=============================================================\n/** The different types of audio file, plus some other types to \n * indicate a failure to load a file, or that one hasn't been\n * loaded yet\n */\nenum class AudioFileFormat\n{\n    Error,\n    NotLoaded,\n    Wave,\n    Aiff\n};\n\n//=============================================================\ntemplate <class T>\nclass AudioFile\n{\npublic:\n    \n    //=============================================================\n    typedef std::vector<std::vector<T> > AudioBuffer;\n    \n    //=============================================================\n    /** Constructor */\n    AudioFile();\n        \n    //=============================================================\n    /** Loads an audio file from a given file path.\n     * @Returns true if the file was successfully loaded\n     */\n    bool load (std::string filePath);\n    \n    /** Saves an audio file to a given file path.\n     * @Returns true if the file was successfully saved\n     */\n    bool save (std::string filePath, AudioFileFormat format = AudioFileFormat::Wave);\n        \n    //=============================================================\n    /** @Returns the sample rate */\n    uint32_t getSampleRate() const;\n    \n    /** @Returns the number of audio channels in the buffer */\n    int getNumChannels() const;\n\n    /** @Returns true if the audio file is mono */\n    bool isMono() const;\n    \n    /** @Returns true if the audio file is stereo */\n    bool isStereo() const;\n    \n    /** @Returns the bit depth of each sample */\n    int getBitDepth() const;\n    \n    /** @Returns the number of samples per channel */\n    int getNumSamplesPerChannel() const;\n    \n    /** @Returns the length in seconds of the audio file based on the number of samples and sample rate */\n    double getLengthInSeconds() const;\n    \n    /** Prints a summary of the audio file to the console */\n    void printSummary() const;\n    \n    //=============================================================\n    \n    /** Set the audio buffer for this AudioFile by copying samples from another buffer.\n     * @Returns true if the buffer was copied successfully.\n     */\n    bool setAudioBuffer (AudioBuffer& newBuffer);\n    \n    /** Sets the audio buffer to a given number of channels and number of samples per channel. This will try to preserve\n     * the existing audio, adding zeros to any new channels or new samples in a given channel.\n     */\n    void setAudioBufferSize (int numChannels, int numSamples);\n    \n    /** Sets the number of samples per channel in the audio buffer. This will try to preserve\n     * the existing audio, adding zeros to new samples in a given channel if the number of samples is increased.\n     */\n    void setNumSamplesPerChannel (int numSamples);\n    \n    /** Sets the number of channels. New channels will have the correct number of samples and be initialised to zero */\n    void setNumChannels (int numChannels);\n    \n    /** Sets the bit depth for the audio file. If you use the save() function, this bit depth rate will be used */\n    void setBitDepth (int numBitsPerSample);\n    \n    /** Sets the sample rate for the audio file. If you use the save() function, this sample rate will be used */\n    void setSampleRate (uint32_t newSampleRate);\n    \n    //=============================================================\n    /** Sets whether the library should log error messages to the console. By default this is true */\n    void shouldLogErrorsToConsole (bool logErrors);\n    \n    //=============================================================\n    /** A vector of vectors holding the audio samples for the AudioFile. You can \n     * access the samples by channel and then by sample index, i.e:\n     *\n     *      samples[channel][sampleIndex]\n     */\n    AudioBuffer samples;\n    \n    //=============================================================\n    /** An optional iXML chunk that can be added to the AudioFile. \n     */\n    std::string iXMLChunk;\n    \nprivate:\n    \n    //=============================================================\n    enum class Endianness\n    {\n        LittleEndian,\n        BigEndian\n    };\n    \n    //=============================================================\n    AudioFileFormat determineAudioFileFormat (std::vector<uint8_t>& fileData);\n    bool decodeWaveFile (std::vector<uint8_t>& fileData);\n    bool decodeAiffFile (std::vector<uint8_t>& fileData);\n    \n    //=============================================================\n    bool saveToWaveFile (std::string filePath);\n    bool saveToAiffFile (std::string filePath);\n    \n    //=============================================================\n    void clearAudioBuffer();\n    \n    //=============================================================\n    int32_t fourBytesToInt (std::vector<uint8_t>& source, int startIndex, Endianness endianness = Endianness::LittleEndian);\n    int16_t twoBytesToInt (std::vector<uint8_t>& source, int startIndex, Endianness endianness = Endianness::LittleEndian);\n    int getIndexOfString (std::vector<uint8_t>& source, std::string s);\n    int getIndexOfChunk (std::vector<uint8_t>& source, const std::string& chunkHeaderID, int startIndex, Endianness endianness = Endianness::LittleEndian);\n    \n    //=============================================================\n    T sixteenBitIntToSample (int16_t sample);\n    int16_t sampleToSixteenBitInt (T sample);\n    \n    //=============================================================\n    uint8_t sampleToSingleByte (T sample);\n    T singleByteToSample (uint8_t sample);\n    \n    uint32_t getAiffSampleRate (std::vector<uint8_t>& fileData, int sampleRateStartIndex);\n    bool tenByteMatch (std::vector<uint8_t>& v1, int startIndex1, std::vector<uint8_t>& v2, int startIndex2);\n    void addSampleRateToAiffData (std::vector<uint8_t>& fileData, uint32_t sampleRate);\n    T clamp (T v1, T minValue, T maxValue);\n    \n    //=============================================================\n    void addStringToFileData (std::vector<uint8_t>& fileData, std::string s);\n    void addInt32ToFileData (std::vector<uint8_t>& fileData, int32_t i, Endianness endianness = Endianness::LittleEndian);\n    void addInt16ToFileData (std::vector<uint8_t>& fileData, int16_t i, Endianness endianness = Endianness::LittleEndian);\n    \n    //=============================================================\n    bool writeDataToFile (std::vector<uint8_t>& fileData, std::string filePath);\n    \n    //=============================================================\n    void reportError (std::string errorMessage);\n    \n    //=============================================================\n    AudioFileFormat audioFileFormat;\n    uint32_t sampleRate;\n    int bitDepth;\n    bool logErrorsToConsole {true};\n};\n\n\n//=============================================================\n// Pre-defined 10-byte representations of common sample rates\nstatic std::unordered_map <uint32_t, std::vector<uint8_t>> aiffSampleRateTable = {\n    {8000, {64, 11, 250, 0, 0, 0, 0, 0, 0, 0}},\n    {11025, {64, 12, 172, 68, 0, 0, 0, 0, 0, 0}},\n    {16000, {64, 12, 250, 0, 0, 0, 0, 0, 0, 0}},\n    {22050, {64, 13, 172, 68, 0, 0, 0, 0, 0, 0}},\n    {32000, {64, 13, 250, 0, 0, 0, 0, 0, 0, 0}},\n    {37800, {64, 14, 147, 168, 0, 0, 0, 0, 0, 0}},\n    {44056, {64, 14, 172, 24, 0, 0, 0, 0, 0, 0}},\n    {44100, {64, 14, 172, 68, 0, 0, 0, 0, 0, 0}},\n    {47250, {64, 14, 184, 146, 0, 0, 0, 0, 0, 0}},\n    {48000, {64, 14, 187, 128, 0, 0, 0, 0, 0, 0}},\n    {50000, {64, 14, 195, 80, 0, 0, 0, 0, 0, 0}},\n    {50400, {64, 14, 196, 224, 0, 0, 0, 0, 0, 0}},\n    {88200, {64, 15, 172, 68, 0, 0, 0, 0, 0, 0}},\n    {96000, {64, 15, 187, 128, 0, 0, 0, 0, 0, 0}},\n    {176400, {64, 16, 172, 68, 0, 0, 0, 0, 0, 0}},\n    {192000, {64, 16, 187, 128, 0, 0, 0, 0, 0, 0}},\n    {352800, {64, 17, 172, 68, 0, 0, 0, 0, 0, 0}},\n    {2822400, {64, 20, 172, 68, 0, 0, 0, 0, 0, 0}},\n    {5644800, {64, 21, 172, 68, 0, 0, 0, 0, 0, 0}}\n};\n\n//=============================================================\nenum WavAudioFormat\n{\n    PCM = 0x0001,\n    IEEEFloat = 0x0003,\n    ALaw = 0x0006,\n    MULaw = 0x0007,\n    Extensible = 0xFFFE\n};\n\n//=============================================================\nenum AIFFAudioFormat\n{\n    Uncompressed,\n    Compressed,\n    Error\n};\n\n//=============================================================\n/* IMPLEMENTATION */\n//=============================================================\n\n//=============================================================\ntemplate <class T>\nAudioFile<T>::AudioFile()\n{\n    static_assert(std::is_floating_point<T>::value, \"ERROR: This version of AudioFile only supports floating point sample formats\");\n\n    bitDepth = 16;\n    sampleRate = 44100;\n    samples.resize (1);\n    samples[0].resize (0);\n    audioFileFormat = AudioFileFormat::NotLoaded;\n}\n\n//=============================================================\ntemplate <class T>\nuint32_t AudioFile<T>::getSampleRate() const\n{\n    return sampleRate;\n}\n\n//=============================================================\ntemplate <class T>\nint AudioFile<T>::getNumChannels() const\n{\n    return (int)samples.size();\n}\n\n//=============================================================\ntemplate <class T>\nbool AudioFile<T>::isMono() const\n{\n    return getNumChannels() == 1;\n}\n\n//=============================================================\ntemplate <class T>\nbool AudioFile<T>::isStereo() const\n{\n    return getNumChannels() == 2;\n}\n\n//=============================================================\ntemplate <class T>\nint AudioFile<T>::getBitDepth() const\n{\n    return bitDepth;\n}\n\n//=============================================================\ntemplate <class T>\nint AudioFile<T>::getNumSamplesPerChannel() const\n{\n    if (samples.size() > 0)\n        return (int) samples[0].size();\n    else\n        return 0;\n}\n\n//=============================================================\ntemplate <class T>\ndouble AudioFile<T>::getLengthInSeconds() const\n{\n    return (double)getNumSamplesPerChannel() / (double)sampleRate;\n}\n\n//=============================================================\ntemplate <class T>\nvoid AudioFile<T>::printSummary() const\n{\n    std::cout << \"|======================================|\" << std::endl;\n    std::cout << \"Num Channels: \" << getNumChannels() << std::endl;\n    std::cout << \"Num Samples Per Channel: \" << getNumSamplesPerChannel() << std::endl;\n    std::cout << \"Sample Rate: \" << sampleRate << std::endl;\n    std::cout << \"Bit Depth: \" << bitDepth << std::endl;\n    std::cout << \"Length in Seconds: \" << getLengthInSeconds() << std::endl;\n    std::cout << \"|======================================|\" << std::endl;\n}\n\n//=============================================================\ntemplate <class T>\nbool AudioFile<T>::setAudioBuffer (AudioBuffer& newBuffer)\n{\n    int numChannels = (int)newBuffer.size();\n    \n    if (numChannels <= 0)\n    {\n        assert (false && \"The buffer your are trying to use has no channels\");\n        return false;\n    }\n    \n    size_t numSamples = newBuffer[0].size();\n    \n    // set the number of channels\n    samples.resize (newBuffer.size());\n    \n    for (int k = 0; k < getNumChannels(); k++)\n    {\n        assert (newBuffer[k].size() == numSamples);\n        \n        samples[k].resize (numSamples);\n        \n        for (size_t i = 0; i < numSamples; i++)\n        {\n            samples[k][i] = newBuffer[k][i];\n        }\n    }\n    \n    return true;\n}\n\n//=============================================================\ntemplate <class T>\nvoid AudioFile<T>::setAudioBufferSize (int numChannels, int numSamples)\n{\n    samples.resize (numChannels);\n    setNumSamplesPerChannel (numSamples);\n}\n\n//=============================================================\ntemplate <class T>\nvoid AudioFile<T>::setNumSamplesPerChannel (int numSamples)\n{\n    int originalSize = getNumSamplesPerChannel();\n    \n    for (int i = 0; i < getNumChannels();i++)\n    {\n        samples[i].resize (numSamples);\n        \n        // set any new samples to zero\n        if (numSamples > originalSize)\n            std::fill (samples[i].begin() + originalSize, samples[i].end(), (T)0.);\n    }\n}\n\n//=============================================================\ntemplate <class T>\nvoid AudioFile<T>::setNumChannels (int numChannels)\n{\n    int originalNumChannels = getNumChannels();\n    int originalNumSamplesPerChannel = getNumSamplesPerChannel();\n    \n    samples.resize (numChannels);\n    \n    // make sure any new channels are set to the right size\n    // and filled with zeros\n    if (numChannels > originalNumChannels)\n    {\n        for (int i = originalNumChannels; i < numChannels; i++)\n        {\n            samples[i].resize (originalNumSamplesPerChannel);\n            std::fill (samples[i].begin(), samples[i].end(), (T)0.);\n        }\n    }\n}\n\n//=============================================================\ntemplate <class T>\nvoid AudioFile<T>::setBitDepth (int numBitsPerSample)\n{\n    bitDepth = numBitsPerSample;\n}\n\n//=============================================================\ntemplate <class T>\nvoid AudioFile<T>::setSampleRate (uint32_t newSampleRate)\n{\n    sampleRate = newSampleRate;\n}\n\n//=============================================================\ntemplate <class T>\nvoid AudioFile<T>::shouldLogErrorsToConsole (bool logErrors)\n{\n    logErrorsToConsole = logErrors;\n}\n\n//=============================================================\ntemplate <class T>\nbool AudioFile<T>::load (std::string filePath)\n{\n    std::ifstream file (filePath, std::ios::binary);\n    \n    // check the file exists\n    if (! file.good())\n    {\n        reportError (\"ERROR: File doesn't exist or otherwise can't load file\\n\"  + filePath);\n        return false;\n    }\n    \n    file.unsetf (std::ios::skipws);\n    std::istream_iterator<uint8_t> begin (file), end;\n    std::vector<uint8_t> fileData (begin, end);\n    \n    // get audio file format\n    audioFileFormat = determineAudioFileFormat (fileData);\n    \n    if (audioFileFormat == AudioFileFormat::Wave)\n    {\n        return decodeWaveFile (fileData);\n    }\n    else if (audioFileFormat == AudioFileFormat::Aiff)\n    {\n        return decodeAiffFile (fileData);\n    }\n    else\n    {\n        reportError (\"Audio File Type: Error\");\n        return false;\n    }\n}\n\n//=============================================================\ntemplate <class T>\nbool AudioFile<T>::decodeWaveFile (std::vector<uint8_t>& fileData)\n{\n    // -----------------------------------------------------------\n    // HEADER CHUNK\n    std::string headerChunkID (fileData.begin(), fileData.begin() + 4);\n    //int32_t fileSizeInBytes = fourBytesToInt (fileData, 4) + 8;\n    std::string format (fileData.begin() + 8, fileData.begin() + 12);\n    \n    // -----------------------------------------------------------\n    // try and find the start points of key chunks\n    int indexOfDataChunk = getIndexOfChunk (fileData, \"data\", 12);\n    int indexOfFormatChunk = getIndexOfChunk (fileData, \"fmt \", 12);\n    int indexOfXMLChunk = getIndexOfChunk (fileData, \"iXML\", 12);\n    \n    // if we can't find the data or format chunks, or the IDs/formats don't seem to be as expected\n    // then it is unlikely we'll able to read this file, so abort\n    if (indexOfDataChunk == -1 || indexOfFormatChunk == -1 || headerChunkID != \"RIFF\" || format != \"WAVE\")\n    {\n        reportError (\"ERROR: this doesn't seem to be a valid .WAV file\");\n        return false;\n    }\n    \n    // -----------------------------------------------------------\n    // FORMAT CHUNK\n    int f = indexOfFormatChunk;\n    std::string formatChunkID (fileData.begin() + f, fileData.begin() + f + 4);\n    //int32_t formatChunkSize = fourBytesToInt (fileData, f + 4);\n    int16_t audioFormat = twoBytesToInt (fileData, f + 8);\n    int16_t numChannels = twoBytesToInt (fileData, f + 10);\n    sampleRate = (uint32_t) fourBytesToInt (fileData, f + 12);\n    int32_t numBytesPerSecond = fourBytesToInt (fileData, f + 16);\n    int16_t numBytesPerBlock = twoBytesToInt (fileData, f + 20);\n    bitDepth = (int) twoBytesToInt (fileData, f + 22);\n    \n    int numBytesPerSample = bitDepth / 8;\n    \n    // check that the audio format is PCM or Float\n    if (audioFormat != WavAudioFormat::PCM && audioFormat != WavAudioFormat::IEEEFloat)\n    {\n        reportError (\"ERROR: this .WAV file is encoded in a format that this library does not support at present\");\n        return false;\n    }\n    \n    // check the number of channels is mono or stereo\n    if (numChannels < 1 || numChannels > 128)\n    {\n        reportError (\"ERROR: this WAV file seems to be an invalid number of channels (or corrupted?)\");\n        return false;\n    }\n    \n    // check header data is consistent\n    if ((numBytesPerSecond != (numChannels * sampleRate * bitDepth) / 8) || (numBytesPerBlock != (numChannels * numBytesPerSample)))\n    {\n        reportError (\"ERROR: the header data in this WAV file seems to be inconsistent\");\n        return false;\n    }\n    \n    // check bit depth is either 8, 16, 24 or 32 bit\n    if (bitDepth != 8 && bitDepth != 16 && bitDepth != 24 && bitDepth != 32)\n    {\n        reportError (\"ERROR: this file has a bit depth that is not 8, 16, 24 or 32 bits\");\n        return false;\n    }\n    \n    // -----------------------------------------------------------\n    // DATA CHUNK\n    int d = indexOfDataChunk;\n    std::string dataChunkID (fileData.begin() + d, fileData.begin() + d + 4);\n    int32_t dataChunkSize = fourBytesToInt (fileData, d + 4);\n    \n    int numSamples = dataChunkSize / (numChannels * bitDepth / 8);\n    int samplesStartIndex = indexOfDataChunk + 8;\n    \n    clearAudioBuffer();\n    samples.resize (numChannels);\n    \n    for (int i = 0; i < numSamples; i++)\n    {\n        for (int channel = 0; channel < numChannels; channel++)\n        {\n            int sampleIndex = samplesStartIndex + (numBytesPerBlock * i) + channel * numBytesPerSample;\n            \n            if (bitDepth == 8)\n            {\n                T sample = singleByteToSample (fileData[sampleIndex]);\n                samples[channel].push_back (sample);\n            }\n            else if (bitDepth == 16)\n            {\n                int16_t sampleAsInt = twoBytesToInt (fileData, sampleIndex);\n                T sample = sixteenBitIntToSample (sampleAsInt);\n                samples[channel].push_back (sample);\n            }\n            else if (bitDepth == 24)\n            {\n                int32_t sampleAsInt = 0;\n                sampleAsInt = (fileData[sampleIndex + 2] << 16) | (fileData[sampleIndex + 1] << 8) | fileData[sampleIndex];\n                \n                if (sampleAsInt & 0x800000) //  if the 24th bit is set, this is a negative number in 24-bit world\n                    sampleAsInt = sampleAsInt | ~0xFFFFFF; // so make sure sign is extended to the 32 bit float\n\n                T sample = (T)sampleAsInt / (T)8388608.;\n                samples[channel].push_back (sample);\n            }\n            else if (bitDepth == 32)\n            {\n                int32_t sampleAsInt = fourBytesToInt (fileData, sampleIndex);\n                T sample;\n                \n                if (audioFormat == WavAudioFormat::IEEEFloat)\n                    sample = (T)reinterpret_cast<float&> (sampleAsInt);\n                else // assume PCM\n                    sample = (T) sampleAsInt / static_cast<float> (std::numeric_limits<std::int32_t>::max());\n                \n                samples[channel].push_back (sample);\n            }\n            else\n            {\n                assert (false);\n            }\n        }\n    }\n\n    // -----------------------------------------------------------\n    // iXML CHUNK\n    if (indexOfXMLChunk != -1)\n    {\n        int32_t chunkSize = fourBytesToInt (fileData, indexOfXMLChunk + 4);\n        iXMLChunk = std::string ((const char*) &fileData[indexOfXMLChunk + 8], chunkSize);\n    }\n\n    return true;\n}\n\n//=============================================================\ntemplate <class T>\nbool AudioFile<T>::decodeAiffFile (std::vector<uint8_t>& fileData)\n{\n    // -----------------------------------------------------------\n    // HEADER CHUNK\n    std::string headerChunkID (fileData.begin(), fileData.begin() + 4);\n    //int32_t fileSizeInBytes = fourBytesToInt (fileData, 4, Endianness::BigEndian) + 8;\n    std::string format (fileData.begin() + 8, fileData.begin() + 12);\n    \n    int audioFormat = format == \"AIFF\" ? AIFFAudioFormat::Uncompressed : format == \"AIFC\" ? AIFFAudioFormat::Compressed : AIFFAudioFormat::Error;\n    \n    // -----------------------------------------------------------\n    // try and find the start points of key chunks\n    int indexOfCommChunk = getIndexOfChunk (fileData, \"COMM\", 12, Endianness::BigEndian);\n    int indexOfSoundDataChunk = getIndexOfChunk (fileData, \"SSND\", 12, Endianness::BigEndian);\n    int indexOfXMLChunk = getIndexOfChunk (fileData, \"iXML\", 12, Endianness::BigEndian);\n    \n    // if we can't find the data or format chunks, or the IDs/formats don't seem to be as expected\n    // then it is unlikely we'll able to read this file, so abort\n    if (indexOfSoundDataChunk == -1 || indexOfCommChunk == -1 || headerChunkID != \"FORM\" || audioFormat == AIFFAudioFormat::Error)\n    {\n        reportError (\"ERROR: this doesn't seem to be a valid AIFF file\");\n        return false;\n    }\n\n    // -----------------------------------------------------------\n    // COMM CHUNK\n    int p = indexOfCommChunk;\n    std::string commChunkID (fileData.begin() + p, fileData.begin() + p + 4);\n    //int32_t commChunkSize = fourBytesToInt (fileData, p + 4, Endianness::BigEndian);\n    int16_t numChannels = twoBytesToInt (fileData, p + 8, Endianness::BigEndian);\n    int32_t numSamplesPerChannel = fourBytesToInt (fileData, p + 10, Endianness::BigEndian);\n    bitDepth = (int) twoBytesToInt (fileData, p + 14, Endianness::BigEndian);\n    sampleRate = getAiffSampleRate (fileData, p + 16);\n    \n    // check the sample rate was properly decoded\n    if (sampleRate == 0)\n    {\n        reportError (\"ERROR: this AIFF file has an unsupported sample rate\");\n        return false;\n    }\n    \n    // check the number of channels is mono or stereo\n    if (numChannels < 1 ||numChannels > 2)\n    {\n        reportError (\"ERROR: this AIFF file seems to be neither mono nor stereo (perhaps multi-track, or corrupted?)\");\n        return false;\n    }\n    \n    // check bit depth is either 8, 16, 24 or 32-bit\n    if (bitDepth != 8 && bitDepth != 16 && bitDepth != 24 && bitDepth != 32)\n    {\n        reportError (\"ERROR: this file has a bit depth that is not 8, 16, 24 or 32 bits\");\n        return false;\n    }\n    \n    // -----------------------------------------------------------\n    // SSND CHUNK\n    int s = indexOfSoundDataChunk;\n    std::string soundDataChunkID (fileData.begin() + s, fileData.begin() + s + 4);\n    int32_t soundDataChunkSize = fourBytesToInt (fileData, s + 4, Endianness::BigEndian);\n    int32_t offset = fourBytesToInt (fileData, s + 8, Endianness::BigEndian);\n    //int32_t blockSize = fourBytesToInt (fileData, s + 12, Endianness::BigEndian);\n    \n    int numBytesPerSample = bitDepth / 8;\n    int numBytesPerFrame = numBytesPerSample * numChannels;\n    int totalNumAudioSampleBytes = numSamplesPerChannel * numBytesPerFrame;\n    int samplesStartIndex = s + 16 + (int)offset;\n        \n    // sanity check the data\n    if ((soundDataChunkSize - 8) != totalNumAudioSampleBytes || totalNumAudioSampleBytes > static_cast<long>(fileData.size() - samplesStartIndex))\n    {\n        reportError (\"ERROR: the metadatafor this file doesn't seem right\");\n        return false;\n    }\n    \n    clearAudioBuffer();\n    samples.resize (numChannels);\n    \n    for (int i = 0; i < numSamplesPerChannel; i++)\n    {\n        for (int channel = 0; channel < numChannels; channel++)\n        {\n            int sampleIndex = samplesStartIndex + (numBytesPerFrame * i) + channel * numBytesPerSample;\n            \n            if (bitDepth == 8)\n            {\n                int8_t sampleAsSigned8Bit = (int8_t)fileData[sampleIndex];\n                T sample = (T)sampleAsSigned8Bit / (T)128.;\n                samples[channel].push_back (sample);\n            }\n            else if (bitDepth == 16)\n            {\n                int16_t sampleAsInt = twoBytesToInt (fileData, sampleIndex, Endianness::BigEndian);\n                T sample = sixteenBitIntToSample (sampleAsInt);\n                samples[channel].push_back (sample);\n            }\n            else if (bitDepth == 24)\n            {\n                int32_t sampleAsInt = 0;\n                sampleAsInt = (fileData[sampleIndex] << 16) | (fileData[sampleIndex + 1] << 8) | fileData[sampleIndex + 2];\n                \n                if (sampleAsInt & 0x800000) //  if the 24th bit is set, this is a negative number in 24-bit world\n                    sampleAsInt = sampleAsInt | ~0xFFFFFF; // so make sure sign is extended to the 32 bit float\n                \n                T sample = (T)sampleAsInt / (T)8388608.;\n                samples[channel].push_back (sample);\n            }\n            else if (bitDepth == 32)\n            {\n                int32_t sampleAsInt = fourBytesToInt (fileData, sampleIndex, Endianness::BigEndian);\n                T sample;\n                \n                if (audioFormat == AIFFAudioFormat::Compressed)\n                    sample = (T)reinterpret_cast<float&> (sampleAsInt);\n                else // assume uncompressed\n                    sample = (T) sampleAsInt / static_cast<float> (std::numeric_limits<std::int32_t>::max());\n                    \n                samples[channel].push_back (sample);\n            }\n            else\n            {\n                assert (false);\n            }\n        }\n    }\n\n    // -----------------------------------------------------------\n    // iXML CHUNK\n    if (indexOfXMLChunk != -1)\n    {\n        int32_t chunkSize = fourBytesToInt (fileData, indexOfXMLChunk + 4);\n        iXMLChunk = std::string ((const char*) &fileData[indexOfXMLChunk + 8], chunkSize);\n    }\n    \n    return true;\n}\n\n//=============================================================\ntemplate <class T>\nuint32_t AudioFile<T>::getAiffSampleRate (std::vector<uint8_t>& fileData, int sampleRateStartIndex)\n{\n    for (auto it : aiffSampleRateTable)\n    {\n        if (tenByteMatch (fileData, sampleRateStartIndex, it.second, 0))\n            return it.first;\n    }\n    \n    return 0;\n}\n\n//=============================================================\ntemplate <class T>\nbool AudioFile<T>::tenByteMatch (std::vector<uint8_t>& v1, int startIndex1, std::vector<uint8_t>& v2, int startIndex2)\n{\n    for (int i = 0; i < 10; i++)\n    {\n        if (v1[startIndex1 + i] != v2[startIndex2 + i])\n            return false;\n    }\n    \n    return true;\n}\n\n//=============================================================\ntemplate <class T>\nvoid AudioFile<T>::addSampleRateToAiffData (std::vector<uint8_t>& fileData, uint32_t sampleRate)\n{\n    if (aiffSampleRateTable.count (sampleRate) > 0)\n    {\n        for (int i = 0; i < 10; i++)\n            fileData.push_back (aiffSampleRateTable[sampleRate][i]);\n    }\n}\n\n//=============================================================\ntemplate <class T>\nbool AudioFile<T>::save (std::string filePath, AudioFileFormat format)\n{\n    if (format == AudioFileFormat::Wave)\n    {\n        return saveToWaveFile (filePath);\n    }\n    else if (format == AudioFileFormat::Aiff)\n    {\n        return saveToAiffFile (filePath);\n    }\n    \n    return false;\n}\n\n//=============================================================\ntemplate <class T>\nbool AudioFile<T>::saveToWaveFile (std::string filePath)\n{\n    std::vector<uint8_t> fileData;\n    \n    int32_t dataChunkSize = getNumSamplesPerChannel() * (getNumChannels() * bitDepth / 8);\n    int16_t audioFormat = bitDepth == 32 ? WavAudioFormat::IEEEFloat : WavAudioFormat::PCM;\n    int32_t formatChunkSize = audioFormat == WavAudioFormat::PCM ? 16 : 18;\n    int32_t iXMLChunkSize = static_cast<int32_t> (iXMLChunk.size());\n    \n    // -----------------------------------------------------------\n    // HEADER CHUNK\n    addStringToFileData (fileData, \"RIFF\");\n    \n    // The file size in bytes is the header chunk size (4, not counting RIFF and WAVE) + the format\n    // chunk size (24) + the metadata part of the data chunk plus the actual data chunk size\n    int32_t fileSizeInBytes = 4 + formatChunkSize + 8 + 8 + dataChunkSize;\n    if (iXMLChunkSize > 0)\n    {\n        fileSizeInBytes += (8 + iXMLChunkSize);\n    }\n\n    addInt32ToFileData (fileData, fileSizeInBytes);\n    \n    addStringToFileData (fileData, \"WAVE\");\n    \n    // -----------------------------------------------------------\n    // FORMAT CHUNK\n    addStringToFileData (fileData, \"fmt \");\n    addInt32ToFileData (fileData, formatChunkSize); // format chunk size (16 for PCM)\n    addInt16ToFileData (fileData, audioFormat); // audio format\n    addInt16ToFileData (fileData, (int16_t)getNumChannels()); // num channels\n    addInt32ToFileData (fileData, (int32_t)sampleRate); // sample rate\n    \n    int32_t numBytesPerSecond = (int32_t) ((getNumChannels() * sampleRate * bitDepth) / 8);\n    addInt32ToFileData (fileData, numBytesPerSecond);\n    \n    int16_t numBytesPerBlock = getNumChannels() * (bitDepth / 8);\n    addInt16ToFileData (fileData, numBytesPerBlock);\n    \n    addInt16ToFileData (fileData, (int16_t)bitDepth);\n    \n    if (audioFormat == WavAudioFormat::IEEEFloat)\n        addInt16ToFileData (fileData, 0); // extension size\n    \n    // -----------------------------------------------------------\n    // DATA CHUNK\n    addStringToFileData (fileData, \"data\");\n    addInt32ToFileData (fileData, dataChunkSize);\n    \n    for (int i = 0; i < getNumSamplesPerChannel(); i++)\n    {\n        for (int channel = 0; channel < getNumChannels(); channel++)\n        {\n            if (bitDepth == 8)\n            {\n                uint8_t byte = sampleToSingleByte (samples[channel][i]);\n                fileData.push_back (byte);\n            }\n            else if (bitDepth == 16)\n            {\n                int16_t sampleAsInt = sampleToSixteenBitInt (samples[channel][i]);\n                addInt16ToFileData (fileData, sampleAsInt);\n            }\n            else if (bitDepth == 24)\n            {\n                int32_t sampleAsIntAgain = (int32_t) (samples[channel][i] * (T)8388608.);\n                \n                uint8_t bytes[3];\n                bytes[2] = (uint8_t) (sampleAsIntAgain >> 16) & 0xFF;\n                bytes[1] = (uint8_t) (sampleAsIntAgain >>  8) & 0xFF;\n                bytes[0] = (uint8_t) sampleAsIntAgain & 0xFF;\n                \n                fileData.push_back (bytes[0]);\n                fileData.push_back (bytes[1]);\n                fileData.push_back (bytes[2]);\n            }\n            else if (bitDepth == 32)\n            {\n                int32_t sampleAsInt;\n                \n                if (audioFormat == WavAudioFormat::IEEEFloat)\n                    sampleAsInt = (int32_t) reinterpret_cast<int32_t&> (samples[channel][i]);\n                else // assume PCM\n                    sampleAsInt = (int32_t) (samples[channel][i] * std::numeric_limits<int32_t>::max());\n                \n                addInt32ToFileData (fileData, sampleAsInt, Endianness::LittleEndian);\n            }\n            else\n            {\n                assert (false && \"Trying to write a file with unsupported bit depth\");\n                return false;\n            }\n        }\n    }\n    \n    // -----------------------------------------------------------\n    // iXML CHUNK\n    if (iXMLChunkSize > 0) \n    {\n        addStringToFileData (fileData, \"iXML\");\n        addInt32ToFileData (fileData, iXMLChunkSize);\n        addStringToFileData (fileData, iXMLChunk);\n    }\n    \n    // check that the various sizes we put in the metadata are correct\n    if (fileSizeInBytes != static_cast<int32_t> (fileData.size() - 8) || dataChunkSize != (getNumSamplesPerChannel() * getNumChannels() * (bitDepth / 8)))\n    {\n        reportError (\"ERROR: couldn't save file to \" + filePath);\n        return false;\n    }\n    \n    // try to write the file\n    return writeDataToFile (fileData, filePath);\n}\n\n//=============================================================\ntemplate <class T>\nbool AudioFile<T>::saveToAiffFile (std::string filePath)\n{\n    std::vector<uint8_t> fileData;\n    \n    int32_t numBytesPerSample = bitDepth / 8;\n    int32_t numBytesPerFrame = numBytesPerSample * getNumChannels();\n    int32_t totalNumAudioSampleBytes = getNumSamplesPerChannel() * numBytesPerFrame;\n    int32_t soundDataChunkSize = totalNumAudioSampleBytes + 8;\n    int32_t iXMLChunkSize = static_cast<int32_t> (iXMLChunk.size());\n    \n    // -----------------------------------------------------------\n    // HEADER CHUNK\n    addStringToFileData (fileData, \"FORM\");\n    \n    // The file size in bytes is the header chunk size (4, not counting FORM and AIFF) + the COMM\n    // chunk size (26) + the metadata part of the SSND chunk plus the actual data chunk size\n    int32_t fileSizeInBytes = 4 + 26 + 16 + totalNumAudioSampleBytes;\n    if (iXMLChunkSize > 0)\n    {\n        fileSizeInBytes += (8 + iXMLChunkSize);\n    }\n\n    addInt32ToFileData (fileData, fileSizeInBytes, Endianness::BigEndian);\n    \n    addStringToFileData (fileData, \"AIFF\");\n    \n    // -----------------------------------------------------------\n    // COMM CHUNK\n    addStringToFileData (fileData, \"COMM\");\n    addInt32ToFileData (fileData, 18, Endianness::BigEndian); // commChunkSize\n    addInt16ToFileData (fileData, getNumChannels(), Endianness::BigEndian); // num channels\n    addInt32ToFileData (fileData, getNumSamplesPerChannel(), Endianness::BigEndian); // num samples per channel\n    addInt16ToFileData (fileData, bitDepth, Endianness::BigEndian); // bit depth\n    addSampleRateToAiffData (fileData, sampleRate);\n    \n    // -----------------------------------------------------------\n    // SSND CHUNK\n    addStringToFileData (fileData, \"SSND\");\n    addInt32ToFileData (fileData, soundDataChunkSize, Endianness::BigEndian);\n    addInt32ToFileData (fileData, 0, Endianness::BigEndian); // offset\n    addInt32ToFileData (fileData, 0, Endianness::BigEndian); // block size\n    \n    for (int i = 0; i < getNumSamplesPerChannel(); i++)\n    {\n        for (int channel = 0; channel < getNumChannels(); channel++)\n        {\n            if (bitDepth == 8)\n            {\n                uint8_t byte = sampleToSingleByte (samples[channel][i]);\n                fileData.push_back (byte);\n            }\n            else if (bitDepth == 16)\n            {\n                int16_t sampleAsInt = sampleToSixteenBitInt (samples[channel][i]);\n                addInt16ToFileData (fileData, sampleAsInt, Endianness::BigEndian);\n            }\n            else if (bitDepth == 24)\n            {\n                int32_t sampleAsIntAgain = (int32_t) (samples[channel][i] * (T)8388608.);\n                \n                uint8_t bytes[3];\n                bytes[0] = (uint8_t) (sampleAsIntAgain >> 16) & 0xFF;\n                bytes[1] = (uint8_t) (sampleAsIntAgain >>  8) & 0xFF;\n                bytes[2] = (uint8_t) sampleAsIntAgain & 0xFF;\n                \n                fileData.push_back (bytes[0]);\n                fileData.push_back (bytes[1]);\n                fileData.push_back (bytes[2]);\n            }\n            else if (bitDepth == 32)\n            {\n                // write samples as signed integers (no implementation yet for floating point, but looking at WAV implementation should help)\n                int32_t sampleAsInt = (int32_t) (samples[channel][i] * std::numeric_limits<int32_t>::max());\n                addInt32ToFileData (fileData, sampleAsInt, Endianness::BigEndian);\n            }\n            else\n            {\n                assert (false && \"Trying to write a file with unsupported bit depth\");\n                return false;\n            }\n        }\n    }\n\n    // -----------------------------------------------------------\n    // iXML CHUNK\n    if (iXMLChunkSize > 0)\n    {\n        addStringToFileData (fileData, \"iXML\");\n        addInt32ToFileData (fileData, iXMLChunkSize);\n        addStringToFileData (fileData, iXMLChunk);\n    }\n    \n    // check that the various sizes we put in the metadata are correct\n    if (fileSizeInBytes != static_cast<int32_t> (fileData.size() - 8) || soundDataChunkSize != getNumSamplesPerChannel() *  numBytesPerFrame + 8)\n    {\n        reportError (\"ERROR: couldn't save file to \" + filePath);\n        return false;\n    }\n    \n    // try to write the file\n    return writeDataToFile (fileData, filePath);\n}\n\n//=============================================================\ntemplate <class T>\nbool AudioFile<T>::writeDataToFile (std::vector<uint8_t>& fileData, std::string filePath)\n{\n    std::ofstream outputFile (filePath, std::ios::binary);\n    \n    if (outputFile.is_open())\n    {\n        for (size_t i = 0; i < fileData.size(); i++)\n        {\n            char value = (char) fileData[i];\n            outputFile.write (&value, sizeof (char));\n        }\n        \n        outputFile.close();\n        \n        return true;\n    }\n    \n    return false;\n}\n\n//=============================================================\ntemplate <class T>\nvoid AudioFile<T>::addStringToFileData (std::vector<uint8_t>& fileData, std::string s)\n{\n    for (size_t i = 0; i < s.length();i++)\n        fileData.push_back ((uint8_t) s[i]);\n}\n\n//=============================================================\ntemplate <class T>\nvoid AudioFile<T>::addInt32ToFileData (std::vector<uint8_t>& fileData, int32_t i, Endianness endianness)\n{\n    uint8_t bytes[4];\n    \n    if (endianness == Endianness::LittleEndian)\n    {\n        bytes[3] = (i >> 24) & 0xFF;\n        bytes[2] = (i >> 16) & 0xFF;\n        bytes[1] = (i >> 8) & 0xFF;\n        bytes[0] = i & 0xFF;\n    }\n    else\n    {\n        bytes[0] = (i >> 24) & 0xFF;\n        bytes[1] = (i >> 16) & 0xFF;\n        bytes[2] = (i >> 8) & 0xFF;\n        bytes[3] = i & 0xFF;\n    }\n    \n    for (int i = 0; i < 4; i++)\n        fileData.push_back (bytes[i]);\n}\n\n//=============================================================\ntemplate <class T>\nvoid AudioFile<T>::addInt16ToFileData (std::vector<uint8_t>& fileData, int16_t i, Endianness endianness)\n{\n    uint8_t bytes[2];\n    \n    if (endianness == Endianness::LittleEndian)\n    {\n        bytes[1] = (i >> 8) & 0xFF;\n        bytes[0] = i & 0xFF;\n    }\n    else\n    {\n        bytes[0] = (i >> 8) & 0xFF;\n        bytes[1] = i & 0xFF;\n    }\n    \n    fileData.push_back (bytes[0]);\n    fileData.push_back (bytes[1]);\n}\n\n//=============================================================\ntemplate <class T>\nvoid AudioFile<T>::clearAudioBuffer()\n{\n    for (size_t i = 0; i < samples.size();i++)\n    {\n        samples[i].clear();\n    }\n    \n    samples.clear();\n}\n\n//=============================================================\ntemplate <class T>\nAudioFileFormat AudioFile<T>::determineAudioFileFormat (std::vector<uint8_t>& fileData)\n{\n    std::string header (fileData.begin(), fileData.begin() + 4);\n    \n    if (header == \"RIFF\")\n        return AudioFileFormat::Wave;\n    else if (header == \"FORM\")\n        return AudioFileFormat::Aiff;\n    else\n        return AudioFileFormat::Error;\n}\n\n//=============================================================\ntemplate <class T>\nint32_t AudioFile<T>::fourBytesToInt (std::vector<uint8_t>& source, int startIndex, Endianness endianness)\n{\n    int32_t result;\n    \n    if (endianness == Endianness::LittleEndian)\n        result = (source[startIndex + 3] << 24) | (source[startIndex + 2] << 16) | (source[startIndex + 1] << 8) | source[startIndex];\n    else\n        result = (source[startIndex] << 24) | (source[startIndex + 1] << 16) | (source[startIndex + 2] << 8) | source[startIndex + 3];\n    \n    return result;\n}\n\n//=============================================================\ntemplate <class T>\nint16_t AudioFile<T>::twoBytesToInt (std::vector<uint8_t>& source, int startIndex, Endianness endianness)\n{\n    int16_t result;\n    \n    if (endianness == Endianness::LittleEndian)\n        result = (source[startIndex + 1] << 8) | source[startIndex];\n    else\n        result = (source[startIndex] << 8) | source[startIndex + 1];\n    \n    return result;\n}\n\n//=============================================================\ntemplate <class T>\nint AudioFile<T>::getIndexOfString (std::vector<uint8_t>& source, std::string stringToSearchFor)\n{\n    int index = -1;\n    int stringLength = (int)stringToSearchFor.length();\n    \n    for (size_t i = 0; i < source.size() - stringLength;i++)\n    {\n        std::string section (source.begin() + i, source.begin() + i + stringLength);\n        \n        if (section == stringToSearchFor)\n        {\n            index = static_cast<int> (i);\n            break;\n        }\n    }\n    \n    return index;\n}\n\n//=============================================================\ntemplate <class T>\nint AudioFile<T>::getIndexOfChunk (std::vector<uint8_t>& source, const std::string& chunkHeaderID, int startIndex, Endianness endianness)\n{\n    constexpr int dataLen = 4;\n    if (chunkHeaderID.size() != dataLen)\n    {\n        assert (false && \"Invalid chunk header ID string\");\n        return -1;\n    }\n\n    int i = startIndex;\n    while (i < source.size() - dataLen)\n    {\n        if (memcmp (&source[i], chunkHeaderID.data(), dataLen) == 0)\n        {\n            return i;\n        }\n\n        i += dataLen;\n        auto chunkSize = fourBytesToInt (source, i, endianness);\n        i += (dataLen + chunkSize);\n    }\n\n    return -1;\n}\n\n//=============================================================\ntemplate <class T>\nT AudioFile<T>::sixteenBitIntToSample (int16_t sample)\n{\n    return static_cast<T> (sample) / static_cast<T> (32768.);\n}\n\n//=============================================================\ntemplate <class T>\nint16_t AudioFile<T>::sampleToSixteenBitInt (T sample)\n{\n    sample = clamp (sample, -1., 1.);\n    return static_cast<int16_t> (sample * 32767.);\n}\n\n//=============================================================\ntemplate <class T>\nuint8_t AudioFile<T>::sampleToSingleByte (T sample)\n{\n    sample = clamp (sample, -1., 1.);\n    sample = (sample + 1.) / 2.;\n    return static_cast<uint8_t> (sample * 255.);\n}\n\n//=============================================================\ntemplate <class T>\nT AudioFile<T>::singleByteToSample (uint8_t sample)\n{\n    return static_cast<T> (sample - 128) / static_cast<T> (128.);\n}\n\n//=============================================================\ntemplate <class T>\nT AudioFile<T>::clamp (T value, T minValue, T maxValue)\n{\n    value = std::min (value, maxValue);\n    value = std::max (value, minValue);\n    return value;\n}\n\n//=============================================================\ntemplate <class T>\nvoid AudioFile<T>::reportError (std::string errorMessage)\n{\n    if (logErrorsToConsole)\n        std::cout << errorMessage << std::endl;\n}\n\n#if defined (_MSC_VER)\n    __pragma(warning (pop))\n#elif defined (__GNUC__)\n    _Pragma(\"GCC diagnostic pop\")\n#endif\n\n#endif /* AudioFile_h */\n"
  },
  {
    "path": "ext/ByteArr.cpp",
    "content": "#include \"ByteArr.h\"\nusing namespace std;\n\nvoid ByteArr::Realloc(const size_t & newSize)\n{\n\tif (newSize < DataSz)\n\t\treturn;\n\n\tBYTE* NewDat = new BYTE[newSize];\n\n\n\n\n\n    smemcpy(NewDat, newSize, Data, DataSz);\n\n\tDataSz = newSize;\n\n\tdelete[] Data;\n\n\tData = NewDat;\n\n\n}\n\nvoid ByteArr::Init()\n{\n    Data = nullptr;\n\tDataSz = 0;\n\tCurrentPos = 0;\n    DontDestroy = false;\n}\n\nByteArr::ByteArr()\n{\n    Init();\n}\n\nvoid ByteArr::SetDestroy(const bool &set)\n{\n    DontDestroy = !set;\n}\n\nByteArr::ByteArr(const size_t & InitSz)\n{\n    Init();\n    CAlloc(InitSz);\n}\n\nByteArr::ByteArr(BYTE * CopyArr, const size_t & ArrSz)\n{\n    Init();\n    Assign(CopyArr, ArrSz);\n\n}\n\nByteArr::ByteArr(const ByteArr & Cpy)\n{\n    Init();\n    Assign(Cpy);\n    CurrentPos = Cpy.Pos();\n}\n\nByteArr::ByteArr(const std::vector<BYTE>& CpyBv)\n{\n    Init();\n    Assign(CpyBv);\n}\n\n#ifdef _QT\nByteArr::ByteArr(const QByteArray &InitBar)\n{\n    Init();\n    Assign(InitBar);\n}\n#endif\nByteArr::ByteArr(const std::vector<ByteArr> &BarC)\n{\n    Init();\n    Assign(BarC);\n}\n\nstd::vector<ByteArr> ByteArr::Split(const ulong &szportion)\n{\n    vector<ByteArr> Ret;\n\n    size_t Pos = 0;\n\n    size_t remaining = DataSz;\n    while (remaining > 0)\n    {\n        size_t targetsz = szportion;\n\n        if (Pos + szportion > DataSz)\n            targetsz = DataSz - Pos;\n\n        ByteArr Bar(Data + Pos,targetsz);\n        Pos += targetsz;\n\n        Ret.push_back(Bar);\n\n        remaining -= targetsz;\n    }\n\n\n\n\n    return Ret;\n}\n\nvoid ByteArr::Request(const size_t &reqSz)\n{\n    const size_t oReq = CurrentPos + reqSz;\n\n    if (oReq > DataSz)\n        IncreaseSize(reqSz);\n\n}\n\nstd::vector<BYTE> ByteArr::ToVector()\n{\n    return std::vector<BYTE>(Data, Data + DataSz);\n\n}\n\nconst BYTE * ByteArr::CoData() const\n{\n    return Data;\n}\n\nBYTE &ByteArr::operator[](const size_t &Pos)\n{\n    return Data[Pos];\n\n}\n\nconst BYTE &ByteArr::operator[](const size_t &cPos) const\n{\n    return Data[cPos];\n\n}\n\nvoid ByteArr::Advance(const size_t &adv)\n{\n    CurrentPos += adv;\n\n}\n\n\nvoid ByteArr::Assign(BYTE * cpyArr, const size_t & cpySz)\n{\n   CAlloc(cpySz);\n\n    smemcpy(Data, cpySz, cpyArr, cpySz);\n\tDataSz = cpySz;\n\n}\n\nvoid ByteArr::Assign(const std::vector<BYTE>& CByteVec)\n{\n    CAlloc(CByteVec.size());\n\n    smemcpy(Data, DataSz, CByteVec.data(), CByteVec.size());\n\n}\n\nvoid ByteArr::Assign(const ByteArr & CpyByte)\n{\n\n    CAlloc(CpyByte.Size());\n\n    smemcpy(Data, DataSz, CpyByte.CoData(), CpyByte.Size());\n\n\n\n}\n\nvoid ByteArr::Assign(const std::vector<ByteArr> &BarComb)\n{\n\n    size_t total = 0;\n    // Two iterations, one gets the size and the other appends.\n    auto It = BarComb.begin();\n    while (It != BarComb.end())\n    {\n\n        total += It->Size();\n\n        ++It;\n    }\n\n\n    CAlloc(total);\n    It = BarComb.begin();\n\n    while (It != BarComb.end()){\n\n        Add((void*)It->CoData(),It->Size());\n\n\n\n        ++It;\n    }\n\n\n}\n\nvoid ByteArr::Seek(const size_t &To)\n{\n    if (To > DataSz)\n        throw std::invalid_argument(\"Tried to seek out of bounds!\");\n\n    CurrentPos = To;\n\n}\n\nvoid ByteArr::Add(void * inDat, const size_t & DatSz)\n{\n\tconst size_t Req = CurrentPos + DatSz;\n\n\tif (Req > DataSz)\n        IncreaseSize(DatSz);\n\n    smemcpy(Data + CurrentPos, DataSz, inDat, DatSz);\n\n    CurrentPos += DatSz;\n}\n\nsize_t ByteArr::Read(void *OutDat, const size_t &oDatSz)\n{\n    const size_t oReq = CurrentPos + oDatSz;\n\n    if (oReq > DataSz)\n        throw std::invalid_argument(\"Tried to read out of bounds!\");\n\n    smemcpy(OutDat,oDatSz,Data + CurrentPos,oDatSz);\n\n    CurrentPos = oReq;\n\n    return CurrentPos;\n\n}\n\nvoid ByteArr::CAlloc(const size_t & SetSize)\n{\n    if (Data && DataSz)\n\t\tdelete[] Data;\n\n\tCurrentPos = 0;\n\n\tData = new BYTE[SetSize];\n\n\tDataSz = SetSize;\n\n}\n\nvoid ByteArr::operator>>(ByteArr &BaEx)\n{\n    // We explicitly export and import sizes in unsigned 64 bits to make sure\n    // there are no compatibility problems between 32 and 64 bit architectures\n\n    // Get the size\n    UINT64 tmpSize = 0;\n    (*this) >> tmpSize;\n\n    // Request the size from the other byte array\n\n    BaEx.Request((size_t)tmpSize);\n\n    // Perform a copy directly onto the other array\n    smemcpy(BaEx.Data + BaEx.Pos(),BaEx.Size(),Data + CurrentPos,(size_t)tmpSize);\n\n\n    // Advance those positions\n    BaEx.Advance(tmpSize);\n    CurrentPos += tmpSize;\n\n\n\n}\n\nvoid ByteArr::operator<<(const ByteArr &BaAdd)\n{\n\n    // We explicitly export and import sizes in unsigned 64 bits to make sure\n    // there are no compatibility problems between 32 and 64 bit architectures\n\n\n    (*this) << (UINT64)BaAdd.Size();\n    Add(BaAdd.Data,BaAdd.Size());\n\n\n\n\n\n}\n\n// QT Functions ##########################################################\n#ifdef _QT\n\n\nvoid ByteArr::Assign(const QByteArray &QBar)\n{\n    CAlloc((size_t)QBar.size());\n\n    smemcpy(Data,DataSz,QBar.data(),(size_t)QBar.size());\n\n}\n\nQByteArray ByteArr::ToQByteArr()\n{\n    QByteArray QB((const char*)Data,(int)DataSz);\n    return QB;\n\n}\n#endif\n#ifdef USE_ZDFS\nvoid ByteArr::operator<<(const SItemW &ItemEx)\n{\n    // Write our attributes\n    (*this) << ItemEx.Attributes;\n\n    // Write basic data\n\n\n    (*this) << ItemEx.FileSzHigh;\n    (*this) << ItemEx.FileSzLow;\n    (*this) << ItemEx.IType;\n\n    (*this) << ItemEx.LastAccessTime;\n    (*this) << ItemEx.LastWriteTime;\n\n    (*this) << ItemEx.Name;\n    (*this) << ItemEx.TimeOfCreation;\n\n    // Write subentries\n    (*this) << ItemEx.SubEntries;\n}\n\nvoid ByteArr::operator<<(const SYSTEMTIME &SysTime)\n{\n    // Convert it to file time to export easier;\n    FILETIME TimeC;\n    SystemTimeToFileTime(&SysTime, &TimeC);\n\n    (*this) << TimeC.dwHighDateTime;\n    (*this) << TimeC.dwLowDateTime;\n\n}\n\nvoid ByteArr::operator<<(const FAttrib &Atr)\n{\n    (*this) << Atr.Archive;\n        (*this) << Atr.Compressed;\n        (*this) << Atr.Hidden;\n        (*this) << Atr.Normal;\n        (*this) << Atr.ReadOnly;\n        (*this) << Atr.System;\n        (*this) << Atr.Temporary;\n\n\n}\n\nvoid ByteArr::operator>>(SItemW &ItemEx)\n{\n    // Write our attributes\n    (*this) >> ItemEx.Attributes;\n\n    // Write basic data\n\n\n    (*this) >> ItemEx.FileSzHigh;\n    (*this) >> ItemEx.FileSzLow;\n    (*this) >> ItemEx.IType;\n\n    (*this) >> ItemEx.LastAccessTime;\n    (*this) >> ItemEx.LastWriteTime;\n\n    (*this) >> ItemEx.Name;\n    (*this) >> ItemEx.TimeOfCreation;\n\n    // Write subentries\n    (*this) >> ItemEx.SubEntries;\n\n}\n\nvoid ByteArr::operator>>(SYSTEMTIME &SysTime)\n{\n\n        FILETIME TimeC;\n\n        (*this) >> TimeC.dwHighDateTime;\n        (*this) >> TimeC.dwLowDateTime;\n\n        FileTimeToSystemTime(&TimeC, &SysTime);\n}\n\nvoid ByteArr::operator>>(FAttrib &ExAtr)\n{\n    (*this) >> ExAtr.Archive;\n        (*this) >> ExAtr.Compressed;\n        (*this) >> ExAtr.Hidden;\n        (*this) >> ExAtr.Normal;\n        (*this) >> ExAtr.ReadOnly;\n        (*this) >> ExAtr.System;\n        (*this) >> ExAtr.Temporary;\n\n}\n#endif\n#ifdef _QT\nvoid ByteArr::operator<<(const QByteArray &QBarEx)\n{\n    ByteArr Temp;\n    Temp.Assign(QBarEx);\n\n    (*this) << Temp;\n\n}\n\nvoid ByteArr::operator>>(QByteArray &QBarry)\n{\n\n    ByteArr Temp1;\n    (*this) >> Temp1;\n\n   QBarry.append(Temp1.ToQByteArr());\n\n\n}\n\n#endif\n// QT Functions ##########################################################\n\n\nByteArr::~ByteArr()\n{\n\ttry {\n        if (Data && DataSz && !DontDestroy)\n\t\t\tdelete[] Data;\n\t\n\t}\n\tcatch (...) {\n\t// Who the hell gives a shit about exceptions here???\n\t\n\t}\n\n}\n\nvoid smemcpy(void* dest,const size_t& destsz, const void* src,const size_t& count ){\n\n    if (count > destsz)\n        throw std::invalid_argument(\"memcpy_s, destionation size is lower than the source!!\");\n\n    memcpy(dest,src,count);\n\n\n}\n"
  },
  {
    "path": "ext/ByteArr.h",
    "content": "#ifndef BYTEARR_H\n#define BYTEARR_H\n/*\n###################################################\n\n  ____        _\n |  _ \\      | |         /\\\n | |_) |_   _| |_ ___   /  \\   _ __ _ __\n |  _ <| | | | __/ _ \\ / /\\ \\ | '__| '__|\n | |_) | |_| | ||  __// ____ \\| |  | |\n |____/ \\__, |\\__\\___/_/    \\_\\_|  |_|\n\t\t __/ |\n\t\t|___/\n###################################################\n# Description: An extensible byte array class that can also act as a\nbuffer to store various types\n# Author: ZDisket\n# Copyright (C) 2019 YOUR MOM GAY LOLOLOL\n####################################################\n*/\n\n\n#ifndef _WIN32\n#ifndef __STDC_WANT_LIB_EXT1__\n#define __STDC_WANT_LIB_EXT1__ 1\n#endif\n#endif\n\n#ifdef _QT\n#include <QString>\n#include <QByteArray>\n#endif\n\n#include <string>\n#include <vector>\n\n\ntypedef unsigned char BYTE;\ntypedef long long INT64;\ntypedef unsigned long long UINT64;\ntypedef std::vector<BYTE> ByteVec;\ntypedef unsigned long ulong;\n\n\n// If not defined, it doesn't exist. We provide our own memcpy_s implementation\n#include <iostream>\n#include <cstring>\n#ifdef USE_ZDFS\n#include \"ZDFS.h\"\n#endif\n\nvoid smemcpy(void* dest,const size_t& destsz, const void* src,const size_t& count );\n\n\n// Class meant to simplify interactions with dynamic size byte arrays\n// And as a buffer for sending stuff.\nclass ByteArr\n{\nprivate:\n\tBYTE* Data;\n\tsize_t DataSz;\n\n\tsize_t CurrentPos;\n\tvoid Realloc(const size_t& newSize);\n\n\tinline void Init();\n\n    bool DontDestroy;\npublic:\n\tByteArr();\n\n    // Set if this byte array will destroy itself on the constructor.\n    void SetDestroy(const bool& set);\n\n\t// Initialize with a certain size.\n\tByteArr(const size_t& InitSz);\n\n\t// Create a new byte arr by copying and REPLACING the contents\n\tByteArr(BYTE* CopyArr, const size_t& ArrSz);\n\t// Create a byte array from another byte array\n\tByteArr(const ByteArr& Cpy);\n\n\t// Create a byte array from a vector of bytes\n\tByteArr(const std::vector<BYTE>& CpyBv);\n#ifdef _QT\n    ByteArr(const QByteArray& InitBar);\n#endif\n    // Create a byte array by combining a vector\n    ByteArr(const std::vector<ByteArr>& BarC);\n\n    // Split the current byte array into portions of a certain size\n    // Returns vector\n    std::vector<ByteArr> Split(const ulong& szportion);\n\n    // Request a certain amount of bytes to be allocated into the byte array.\n    // If necessary, will resize.\n    void Request(const size_t& reqSz);\n\n\t// Copy the Byte Array into a vector.\n\tstd::vector<BYTE> ToVector();\n\t\n\t// Get a const reference to the raw array\n\tconst BYTE* CoData() const;\n\n\tBYTE* GetData() { return Data; }\n\n    BYTE& operator[](const size_t& Pos);\n    const BYTE& operator[](const size_t& cPos) const;\n\n    void Advance(const size_t& adv);\n\n\t// Assign a raw BYTE* by copy and REPLACE the contents\n\tvoid Assign(BYTE* cpyArr, const size_t& cpySz);\n\n\t// Assign a vector of bytes.\n\tvoid Assign(const std::vector<BYTE>& CByteVec);\n\n\t// Assign a byte array and copy contents.\n\tvoid Assign(const ByteArr& CpyByte);\n\n    // Assign a combination of byte arrays.\n    void Assign(const std::vector<ByteArr>& BarComb);\n\n\t// Get the size of the array\n\tinline size_t Size() const { return DataSz; }\n\n\n\n\tinline void IncreaseSize(const size_t& Add) { Realloc(DataSz + Add); }\n\n    inline size_t Pos() const { return CurrentPos; }\n    void Seek(const size_t& To);\n\n\t// Add something raw to the byte array. It's highly recommended \n\t// that you instead use the overloaded operator <<\n\tvoid Add(void* inDat, const size_t& DatSz);\n\n    // Reads the array and returns the current position\n    size_t Read(void* OutDat,const size_t& oDatSz);\n\n\t// REPLACE the array and allocate a new one with specified size.\n\tvoid CAlloc(const size_t& SetSize);\n\tinline void CAlloc(const INT64& SetSz) { CAlloc((size_t)SetSz); }\n\t// Add a simple data type to the bytearr\n\ttemplate<typename Ty>\n\tvoid operator<<(const Ty& In) {\n\n        Add((void*)&In,sizeof(In));\n\t\n\t}\n\t\n\ttemplate<typename Char>\n\tvoid operator<<(const std::basic_string<Char>& Str) {\n\t\t// Add the size \n\t\t(*this) << Str.size();\n\n\t\tAdd((void*)Str.data(), Str.size() * sizeof(Char));\n\n\n\n\n\t\n\t}\n    template<typename Tyo>\n    void operator>> (Tyo& Out){\n        Read((void*)&Out,sizeof(Out));\n\n\n    }\n    template<typename Char>\n    void operator>>(std::basic_string<Char>& oStr){\n        size_t rsz = 0;\n        (*this) >> rsz;\n        oStr.resize(rsz);\n\n        Read((void*)oStr.data(),rsz * sizeof(Char));\n\n\n    }\n    template<typename V>\n    void operator>>(std::vector<V>& OutVec){\n\n        size_t vsz = 0;\n        (*this) >> vsz;\n\n        OutVec.reserve(vsz);\n\n        size_t p = 0;\n\n        while (p != vsz){\n            V temp;\n            (*this) >> temp;\n            OutVec.push_back(temp);\n\n            ++p;\n        }\n\n    }\n\n    template<typename V>\n    void operator<<(const std::vector<V>& InVec){\n\n      (*this) << InVec.size();\n\n\n        size_t p = 0;\n\n        while (p != InVec.size()){\n            (*this) << InVec[p];\n\n            ++p;\n        }\n\n    }\n\n    // Append a byte array\n    void operator<<(const ByteArr& BaAdd);\n\n    // Export a previously stored byte array. Note that it appends.\n    void operator>>(ByteArr& BaEx);\n\n\n#ifdef _QT\n    // It's much  safer to use u32 string as we guarantee that all platforms will support it\n    // equally.\n\n    void operator<<(const QString& QsEx)\n    {\n        std::u32string StrEx = QsEx.toStdU32String();\n        (*this) << StrEx;\n\n    }\n    void operator>>(QString& QsOut)\n    {\n       std::u32string StrOut;\n       (*this) >> StrOut;\n       QsOut = QString::fromStdU32String(StrOut);\n\n\n\n    }\n\n    void Assign(const QByteArray& QBar);\n    QByteArray ToQByteArr();\n\n    void operator<<(const QByteArray& QBarEx);\n\n\n    void operator>>(QByteArray& QBarry);\n\n\n\n#endif\n\n\n\n#ifdef USE_ZDFS\n\n    void operator>>(FAttrib& ExAtr);\n\n\n    void operator>>(SYSTEMTIME& SysTime);\n\n\n    void operator>>(SItemW& ItemEx);\n\n    void operator<<(const FAttrib& Atr);\n\n    void operator<<(const SYSTEMTIME& SysTime);\n\n    void operator<<(const SItemW& ItemEx);\n#endif\n\n\t~ByteArr();\n};\n\n#endif\n"
  },
  {
    "path": "ext/CppFlow/context.h",
    "content": "//\n// Created by serizba on 27/6/20.\n//\n\n#ifndef CPPFLOW2_CONTEXT_H\n#define CPPFLOW2_CONTEXT_H\n\n#include <memory>\n#include <stdexcept>\n#include <utility>\n\n#include <tensorflow/c/c_api.h>\n#include <tensorflow/c/eager/c_api.h>\n\nnamespace cppflow {\n\n    inline bool status_check(TF_Status* status) {\n        if (TF_GetCode(status) != TF_OK) {\n            throw std::runtime_error(TF_Message(status));\n        }\n        return true;\n    }\n\n    class context {\n        public:\n            static TFE_Context* get_context();\n            static TF_Status* get_status();\n\n        private:\n            TFE_Context* tfe_context{nullptr};\n\n        public:\n            explicit context(TFE_ContextOptions* opts = nullptr);\n\n            context(context const&) = delete;\n            context& operator=(context const&) = delete;\n            context(context&&) noexcept;\n            context& operator=(context&&) noexcept;\n\n            ~context();\n    };\n\n    // TODO: create ContextManager class if needed\n    // Set new context, thread unsafe, must be called at the beginning.\n    //  TFE_ContextOptions* tfe_opts = ...\n    //  cppflow::get_global_context() = cppflow::context(tfe_opts);\n    inline context& get_global_context() {\n        static context global_context;\n        return global_context;\n    }\n\n}\n\nnamespace cppflow {\n\n    inline TFE_Context* context::get_context() {\n        return get_global_context().tfe_context;\n    }\n\n    inline TF_Status* context::get_status() {\n        thread_local std::unique_ptr<TF_Status, decltype(&TF_DeleteStatus)> local_tf_status(TF_NewStatus(), &TF_DeleteStatus);\n        return local_tf_status.get();\n    }\n\n    inline context::context(TFE_ContextOptions* opts) {\n        auto tf_status = context::get_status();\n        if(opts == nullptr) {\n            std::unique_ptr<TFE_ContextOptions, decltype(&TFE_DeleteContextOptions)> new_opts(TFE_NewContextOptions(), &TFE_DeleteContextOptions);\n            this->tfe_context = TFE_NewContext(new_opts.get(), tf_status);\n        } else {\n            this->tfe_context = TFE_NewContext(opts, tf_status);\n        }\n        status_check(tf_status);\n    }\n\n    inline context::context(context&& ctx) noexcept :\n        tfe_context(std::exchange(ctx.tfe_context, nullptr))\n    {\n    }\n\n    inline context& context::operator=(context&& ctx) noexcept {\n        tfe_context = std::exchange(ctx.tfe_context, tfe_context);\n        return *this;\n    }\n\n    inline context::~context() {\n        TFE_DeleteContext(this->tfe_context);\n    }\n\n}\n\n#endif //CPPFLOW2_CONTEXT_H\n"
  },
  {
    "path": "ext/CppFlow/cppflow.h",
    "content": "//\n// Created by serizba on 17/9/20.\n//\n\n#ifndef EXAMPLE_CPPFLOW_H\n#define EXAMPLE_CPPFLOW_H\n\n#include \"tensor.h\"\n#include \"model.h\"\n#include \"raw_ops.h\"\n#include \"ops.h\"\n#include \"datatype.h\"\n\n#include <tensorflow/c/c_api.h>\n\nnamespace cppflow {\n\n    /**\n     * Version of TensorFlow and CppFlow\n     * @return A string containing the version of TensorFow and CppFlow\n     */\n    std::string version();\n\n}\n\n/******************************\n *   IMPLEMENTATION DETAILS   *\n ******************************/\n\nnamespace cppflow {\n    inline std::string version() {\n        return \"TensorFlow: \" + std::string(TF_Version()) + \" CppFlow: 2.0.0\";\n    }\n}\n\n#endif //EXAMPLE_CPPFLOW_H\n"
  },
  {
    "path": "ext/CppFlow/datatype.h",
    "content": "//\n// Created by serizba on 12/7/20.\n//\n\n#ifndef CPPFLOW2_DATATYPE_H\n#define CPPFLOW2_DATATYPE_H\n\n#include <type_traits>\n#include <string>\n#include <typeinfo>\n#include <ostream>\n#include <stdexcept>\n\nnamespace cppflow {\n\n    using datatype = TF_DataType;\n\n    /**\n     * @return A string representing dt\n     *\n     */\n    inline std::string to_string(datatype dt) {\n        switch (dt) {\n            case TF_FLOAT:\n                return \"TF_FLOAT\";\n            case TF_DOUBLE:\n                return \"TF_DOUBLE\";\n            case TF_INT32:\n                return \"TF_INT32\";\n            case TF_UINT8:\n                return \"TF_UINT8\";\n            case TF_INT16:\n                return \"TF_INT16\";\n            case TF_INT8:\n                return \"TF_INT8\";\n            case TF_STRING:\n                return \"TF_STRING\";\n            case TF_COMPLEX64:\n                return \"TF_COMPLEX64\";\n            case TF_INT64:\n                return \"TF_INT64\";\n            case TF_BOOL:\n                return \"TF_BOOL\";\n            case TF_QINT8:\n                return \"TF_QINT8\";\n            case TF_QUINT8:\n                return \"TF_QUINT8\";\n            case TF_QINT32:\n                return \"TF_QINT32\";\n            case TF_BFLOAT16:\n                return \"TF_BFLOAT16\";\n            case TF_QINT16:\n                return \"TF_QINT16\";\n            case TF_QUINT16:\n                return \"TF_QUINT16\";\n            case TF_UINT16:\n                return \"TF_UINT16\";\n            case TF_COMPLEX128:\n                return \"TF_COMPLEX128\";\n            case TF_HALF:\n                return \"TF_HALF\";\n            case TF_RESOURCE:\n                return \"TF_RESOURCE\";\n            case TF_VARIANT:\n                return \"TF_VARIANT\";\n            case TF_UINT32:\n                return \"TF_UINT32\";\n            case TF_UINT64:\n                return \"TF_UINT64\";\n            default:\n                return \"DATATYPE_NOT_KNOWN\";\n        }\n    }\n\n    /**\n     *\n     * @tparam T\n     * @return The TensorFlow type of T\n     */\n    template<typename T>\n    TF_DataType deduce_tf_type() {\n        if (std::is_same<T, float>::value)\n            return TF_FLOAT;\n        if (std::is_same<T, double>::value)\n            return TF_DOUBLE;\n        if (std::is_same<T, int32_t >::value)\n            return TF_INT32;\n        if (std::is_same<T, uint8_t>::value)\n            return TF_UINT8;\n        if (std::is_same<T, int16_t>::value)\n            return TF_INT16;\n        if (std::is_same<T, int8_t>::value)\n            return TF_INT8;\n        if (std::is_same<T, int64_t>::value)\n            return TF_INT64;\n        if (std::is_same<T, unsigned char>::value)\n            return TF_BOOL;\n        if (std::is_same<T, uint16_t>::value)\n            return TF_UINT16;\n        if (std::is_same<T, uint32_t>::value)\n            return TF_UINT32;\n        if (std::is_same<T, uint64_t>::value)\n            return TF_UINT64;\n\n        // decode with `c++filt --type $output` for gcc\n        throw std::runtime_error{\"Could not deduce type! type_name: \" + std::string(typeid(T).name())};\n    }\n\n    /**\n     * @return  The stream os after inserting the string representation of dt\n     *\n     */\n    inline std::ostream& operator<<(std::ostream& os, datatype dt) {\n        os << to_string(dt);\n        return os;\n    }\n\n}\n#endif //CPPFLOW2_DATATYPE_H\n"
  },
  {
    "path": "ext/CppFlow/defer.h",
    "content": "#pragma once\n#include <functional>\n\nnamespace cppflow {\n\nclass defer {\npublic:\n    typedef std::function<void ()> Func;\n\n    explicit defer(const Func& func) : _func(func) {}\n    ~defer() {\n        _func();\n    }\n\n    defer(const defer&) = delete;\n    defer(defer&&) = delete;\n    defer& operator=(const defer&) = delete;\n    void* operator new (size_t) = delete;\n    void operator delete (void*) = delete;\n\nprivate:\n    Func _func;\n};\n\n} // namespace cppflow\n"
  },
  {
    "path": "ext/CppFlow/model.h",
    "content": "//\n// Created by serizba on 29/6/20.\n//\n\n#ifndef CPPFLOW2_MODEL_H\n#define CPPFLOW2_MODEL_H\n\n#include <tensorflow/c/c_api.h>\n#include <string>\n#include <fstream>\n#include <iostream>\n#include <vector>\n\n#include \"context.h\"\n#include \"defer.h\"\n#include \"tensor.h\"\n\nnamespace cppflow {\n\n    class model {\n    public:\n        explicit model(const std::string& filename);\n\n        std::vector<std::string> get_operations() const;\n        std::vector<int64_t> get_operation_shape(const std::string& operation) const;\n\n        std::vector<tensor> operator()(std::vector<std::tuple<std::string, tensor>> inputs, std::vector<std::string> outputs);\n        tensor operator()(const tensor& input);\n\n        ~model() = default;\n        model(const model &model) = default;\n        model(model &&model) = default;\n        model &operator=(const model &other) = default;\n        model &operator=(model &&other) = default;\n\n    private:\n\n        std::shared_ptr<TF_Graph> graph;\n        std::shared_ptr<TF_Session> session;\n    };\n}\n\n\nnamespace cppflow {\n\n    inline model::model(const std::string &filename) {\n        this->graph = {TF_NewGraph(), TF_DeleteGraph};\n\n        // Create the session.\n        std::unique_ptr<TF_SessionOptions, decltype(&TF_DeleteSessionOptions)> session_options = {TF_NewSessionOptions(), TF_DeleteSessionOptions};\n        std::unique_ptr<TF_Buffer, decltype(&TF_DeleteBuffer)> run_options = {TF_NewBufferFromString(\"\", 0), TF_DeleteBuffer};\n        std::unique_ptr<TF_Buffer, decltype(&TF_DeleteBuffer)> meta_graph = {TF_NewBuffer(), TF_DeleteBuffer};\n\n        auto session_deleter = [](TF_Session* sess) {\n            TF_DeleteSession(sess, context::get_status());\n            status_check(context::get_status());\n        };\n\n        int tag_len = 1;\n        const char* tag = \"serve\";\n        this->session = {TF_LoadSessionFromSavedModel(session_options.get(), run_options.get(), filename.c_str(),\n                                &tag, tag_len, this->graph.get(), meta_graph.get(), context::get_status()),\n                         session_deleter};\n\n        status_check(context::get_status());\n    }\n\n    inline std::vector<std::string> model::get_operations() const {\n        std::vector<std::string> result;\n        size_t pos = 0;\n        TF_Operation* oper;\n\n        // Iterate through the operations of a graph\n        while ((oper = TF_GraphNextOperation(this->graph.get(), &pos)) != nullptr) {\n            result.emplace_back(TF_OperationName(oper));\n        }\n        return result;\n    }\n\n    inline std::vector<int64_t> model::get_operation_shape(const std::string& operation) const {\n        // Get operation by the name\n        TF_Output out_op;\n        out_op.oper = TF_GraphOperationByName(this->graph.get(), operation.c_str());\n        out_op.index = 0;\n\n        std::vector<int64_t> shape;\n\n        // Operation does not exist\n        if (!out_op.oper)\n            throw std::runtime_error(\"No operation named \\\"\" + operation + \"\\\" exists\");\n\n        // DIMENSIONS\n\n        // Get number of dimensions\n        int n_dims = TF_GraphGetTensorNumDims(this->graph.get(), out_op, context::get_status());\n\n        // If is not a scalar\n        if (n_dims > 0) {\n            // Get dimensions\n            auto* dims = new int64_t[n_dims];\n            TF_GraphGetTensorShape(this->graph.get(), out_op, dims, n_dims, context::get_status());\n\n            // Check error on Model Status\n            status_check(context::get_status());\n\n            shape = std::vector<int64_t>(dims, dims + n_dims);\n\n            delete[] dims;\n        }\n\n        return shape;\n    }\n\n    inline std::tuple<std::string, int> parse_name(const std::string& name) {\n        auto idx = name.find(':');\n        return (idx == -1 ? std::make_tuple(name, 0) : std::make_tuple(name.substr(0, idx), std::stoi(name.substr(idx + 1))));\n    }\n\n    inline std::vector<tensor> model::operator()(std::vector<std::tuple<std::string, tensor>> inputs, std::vector<std::string> outputs) {\n\n        std::vector<TF_Output> inp_ops(inputs.size());\n        std::vector<TF_Tensor*> inp_val(inputs.size(), nullptr);\n\n        for (int i=0; i<inputs.size(); i++) {\n\n            // Operations\n            const auto[op_name, op_idx] = parse_name(std::get<0>(inputs[i]));\n            inp_ops[i].oper = TF_GraphOperationByName(this->graph.get(), op_name.c_str());\n            inp_ops[i].index = op_idx;\n\n            if (!inp_ops[i].oper)\n                throw std::runtime_error(\"No operation named \\\"\" + op_name + \"\\\" exists\");\n\n            // Values\n            inp_val[i] = std::get<1>(inputs[i]).get_tensor().get();\n        }\n\n        std::vector<TF_Output> out_ops(outputs.size());\n        auto out_val = std::make_unique<TF_Tensor*[]>(outputs.size());\n        for (int i=0; i<outputs.size(); i++) {\n\n            const auto[op_name, op_idx] = parse_name(outputs[i]);\n            out_ops[i].oper = TF_GraphOperationByName(this->graph.get(), op_name.c_str());\n            out_ops[i].index = op_idx;\n\n            if (!out_ops[i].oper)\n                throw std::runtime_error(\"No operation named \\\"\" + op_name + \"\\\" exists\");\n\n        }\n\n        TF_SessionRun(this->session.get(), NULL,\n                inp_ops.data(), inp_val.data(), inputs.size(),\n                out_ops.data(), out_val.get(), outputs.size(),\n                NULL, 0,NULL , context::get_status());\n        status_check(context::get_status());\n\n        std::vector<tensor> result;\n        result.reserve(outputs.size());\n        for (int i=0; i<outputs.size(); i++) {\n            result.emplace_back(tensor(out_val[i]));\n        }\n\n        return result;\n    }\n\n    inline tensor model::operator()(const tensor& input) {\n        return (*this)({{\"serving_default_input_1\", input}}, {\"StatefulPartitionedCall\"})[0];\n    }\n}\n\n#endif //CPPFLOW2_MODEL_H\n"
  },
  {
    "path": "ext/CppFlow/ops.h",
    "content": "//\n// Created by serizba on 31/7/20.\n//\n\n#ifndef CPPFLOW2_OPS_H\n#define CPPFLOW2_OPS_H\n\n\n#include \"tensor.h\"\n#include \"raw_ops.h\"\n\nnamespace cppflow {\n\n    /**\n     * @name Operators\n     */\n    //@{\n\n    /**\n     * @returns x + y elementwise\n     */\n    tensor operator+(const tensor& x, const tensor& y);\n\n    /**\n     * @returns x - y elementwise\n     */\n    tensor operator-(const tensor& x, const tensor& y);\n\n    /**\n     * @returns x * y elementwise\n     */\n    tensor operator*(const tensor& x, const tensor& y);\n\n    /**\n     * @return x / y elementwise\n     */\n    tensor operator/(const tensor& x, const tensor& y);\n\n    std::ostream& operator<<(std::ostream& os, const cppflow::tensor& t);\n\n    //@}\n\n    /**\n     * @return A string representing t in the form:\n     * (tensor: shape=?, data=\n     * ?)\n     */\n    std::string to_string(const tensor& t);\n}\n\n/******************************\n *   IMPLEMENTATION DETAILS   *\n ******************************/\n\nnamespace cppflow {\n\n    // Operators\n\n    inline tensor operator+(const tensor& x, const tensor& y) {\n        return add(x, y);\n    }\n\n    inline tensor operator-(const tensor& x, const tensor& y) {\n        return sub(x, y);\n    }\n\n    inline tensor operator*(const tensor& x, const tensor& y) {\n        return mul(x, y);\n    }\n\n    inline tensor operator/(const tensor& x, const tensor& y) {\n        return div(x, y);\n    }\n\n    inline std::ostream& operator<<(std::ostream& os, const cppflow::tensor& t) {\n        std::string res =  to_string(t);\n        return os << res;\n    }\n\n\n    inline std::string to_string(const tensor &t) {\n        auto res_tensor = string_format({t.shape(), t}, \"(tensor: shape=%s, data=\\n%s)\");\n        auto res_tensor_h = res_tensor.get_tensor();\n\n#ifdef TENSORFLOW_C_TF_TSTRING_H_\n        // For future version TensorFlow 2.4\n        //auto *t_str = reinterpret_cast<TF_TString *>(TF_TensorData(res_tensor_h.get()));\n        auto *t_str = (TF_TString *)(TF_TensorData(res_tensor_h.get()));\n        auto result = std::string(TF_TString_GetDataPointer(t_str), TF_TString_GetSize(t_str));\n#else\n        const char* dst[1] = {nullptr};\n        size_t dst_len[1] = {3};\n        TF_StringDecode(static_cast<char*>(TF_TensorData(res_tensor_h.get())) + 8, TF_TensorByteSize(res_tensor_h.get()), dst, dst_len, context::get_status());\n        status_check(context::get_status());\n        auto result = std::string(dst[0], *dst_len);\n#endif // TENSORFLOW_C_TF_TSTRING_H_\n\n        return result;\n    }\n\n}\n\n#endif //CPPFLOW2_OPS_H\n"
  },
  {
    "path": "ext/CppFlow/raw_ops.h",
    "content": "\n/**\n * @file ops.h\n * TensorFlow raw_ops mappings\n */\n\n#ifndef CPPFLOW2_RAW_OPS_H\n#define CPPFLOW2_RAW_OPS_H\n\n#include <cstdint>\n#include <vector>\n#include <limits>\n#include <algorithm>\n\n#include <tensorflow/c/eager/c_api.h>\n#include <tensorflow/c/tf_datatype.h>\n#include <tensorflow/c/tf_tensor.h>\n\n#include \"tensor.h\"\n#include \"datatype.h\"\n\n\n// disable the 100000 quintillion quadrupsextillion warnings\n#pragma warning(disable: 4267)\n\nnamespace cppflow {\n\n\n\ninline tensor abs(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Abs\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor accumulate_n_v2(const std::vector<tensor>&inputs, const std::vector<int64_t>& shape) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AccumulateNV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> inputs_handles; inputs_handles.reserve(inputs.size());\n    std::transform(inputs.begin(), inputs.end(), std::back_inserter(inputs_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), inputs_handles.data(), inputs.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"N\", inputs.size());\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor accumulator_num_accumulated(const tensor& handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AccumulatorNumAccumulated\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor accumulator_take_gradient(const tensor& handle, const tensor& num_required, datatype dtype) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AccumulatorTakeGradient\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_required.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor acos(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Acos\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor acosh(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Acosh\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor add(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Add\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor add_many_sparse_to_tensors_map(const tensor& sparse_indices, const tensor& sparse_values, const tensor& sparse_shape, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AddManySparseToTensorsMap\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), sparse_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sparse_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sparse_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor add_n(const std::vector<tensor>&inputs) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AddN\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> inputs_handles; inputs_handles.reserve(inputs.size());\n    std::transform(inputs.begin(), inputs.end(), std::back_inserter(inputs_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), inputs_handles.data(), inputs.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"N\", inputs.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor add_sparse_to_tensors_map(const tensor& sparse_indices, const tensor& sparse_values, const tensor& sparse_shape, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AddSparseToTensorsMap\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), sparse_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sparse_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sparse_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor add_v2(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AddV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor adjust_contrast(const tensor& images, const tensor& contrast_factor, const tensor& min_value, const tensor& max_value) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AdjustContrast\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), images.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), contrast_factor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), min_value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), max_value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor adjust_contrastv2(const tensor& images, const tensor& contrast_factor) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AdjustContrastv2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), images.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), contrast_factor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor adjust_hue(const tensor& images, const tensor& delta) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AdjustHue\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), images.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), delta.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor adjust_saturation(const tensor& images, const tensor& scale) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AdjustSaturation\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), images.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), scale.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor all(const tensor& input, const tensor& reduction_indices, bool keep_dims=false, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"All\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), reduction_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"keep_dims\", (unsigned char)keep_dims);\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor all_to_all(const tensor& input, const tensor& group_assignment, int64_t concat_dimension, int64_t split_dimension, int64_t split_count) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AllToAll\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), group_assignment.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"concat_dimension\", concat_dimension);\n    TFE_OpSetAttrInt(op.get(), \"split_dimension\", split_dimension);\n    TFE_OpSetAttrInt(op.get(), \"split_count\", split_count);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor angle(const tensor& input, datatype Tout=static_cast<datatype>(1)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Angle\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tout\", Tout);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor anonymous_iterator(const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AnonymousIterator\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor any(const tensor& input, const tensor& reduction_indices, bool keep_dims=false, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Any\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), reduction_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"keep_dims\", (unsigned char)keep_dims);\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor apply_ada_max(const tensor& var, const tensor& m, const tensor& v, const tensor& beta1_power, const tensor& lr, const tensor& beta1, const tensor& beta2, const tensor& epsilon, const tensor& grad, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ApplyAdaMax\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), m.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), v.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), beta1_power.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), beta1.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), beta2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), epsilon.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor apply_adadelta(const tensor& var, const tensor& accum, const tensor& accum_update, const tensor& lr, const tensor& rho, const tensor& epsilon, const tensor& grad, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ApplyAdadelta\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), accum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), accum_update.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rho.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), epsilon.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor apply_adagrad(const tensor& var, const tensor& accum, const tensor& lr, const tensor& grad, bool use_locking=false, bool update_slots=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ApplyAdagrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), accum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n    TFE_OpSetAttrBool(op.get(), \"update_slots\", (unsigned char)update_slots);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor apply_adagrad_d_a(const tensor& var, const tensor& gradient_accumulator, const tensor& gradient_squared_accumulator, const tensor& grad, const tensor& lr, const tensor& l1, const tensor& l2, const tensor& global_step, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ApplyAdagradDA\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), gradient_accumulator.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), gradient_squared_accumulator.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l1.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), global_step.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor apply_adagrad_v2(const tensor& var, const tensor& accum, const tensor& lr, const tensor& epsilon, const tensor& grad, bool use_locking=false, bool update_slots=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ApplyAdagradV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), accum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), epsilon.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n    TFE_OpSetAttrBool(op.get(), \"update_slots\", (unsigned char)update_slots);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor apply_adam(const tensor& var, const tensor& m, const tensor& v, const tensor& beta1_power, const tensor& beta2_power, const tensor& lr, const tensor& beta1, const tensor& beta2, const tensor& epsilon, const tensor& grad, bool use_locking=false, bool use_nesterov=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ApplyAdam\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), m.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), v.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), beta1_power.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), beta2_power.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), beta1.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), beta2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), epsilon.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n    TFE_OpSetAttrBool(op.get(), \"use_nesterov\", (unsigned char)use_nesterov);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor apply_add_sign(const tensor& var, const tensor& m, const tensor& lr, const tensor& alpha, const tensor& sign_decay, const tensor& beta, const tensor& grad, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ApplyAddSign\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), m.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), alpha.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sign_decay.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), beta.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor apply_centered_r_m_s_prop(const tensor& var, const tensor& mg, const tensor& ms, const tensor& mom, const tensor& lr, const tensor& rho, const tensor& momentum, const tensor& epsilon, const tensor& grad, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ApplyCenteredRMSProp\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), mg.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), ms.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), mom.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rho.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), momentum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), epsilon.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor apply_ftrl(const tensor& var, const tensor& accum, const tensor& linear, const tensor& grad, const tensor& lr, const tensor& l1, const tensor& l2, const tensor& lr_power, bool use_locking=false, bool multiply_linear_by_lr=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ApplyFtrl\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), accum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), linear.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l1.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr_power.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n    TFE_OpSetAttrBool(op.get(), \"multiply_linear_by_lr\", (unsigned char)multiply_linear_by_lr);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor apply_ftrl_v2(const tensor& var, const tensor& accum, const tensor& linear, const tensor& grad, const tensor& lr, const tensor& l1, const tensor& l2, const tensor& l2_shrinkage, const tensor& lr_power, bool use_locking=false, bool multiply_linear_by_lr=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ApplyFtrlV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), accum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), linear.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l1.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l2_shrinkage.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr_power.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n    TFE_OpSetAttrBool(op.get(), \"multiply_linear_by_lr\", (unsigned char)multiply_linear_by_lr);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor apply_gradient_descent(const tensor& var, const tensor& alpha, const tensor& delta, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ApplyGradientDescent\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), alpha.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), delta.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor apply_momentum(const tensor& var, const tensor& accum, const tensor& lr, const tensor& grad, const tensor& momentum, bool use_locking=false, bool use_nesterov=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ApplyMomentum\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), accum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), momentum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n    TFE_OpSetAttrBool(op.get(), \"use_nesterov\", (unsigned char)use_nesterov);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor apply_power_sign(const tensor& var, const tensor& m, const tensor& lr, const tensor& logbase, const tensor& sign_decay, const tensor& beta, const tensor& grad, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ApplyPowerSign\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), m.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), logbase.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sign_decay.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), beta.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor apply_proximal_adagrad(const tensor& var, const tensor& accum, const tensor& lr, const tensor& l1, const tensor& l2, const tensor& grad, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ApplyProximalAdagrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), accum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l1.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor apply_proximal_gradient_descent(const tensor& var, const tensor& alpha, const tensor& l1, const tensor& l2, const tensor& delta, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ApplyProximalGradientDescent\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), alpha.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l1.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), delta.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor apply_r_m_s_prop(const tensor& var, const tensor& ms, const tensor& mom, const tensor& lr, const tensor& rho, const tensor& momentum, const tensor& epsilon, const tensor& grad, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ApplyRMSProp\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), ms.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), mom.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rho.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), momentum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), epsilon.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor approximate_equal(const tensor& x, const tensor& y, float tolerance=1.0000e-05) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ApproximateEqual\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrFloat(op.get(), \"tolerance\", tolerance);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor arg_max(const tensor& input, const tensor& dimension, datatype Tidx=static_cast<datatype>(3), datatype output_type=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ArgMax\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dimension.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n    TFE_OpSetAttrType(op.get(), \"output_type\", output_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor arg_min(const tensor& input, const tensor& dimension, datatype Tidx=static_cast<datatype>(3), datatype output_type=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ArgMin\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dimension.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n    TFE_OpSetAttrType(op.get(), \"output_type\", output_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor as_string(const tensor& input, int64_t precision=-1, bool scientific=false, bool shortest=false, int64_t width=-1, const std::string& fill=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AsString\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"precision\", precision);\n    TFE_OpSetAttrBool(op.get(), \"scientific\", (unsigned char)scientific);\n    TFE_OpSetAttrBool(op.get(), \"shortest\", (unsigned char)shortest);\n    TFE_OpSetAttrInt(op.get(), \"width\", width);\n    TFE_OpSetAttrString(op.get(), \"fill\", (void*) fill.c_str(), fill.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor asin(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Asin\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor asinh(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Asinh\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor assert_cardinality_dataset(const tensor& input_dataset, const tensor& cardinality, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AssertCardinalityDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), cardinality.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor assert_next_dataset(const tensor& input_dataset, const tensor& transformations, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AssertNextDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), transformations.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor assign(const tensor& ref, const tensor& value, bool validate_shape=true, bool use_locking=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Assign\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"validate_shape\", (unsigned char)validate_shape);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor assign_add(const tensor& ref, const tensor& value, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AssignAdd\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor assign_sub(const tensor& ref, const tensor& value, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AssignSub\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor atan(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Atan\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor atan2(const tensor& y, const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Atan2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor atanh(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Atanh\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor audio_spectrogram(const tensor& input, int64_t window_size, int64_t stride, bool magnitude_squared=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AudioSpectrogram\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"window_size\", window_size);\n    TFE_OpSetAttrInt(op.get(), \"stride\", stride);\n    TFE_OpSetAttrBool(op.get(), \"magnitude_squared\", (unsigned char)magnitude_squared);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor audio_summary(const tensor& tag, const tensor& input_tensor, float sample_rate, int64_t max_outputs=3) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AudioSummary\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), tag.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrFloat(op.get(), \"sample_rate\", sample_rate);\n    TFE_OpSetAttrInt(op.get(), \"max_outputs\", max_outputs);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor audio_summary_v2(const tensor& tag, const tensor& input_tensor, const tensor& sample_rate, int64_t max_outputs=3) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AudioSummaryV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), tag.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sample_rate.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"max_outputs\", max_outputs);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor auto_shard_dataset(const tensor& input_dataset, const tensor& num_workers, const tensor& index, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, int64_t auto_shard_policy=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AutoShardDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_workers.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), index.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"auto_shard_policy\", auto_shard_policy);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor avg_pool(const tensor& value, const std::vector<int64_t>& ksize, const std::vector<int64_t>& strides, const std::string& padding, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AvgPool\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"ksize\", ksize.data(), ksize.size());\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor avg_pool3_d(const tensor& input, const std::vector<int64_t>& ksize, const std::vector<int64_t>& strides, const std::string& padding, const std::string& data_format=\"NDHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AvgPool3D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"ksize\", ksize.data(), ksize.size());\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor avg_pool3_d_grad(const tensor& orig_input_shape, const tensor& grad, const std::vector<int64_t>& ksize, const std::vector<int64_t>& strides, const std::string& padding, const std::string& data_format=\"NDHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AvgPool3DGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), orig_input_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"ksize\", ksize.data(), ksize.size());\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor avg_pool_grad(const tensor& orig_input_shape, const tensor& grad, const std::vector<int64_t>& ksize, const std::vector<int64_t>& strides, const std::string& padding, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"AvgPoolGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), orig_input_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"ksize\", ksize.data(), ksize.size());\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor banded_triangular_solve(const tensor& matrix, const tensor& rhs, bool lower=true, bool adjoint=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BandedTriangularSolve\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), matrix.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rhs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"lower\", (unsigned char)lower);\n    TFE_OpSetAttrBool(op.get(), \"adjoint\", (unsigned char)adjoint);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor barrier(const std::vector<datatype>& component_types, const std::vector< std::vector<int64_t>>& shapes, int64_t capacity=-1, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Barrier\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"component_types\", reinterpret_cast<const enum TF_DataType *>(component_types.data()), component_types.size());\n    \n    std::vector<const int64_t*> shapes_values; shapes_values.reserve(shapes.size());\n    std::vector<int> shapes_ndims; shapes_ndims.reserve(shapes.size());\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_values), [](const auto& v) { return v.data();});\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"shapes\", shapes_values.data(), shapes_ndims.data(), shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor barrier_incomplete_size(const tensor& handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BarrierIncompleteSize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor barrier_ready_size(const tensor& handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BarrierReadySize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_cholesky(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchCholesky\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_cholesky_grad(const tensor& l, const tensor& grad) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchCholeskyGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), l.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_dataset(const tensor& input_dataset, const tensor& batch_size, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), batch_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_dataset_v2(const tensor& input_dataset, const tensor& batch_size, const tensor& drop_remainder, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, bool parallel_copy=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchDatasetV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), batch_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), drop_remainder.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrBool(op.get(), \"parallel_copy\", (unsigned char)parallel_copy);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_f_f_t(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchFFT\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_f_f_t2_d(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchFFT2D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_f_f_t3_d(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchFFT3D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_i_f_f_t(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchIFFT\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_i_f_f_t2_d(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchIFFT2D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_i_f_f_t3_d(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchIFFT3D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_mat_mul(const tensor& x, const tensor& y, bool adj_x=false, bool adj_y=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchMatMul\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"adj_x\", (unsigned char)adj_x);\n    TFE_OpSetAttrBool(op.get(), \"adj_y\", (unsigned char)adj_y);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_mat_mul_v2(const tensor& x, const tensor& y, bool adj_x=false, bool adj_y=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchMatMulV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"adj_x\", (unsigned char)adj_x);\n    TFE_OpSetAttrBool(op.get(), \"adj_y\", (unsigned char)adj_y);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_matrix_band_part(const tensor& input, const tensor& num_lower, const tensor& num_upper) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchMatrixBandPart\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_lower.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_upper.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_matrix_determinant(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchMatrixDeterminant\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_matrix_diag(const tensor& diagonal) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchMatrixDiag\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), diagonal.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_matrix_diag_part(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchMatrixDiagPart\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_matrix_inverse(const tensor& input, bool adjoint=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchMatrixInverse\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"adjoint\", (unsigned char)adjoint);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_matrix_set_diag(const tensor& input, const tensor& diagonal) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchMatrixSetDiag\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), diagonal.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_matrix_solve(const tensor& matrix, const tensor& rhs, bool adjoint=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchMatrixSolve\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), matrix.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rhs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"adjoint\", (unsigned char)adjoint);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_matrix_solve_ls(const tensor& matrix, const tensor& rhs, const tensor& l2_regularizer, bool fast=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchMatrixSolveLs\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), matrix.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rhs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l2_regularizer.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"fast\", (unsigned char)fast);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_matrix_triangular_solve(const tensor& matrix, const tensor& rhs, bool lower=true, bool adjoint=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchMatrixTriangularSolve\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), matrix.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rhs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"lower\", (unsigned char)lower);\n    TFE_OpSetAttrBool(op.get(), \"adjoint\", (unsigned char)adjoint);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_norm_with_global_normalization(const tensor& t, const tensor& m, const tensor& v, const tensor& beta, const tensor& gamma, float variance_epsilon, bool scale_after_normalization) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchNormWithGlobalNormalization\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), t.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), m.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), v.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), beta.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), gamma.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrFloat(op.get(), \"variance_epsilon\", variance_epsilon);\n    TFE_OpSetAttrBool(op.get(), \"scale_after_normalization\", (unsigned char)scale_after_normalization);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_self_adjoint_eig(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchSelfAdjointEig\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_to_space(const tensor& input, const tensor& crops, int64_t block_size, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchToSpace\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), crops.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"block_size\", block_size);\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor batch_to_space_n_d(const tensor& input, const tensor& block_shape, const tensor& crops, datatype Tblock_shape=static_cast<datatype>(3), datatype Tcrops=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BatchToSpaceND\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), block_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), crops.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tblock_shape\", Tblock_shape);\n    TFE_OpSetAttrType(op.get(), \"Tcrops\", Tcrops);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bessel_i0(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BesselI0\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bessel_i0e(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BesselI0e\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bessel_i1(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BesselI1\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bessel_i1e(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BesselI1e\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bessel_j0(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BesselJ0\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bessel_j1(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BesselJ1\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bessel_k0(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BesselK0\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bessel_k0e(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BesselK0e\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bessel_k1(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BesselK1\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bessel_k1e(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BesselK1e\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bessel_y0(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BesselY0\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bessel_y1(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BesselY1\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor betainc(const tensor& a, const tensor& b, const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Betainc\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), a.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), b.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bias_add(const tensor& value, const tensor& bias, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BiasAdd\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), bias.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bias_add_grad(const tensor& out_backprop, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BiasAddGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), out_backprop.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bias_add_v1(const tensor& value, const tensor& bias) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BiasAddV1\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), bias.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bincount(const tensor& arr, const tensor& size, const tensor& weights) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Bincount\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), arr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), weights.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bitcast(const tensor& input, datatype type) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Bitcast\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"type\", type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bitwise_and(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BitwiseAnd\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bitwise_or(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BitwiseOr\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bitwise_xor(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BitwiseXor\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor boosted_trees_aggregate_stats(const tensor& node_ids, const tensor& gradients, const tensor& hessians, const tensor& feature, int64_t max_splits, int64_t num_buckets) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BoostedTreesAggregateStats\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), node_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), gradients.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), hessians.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), feature.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"max_splits\", max_splits);\n    TFE_OpSetAttrInt(op.get(), \"num_buckets\", num_buckets);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor boosted_trees_bucketize(const std::vector<tensor>&float_values, const std::vector<tensor>&bucket_boundaries) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BoostedTreesBucketize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> float_values_handles; float_values_handles.reserve(float_values.size());\n    std::transform(float_values.begin(), float_values.end(), std::back_inserter(float_values_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), float_values_handles.data(), float_values.size(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> bucket_boundaries_handles; bucket_boundaries_handles.reserve(bucket_boundaries.size());\n    std::transform(bucket_boundaries.begin(), bucket_boundaries.end(), std::back_inserter(bucket_boundaries_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), bucket_boundaries_handles.data(), bucket_boundaries.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_features\", float_values.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor boosted_trees_center_bias(const tensor& tree_ensemble_handle, const tensor& mean_gradients, const tensor& mean_hessians, const tensor& l1, const tensor& l2) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BoostedTreesCenterBias\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), tree_ensemble_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), mean_gradients.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), mean_hessians.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l1.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor boosted_trees_ensemble_resource_handle_op(const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BoostedTreesEnsembleResourceHandleOp\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor boosted_trees_example_debug_outputs(const tensor& tree_ensemble_handle, const std::vector<tensor>&bucketized_features, int64_t logits_dimension) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BoostedTreesExampleDebugOutputs\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), tree_ensemble_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> bucketized_features_handles; bucketized_features_handles.reserve(bucketized_features.size());\n    std::transform(bucketized_features.begin(), bucketized_features.end(), std::back_inserter(bucketized_features_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), bucketized_features_handles.data(), bucketized_features.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_bucketized_features\", bucketized_features.size());\n    TFE_OpSetAttrInt(op.get(), \"logits_dimension\", logits_dimension);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor boosted_trees_flush_quantile_summaries(const tensor& quantile_stream_resource_handle, int64_t num_features) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BoostedTreesFlushQuantileSummaries\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), quantile_stream_resource_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_features\", num_features);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor boosted_trees_make_quantile_summaries(const std::vector<tensor>&float_values, const tensor& example_weights, const tensor& epsilon) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BoostedTreesMakeQuantileSummaries\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> float_values_handles; float_values_handles.reserve(float_values.size());\n    std::transform(float_values.begin(), float_values.end(), std::back_inserter(float_values_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), float_values_handles.data(), float_values.size(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), example_weights.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), epsilon.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_features\", float_values.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor boosted_trees_make_stats_summary(const tensor& node_ids, const tensor& gradients, const tensor& hessians, const std::vector<tensor>&bucketized_features_list, int64_t max_splits, int64_t num_buckets) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BoostedTreesMakeStatsSummary\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), node_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), gradients.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), hessians.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> bucketized_features_list_handles; bucketized_features_list_handles.reserve(bucketized_features_list.size());\n    std::transform(bucketized_features_list.begin(), bucketized_features_list.end(), std::back_inserter(bucketized_features_list_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), bucketized_features_list_handles.data(), bucketized_features_list.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"max_splits\", max_splits);\n    TFE_OpSetAttrInt(op.get(), \"num_buckets\", num_buckets);\n    TFE_OpSetAttrInt(op.get(), \"num_features\", bucketized_features_list.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor boosted_trees_predict(const tensor& tree_ensemble_handle, const std::vector<tensor>&bucketized_features, int64_t logits_dimension) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BoostedTreesPredict\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), tree_ensemble_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> bucketized_features_handles; bucketized_features_handles.reserve(bucketized_features.size());\n    std::transform(bucketized_features.begin(), bucketized_features.end(), std::back_inserter(bucketized_features_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), bucketized_features_handles.data(), bucketized_features.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_bucketized_features\", bucketized_features.size());\n    TFE_OpSetAttrInt(op.get(), \"logits_dimension\", logits_dimension);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor boosted_trees_quantile_stream_resource_get_bucket_boundaries(const tensor& quantile_stream_resource_handle, int64_t num_features) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BoostedTreesQuantileStreamResourceGetBucketBoundaries\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), quantile_stream_resource_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_features\", num_features);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor boosted_trees_quantile_stream_resource_handle_op(const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BoostedTreesQuantileStreamResourceHandleOp\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor broadcast_args(const tensor& s0, const tensor& s1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BroadcastArgs\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), s0.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), s1.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor broadcast_to(const tensor& input, const tensor& shape, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BroadcastTo\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bucketize(const tensor& input, const std::vector<float>& boundaries) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Bucketize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrFloatList(op.get(), \"boundaries\", boundaries.data(), boundaries.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor bytes_produced_stats_dataset(const tensor& input_dataset, const tensor& tag, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"BytesProducedStatsDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), tag.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor c_s_r_sparse_matrix_to_dense(const tensor& sparse_input, datatype type) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CSRSparseMatrixToDense\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), sparse_input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"type\", type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor c_s_v_dataset(const tensor& filenames, const tensor& compression_type, const tensor& buffer_size, const tensor& header, const tensor& field_delim, const tensor& use_quote_delim, const tensor& na_value, const tensor& select_cols, const std::vector<tensor>&record_defaults, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CSVDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), filenames.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), compression_type.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), buffer_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), header.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), field_delim.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), use_quote_delim.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), na_value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), select_cols.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> record_defaults_handles; record_defaults_handles.reserve(record_defaults.size());\n    std::transform(record_defaults.begin(), record_defaults.end(), std::back_inserter(record_defaults_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), record_defaults_handles.data(), record_defaults.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor cache_dataset(const tensor& input_dataset, const tensor& filename, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CacheDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filename.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor cache_dataset_v2(const tensor& input_dataset, const tensor& filename, const tensor& cache, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CacheDatasetV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filename.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), cache.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor cast(const tensor& x, datatype SrcT, datatype DstT, bool Truncate=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Cast\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"SrcT\", SrcT);\n    TFE_OpSetAttrType(op.get(), \"DstT\", DstT);\n    TFE_OpSetAttrBool(op.get(), \"Truncate\", (unsigned char)Truncate);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor ceil(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Ceil\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor check_numerics(const tensor& input_tensor, const std::string& message) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CheckNumerics\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"message\", (void*) message.c_str(), message.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor check_numerics_v2(const tensor& input_tensor, const std::string& message) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CheckNumericsV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"message\", (void*) message.c_str(), message.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor cholesky(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Cholesky\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor cholesky_grad(const tensor& l, const tensor& grad) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CholeskyGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), l.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor choose_fastest_dataset(const std::vector<tensor>&input_datasets, int64_t num_experiments, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ChooseFastestDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> input_datasets_handles; input_datasets_handles.reserve(input_datasets.size());\n    std::transform(input_datasets.begin(), input_datasets.end(), std::back_inserter(input_datasets_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), input_datasets_handles.data(), input_datasets.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"N\", input_datasets.size());\n    TFE_OpSetAttrInt(op.get(), \"num_experiments\", num_experiments);\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor clip_by_value(const tensor& t, const tensor& clip_value_min, const tensor& clip_value_max) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ClipByValue\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), t.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), clip_value_min.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), clip_value_max.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor collective_bcast_recv(int64_t group_size, int64_t group_key, int64_t instance_key, const std::vector<int64_t>& shape, const std::string& communication_hint=\"auto\", float timeout_seconds=0.0000e+00) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CollectiveBcastRecv\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"group_size\", group_size);\n    TFE_OpSetAttrInt(op.get(), \"group_key\", group_key);\n    TFE_OpSetAttrInt(op.get(), \"instance_key\", instance_key);\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrString(op.get(), \"communication_hint\", (void*) communication_hint.c_str(), communication_hint.size());\n    TFE_OpSetAttrFloat(op.get(), \"timeout_seconds\", timeout_seconds);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor collective_bcast_send(const tensor& input, int64_t group_size, int64_t group_key, int64_t instance_key, const std::vector<int64_t>& shape, const std::string& communication_hint=\"auto\", float timeout_seconds=0.0000e+00) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CollectiveBcastSend\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"group_size\", group_size);\n    TFE_OpSetAttrInt(op.get(), \"group_key\", group_key);\n    TFE_OpSetAttrInt(op.get(), \"instance_key\", instance_key);\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrString(op.get(), \"communication_hint\", (void*) communication_hint.c_str(), communication_hint.size());\n    TFE_OpSetAttrFloat(op.get(), \"timeout_seconds\", timeout_seconds);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor collective_gather(const tensor& input, int64_t group_size, int64_t group_key, int64_t instance_key, const std::vector<int64_t>& shape, const std::string& communication_hint=\"auto\", float timeout_seconds=0.0000e+00) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CollectiveGather\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"group_size\", group_size);\n    TFE_OpSetAttrInt(op.get(), \"group_key\", group_key);\n    TFE_OpSetAttrInt(op.get(), \"instance_key\", instance_key);\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrString(op.get(), \"communication_hint\", (void*) communication_hint.c_str(), communication_hint.size());\n    TFE_OpSetAttrFloat(op.get(), \"timeout_seconds\", timeout_seconds);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor collective_permute(const tensor& input, const tensor& source_target_pairs) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CollectivePermute\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), source_target_pairs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor collective_reduce(const tensor& input, int64_t group_size, int64_t group_key, int64_t instance_key, const std::string& merge_op, const std::string& final_op, const std::vector<int64_t>& subdiv_offsets, const std::vector<int64_t>& wait_for, const std::string& communication_hint=\"auto\", float timeout_seconds=0.0000e+00) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CollectiveReduce\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"group_size\", group_size);\n    TFE_OpSetAttrInt(op.get(), \"group_key\", group_key);\n    TFE_OpSetAttrInt(op.get(), \"instance_key\", instance_key);\n    TFE_OpSetAttrString(op.get(), \"merge_op\", (void*) merge_op.c_str(), merge_op.size());\n    TFE_OpSetAttrString(op.get(), \"final_op\", (void*) final_op.c_str(), final_op.size());\n    TFE_OpSetAttrIntList(op.get(), \"subdiv_offsets\", subdiv_offsets.data(), subdiv_offsets.size());\n    TFE_OpSetAttrIntList(op.get(), \"wait_for\", wait_for.data(), wait_for.size());\n    TFE_OpSetAttrString(op.get(), \"communication_hint\", (void*) communication_hint.c_str(), communication_hint.size());\n    TFE_OpSetAttrFloat(op.get(), \"timeout_seconds\", timeout_seconds);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor compare_and_bitpack(const tensor& input, const tensor& threshold) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CompareAndBitpack\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), threshold.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor complex(const tensor& real, const tensor& imag, datatype Tout=static_cast<datatype>(8)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Complex\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), real.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), imag.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tout\", Tout);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor complex_abs(const tensor& x, datatype Tout=static_cast<datatype>(1)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ComplexAbs\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tout\", Tout);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor compress_element(const std::vector<tensor>&components, const std::vector<datatype>& input_types) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CompressElement\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> components_handles; components_handles.reserve(components.size());\n    std::transform(components.begin(), components.end(), std::back_inserter(components_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), components_handles.data(), components.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"input_types\", reinterpret_cast<const enum TF_DataType *>(input_types.data()), input_types.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor concat(const tensor& concat_dim, const std::vector<tensor>&values) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Concat\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), concat_dim.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> values_handles; values_handles.reserve(values.size());\n    std::transform(values.begin(), values.end(), std::back_inserter(values_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), values_handles.data(), values.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"N\", values.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor concat_offset(const tensor& concat_dim, const std::vector<tensor>&shape) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ConcatOffset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), concat_dim.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> shape_handles; shape_handles.reserve(shape.size());\n    std::transform(shape.begin(), shape.end(), std::back_inserter(shape_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), shape_handles.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"N\", shape.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor concat_v2(const std::vector<tensor>&values, const tensor& axis, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ConcatV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> values_handles; values_handles.reserve(values.size());\n    std::transform(values.begin(), values.end(), std::back_inserter(values_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), values_handles.data(), values.size(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), axis.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"N\", values.size());\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor concatenate_dataset(const tensor& input_dataset, const tensor& another_dataset, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ConcatenateDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), another_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor conditional_accumulator(datatype dtype, const std::vector<int64_t>& shape, const std::string& container=\"\", const std::string& shared_name=\"\", const std::string& reduction_type=\"MEAN\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ConditionalAccumulator\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrString(op.get(), \"reduction_type\", (void*) reduction_type.c_str(), reduction_type.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor configure_distributed_t_p_u(const std::string& embedding_config=\"\", const std::string& tpu_embedding_config=\"\", bool is_global_init=false, bool enable_whole_mesh_compilations=false, bool compilation_failure_closes_chips=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ConfigureDistributedTPU\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"embedding_config\", (void*) embedding_config.c_str(), embedding_config.size());\n    TFE_OpSetAttrString(op.get(), \"tpu_embedding_config\", (void*) tpu_embedding_config.c_str(), tpu_embedding_config.size());\n    TFE_OpSetAttrBool(op.get(), \"is_global_init\", (unsigned char)is_global_init);\n    TFE_OpSetAttrBool(op.get(), \"enable_whole_mesh_compilations\", (unsigned char)enable_whole_mesh_compilations);\n    TFE_OpSetAttrBool(op.get(), \"compilation_failure_closes_chips\", (unsigned char)compilation_failure_closes_chips);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor conj(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Conj\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor conjugate_transpose(const tensor& x, const tensor& perm, datatype Tperm=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ConjugateTranspose\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), perm.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tperm\", Tperm);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor const_tensor(const tensor& value, datatype dtype) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Const\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    \n    TFE_OpSetAttrTensor(op.get(), \"value\", value.get_tensor().get(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor conv2_d(const tensor& input, const tensor& filter, const std::vector<int64_t>& strides, const std::string& padding, const std::vector<int64_t>& explicit_paddings, const std::vector<int64_t>& dilations, bool use_cudnn_on_gpu=true, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Conv2D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filter.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrIntList(op.get(), \"explicit_paddings\", explicit_paddings.data(), explicit_paddings.size());\n    TFE_OpSetAttrIntList(op.get(), \"dilations\", dilations.data(), dilations.size());\n    TFE_OpSetAttrBool(op.get(), \"use_cudnn_on_gpu\", (unsigned char)use_cudnn_on_gpu);\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor conv2_d_backprop_filter(const tensor& input, const tensor& filter_sizes, const tensor& out_backprop, const std::vector<int64_t>& strides, const std::string& padding, const std::vector<int64_t>& explicit_paddings, const std::vector<int64_t>& dilations, bool use_cudnn_on_gpu=true, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Conv2DBackpropFilter\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filter_sizes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), out_backprop.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrIntList(op.get(), \"explicit_paddings\", explicit_paddings.data(), explicit_paddings.size());\n    TFE_OpSetAttrIntList(op.get(), \"dilations\", dilations.data(), dilations.size());\n    TFE_OpSetAttrBool(op.get(), \"use_cudnn_on_gpu\", (unsigned char)use_cudnn_on_gpu);\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor conv2_d_backprop_input(const tensor& input_sizes, const tensor& filter, const tensor& out_backprop, const std::vector<int64_t>& strides, const std::string& padding, const std::vector<int64_t>& explicit_paddings, const std::vector<int64_t>& dilations, bool use_cudnn_on_gpu=true, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Conv2DBackpropInput\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_sizes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filter.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), out_backprop.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrIntList(op.get(), \"explicit_paddings\", explicit_paddings.data(), explicit_paddings.size());\n    TFE_OpSetAttrIntList(op.get(), \"dilations\", dilations.data(), dilations.size());\n    TFE_OpSetAttrBool(op.get(), \"use_cudnn_on_gpu\", (unsigned char)use_cudnn_on_gpu);\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor conv3_d(const tensor& input, const tensor& filter, const std::vector<int64_t>& strides, const std::string& padding, const std::vector<int64_t>& dilations, const std::string& data_format=\"NDHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Conv3D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filter.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrIntList(op.get(), \"dilations\", dilations.data(), dilations.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor conv3_d_backprop_filter(const tensor& input, const tensor& filter, const tensor& out_backprop, const std::vector<int64_t>& strides, const std::string& padding, const std::vector<int64_t>& dilations) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Conv3DBackpropFilter\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filter.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), out_backprop.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrIntList(op.get(), \"dilations\", dilations.data(), dilations.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor conv3_d_backprop_filter_v2(const tensor& input, const tensor& filter_sizes, const tensor& out_backprop, const std::vector<int64_t>& strides, const std::string& padding, const std::vector<int64_t>& dilations, const std::string& data_format=\"NDHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Conv3DBackpropFilterV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filter_sizes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), out_backprop.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrIntList(op.get(), \"dilations\", dilations.data(), dilations.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor conv3_d_backprop_input(const tensor& input, const tensor& filter, const tensor& out_backprop, const std::vector<int64_t>& strides, const std::string& padding, const std::vector<int64_t>& dilations) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Conv3DBackpropInput\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filter.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), out_backprop.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrIntList(op.get(), \"dilations\", dilations.data(), dilations.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor conv3_d_backprop_input_v2(const tensor& input_sizes, const tensor& filter, const tensor& out_backprop, const std::vector<int64_t>& strides, const std::string& padding, const std::vector<int64_t>& dilations, const std::string& data_format=\"NDHWC\", datatype Tshape=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Conv3DBackpropInputV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_sizes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filter.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), out_backprop.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrIntList(op.get(), \"dilations\", dilations.data(), dilations.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n    TFE_OpSetAttrType(op.get(), \"Tshape\", Tshape);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor copy(const tensor& input, const std::vector< std::string>& debug_ops_spec, const std::string& tensor_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Copy\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n    std::vector<std::size_t> debug_ops_spec_sizes; debug_ops_spec_sizes.reserve(debug_ops_spec.size());\n    std::transform(debug_ops_spec.begin(), debug_ops_spec.end(), std::back_inserter(debug_ops_spec_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"debug_ops_spec\", reinterpret_cast<const void *const *>(debug_ops_spec.data()), debug_ops_spec_sizes.data(), debug_ops_spec.size());\n    \n    TFE_OpSetAttrString(op.get(), \"tensor_name\", (void*) tensor_name.c_str(), tensor_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor copy_host(const tensor& input, const std::vector< std::string>& debug_ops_spec, const std::string& tensor_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CopyHost\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n    std::vector<std::size_t> debug_ops_spec_sizes; debug_ops_spec_sizes.reserve(debug_ops_spec.size());\n    std::transform(debug_ops_spec.begin(), debug_ops_spec.end(), std::back_inserter(debug_ops_spec_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"debug_ops_spec\", reinterpret_cast<const void *const *>(debug_ops_spec.data()), debug_ops_spec_sizes.data(), debug_ops_spec.size());\n    \n    TFE_OpSetAttrString(op.get(), \"tensor_name\", (void*) tensor_name.c_str(), tensor_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor cos(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Cos\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor cosh(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Cosh\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor count_up_to(const tensor& ref, int64_t limit) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CountUpTo\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"limit\", limit);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor crop_and_resize(const tensor& image, const tensor& boxes, const tensor& box_ind, const tensor& crop_size, const std::string& method=\"bilinear\", float extrapolation_value=0.0000e+00) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CropAndResize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), image.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), boxes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), box_ind.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), crop_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"method\", (void*) method.c_str(), method.size());\n    TFE_OpSetAttrFloat(op.get(), \"extrapolation_value\", extrapolation_value);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor crop_and_resize_grad_boxes(const tensor& grads, const tensor& image, const tensor& boxes, const tensor& box_ind, const std::string& method=\"bilinear\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CropAndResizeGradBoxes\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), grads.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), image.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), boxes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), box_ind.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"method\", (void*) method.c_str(), method.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor crop_and_resize_grad_image(const tensor& grads, const tensor& boxes, const tensor& box_ind, const tensor& image_size, const std::string& method=\"bilinear\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CropAndResizeGradImage\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), grads.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), boxes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), box_ind.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), image_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"method\", (void*) method.c_str(), method.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor cross(const tensor& a, const tensor& b) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Cross\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), a.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), b.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor cross_replica_sum(const tensor& input, const tensor& group_assignment) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CrossReplicaSum\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), group_assignment.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor cudnn_r_n_n_canonical_to_params(const tensor& num_layers, const tensor& num_units, const tensor& input_size, const std::vector<tensor>&weights, const std::vector<tensor>&biases, const std::string& rnn_mode=\"lstm\", const std::string& input_mode=\"linear_input\", const std::string& direction=\"unidirectional\", float dropout=0.0000e+00, int64_t seed=0, int64_t seed2=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CudnnRNNCanonicalToParams\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), num_layers.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_units.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> weights_handles; weights_handles.reserve(weights.size());\n    std::transform(weights.begin(), weights.end(), std::back_inserter(weights_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), weights_handles.data(), weights.size(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> biases_handles; biases_handles.reserve(biases.size());\n    std::transform(biases.begin(), biases.end(), std::back_inserter(biases_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), biases_handles.data(), biases.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_params\", weights.size());\n    TFE_OpSetAttrString(op.get(), \"rnn_mode\", (void*) rnn_mode.c_str(), rnn_mode.size());\n    TFE_OpSetAttrString(op.get(), \"input_mode\", (void*) input_mode.c_str(), input_mode.size());\n    TFE_OpSetAttrString(op.get(), \"direction\", (void*) direction.c_str(), direction.size());\n    TFE_OpSetAttrFloat(op.get(), \"dropout\", dropout);\n    TFE_OpSetAttrInt(op.get(), \"seed\", seed);\n    TFE_OpSetAttrInt(op.get(), \"seed2\", seed2);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor cudnn_r_n_n_canonical_to_params_v2(const tensor& num_layers, const tensor& num_units, const tensor& input_size, const std::vector<tensor>&weights, const std::vector<tensor>&biases, const std::string& rnn_mode=\"lstm\", const std::string& input_mode=\"linear_input\", const std::string& direction=\"unidirectional\", float dropout=0.0000e+00, int64_t seed=0, int64_t seed2=0, int64_t num_proj=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CudnnRNNCanonicalToParamsV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), num_layers.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_units.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> weights_handles; weights_handles.reserve(weights.size());\n    std::transform(weights.begin(), weights.end(), std::back_inserter(weights_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), weights_handles.data(), weights.size(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> biases_handles; biases_handles.reserve(biases.size());\n    std::transform(biases.begin(), biases.end(), std::back_inserter(biases_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), biases_handles.data(), biases.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_params_weights\", weights.size());\n    TFE_OpSetAttrInt(op.get(), \"num_params_biases\", biases.size());\n    TFE_OpSetAttrString(op.get(), \"rnn_mode\", (void*) rnn_mode.c_str(), rnn_mode.size());\n    TFE_OpSetAttrString(op.get(), \"input_mode\", (void*) input_mode.c_str(), input_mode.size());\n    TFE_OpSetAttrString(op.get(), \"direction\", (void*) direction.c_str(), direction.size());\n    TFE_OpSetAttrFloat(op.get(), \"dropout\", dropout);\n    TFE_OpSetAttrInt(op.get(), \"seed\", seed);\n    TFE_OpSetAttrInt(op.get(), \"seed2\", seed2);\n    TFE_OpSetAttrInt(op.get(), \"num_proj\", num_proj);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor cudnn_r_n_n_params_size(const tensor& num_layers, const tensor& num_units, const tensor& input_size, datatype S, const std::string& rnn_mode=\"lstm\", const std::string& input_mode=\"linear_input\", const std::string& direction=\"unidirectional\", float dropout=0.0000e+00, int64_t seed=0, int64_t seed2=0, int64_t num_proj=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CudnnRNNParamsSize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), num_layers.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_units.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"S\", S);\n    TFE_OpSetAttrString(op.get(), \"rnn_mode\", (void*) rnn_mode.c_str(), rnn_mode.size());\n    TFE_OpSetAttrString(op.get(), \"input_mode\", (void*) input_mode.c_str(), input_mode.size());\n    TFE_OpSetAttrString(op.get(), \"direction\", (void*) direction.c_str(), direction.size());\n    TFE_OpSetAttrFloat(op.get(), \"dropout\", dropout);\n    TFE_OpSetAttrInt(op.get(), \"seed\", seed);\n    TFE_OpSetAttrInt(op.get(), \"seed2\", seed2);\n    TFE_OpSetAttrInt(op.get(), \"num_proj\", num_proj);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor cumprod(const tensor& x, const tensor& axis, bool exclusive=false, bool reverse=false, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Cumprod\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), axis.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"exclusive\", (unsigned char)exclusive);\n    TFE_OpSetAttrBool(op.get(), \"reverse\", (unsigned char)reverse);\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor cumsum(const tensor& x, const tensor& axis, bool exclusive=false, bool reverse=false, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Cumsum\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), axis.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"exclusive\", (unsigned char)exclusive);\n    TFE_OpSetAttrBool(op.get(), \"reverse\", (unsigned char)reverse);\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor cumulative_logsumexp(const tensor& x, const tensor& axis, bool exclusive=false, bool reverse=false, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"CumulativeLogsumexp\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), axis.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"exclusive\", (unsigned char)exclusive);\n    TFE_OpSetAttrBool(op.get(), \"reverse\", (unsigned char)reverse);\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor data_format_dim_map(const tensor& x, const std::string& src_format=\"NHWC\", const std::string& dst_format=\"NCHW\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DataFormatDimMap\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"src_format\", (void*) src_format.c_str(), src_format.size());\n    TFE_OpSetAttrString(op.get(), \"dst_format\", (void*) dst_format.c_str(), dst_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor data_format_vec_permute(const tensor& x, const std::string& src_format=\"NHWC\", const std::string& dst_format=\"NCHW\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DataFormatVecPermute\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"src_format\", (void*) src_format.c_str(), src_format.size());\n    TFE_OpSetAttrString(op.get(), \"dst_format\", (void*) dst_format.c_str(), dst_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor data_service_dataset(const tensor& dataset_id, const tensor& processing_mode, const tensor& address, const tensor& protocol, const tensor& job_name, const tensor& max_outstanding_requests, const tensor& iteration_counter, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, int64_t task_refresh_interval_hint_ms=-1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DataServiceDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), dataset_id.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), processing_mode.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), address.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), protocol.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), job_name.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), max_outstanding_requests.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), iteration_counter.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"task_refresh_interval_hint_ms\", task_refresh_interval_hint_ms);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dataset_cardinality(const tensor& input_dataset) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DatasetCardinality\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dataset_from_graph(const tensor& graph_def) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DatasetFromGraph\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), graph_def.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dataset_to_graph(const tensor& input_dataset, const std::vector< std::string>& stateful_whitelist, bool allow_stateful=false, bool strip_device_assignment=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DatasetToGraph\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n    std::vector<std::size_t> stateful_whitelist_sizes; stateful_whitelist_sizes.reserve(stateful_whitelist.size());\n    std::transform(stateful_whitelist.begin(), stateful_whitelist.end(), std::back_inserter(stateful_whitelist_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"stateful_whitelist\", reinterpret_cast<const void *const *>(stateful_whitelist.data()), stateful_whitelist_sizes.data(), stateful_whitelist.size());\n    \n    TFE_OpSetAttrBool(op.get(), \"allow_stateful\", (unsigned char)allow_stateful);\n    TFE_OpSetAttrBool(op.get(), \"strip_device_assignment\", (unsigned char)strip_device_assignment);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dataset_to_graph_v2(const tensor& input_dataset, int64_t external_state_policy=0, bool strip_device_assignment=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DatasetToGraphV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"external_state_policy\", external_state_policy);\n    TFE_OpSetAttrBool(op.get(), \"strip_device_assignment\", (unsigned char)strip_device_assignment);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dataset_to_single_element(const tensor& dataset, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DatasetToSingleElement\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dawsn(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Dawsn\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor debug_gradient_identity(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DebugGradientIdentity\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor debug_gradient_ref_identity(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DebugGradientRefIdentity\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor debug_identity(const tensor& input, const std::vector< std::string>& debug_urls, const std::string& device_name=\"\", const std::string& tensor_name=\"\", bool gated_grpc=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DebugIdentity\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n    std::vector<std::size_t> debug_urls_sizes; debug_urls_sizes.reserve(debug_urls.size());\n    std::transform(debug_urls.begin(), debug_urls.end(), std::back_inserter(debug_urls_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"debug_urls\", reinterpret_cast<const void *const *>(debug_urls.data()), debug_urls_sizes.data(), debug_urls.size());\n    \n    TFE_OpSetAttrString(op.get(), \"device_name\", (void*) device_name.c_str(), device_name.size());\n    TFE_OpSetAttrString(op.get(), \"tensor_name\", (void*) tensor_name.c_str(), tensor_name.size());\n    TFE_OpSetAttrBool(op.get(), \"gated_grpc\", (unsigned char)gated_grpc);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor debug_identity_v2(const tensor& input, const std::vector< std::string>& debug_urls, const std::string& tfdbg_context_id=\"\", const std::string& op_name=\"\", int64_t output_slot=-1, int64_t tensor_debug_mode=-1, int64_t circular_buffer_size=1000, const std::string& tfdbg_run_id=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DebugIdentityV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n    std::vector<std::size_t> debug_urls_sizes; debug_urls_sizes.reserve(debug_urls.size());\n    std::transform(debug_urls.begin(), debug_urls.end(), std::back_inserter(debug_urls_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"debug_urls\", reinterpret_cast<const void *const *>(debug_urls.data()), debug_urls_sizes.data(), debug_urls.size());\n    \n    TFE_OpSetAttrString(op.get(), \"tfdbg_context_id\", (void*) tfdbg_context_id.c_str(), tfdbg_context_id.size());\n    TFE_OpSetAttrString(op.get(), \"op_name\", (void*) op_name.c_str(), op_name.size());\n    TFE_OpSetAttrInt(op.get(), \"output_slot\", output_slot);\n    TFE_OpSetAttrInt(op.get(), \"tensor_debug_mode\", tensor_debug_mode);\n    TFE_OpSetAttrInt(op.get(), \"circular_buffer_size\", circular_buffer_size);\n    TFE_OpSetAttrString(op.get(), \"tfdbg_run_id\", (void*) tfdbg_run_id.c_str(), tfdbg_run_id.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor debug_nan_count(const tensor& input, const std::vector< std::string>& debug_urls, const std::string& device_name=\"\", const std::string& tensor_name=\"\", bool gated_grpc=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DebugNanCount\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n    std::vector<std::size_t> debug_urls_sizes; debug_urls_sizes.reserve(debug_urls.size());\n    std::transform(debug_urls.begin(), debug_urls.end(), std::back_inserter(debug_urls_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"debug_urls\", reinterpret_cast<const void *const *>(debug_urls.data()), debug_urls_sizes.data(), debug_urls.size());\n    \n    TFE_OpSetAttrString(op.get(), \"device_name\", (void*) device_name.c_str(), device_name.size());\n    TFE_OpSetAttrString(op.get(), \"tensor_name\", (void*) tensor_name.c_str(), tensor_name.size());\n    TFE_OpSetAttrBool(op.get(), \"gated_grpc\", (unsigned char)gated_grpc);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor debug_numeric_summary(const tensor& input, const std::vector< std::string>& debug_urls, const std::string& device_name=\"\", const std::string& tensor_name=\"\", float lower_bound=-std::numeric_limits<float>::infinity(), float upper_bound=std::numeric_limits<float>::infinity(), bool mute_if_healthy=false, bool gated_grpc=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DebugNumericSummary\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n    std::vector<std::size_t> debug_urls_sizes; debug_urls_sizes.reserve(debug_urls.size());\n    std::transform(debug_urls.begin(), debug_urls.end(), std::back_inserter(debug_urls_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"debug_urls\", reinterpret_cast<const void *const *>(debug_urls.data()), debug_urls_sizes.data(), debug_urls.size());\n    \n    TFE_OpSetAttrString(op.get(), \"device_name\", (void*) device_name.c_str(), device_name.size());\n    TFE_OpSetAttrString(op.get(), \"tensor_name\", (void*) tensor_name.c_str(), tensor_name.size());\n    TFE_OpSetAttrFloat(op.get(), \"lower_bound\", lower_bound);\n    TFE_OpSetAttrFloat(op.get(), \"upper_bound\", upper_bound);\n    TFE_OpSetAttrBool(op.get(), \"mute_if_healthy\", (unsigned char)mute_if_healthy);\n    TFE_OpSetAttrBool(op.get(), \"gated_grpc\", (unsigned char)gated_grpc);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor debug_numeric_summary_v2(const tensor& input, datatype output_dtype=static_cast<datatype>(1), int64_t tensor_debug_mode=-1, int64_t tensor_id=-1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DebugNumericSummaryV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"output_dtype\", output_dtype);\n    TFE_OpSetAttrInt(op.get(), \"tensor_debug_mode\", tensor_debug_mode);\n    TFE_OpSetAttrInt(op.get(), \"tensor_id\", tensor_id);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor decode_and_crop_jpeg(const tensor& contents, const tensor& crop_window, int64_t channels=0, int64_t ratio=1, bool fancy_upscaling=true, bool try_recover_truncated=false, float acceptable_fraction=1.0000e+00, const std::string& dct_method=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DecodeAndCropJpeg\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), contents.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), crop_window.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"channels\", channels);\n    TFE_OpSetAttrInt(op.get(), \"ratio\", ratio);\n    TFE_OpSetAttrBool(op.get(), \"fancy_upscaling\", (unsigned char)fancy_upscaling);\n    TFE_OpSetAttrBool(op.get(), \"try_recover_truncated\", (unsigned char)try_recover_truncated);\n    TFE_OpSetAttrFloat(op.get(), \"acceptable_fraction\", acceptable_fraction);\n    TFE_OpSetAttrString(op.get(), \"dct_method\", (void*) dct_method.c_str(), dct_method.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor decode_base64(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DecodeBase64\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor decode_bmp(const tensor& contents, int64_t channels=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DecodeBmp\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), contents.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"channels\", channels);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor decode_c_s_v(const tensor& records, const std::vector<tensor>&record_defaults, const std::vector<datatype>& OUT_TYPE, const std::vector<int64_t>& select_cols, const std::string& field_delim=\",\", bool use_quote_delim=true, const std::string& na_value=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DecodeCSV\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), records.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> record_defaults_handles; record_defaults_handles.reserve(record_defaults.size());\n    std::transform(record_defaults.begin(), record_defaults.end(), std::back_inserter(record_defaults_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), record_defaults_handles.data(), record_defaults.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"OUT_TYPE\", reinterpret_cast<const enum TF_DataType *>(OUT_TYPE.data()), OUT_TYPE.size());\n    TFE_OpSetAttrIntList(op.get(), \"select_cols\", select_cols.data(), select_cols.size());\n    TFE_OpSetAttrString(op.get(), \"field_delim\", (void*) field_delim.c_str(), field_delim.size());\n    TFE_OpSetAttrBool(op.get(), \"use_quote_delim\", (unsigned char)use_quote_delim);\n    TFE_OpSetAttrString(op.get(), \"na_value\", (void*) na_value.c_str(), na_value.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor decode_compressed(const tensor& bytes, const std::string& compression_type=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DecodeCompressed\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), bytes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"compression_type\", (void*) compression_type.c_str(), compression_type.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor decode_gif(const tensor& contents) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DecodeGif\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), contents.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor decode_j_s_o_n_example(const tensor& json_examples) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DecodeJSONExample\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), json_examples.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor decode_jpeg(const tensor& contents, int64_t channels=0, int64_t ratio=1, bool fancy_upscaling=true, bool try_recover_truncated=false, float acceptable_fraction=1.0000e+00, const std::string& dct_method=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DecodeJpeg\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), contents.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"channels\", channels);\n    TFE_OpSetAttrInt(op.get(), \"ratio\", ratio);\n    TFE_OpSetAttrBool(op.get(), \"fancy_upscaling\", (unsigned char)fancy_upscaling);\n    TFE_OpSetAttrBool(op.get(), \"try_recover_truncated\", (unsigned char)try_recover_truncated);\n    TFE_OpSetAttrFloat(op.get(), \"acceptable_fraction\", acceptable_fraction);\n    TFE_OpSetAttrString(op.get(), \"dct_method\", (void*) dct_method.c_str(), dct_method.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor decode_padded_raw(const tensor& input_bytes, const tensor& fixed_length, datatype out_type, bool little_endian=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DecodePaddedRaw\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_bytes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), fixed_length.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"out_type\", out_type);\n    TFE_OpSetAttrBool(op.get(), \"little_endian\", (unsigned char)little_endian);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor decode_png(const tensor& contents, int64_t channels=0, datatype dtype=static_cast<datatype>(4)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DecodePng\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), contents.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"channels\", channels);\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor decode_raw(const tensor& bytes, datatype out_type, bool little_endian=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DecodeRaw\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), bytes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"out_type\", out_type);\n    TFE_OpSetAttrBool(op.get(), \"little_endian\", (unsigned char)little_endian);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor deep_copy(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DeepCopy\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dense_bincount(const tensor& input, const tensor& size, const tensor& weights, datatype Tidx, bool binary_output=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DenseBincount\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), weights.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n    TFE_OpSetAttrBool(op.get(), \"binary_output\", (unsigned char)binary_output);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dense_to_c_s_r_sparse_matrix(const tensor& dense_input, const tensor& indices) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DenseToCSRSparseMatrix\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), dense_input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dense_to_sparse_batch_dataset(const tensor& input_dataset, const tensor& batch_size, const tensor& row_shape, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DenseToSparseBatchDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), batch_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), row_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor depth_to_space(const tensor& input, int64_t block_size, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DepthToSpace\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"block_size\", block_size);\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor depthwise_conv2d_native(const tensor& input, const tensor& filter, const std::vector<int64_t>& strides, const std::string& padding, const std::vector<int64_t>& explicit_paddings, const std::vector<int64_t>& dilations, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DepthwiseConv2dNative\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filter.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrIntList(op.get(), \"explicit_paddings\", explicit_paddings.data(), explicit_paddings.size());\n    TFE_OpSetAttrIntList(op.get(), \"dilations\", dilations.data(), dilations.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor depthwise_conv2d_native_backprop_filter(const tensor& input, const tensor& filter_sizes, const tensor& out_backprop, const std::vector<int64_t>& strides, const std::string& padding, const std::vector<int64_t>& explicit_paddings, const std::vector<int64_t>& dilations, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DepthwiseConv2dNativeBackpropFilter\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filter_sizes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), out_backprop.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrIntList(op.get(), \"explicit_paddings\", explicit_paddings.data(), explicit_paddings.size());\n    TFE_OpSetAttrIntList(op.get(), \"dilations\", dilations.data(), dilations.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor depthwise_conv2d_native_backprop_input(const tensor& input_sizes, const tensor& filter, const tensor& out_backprop, const std::vector<int64_t>& strides, const std::string& padding, const std::vector<int64_t>& explicit_paddings, const std::vector<int64_t>& dilations, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DepthwiseConv2dNativeBackpropInput\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_sizes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filter.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), out_backprop.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrIntList(op.get(), \"explicit_paddings\", explicit_paddings.data(), explicit_paddings.size());\n    TFE_OpSetAttrIntList(op.get(), \"dilations\", dilations.data(), dilations.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dequantize(const tensor& input, const tensor& min_range, const tensor& max_range, const std::string& mode=\"MIN_COMBINED\", bool narrow_range=false, int64_t axis=-1, datatype dtype=static_cast<datatype>(1)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Dequantize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), min_range.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), max_range.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"mode\", (void*) mode.c_str(), mode.size());\n    TFE_OpSetAttrBool(op.get(), \"narrow_range\", (unsigned char)narrow_range);\n    TFE_OpSetAttrInt(op.get(), \"axis\", axis);\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor destroy_temporary_variable(const tensor& ref, const std::string& var_name) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DestroyTemporaryVariable\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"var_name\", (void*) var_name.c_str(), var_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor device_index(const std::vector< std::string>& device_names) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DeviceIndex\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    \n    std::vector<std::size_t> device_names_sizes; device_names_sizes.reserve(device_names.size());\n    std::transform(device_names.begin(), device_names.end(), std::back_inserter(device_names_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"device_names\", reinterpret_cast<const void *const *>(device_names.data()), device_names_sizes.data(), device_names.size());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor diag(const tensor& diagonal) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Diag\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), diagonal.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor diag_part(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DiagPart\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor digamma(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Digamma\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dilation2_d(const tensor& input, const tensor& filter, const std::vector<int64_t>& strides, const std::vector<int64_t>& rates, const std::string& padding) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Dilation2D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filter.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrIntList(op.get(), \"rates\", rates.data(), rates.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dilation2_d_backprop_filter(const tensor& input, const tensor& filter, const tensor& out_backprop, const std::vector<int64_t>& strides, const std::vector<int64_t>& rates, const std::string& padding) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Dilation2DBackpropFilter\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filter.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), out_backprop.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrIntList(op.get(), \"rates\", rates.data(), rates.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dilation2_d_backprop_input(const tensor& input, const tensor& filter, const tensor& out_backprop, const std::vector<int64_t>& strides, const std::vector<int64_t>& rates, const std::string& padding) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Dilation2DBackpropInput\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filter.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), out_backprop.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrIntList(op.get(), \"rates\", rates.data(), rates.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor directed_interleave_dataset(const tensor& selector_input_dataset, const std::vector<tensor>&data_input_datasets, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DirectedInterleaveDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), selector_input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> data_input_datasets_handles; data_input_datasets_handles.reserve(data_input_datasets.size());\n    std::transform(data_input_datasets.begin(), data_input_datasets.end(), std::back_inserter(data_input_datasets_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), data_input_datasets_handles.data(), data_input_datasets.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"N\", data_input_datasets.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor div(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Div\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor div_no_nan(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DivNoNan\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor draw_bounding_boxes(const tensor& images, const tensor& boxes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DrawBoundingBoxes\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), images.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), boxes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor draw_bounding_boxes_v2(const tensor& images, const tensor& boxes, const tensor& colors) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DrawBoundingBoxesV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), images.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), boxes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), colors.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dummy_iteration_counter() {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DummyIterationCounter\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dummy_memory_cache() {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DummyMemoryCache\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dummy_seed_generator() {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DummySeedGenerator\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dynamic_partition(const tensor& data, const tensor& partitions, int64_t num_partitions) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DynamicPartition\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), partitions.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_partitions\", num_partitions);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor dynamic_stitch(const std::vector<tensor>&indices, const std::vector<tensor>&data) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"DynamicStitch\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> indices_handles; indices_handles.reserve(indices.size());\n    std::transform(indices.begin(), indices.end(), std::back_inserter(indices_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), indices_handles.data(), indices.size(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> data_handles; data_handles.reserve(data.size());\n    std::transform(data.begin(), data.end(), std::back_inserter(data_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), data_handles.data(), data.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"N\", indices.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor eager_py_func(const std::vector<tensor>&input, const std::string& token, const std::vector<datatype>& Tin, const std::vector<datatype>& Tout, bool is_async=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"EagerPyFunc\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> input_handles; input_handles.reserve(input.size());\n    std::transform(input.begin(), input.end(), std::back_inserter(input_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), input_handles.data(), input.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"token\", (void*) token.c_str(), token.size());\n    TFE_OpSetAttrTypeList(op.get(), \"Tin\", reinterpret_cast<const enum TF_DataType *>(Tin.data()), Tin.size());\n    TFE_OpSetAttrTypeList(op.get(), \"Tout\", reinterpret_cast<const enum TF_DataType *>(Tout.data()), Tout.size());\n    TFE_OpSetAttrBool(op.get(), \"is_async\", (unsigned char)is_async);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor edit_distance(const tensor& hypothesis_indices, const tensor& hypothesis_values, const tensor& hypothesis_shape, const tensor& truth_indices, const tensor& truth_values, const tensor& truth_shape, bool normalize=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"EditDistance\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), hypothesis_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), hypothesis_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), hypothesis_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), truth_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), truth_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), truth_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"normalize\", (unsigned char)normalize);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor einsum(const std::vector<tensor>&inputs, const std::string& equation) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Einsum\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> inputs_handles; inputs_handles.reserve(inputs.size());\n    std::transform(inputs.begin(), inputs.end(), std::back_inserter(inputs_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), inputs_handles.data(), inputs.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"equation\", (void*) equation.c_str(), equation.size());\n    TFE_OpSetAttrInt(op.get(), \"N\", inputs.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor elu(const tensor& features) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Elu\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), features.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor elu_grad(const tensor& gradients, const tensor& outputs) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"EluGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), gradients.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), outputs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor empty(const tensor& shape, datatype dtype, bool init=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Empty\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrBool(op.get(), \"init\", (unsigned char)init);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor empty_tensor_list(const tensor& element_shape, const tensor& max_num_elements, datatype element_dtype, datatype shape_type) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"EmptyTensorList\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), element_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), max_num_elements.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"element_dtype\", element_dtype);\n    TFE_OpSetAttrType(op.get(), \"shape_type\", shape_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor encode_base64(const tensor& input, bool pad=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"EncodeBase64\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"pad\", (unsigned char)pad);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor encode_jpeg(const tensor& image, const std::string& format=\"\", int64_t quality=95, bool progressive=false, bool optimize_size=false, bool chroma_downsampling=true, const std::string& density_unit=\"in\", int64_t x_density=300, int64_t y_density=300, const std::string& xmp_metadata=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"EncodeJpeg\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), image.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"format\", (void*) format.c_str(), format.size());\n    TFE_OpSetAttrInt(op.get(), \"quality\", quality);\n    TFE_OpSetAttrBool(op.get(), \"progressive\", (unsigned char)progressive);\n    TFE_OpSetAttrBool(op.get(), \"optimize_size\", (unsigned char)optimize_size);\n    TFE_OpSetAttrBool(op.get(), \"chroma_downsampling\", (unsigned char)chroma_downsampling);\n    TFE_OpSetAttrString(op.get(), \"density_unit\", (void*) density_unit.c_str(), density_unit.size());\n    TFE_OpSetAttrInt(op.get(), \"x_density\", x_density);\n    TFE_OpSetAttrInt(op.get(), \"y_density\", y_density);\n    TFE_OpSetAttrString(op.get(), \"xmp_metadata\", (void*) xmp_metadata.c_str(), xmp_metadata.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor encode_jpeg_variable_quality(const tensor& images, const tensor& quality) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"EncodeJpegVariableQuality\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), images.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), quality.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor encode_png(const tensor& image, int64_t compression=-1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"EncodePng\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), image.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"compression\", compression);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor encode_proto(const tensor& sizes, const std::vector<tensor>&values, const std::vector< std::string>& field_names, const std::string& message_type, const std::vector<datatype>& Tinput_types, const std::string& descriptor_source=\"local://\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"EncodeProto\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), sizes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> values_handles; values_handles.reserve(values.size());\n    std::transform(values.begin(), values.end(), std::back_inserter(values_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), values_handles.data(), values.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n    std::vector<std::size_t> field_names_sizes; field_names_sizes.reserve(field_names.size());\n    std::transform(field_names.begin(), field_names.end(), std::back_inserter(field_names_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"field_names\", reinterpret_cast<const void *const *>(field_names.data()), field_names_sizes.data(), field_names.size());\n    \n    TFE_OpSetAttrString(op.get(), \"message_type\", (void*) message_type.c_str(), message_type.size());\n    TFE_OpSetAttrTypeList(op.get(), \"Tinput_types\", reinterpret_cast<const enum TF_DataType *>(Tinput_types.data()), Tinput_types.size());\n    TFE_OpSetAttrString(op.get(), \"descriptor_source\", (void*) descriptor_source.c_str(), descriptor_source.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor encode_wav(const tensor& audio, const tensor& sample_rate) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"EncodeWav\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), audio.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sample_rate.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor ensure_shape(const tensor& input, const std::vector<int64_t>& shape) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"EnsureShape\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor enter(const tensor& data, const std::string& frame_name, bool is_constant=false, int64_t parallel_iterations=10) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Enter\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"frame_name\", (void*) frame_name.c_str(), frame_name.size());\n    TFE_OpSetAttrBool(op.get(), \"is_constant\", (unsigned char)is_constant);\n    TFE_OpSetAttrInt(op.get(), \"parallel_iterations\", parallel_iterations);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor equal(const tensor& x, const tensor& y, bool incompatible_shape_error=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Equal\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"incompatible_shape_error\", (unsigned char)incompatible_shape_error);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor erf(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Erf\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor erfc(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Erfc\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor erfinv(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Erfinv\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor euclidean_norm(const tensor& input, const tensor& reduction_indices, bool keep_dims=false, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"EuclideanNorm\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), reduction_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"keep_dims\", (unsigned char)keep_dims);\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor exit(const tensor& data) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Exit\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor exp(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Exp\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor expand_dims(const tensor& input, const tensor& dim, datatype Tdim=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExpandDims\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dim.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tdim\", Tdim);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_assert_next_dataset(const tensor& input_dataset, const tensor& transformations, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalAssertNextDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), transformations.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_auto_shard_dataset(const tensor& input_dataset, const tensor& num_workers, const tensor& index, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, int64_t auto_shard_policy=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalAutoShardDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_workers.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), index.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"auto_shard_policy\", auto_shard_policy);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_bytes_produced_stats_dataset(const tensor& input_dataset, const tensor& tag, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalBytesProducedStatsDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), tag.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_c_s_v_dataset(const tensor& filenames, const tensor& compression_type, const tensor& buffer_size, const tensor& header, const tensor& field_delim, const tensor& use_quote_delim, const tensor& na_value, const tensor& select_cols, const std::vector<tensor>&record_defaults, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalCSVDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), filenames.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), compression_type.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), buffer_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), header.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), field_delim.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), use_quote_delim.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), na_value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), select_cols.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> record_defaults_handles; record_defaults_handles.reserve(record_defaults.size());\n    std::transform(record_defaults.begin(), record_defaults.end(), std::back_inserter(record_defaults_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), record_defaults_handles.data(), record_defaults.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_choose_fastest_dataset(const std::vector<tensor>&input_datasets, int64_t num_experiments, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalChooseFastestDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> input_datasets_handles; input_datasets_handles.reserve(input_datasets.size());\n    std::transform(input_datasets.begin(), input_datasets.end(), std::back_inserter(input_datasets_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), input_datasets_handles.data(), input_datasets.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"N\", input_datasets.size());\n    TFE_OpSetAttrInt(op.get(), \"num_experiments\", num_experiments);\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_dataset_cardinality(const tensor& input_dataset) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalDatasetCardinality\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_dense_to_sparse_batch_dataset(const tensor& input_dataset, const tensor& batch_size, const tensor& row_shape, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalDenseToSparseBatchDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), batch_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), row_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_directed_interleave_dataset(const tensor& selector_input_dataset, const std::vector<tensor>&data_input_datasets, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalDirectedInterleaveDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), selector_input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> data_input_datasets_handles; data_input_datasets_handles.reserve(data_input_datasets.size());\n    std::transform(data_input_datasets.begin(), data_input_datasets.end(), std::back_inserter(data_input_datasets_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), data_input_datasets_handles.data(), data_input_datasets.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"N\", data_input_datasets.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_ignore_errors_dataset(const tensor& input_dataset, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalIgnoreErrorsDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_iterator_get_device(const tensor& resource) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalIteratorGetDevice\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), resource.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_l_m_d_b_dataset(const tensor& filenames, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalLMDBDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), filenames.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_latency_stats_dataset(const tensor& input_dataset, const tensor& tag, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalLatencyStatsDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), tag.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_matching_files_dataset(const tensor& patterns) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalMatchingFilesDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), patterns.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_max_intra_op_parallelism_dataset(const tensor& input_dataset, const tensor& max_intra_op_parallelism, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalMaxIntraOpParallelismDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), max_intra_op_parallelism.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_non_serializable_dataset(const tensor& input_dataset, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalNonSerializableDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_parse_example_dataset(const tensor& input_dataset, const tensor& num_parallel_calls, const std::vector<tensor>&dense_defaults, const std::vector< std::string>& sparse_keys, const std::vector< std::string>& dense_keys, const std::vector<datatype>& sparse_types, const std::vector<datatype>& Tdense, const std::vector< std::vector<int64_t>>& dense_shapes, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, bool sloppy=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalParseExampleDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_parallel_calls.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> dense_defaults_handles; dense_defaults_handles.reserve(dense_defaults.size());\n    std::transform(dense_defaults.begin(), dense_defaults.end(), std::back_inserter(dense_defaults_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), dense_defaults_handles.data(), dense_defaults.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n    std::vector<std::size_t> sparse_keys_sizes; sparse_keys_sizes.reserve(sparse_keys.size());\n    std::transform(sparse_keys.begin(), sparse_keys.end(), std::back_inserter(sparse_keys_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"sparse_keys\", reinterpret_cast<const void *const *>(sparse_keys.data()), sparse_keys_sizes.data(), sparse_keys.size());\n    \n    \n    std::vector<std::size_t> dense_keys_sizes; dense_keys_sizes.reserve(dense_keys.size());\n    std::transform(dense_keys.begin(), dense_keys.end(), std::back_inserter(dense_keys_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"dense_keys\", reinterpret_cast<const void *const *>(dense_keys.data()), dense_keys_sizes.data(), dense_keys.size());\n    \n    TFE_OpSetAttrTypeList(op.get(), \"sparse_types\", reinterpret_cast<const enum TF_DataType *>(sparse_types.data()), sparse_types.size());\n    TFE_OpSetAttrTypeList(op.get(), \"Tdense\", reinterpret_cast<const enum TF_DataType *>(Tdense.data()), Tdense.size());\n    \n    std::vector<const int64_t*> dense_shapes_values; dense_shapes_values.reserve(dense_shapes.size());\n    std::vector<int> dense_shapes_ndims; dense_shapes_ndims.reserve(dense_shapes.size());\n    std::transform(dense_shapes.begin(), dense_shapes.end(), std::back_inserter(dense_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(dense_shapes.begin(), dense_shapes.end(), std::back_inserter(dense_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"dense_shapes\", dense_shapes_values.data(), dense_shapes_ndims.data(), dense_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrBool(op.get(), \"sloppy\", (unsigned char)sloppy);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_private_thread_pool_dataset(const tensor& input_dataset, const tensor& num_threads, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalPrivateThreadPoolDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_threads.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_random_dataset(const tensor& seed, const tensor& seed2, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalRandomDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), seed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_rebatch_dataset(const tensor& input_dataset, const tensor& num_replicas, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, bool use_fallback=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalRebatchDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_replicas.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrBool(op.get(), \"use_fallback\", (unsigned char)use_fallback);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_set_stats_aggregator_dataset(const tensor& input_dataset, const tensor& stats_aggregator, const tensor& tag, const tensor& counter_prefix, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalSetStatsAggregatorDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), stats_aggregator.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), tag.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), counter_prefix.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_sleep_dataset(const tensor& input_dataset, const tensor& sleep_microseconds, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalSleepDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sleep_microseconds.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_sliding_window_dataset(const tensor& input_dataset, const tensor& window_size, const tensor& window_shift, const tensor& window_stride, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalSlidingWindowDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), window_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), window_shift.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), window_stride.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_sql_dataset(const tensor& driver_name, const tensor& data_source_name, const tensor& query, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalSqlDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), driver_name.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), data_source_name.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), query.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_stats_aggregator_handle(const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalStatsAggregatorHandle\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_stats_aggregator_summary(const tensor& iterator) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalStatsAggregatorSummary\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), iterator.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_thread_pool_dataset(const tensor& input_dataset, const tensor& thread_pool, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalThreadPoolDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), thread_pool.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_thread_pool_handle(int64_t num_threads, const std::string& display_name, int64_t max_intra_op_parallelism=1, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalThreadPoolHandle\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_threads\", num_threads);\n    TFE_OpSetAttrString(op.get(), \"display_name\", (void*) display_name.c_str(), display_name.size());\n    TFE_OpSetAttrInt(op.get(), \"max_intra_op_parallelism\", max_intra_op_parallelism);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_unbatch_dataset(const tensor& input_dataset, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalUnbatchDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor experimental_unique_dataset(const tensor& input_dataset, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExperimentalUniqueDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor expint(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Expint\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor expm1(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Expm1\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor extract_glimpse(const tensor& input, const tensor& size, const tensor& offsets, bool centered=true, bool normalized=true, bool uniform_noise=true, const std::string& noise=\"uniform\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExtractGlimpse\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), offsets.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"centered\", (unsigned char)centered);\n    TFE_OpSetAttrBool(op.get(), \"normalized\", (unsigned char)normalized);\n    TFE_OpSetAttrBool(op.get(), \"uniform_noise\", (unsigned char)uniform_noise);\n    TFE_OpSetAttrString(op.get(), \"noise\", (void*) noise.c_str(), noise.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor extract_glimpse_v2(const tensor& input, const tensor& size, const tensor& offsets, bool centered=true, bool normalized=true, bool uniform_noise=true, const std::string& noise=\"uniform\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExtractGlimpseV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), offsets.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"centered\", (unsigned char)centered);\n    TFE_OpSetAttrBool(op.get(), \"normalized\", (unsigned char)normalized);\n    TFE_OpSetAttrBool(op.get(), \"uniform_noise\", (unsigned char)uniform_noise);\n    TFE_OpSetAttrString(op.get(), \"noise\", (void*) noise.c_str(), noise.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor extract_image_patches(const tensor& images, const std::vector<int64_t>& ksizes, const std::vector<int64_t>& strides, const std::vector<int64_t>& rates, const std::string& padding) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExtractImagePatches\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), images.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"ksizes\", ksizes.data(), ksizes.size());\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrIntList(op.get(), \"rates\", rates.data(), rates.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor extract_jpeg_shape(const tensor& contents, datatype output_type=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExtractJpegShape\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), contents.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"output_type\", output_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor extract_volume_patches(const tensor& input, const std::vector<int64_t>& ksizes, const std::vector<int64_t>& strides, const std::string& padding) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ExtractVolumePatches\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"ksizes\", ksizes.data(), ksizes.size());\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor f_f_t(const tensor& input, datatype Tcomplex=static_cast<datatype>(8)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FFT\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tcomplex\", Tcomplex);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor f_f_t2_d(const tensor& input, datatype Tcomplex=static_cast<datatype>(8)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FFT2D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tcomplex\", Tcomplex);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor f_f_t3_d(const tensor& input, datatype Tcomplex=static_cast<datatype>(8)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FFT3D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tcomplex\", Tcomplex);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor f_i_f_o_queue(const std::vector<datatype>& component_types, const std::vector< std::vector<int64_t>>& shapes, int64_t capacity=-1, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FIFOQueue\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"component_types\", reinterpret_cast<const enum TF_DataType *>(component_types.data()), component_types.size());\n    \n    std::vector<const int64_t*> shapes_values; shapes_values.reserve(shapes.size());\n    std::vector<int> shapes_ndims; shapes_ndims.reserve(shapes.size());\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_values), [](const auto& v) { return v.data();});\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"shapes\", shapes_values.data(), shapes_ndims.data(), shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor f_i_f_o_queue_v2(const std::vector<datatype>& component_types, const std::vector< std::vector<int64_t>>& shapes, int64_t capacity=-1, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FIFOQueueV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"component_types\", reinterpret_cast<const enum TF_DataType *>(component_types.data()), component_types.size());\n    \n    std::vector<const int64_t*> shapes_values; shapes_values.reserve(shapes.size());\n    std::vector<int> shapes_ndims; shapes_ndims.reserve(shapes.size());\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_values), [](const auto& v) { return v.data();});\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"shapes\", shapes_values.data(), shapes_ndims.data(), shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fact() {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Fact\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fake_param(datatype dtype, const std::vector<int64_t>& shape) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FakeParam\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fake_quant_with_min_max_args(const tensor& inputs, float min=-6.0000e+00, float max=6.0000e+00, int64_t num_bits=8, bool narrow_range=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FakeQuantWithMinMaxArgs\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), inputs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrFloat(op.get(), \"min\", min);\n    TFE_OpSetAttrFloat(op.get(), \"max\", max);\n    TFE_OpSetAttrInt(op.get(), \"num_bits\", num_bits);\n    TFE_OpSetAttrBool(op.get(), \"narrow_range\", (unsigned char)narrow_range);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fake_quant_with_min_max_args_gradient(const tensor& gradients, const tensor& inputs, float min=-6.0000e+00, float max=6.0000e+00, int64_t num_bits=8, bool narrow_range=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FakeQuantWithMinMaxArgsGradient\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), gradients.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), inputs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrFloat(op.get(), \"min\", min);\n    TFE_OpSetAttrFloat(op.get(), \"max\", max);\n    TFE_OpSetAttrInt(op.get(), \"num_bits\", num_bits);\n    TFE_OpSetAttrBool(op.get(), \"narrow_range\", (unsigned char)narrow_range);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fake_quant_with_min_max_vars(const tensor& inputs, const tensor& min, const tensor& max, int64_t num_bits=8, bool narrow_range=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FakeQuantWithMinMaxVars\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), inputs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), min.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), max.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_bits\", num_bits);\n    TFE_OpSetAttrBool(op.get(), \"narrow_range\", (unsigned char)narrow_range);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fake_quant_with_min_max_vars_per_channel(const tensor& inputs, const tensor& min, const tensor& max, int64_t num_bits=8, bool narrow_range=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FakeQuantWithMinMaxVarsPerChannel\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), inputs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), min.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), max.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_bits\", num_bits);\n    TFE_OpSetAttrBool(op.get(), \"narrow_range\", (unsigned char)narrow_range);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fake_queue(const tensor& resource) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FakeQueue\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), resource.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fill(const tensor& dims, const tensor& value, datatype index_type=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Fill\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), dims.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"index_type\", index_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor filter_by_last_component_dataset(const tensor& input_dataset, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FilterByLastComponentDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fingerprint(const tensor& data, const tensor& method) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Fingerprint\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), method.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fixed_length_record_dataset(const tensor& filenames, const tensor& header_bytes, const tensor& record_bytes, const tensor& footer_bytes, const tensor& buffer_size) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FixedLengthRecordDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), filenames.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), header_bytes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), record_bytes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), footer_bytes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), buffer_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fixed_length_record_dataset_v2(const tensor& filenames, const tensor& header_bytes, const tensor& record_bytes, const tensor& footer_bytes, const tensor& buffer_size, const tensor& compression_type) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FixedLengthRecordDatasetV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), filenames.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), header_bytes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), record_bytes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), footer_bytes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), buffer_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), compression_type.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fixed_length_record_reader(int64_t record_bytes, int64_t header_bytes=0, int64_t footer_bytes=0, int64_t hop_bytes=0, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FixedLengthRecordReader\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"record_bytes\", record_bytes);\n    TFE_OpSetAttrInt(op.get(), \"header_bytes\", header_bytes);\n    TFE_OpSetAttrInt(op.get(), \"footer_bytes\", footer_bytes);\n    TFE_OpSetAttrInt(op.get(), \"hop_bytes\", hop_bytes);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fixed_length_record_reader_v2(int64_t record_bytes, int64_t header_bytes=0, int64_t footer_bytes=0, int64_t hop_bytes=0, const std::string& container=\"\", const std::string& shared_name=\"\", const std::string& encoding=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FixedLengthRecordReaderV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"record_bytes\", record_bytes);\n    TFE_OpSetAttrInt(op.get(), \"header_bytes\", header_bytes);\n    TFE_OpSetAttrInt(op.get(), \"footer_bytes\", footer_bytes);\n    TFE_OpSetAttrInt(op.get(), \"hop_bytes\", hop_bytes);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrString(op.get(), \"encoding\", (void*) encoding.c_str(), encoding.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor floor(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Floor\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor floor_div(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FloorDiv\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor floor_mod(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FloorMod\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fractional_avg_pool_grad(const tensor& orig_input_input_tensor_shape, const tensor& out_backprop, const tensor& row_pooling_sequence, const tensor& col_pooling_sequence, bool overlapping=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FractionalAvgPoolGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), orig_input_input_tensor_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), out_backprop.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), row_pooling_sequence.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), col_pooling_sequence.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"overlapping\", (unsigned char)overlapping);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fractional_max_pool_grad(const tensor& orig_input, const tensor& orig_output, const tensor& out_backprop, const tensor& row_pooling_sequence, const tensor& col_pooling_sequence, bool overlapping=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FractionalMaxPoolGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), orig_input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), orig_output.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), out_backprop.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), row_pooling_sequence.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), col_pooling_sequence.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"overlapping\", (unsigned char)overlapping);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fresnel_cos(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FresnelCos\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fresnel_sin(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FresnelSin\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fused_pad_conv2_d(const tensor& input, const tensor& paddings, const tensor& filter, const std::string& mode, const std::vector<int64_t>& strides, const std::string& padding) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FusedPadConv2D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), paddings.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filter.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"mode\", (void*) mode.c_str(), mode.size());\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor fused_resize_and_pad_conv2_d(const tensor& input, const tensor& size, const tensor& paddings, const tensor& filter, const std::string& mode, const std::vector<int64_t>& strides, const std::string& padding, bool resize_align_corners=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"FusedResizeAndPadConv2D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), paddings.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), filter.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"mode\", (void*) mode.c_str(), mode.size());\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrBool(op.get(), \"resize_align_corners\", (unsigned char)resize_align_corners);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor gather(const tensor& params, const tensor& indices, datatype Tparams, datatype Tindices, bool validate_indices=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Gather\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), params.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tparams\", Tparams);\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"validate_indices\", (unsigned char)validate_indices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor gather_nd(const tensor& params, const tensor& indices, datatype Tparams, datatype Tindices) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"GatherNd\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), params.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tparams\", Tparams);\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor gather_v2(const tensor& params, const tensor& indices, const tensor& axis, datatype Tparams, datatype Tindices, datatype Taxis, int64_t batch_dims=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"GatherV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), params.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), axis.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tparams\", Tparams);\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrType(op.get(), \"Taxis\", Taxis);\n    TFE_OpSetAttrInt(op.get(), \"batch_dims\", batch_dims);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor get_session_handle(const tensor& value) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"GetSessionHandle\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor get_session_handle_v2(const tensor& value) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"GetSessionHandleV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor get_session_tensor(const tensor& handle, datatype dtype) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"GetSessionTensor\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor greater(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Greater\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor greater_equal(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"GreaterEqual\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor guarantee_const_tensor(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"GuaranteeConst\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor h_s_v_to_r_g_b(const tensor& images) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"HSVToRGB\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), images.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor hash_table(datatype key_dtype, datatype value_dtype, const std::string& container=\"\", const std::string& shared_name=\"\", bool use_node_name_sharing=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"HashTable\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"key_dtype\", key_dtype);\n    TFE_OpSetAttrType(op.get(), \"value_dtype\", value_dtype);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrBool(op.get(), \"use_node_name_sharing\", (unsigned char)use_node_name_sharing);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor hash_table_v2(datatype key_dtype, datatype value_dtype, const std::string& container=\"\", const std::string& shared_name=\"\", bool use_node_name_sharing=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"HashTableV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"key_dtype\", key_dtype);\n    TFE_OpSetAttrType(op.get(), \"value_dtype\", value_dtype);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrBool(op.get(), \"use_node_name_sharing\", (unsigned char)use_node_name_sharing);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor histogram_fixed_width(const tensor& values, const tensor& value_range, const tensor& nbins, datatype dtype=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"HistogramFixedWidth\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value_range.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), nbins.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor histogram_summary(const tensor& tag, const tensor& values) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"HistogramSummary\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), tag.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor i_f_f_t(const tensor& input, datatype Tcomplex=static_cast<datatype>(8)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IFFT\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tcomplex\", Tcomplex);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor i_f_f_t2_d(const tensor& input, datatype Tcomplex=static_cast<datatype>(8)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IFFT2D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tcomplex\", Tcomplex);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor i_f_f_t3_d(const tensor& input, datatype Tcomplex=static_cast<datatype>(8)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IFFT3D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tcomplex\", Tcomplex);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor i_r_f_f_t(const tensor& input, const tensor& fft_length, datatype Treal=static_cast<datatype>(1), datatype Tcomplex=static_cast<datatype>(8)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IRFFT\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), fft_length.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Treal\", Treal);\n    TFE_OpSetAttrType(op.get(), \"Tcomplex\", Tcomplex);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor i_r_f_f_t2_d(const tensor& input, const tensor& fft_length, datatype Treal=static_cast<datatype>(1), datatype Tcomplex=static_cast<datatype>(8)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IRFFT2D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), fft_length.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Treal\", Treal);\n    TFE_OpSetAttrType(op.get(), \"Tcomplex\", Tcomplex);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor i_r_f_f_t3_d(const tensor& input, const tensor& fft_length, datatype Treal=static_cast<datatype>(1), datatype Tcomplex=static_cast<datatype>(8)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IRFFT3D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), fft_length.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Treal\", Treal);\n    TFE_OpSetAttrType(op.get(), \"Tcomplex\", Tcomplex);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor identity(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Identity\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor identity_n(const std::vector<tensor>&input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IdentityN\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> input_handles; input_handles.reserve(input.size());\n    std::transform(input.begin(), input.end(), std::back_inserter(input_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), input_handles.data(), input.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor identity_reader(const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IdentityReader\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor identity_reader_v2(const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IdentityReaderV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor igamma(const tensor& a, const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Igamma\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), a.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor igamma_grad_a(const tensor& a, const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IgammaGradA\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), a.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor igammac(const tensor& a, const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Igammac\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), a.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor ignore_errors_dataset(const tensor& input_dataset, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IgnoreErrorsDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor imag(const tensor& input, datatype Tout=static_cast<datatype>(1)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Imag\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tout\", Tout);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor image_projective_transform_v2(const tensor& images, const tensor& transforms, const tensor& output_shape, datatype dtype, const std::string& interpolation, const std::string& fill_mode=\"CONSTANT\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ImageProjectiveTransformV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), images.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), transforms.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), output_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrString(op.get(), \"interpolation\", (void*) interpolation.c_str(), interpolation.size());\n    TFE_OpSetAttrString(op.get(), \"fill_mode\", (void*) fill_mode.c_str(), fill_mode.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor image_summary(const tensor& tag, const tensor& input_tensor, const tensor& bad_color, int64_t max_images=3) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ImageSummary\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), tag.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n    TFE_OpSetAttrTensor(op.get(), \"bad_color\", bad_color.get_tensor().get(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"max_images\", max_images);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor immutable_const_tensor(datatype dtype, const std::vector<int64_t>& shape, const std::string& memory_region_name) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ImmutableConst\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrString(op.get(), \"memory_region_name\", (void*) memory_region_name.c_str(), memory_region_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor in_top_k(const tensor& predictions, const tensor& targets, int64_t k) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"InTopK\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), predictions.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), targets.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"k\", k);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor in_top_k_v2(const tensor& predictions, const tensor& targets, const tensor& k) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"InTopKV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), predictions.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), targets.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), k.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor infeed_dequeue(datatype dtype, const std::vector<int64_t>& shape) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"InfeedDequeue\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor infeed_dequeue_tuple(const std::vector<datatype>& dtypes, const std::vector< std::vector<int64_t>>& shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"InfeedDequeueTuple\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"dtypes\", reinterpret_cast<const enum TF_DataType *>(dtypes.data()), dtypes.size());\n    \n    std::vector<const int64_t*> shapes_values; shapes_values.reserve(shapes.size());\n    std::vector<int> shapes_ndims; shapes_ndims.reserve(shapes.size());\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_values), [](const auto& v) { return v.data();});\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"shapes\", shapes_values.data(), shapes_ndims.data(), shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor inplace_add(const tensor& x, const tensor& i, const tensor& v) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"InplaceAdd\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), i.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), v.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor inplace_sub(const tensor& x, const tensor& i, const tensor& v) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"InplaceSub\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), i.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), v.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor inplace_update(const tensor& x, const tensor& i, const tensor& v) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"InplaceUpdate\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), i.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), v.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor inv(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Inv\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor inv_grad(const tensor& y, const tensor& dy) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"InvGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dy.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor invert(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Invert\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor invert_permutation(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"InvertPermutation\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor is_boosted_trees_ensemble_initialized(const tensor& tree_ensemble_handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IsBoostedTreesEnsembleInitialized\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), tree_ensemble_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor is_boosted_trees_quantile_stream_resource_initialized(const tensor& quantile_stream_resource_handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IsBoostedTreesQuantileStreamResourceInitialized\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), quantile_stream_resource_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor is_finite(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IsFinite\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor is_inf(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IsInf\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor is_nan(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IsNan\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor is_variable_initialized(const tensor& ref, datatype dtype) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IsVariableInitialized\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor iterator(const std::string& shared_name, const std::string& container, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Iterator\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor iterator_from_string_handle(const tensor& string_handle, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IteratorFromStringHandle\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), string_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor iterator_from_string_handle_v2(const tensor& string_handle, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IteratorFromStringHandleV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), string_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor iterator_get_device(const tensor& resource) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IteratorGetDevice\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), resource.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor iterator_get_next(const tensor& iterator, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IteratorGetNext\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), iterator.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor iterator_get_next_as_optional(const tensor& iterator, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IteratorGetNextAsOptional\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), iterator.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor iterator_get_next_sync(const tensor& iterator, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IteratorGetNextSync\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), iterator.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor iterator_to_string_handle(const tensor& resource_handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IteratorToStringHandle\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), resource_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor iterator_v2(const std::string& shared_name, const std::string& container, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"IteratorV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor l2_loss(const tensor& t) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"L2Loss\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), t.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor l_m_d_b_dataset(const tensor& filenames, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LMDBDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), filenames.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor l_m_d_b_reader(const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LMDBReader\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor l_r_n(const tensor& input, int64_t depth_radius=5, float bias=1.0000e+00, float alpha=1.0000e+00, float beta=5.0000e-01) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LRN\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"depth_radius\", depth_radius);\n    TFE_OpSetAttrFloat(op.get(), \"bias\", bias);\n    TFE_OpSetAttrFloat(op.get(), \"alpha\", alpha);\n    TFE_OpSetAttrFloat(op.get(), \"beta\", beta);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor l_r_n_grad(const tensor& input_grads, const tensor& input_image, const tensor& output_image, int64_t depth_radius=5, float bias=1.0000e+00, float alpha=1.0000e+00, float beta=5.0000e-01) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LRNGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_grads.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_image.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), output_image.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"depth_radius\", depth_radius);\n    TFE_OpSetAttrFloat(op.get(), \"bias\", bias);\n    TFE_OpSetAttrFloat(op.get(), \"alpha\", alpha);\n    TFE_OpSetAttrFloat(op.get(), \"beta\", beta);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor latency_stats_dataset(const tensor& input_dataset, const tensor& tag, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LatencyStatsDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), tag.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor leaky_relu(const tensor& features, float alpha=2.0000e-01) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LeakyRelu\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), features.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrFloat(op.get(), \"alpha\", alpha);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor leaky_relu_grad(const tensor& gradients, const tensor& features, float alpha=2.0000e-01) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LeakyReluGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), gradients.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), features.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrFloat(op.get(), \"alpha\", alpha);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor left_shift(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LeftShift\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor less(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Less\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor less_equal(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LessEqual\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor lgamma(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Lgamma\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor lin_space(const tensor& start, const tensor& stop, const tensor& num, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LinSpace\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), start.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), stop.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor load_and_remap_matrix(const tensor& ckpt_path, const tensor& old_input_tensor_name, const tensor& row_remapping, const tensor& col_remapping, const tensor& initializing_values, int64_t num_rows, int64_t num_cols, int64_t max_rows_in_memory=-1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LoadAndRemapMatrix\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ckpt_path.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), old_input_tensor_name.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), row_remapping.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), col_remapping.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), initializing_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_rows\", num_rows);\n    TFE_OpSetAttrInt(op.get(), \"num_cols\", num_cols);\n    TFE_OpSetAttrInt(op.get(), \"max_rows_in_memory\", max_rows_in_memory);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor log(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Log\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor log1p(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Log1p\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor log_softmax(const tensor& logits) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LogSoftmax\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), logits.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor logical_and(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LogicalAnd\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor logical_not(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LogicalNot\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor logical_or(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LogicalOr\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor lookup_table_find(const tensor& table_handle, const tensor& keys, const tensor& default_value, datatype Tin, datatype Tout) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LookupTableFind\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), table_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), keys.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), default_value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tin\", Tin);\n    TFE_OpSetAttrType(op.get(), \"Tout\", Tout);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor lookup_table_find_v2(const tensor& table_handle, const tensor& keys, const tensor& default_value, datatype Tin, datatype Tout) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LookupTableFindV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), table_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), keys.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), default_value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tin\", Tin);\n    TFE_OpSetAttrType(op.get(), \"Tout\", Tout);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor lookup_table_size(const tensor& table_handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LookupTableSize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), table_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor lookup_table_size_v2(const tensor& table_handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LookupTableSizeV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), table_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor loop_cond(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LoopCond\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor lower_bound(const tensor& sorted_inputs, const tensor& values, datatype out_type=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"LowerBound\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), sorted_inputs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"out_type\", out_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor map_incomplete_size(const std::vector<datatype>& dtypes, int64_t capacity=0, int64_t memory_limit=0, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MapIncompleteSize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"dtypes\", reinterpret_cast<const enum TF_DataType *>(dtypes.data()), dtypes.size());\n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrInt(op.get(), \"memory_limit\", memory_limit);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor map_peek(const tensor& key, const tensor& indices, const std::vector<datatype>& dtypes, int64_t capacity=0, int64_t memory_limit=0, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MapPeek\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), key.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"dtypes\", reinterpret_cast<const enum TF_DataType *>(dtypes.data()), dtypes.size());\n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrInt(op.get(), \"memory_limit\", memory_limit);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor map_size(const std::vector<datatype>& dtypes, int64_t capacity=0, int64_t memory_limit=0, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MapSize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"dtypes\", reinterpret_cast<const enum TF_DataType *>(dtypes.data()), dtypes.size());\n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrInt(op.get(), \"memory_limit\", memory_limit);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor map_unstage(const tensor& key, const tensor& indices, const std::vector<datatype>& dtypes, int64_t capacity=0, int64_t memory_limit=0, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MapUnstage\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), key.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"dtypes\", reinterpret_cast<const enum TF_DataType *>(dtypes.data()), dtypes.size());\n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrInt(op.get(), \"memory_limit\", memory_limit);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor mat_mul(const tensor& a, const tensor& b, bool transpose_a=false, bool transpose_b=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatMul\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), a.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), b.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"transpose_a\", (unsigned char)transpose_a);\n    TFE_OpSetAttrBool(op.get(), \"transpose_b\", (unsigned char)transpose_b);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matching_files(const tensor& pattern) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatchingFiles\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), pattern.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matching_files_dataset(const tensor& patterns) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatchingFilesDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), patterns.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_band_part(const tensor& input, const tensor& num_lower, const tensor& num_upper, datatype Tindex=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixBandPart\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_lower.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_upper.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindex\", Tindex);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_determinant(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixDeterminant\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_diag(const tensor& diagonal) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixDiag\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), diagonal.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_diag_part(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixDiagPart\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_diag_part_v2(const tensor& input, const tensor& k, const tensor& padding_value) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixDiagPartV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), k.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), padding_value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_diag_part_v3(const tensor& input, const tensor& k, const tensor& padding_value, const std::string& align=\"RIGHT_LEFT\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixDiagPartV3\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), k.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), padding_value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"align\", (void*) align.c_str(), align.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_diag_v2(const tensor& diagonal, const tensor& k, const tensor& num_rows, const tensor& num_cols, const tensor& padding_value) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixDiagV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), diagonal.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), k.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_rows.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_cols.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), padding_value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_diag_v3(const tensor& diagonal, const tensor& k, const tensor& num_rows, const tensor& num_cols, const tensor& padding_value, const std::string& align=\"RIGHT_LEFT\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixDiagV3\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), diagonal.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), k.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_rows.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_cols.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), padding_value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"align\", (void*) align.c_str(), align.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_exponential(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixExponential\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_inverse(const tensor& input, bool adjoint=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixInverse\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"adjoint\", (unsigned char)adjoint);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_logarithm(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixLogarithm\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_set_diag(const tensor& input, const tensor& diagonal) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixSetDiag\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), diagonal.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_set_diag_v2(const tensor& input, const tensor& diagonal, const tensor& k) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixSetDiagV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), diagonal.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), k.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_set_diag_v3(const tensor& input, const tensor& diagonal, const tensor& k, const std::string& align=\"RIGHT_LEFT\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixSetDiagV3\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), diagonal.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), k.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"align\", (void*) align.c_str(), align.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_solve(const tensor& matrix, const tensor& rhs, bool adjoint=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixSolve\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), matrix.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rhs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"adjoint\", (unsigned char)adjoint);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_solve_ls(const tensor& matrix, const tensor& rhs, const tensor& l2_regularizer, bool fast=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixSolveLs\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), matrix.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rhs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l2_regularizer.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"fast\", (unsigned char)fast);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_square_root(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixSquareRoot\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor matrix_triangular_solve(const tensor& matrix, const tensor& rhs, bool lower=true, bool adjoint=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MatrixTriangularSolve\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), matrix.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rhs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"lower\", (unsigned char)lower);\n    TFE_OpSetAttrBool(op.get(), \"adjoint\", (unsigned char)adjoint);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor max(const tensor& input, const tensor& reduction_indices, bool keep_dims=false, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Max\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), reduction_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"keep_dims\", (unsigned char)keep_dims);\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor max_intra_op_parallelism_dataset(const tensor& input_dataset, const tensor& max_intra_op_parallelism, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MaxIntraOpParallelismDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), max_intra_op_parallelism.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor max_pool(const tensor& input, const std::vector<int64_t>& ksize, const std::vector<int64_t>& strides, const std::string& padding, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MaxPool\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"ksize\", ksize.data(), ksize.size());\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor max_pool3_d(const tensor& input, const std::vector<int64_t>& ksize, const std::vector<int64_t>& strides, const std::string& padding, const std::string& data_format=\"NDHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MaxPool3D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"ksize\", ksize.data(), ksize.size());\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor max_pool3_d_grad(const tensor& orig_input, const tensor& orig_output, const tensor& grad, const std::vector<int64_t>& ksize, const std::vector<int64_t>& strides, const std::string& padding, const std::string& data_format=\"NDHWC\", datatype TInput=static_cast<datatype>(1)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MaxPool3DGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), orig_input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), orig_output.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"ksize\", ksize.data(), ksize.size());\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n    TFE_OpSetAttrType(op.get(), \"TInput\", TInput);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor max_pool3_d_grad_grad(const tensor& orig_input, const tensor& orig_output, const tensor& grad, const std::vector<int64_t>& ksize, const std::vector<int64_t>& strides, const std::string& padding, const std::string& data_format=\"NDHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MaxPool3DGradGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), orig_input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), orig_output.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"ksize\", ksize.data(), ksize.size());\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor max_pool_grad(const tensor& orig_input, const tensor& orig_output, const tensor& grad, const std::vector<int64_t>& ksize, const std::vector<int64_t>& strides, const std::string& padding, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MaxPoolGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), orig_input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), orig_output.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"ksize\", ksize.data(), ksize.size());\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor max_pool_grad_grad(const tensor& orig_input, const tensor& orig_output, const tensor& grad, const std::vector<int64_t>& ksize, const std::vector<int64_t>& strides, const std::string& padding, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MaxPoolGradGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), orig_input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), orig_output.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"ksize\", ksize.data(), ksize.size());\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor max_pool_grad_grad_v2(const tensor& orig_input, const tensor& orig_output, const tensor& grad, const tensor& ksize, const tensor& strides, const std::string& padding, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MaxPoolGradGradV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), orig_input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), orig_output.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), ksize.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), strides.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor max_pool_grad_grad_with_argmax(const tensor& input, const tensor& grad, const tensor& argmax, const std::vector<int64_t>& ksize, const std::vector<int64_t>& strides, const std::string& padding, datatype Targmax, bool include_batch_in_index=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MaxPoolGradGradWithArgmax\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), argmax.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"ksize\", ksize.data(), ksize.size());\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrType(op.get(), \"Targmax\", Targmax);\n    TFE_OpSetAttrBool(op.get(), \"include_batch_in_index\", (unsigned char)include_batch_in_index);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor max_pool_grad_v2(const tensor& orig_input, const tensor& orig_output, const tensor& grad, const tensor& ksize, const tensor& strides, const std::string& padding, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MaxPoolGradV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), orig_input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), orig_output.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), ksize.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), strides.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor max_pool_grad_with_argmax(const tensor& input, const tensor& grad, const tensor& argmax, const std::vector<int64_t>& ksize, const std::vector<int64_t>& strides, const std::string& padding, datatype Targmax, bool include_batch_in_index=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MaxPoolGradWithArgmax\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), argmax.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"ksize\", ksize.data(), ksize.size());\n    TFE_OpSetAttrIntList(op.get(), \"strides\", strides.data(), strides.size());\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrType(op.get(), \"Targmax\", Targmax);\n    TFE_OpSetAttrBool(op.get(), \"include_batch_in_index\", (unsigned char)include_batch_in_index);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor max_pool_v2(const tensor& input, const tensor& ksize, const tensor& strides, const std::string& padding, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MaxPoolV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), ksize.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), strides.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"padding\", (void*) padding.c_str(), padding.size());\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor maximum(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Maximum\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor mean(const tensor& input, const tensor& reduction_indices, bool keep_dims=false, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Mean\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), reduction_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"keep_dims\", (unsigned char)keep_dims);\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor merge_summary(const std::vector<tensor>&inputs) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MergeSummary\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> inputs_handles; inputs_handles.reserve(inputs.size());\n    std::transform(inputs.begin(), inputs.end(), std::back_inserter(inputs_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), inputs_handles.data(), inputs.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"N\", inputs.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor mfcc(const tensor& spectrogram, const tensor& sample_rate, float upper_frequency_limit=4.0000e+03, float lower_frequency_limit=2.0000e+01, int64_t filterbank_channel_count=40, int64_t dct_coefficient_count=13) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Mfcc\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), spectrogram.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sample_rate.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrFloat(op.get(), \"upper_frequency_limit\", upper_frequency_limit);\n    TFE_OpSetAttrFloat(op.get(), \"lower_frequency_limit\", lower_frequency_limit);\n    TFE_OpSetAttrInt(op.get(), \"filterbank_channel_count\", filterbank_channel_count);\n    TFE_OpSetAttrInt(op.get(), \"dct_coefficient_count\", dct_coefficient_count);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor min(const tensor& input, const tensor& reduction_indices, bool keep_dims=false, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Min\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), reduction_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"keep_dims\", (unsigned char)keep_dims);\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor minimum(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Minimum\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor mirror_pad(const tensor& input, const tensor& paddings, const std::string& mode, datatype Tpaddings=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MirrorPad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), paddings.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"mode\", (void*) mode.c_str(), mode.size());\n    TFE_OpSetAttrType(op.get(), \"Tpaddings\", Tpaddings);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor mirror_pad_grad(const tensor& input, const tensor& paddings, const std::string& mode, datatype Tpaddings=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MirrorPadGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), paddings.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"mode\", (void*) mode.c_str(), mode.size());\n    TFE_OpSetAttrType(op.get(), \"Tpaddings\", Tpaddings);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor mod(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Mod\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor model_dataset(const tensor& input_dataset, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, int64_t algorithm=0, int64_t cpu_budget=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ModelDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"algorithm\", algorithm);\n    TFE_OpSetAttrInt(op.get(), \"cpu_budget\", cpu_budget);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor mul(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Mul\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor mul_no_nan(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MulNoNan\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor multi_device_iterator(const std::vector< std::string>& devices, const std::string& shared_name, const std::string& container, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MultiDeviceIterator\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    \n    std::vector<std::size_t> devices_sizes; devices_sizes.reserve(devices.size());\n    std::transform(devices.begin(), devices.end(), std::back_inserter(devices_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"devices\", reinterpret_cast<const void *const *>(devices.data()), devices_sizes.data(), devices.size());\n    \n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor multi_device_iterator_from_string_handle(const tensor& string_handle, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MultiDeviceIteratorFromStringHandle\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), string_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor multi_device_iterator_get_next_from_shard(const tensor& multi_device_iterator, const tensor& shard_num, const tensor& incarnation_id, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MultiDeviceIteratorGetNextFromShard\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), multi_device_iterator.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), shard_num.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), incarnation_id.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor multi_device_iterator_init(const tensor& dataset, const tensor& multi_device_iterator, const tensor& max_buffer_size) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MultiDeviceIteratorInit\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), multi_device_iterator.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), max_buffer_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor multi_device_iterator_to_string_handle(const tensor& multi_device_iterator) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MultiDeviceIteratorToStringHandle\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), multi_device_iterator.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor multinomial(const tensor& logits, const tensor& num_samples, int64_t seed=0, int64_t seed2=0, datatype output_dtype=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Multinomial\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), logits.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_samples.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"seed\", seed);\n    TFE_OpSetAttrInt(op.get(), \"seed2\", seed2);\n    TFE_OpSetAttrType(op.get(), \"output_dtype\", output_dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor mutable_dense_hash_table(const tensor& empty_key, datatype key_dtype, datatype value_dtype, const std::vector<int64_t>& value_shape, const std::string& container=\"\", const std::string& shared_name=\"\", bool use_node_name_sharing=false, int64_t initial_num_buckets=131072, float max_load_factor=8.0000e-01) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MutableDenseHashTable\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), empty_key.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"key_dtype\", key_dtype);\n    TFE_OpSetAttrType(op.get(), \"value_dtype\", value_dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"value_shape\", value_shape.data(), value_shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrBool(op.get(), \"use_node_name_sharing\", (unsigned char)use_node_name_sharing);\n    TFE_OpSetAttrInt(op.get(), \"initial_num_buckets\", initial_num_buckets);\n    TFE_OpSetAttrFloat(op.get(), \"max_load_factor\", max_load_factor);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor mutable_dense_hash_table_v2(const tensor& empty_key, const tensor& deleted_key, datatype key_dtype, datatype value_dtype, const std::vector<int64_t>& value_shape, const std::string& container=\"\", const std::string& shared_name=\"\", bool use_node_name_sharing=false, int64_t initial_num_buckets=131072, float max_load_factor=8.0000e-01) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MutableDenseHashTableV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), empty_key.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), deleted_key.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"key_dtype\", key_dtype);\n    TFE_OpSetAttrType(op.get(), \"value_dtype\", value_dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"value_shape\", value_shape.data(), value_shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrBool(op.get(), \"use_node_name_sharing\", (unsigned char)use_node_name_sharing);\n    TFE_OpSetAttrInt(op.get(), \"initial_num_buckets\", initial_num_buckets);\n    TFE_OpSetAttrFloat(op.get(), \"max_load_factor\", max_load_factor);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor mutable_hash_table(datatype key_dtype, datatype value_dtype, const std::string& container=\"\", const std::string& shared_name=\"\", bool use_node_name_sharing=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MutableHashTable\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"key_dtype\", key_dtype);\n    TFE_OpSetAttrType(op.get(), \"value_dtype\", value_dtype);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrBool(op.get(), \"use_node_name_sharing\", (unsigned char)use_node_name_sharing);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor mutable_hash_table_of_tensors(datatype key_dtype, datatype value_dtype, const std::vector<int64_t>& value_shape, const std::string& container=\"\", const std::string& shared_name=\"\", bool use_node_name_sharing=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MutableHashTableOfTensors\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"key_dtype\", key_dtype);\n    TFE_OpSetAttrType(op.get(), \"value_dtype\", value_dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"value_shape\", value_shape.data(), value_shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrBool(op.get(), \"use_node_name_sharing\", (unsigned char)use_node_name_sharing);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor mutable_hash_table_of_tensors_v2(datatype key_dtype, datatype value_dtype, const std::vector<int64_t>& value_shape, const std::string& container=\"\", const std::string& shared_name=\"\", bool use_node_name_sharing=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MutableHashTableOfTensorsV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"key_dtype\", key_dtype);\n    TFE_OpSetAttrType(op.get(), \"value_dtype\", value_dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"value_shape\", value_shape.data(), value_shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrBool(op.get(), \"use_node_name_sharing\", (unsigned char)use_node_name_sharing);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor mutable_hash_table_v2(datatype key_dtype, datatype value_dtype, const std::string& container=\"\", const std::string& shared_name=\"\", bool use_node_name_sharing=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MutableHashTableV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"key_dtype\", key_dtype);\n    TFE_OpSetAttrType(op.get(), \"value_dtype\", value_dtype);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrBool(op.get(), \"use_node_name_sharing\", (unsigned char)use_node_name_sharing);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor mutex_lock(const tensor& mutex) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MutexLock\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), mutex.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor mutex_v2(const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"MutexV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor nccl_all_reduce(const tensor& input, const std::string& reduction, int64_t num_devices, const std::string& shared_name) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"NcclAllReduce\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"reduction\", (void*) reduction.c_str(), reduction.size());\n    TFE_OpSetAttrInt(op.get(), \"num_devices\", num_devices);\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor nccl_broadcast(const tensor& input, const std::vector<int64_t>& shape) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"NcclBroadcast\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor nccl_reduce(const std::vector<tensor>&input, const std::string& reduction) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"NcclReduce\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> input_handles; input_handles.reserve(input.size());\n    std::transform(input.begin(), input.end(), std::back_inserter(input_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), input_handles.data(), input.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"reduction\", (void*) reduction.c_str(), reduction.size());\n    TFE_OpSetAttrInt(op.get(), \"num_devices\", input.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor ndtri(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Ndtri\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor neg(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Neg\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor next_after(const tensor& x1, const tensor& x2) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"NextAfter\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x1.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), x2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor next_iteration(const tensor& data) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"NextIteration\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor non_deterministic_ints(const tensor& shape, datatype dtype=static_cast<datatype>(9), datatype shape_dtype=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"NonDeterministicInts\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrType(op.get(), \"shape_dtype\", shape_dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor non_max_suppression(const tensor& boxes, const tensor& scores, const tensor& max_output_size, float iou_threshold=5.0000e-01) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"NonMaxSuppression\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), boxes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), scores.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), max_output_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrFloat(op.get(), \"iou_threshold\", iou_threshold);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor non_max_suppression_v2(const tensor& boxes, const tensor& scores, const tensor& max_output_size, const tensor& iou_threshold, datatype T_threshold=static_cast<datatype>(1)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"NonMaxSuppressionV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), boxes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), scores.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), max_output_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), iou_threshold.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"T_threshold\", T_threshold);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor non_max_suppression_v3(const tensor& boxes, const tensor& scores, const tensor& max_output_size, const tensor& iou_threshold, const tensor& score_threshold, datatype T_threshold=static_cast<datatype>(1)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"NonMaxSuppressionV3\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), boxes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), scores.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), max_output_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), iou_threshold.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), score_threshold.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"T_threshold\", T_threshold);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor non_max_suppression_with_overlaps(const tensor& overlaps, const tensor& scores, const tensor& max_output_size, const tensor& overlap_threshold, const tensor& score_threshold) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"NonMaxSuppressionWithOverlaps\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), overlaps.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), scores.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), max_output_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), overlap_threshold.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), score_threshold.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor non_serializable_dataset(const tensor& input_dataset, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"NonSerializableDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor not_equal(const tensor& x, const tensor& y, bool incompatible_shape_error=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"NotEqual\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"incompatible_shape_error\", (unsigned char)incompatible_shape_error);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor nth_element(const tensor& input, const tensor& n, bool reverse=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"NthElement\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), n.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"reverse\", (unsigned char)reverse);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor one_hot(const tensor& indices, const tensor& depth, const tensor& on_value, const tensor& off_value, int64_t axis=-1, datatype TI=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"OneHot\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), depth.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), on_value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), off_value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"axis\", axis);\n    TFE_OpSetAttrType(op.get(), \"TI\", TI);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor ones_like(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"OnesLike\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor optimize_dataset(const tensor& input_dataset, const tensor& optimizations, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, const std::vector< std::string>& optimization_configs) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"OptimizeDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), optimizations.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<std::size_t> optimization_configs_sizes; optimization_configs_sizes.reserve(optimization_configs.size());\n    std::transform(optimization_configs.begin(), optimization_configs.end(), std::back_inserter(optimization_configs_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"optimization_configs\", reinterpret_cast<const void *const *>(optimization_configs.data()), optimization_configs_sizes.data(), optimization_configs.size());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor optional_from_value(const std::vector<tensor>&components, const std::vector<datatype>& Toutput_types) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"OptionalFromValue\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> components_handles; components_handles.reserve(components.size());\n    std::transform(components.begin(), components.end(), std::back_inserter(components_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), components_handles.data(), components.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"Toutput_types\", reinterpret_cast<const enum TF_DataType *>(Toutput_types.data()), Toutput_types.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor optional_get_value(const tensor& optional, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"OptionalGetValue\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), optional.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor optional_has_value(const tensor& optional) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"OptionalHasValue\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), optional.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor optional_none() {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"OptionalNone\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor ordered_map_incomplete_size(const std::vector<datatype>& dtypes, int64_t capacity=0, int64_t memory_limit=0, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"OrderedMapIncompleteSize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"dtypes\", reinterpret_cast<const enum TF_DataType *>(dtypes.data()), dtypes.size());\n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrInt(op.get(), \"memory_limit\", memory_limit);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor ordered_map_peek(const tensor& key, const tensor& indices, const std::vector<datatype>& dtypes, int64_t capacity=0, int64_t memory_limit=0, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"OrderedMapPeek\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), key.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"dtypes\", reinterpret_cast<const enum TF_DataType *>(dtypes.data()), dtypes.size());\n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrInt(op.get(), \"memory_limit\", memory_limit);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor ordered_map_size(const std::vector<datatype>& dtypes, int64_t capacity=0, int64_t memory_limit=0, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"OrderedMapSize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"dtypes\", reinterpret_cast<const enum TF_DataType *>(dtypes.data()), dtypes.size());\n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrInt(op.get(), \"memory_limit\", memory_limit);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor ordered_map_unstage(const tensor& key, const tensor& indices, const std::vector<datatype>& dtypes, int64_t capacity=0, int64_t memory_limit=0, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"OrderedMapUnstage\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), key.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"dtypes\", reinterpret_cast<const enum TF_DataType *>(dtypes.data()), dtypes.size());\n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrInt(op.get(), \"memory_limit\", memory_limit);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor outfeed_dequeue(datatype dtype, const std::vector<int64_t>& shape, int64_t device_ordinal=-1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"OutfeedDequeue\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"device_ordinal\", device_ordinal);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor outfeed_dequeue_tuple(const std::vector<datatype>& dtypes, const std::vector< std::vector<int64_t>>& shapes, int64_t device_ordinal=-1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"OutfeedDequeueTuple\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"dtypes\", reinterpret_cast<const enum TF_DataType *>(dtypes.data()), dtypes.size());\n    \n    std::vector<const int64_t*> shapes_values; shapes_values.reserve(shapes.size());\n    std::vector<int> shapes_ndims; shapes_ndims.reserve(shapes.size());\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_values), [](const auto& v) { return v.data();});\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"shapes\", shapes_values.data(), shapes_ndims.data(), shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"device_ordinal\", device_ordinal);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor pack(const std::vector<tensor>&values, int64_t axis=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Pack\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> values_handles; values_handles.reserve(values.size());\n    std::transform(values.begin(), values.end(), std::back_inserter(values_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), values_handles.data(), values.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"N\", values.size());\n    TFE_OpSetAttrInt(op.get(), \"axis\", axis);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor pad(const tensor& input, const tensor& paddings, datatype Tpaddings=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Pad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), paddings.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tpaddings\", Tpaddings);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor pad_v2(const tensor& input, const tensor& paddings, const tensor& constant_values, datatype Tpaddings=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"PadV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), paddings.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), constant_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tpaddings\", Tpaddings);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor padded_batch_dataset(const tensor& input_dataset, const tensor& batch_size, const std::vector<tensor>&padded_shapes, const std::vector<tensor>&padding_values, const std::vector<datatype>& Toutput_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"PaddedBatchDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), batch_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> padded_shapes_handles; padded_shapes_handles.reserve(padded_shapes.size());\n    std::transform(padded_shapes.begin(), padded_shapes.end(), std::back_inserter(padded_shapes_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), padded_shapes_handles.data(), padded_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> padding_values_handles; padding_values_handles.reserve(padding_values.size());\n    std::transform(padding_values.begin(), padding_values.end(), std::back_inserter(padding_values_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), padding_values_handles.data(), padding_values.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"Toutput_types\", reinterpret_cast<const enum TF_DataType *>(Toutput_types.data()), Toutput_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"N\", padded_shapes.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor padded_batch_dataset_v2(const tensor& input_dataset, const tensor& batch_size, const std::vector<tensor>&padded_shapes, const std::vector<tensor>&padding_values, const tensor& drop_remainder, const std::vector<datatype>& Toutput_types, const std::vector< std::vector<int64_t>>& output_shapes, bool parallel_copy=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"PaddedBatchDatasetV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), batch_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> padded_shapes_handles; padded_shapes_handles.reserve(padded_shapes.size());\n    std::transform(padded_shapes.begin(), padded_shapes.end(), std::back_inserter(padded_shapes_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), padded_shapes_handles.data(), padded_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> padding_values_handles; padding_values_handles.reserve(padding_values.size());\n    std::transform(padding_values.begin(), padding_values.end(), std::back_inserter(padding_values_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), padding_values_handles.data(), padding_values.size(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), drop_remainder.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"Toutput_types\", reinterpret_cast<const enum TF_DataType *>(Toutput_types.data()), Toutput_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"N\", padded_shapes.size());\n    TFE_OpSetAttrBool(op.get(), \"parallel_copy\", (unsigned char)parallel_copy);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor padding_f_i_f_o_queue(const std::vector<datatype>& component_types, const std::vector< std::vector<int64_t>>& shapes, int64_t capacity=-1, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"PaddingFIFOQueue\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"component_types\", reinterpret_cast<const enum TF_DataType *>(component_types.data()), component_types.size());\n    \n    std::vector<const int64_t*> shapes_values; shapes_values.reserve(shapes.size());\n    std::vector<int> shapes_ndims; shapes_ndims.reserve(shapes.size());\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_values), [](const auto& v) { return v.data();});\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"shapes\", shapes_values.data(), shapes_ndims.data(), shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor padding_f_i_f_o_queue_v2(const std::vector<datatype>& component_types, const std::vector< std::vector<int64_t>>& shapes, int64_t capacity=-1, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"PaddingFIFOQueueV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"component_types\", reinterpret_cast<const enum TF_DataType *>(component_types.data()), component_types.size());\n    \n    std::vector<const int64_t*> shapes_values; shapes_values.reserve(shapes.size());\n    std::vector<int> shapes_ndims; shapes_ndims.reserve(shapes.size());\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_values), [](const auto& v) { return v.data();});\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"shapes\", shapes_values.data(), shapes_ndims.data(), shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor parallel_concat(const std::vector<tensor>&values, const std::vector<int64_t>& shape) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ParallelConcat\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> values_handles; values_handles.reserve(values.size());\n    std::transform(values.begin(), values.end(), std::back_inserter(values_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), values_handles.data(), values.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"N\", values.size());\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor parallel_dynamic_stitch(const std::vector<tensor>&indices, const std::vector<tensor>&data) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ParallelDynamicStitch\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> indices_handles; indices_handles.reserve(indices.size());\n    std::transform(indices.begin(), indices.end(), std::back_inserter(indices_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), indices_handles.data(), indices.size(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> data_handles; data_handles.reserve(data.size());\n    std::transform(data.begin(), data.end(), std::back_inserter(data_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), data_handles.data(), data.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"N\", indices.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor parameterized_truncated_normal(const tensor& shape, const tensor& means, const tensor& stdevs, const tensor& minvals, const tensor& maxvals, datatype dtype, int64_t seed=0, int64_t seed2=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ParameterizedTruncatedNormal\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), means.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), stdevs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), minvals.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), maxvals.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrInt(op.get(), \"seed\", seed);\n    TFE_OpSetAttrInt(op.get(), \"seed2\", seed2);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor parse_example_dataset(const tensor& input_dataset, const tensor& num_parallel_calls, const std::vector<tensor>&dense_defaults, const std::vector< std::string>& sparse_keys, const std::vector< std::string>& dense_keys, const std::vector<datatype>& sparse_types, const std::vector<datatype>& Tdense, const std::vector< std::vector<int64_t>>& dense_shapes, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, const std::vector< std::string>& ragged_keys, const std::vector<datatype>& ragged_value_types, const std::vector<datatype>& ragged_split_types, bool sloppy=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ParseExampleDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_parallel_calls.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> dense_defaults_handles; dense_defaults_handles.reserve(dense_defaults.size());\n    std::transform(dense_defaults.begin(), dense_defaults.end(), std::back_inserter(dense_defaults_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), dense_defaults_handles.data(), dense_defaults.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n    std::vector<std::size_t> sparse_keys_sizes; sparse_keys_sizes.reserve(sparse_keys.size());\n    std::transform(sparse_keys.begin(), sparse_keys.end(), std::back_inserter(sparse_keys_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"sparse_keys\", reinterpret_cast<const void *const *>(sparse_keys.data()), sparse_keys_sizes.data(), sparse_keys.size());\n    \n    \n    std::vector<std::size_t> dense_keys_sizes; dense_keys_sizes.reserve(dense_keys.size());\n    std::transform(dense_keys.begin(), dense_keys.end(), std::back_inserter(dense_keys_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"dense_keys\", reinterpret_cast<const void *const *>(dense_keys.data()), dense_keys_sizes.data(), dense_keys.size());\n    \n    TFE_OpSetAttrTypeList(op.get(), \"sparse_types\", reinterpret_cast<const enum TF_DataType *>(sparse_types.data()), sparse_types.size());\n    TFE_OpSetAttrTypeList(op.get(), \"Tdense\", reinterpret_cast<const enum TF_DataType *>(Tdense.data()), Tdense.size());\n    \n    std::vector<const int64_t*> dense_shapes_values; dense_shapes_values.reserve(dense_shapes.size());\n    std::vector<int> dense_shapes_ndims; dense_shapes_ndims.reserve(dense_shapes.size());\n    std::transform(dense_shapes.begin(), dense_shapes.end(), std::back_inserter(dense_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(dense_shapes.begin(), dense_shapes.end(), std::back_inserter(dense_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"dense_shapes\", dense_shapes_values.data(), dense_shapes_ndims.data(), dense_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<std::size_t> ragged_keys_sizes; ragged_keys_sizes.reserve(ragged_keys.size());\n    std::transform(ragged_keys.begin(), ragged_keys.end(), std::back_inserter(ragged_keys_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"ragged_keys\", reinterpret_cast<const void *const *>(ragged_keys.data()), ragged_keys_sizes.data(), ragged_keys.size());\n    \n    TFE_OpSetAttrTypeList(op.get(), \"ragged_value_types\", reinterpret_cast<const enum TF_DataType *>(ragged_value_types.data()), ragged_value_types.size());\n    TFE_OpSetAttrTypeList(op.get(), \"ragged_split_types\", reinterpret_cast<const enum TF_DataType *>(ragged_split_types.data()), ragged_split_types.size());\n    TFE_OpSetAttrBool(op.get(), \"sloppy\", (unsigned char)sloppy);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor parse_example_dataset_v2(const tensor& input_dataset, const tensor& num_parallel_calls, const std::vector<tensor>&dense_defaults, const std::vector< std::string>& sparse_keys, const std::vector< std::string>& dense_keys, const std::vector<datatype>& sparse_types, const std::vector<datatype>& Tdense, const std::vector< std::vector<int64_t>>& dense_shapes, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, const std::vector< std::string>& ragged_keys, const std::vector<datatype>& ragged_value_types, const std::vector<datatype>& ragged_split_types, const std::string& deterministic=\"default\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ParseExampleDatasetV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_parallel_calls.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> dense_defaults_handles; dense_defaults_handles.reserve(dense_defaults.size());\n    std::transform(dense_defaults.begin(), dense_defaults.end(), std::back_inserter(dense_defaults_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), dense_defaults_handles.data(), dense_defaults.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n    std::vector<std::size_t> sparse_keys_sizes; sparse_keys_sizes.reserve(sparse_keys.size());\n    std::transform(sparse_keys.begin(), sparse_keys.end(), std::back_inserter(sparse_keys_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"sparse_keys\", reinterpret_cast<const void *const *>(sparse_keys.data()), sparse_keys_sizes.data(), sparse_keys.size());\n    \n    \n    std::vector<std::size_t> dense_keys_sizes; dense_keys_sizes.reserve(dense_keys.size());\n    std::transform(dense_keys.begin(), dense_keys.end(), std::back_inserter(dense_keys_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"dense_keys\", reinterpret_cast<const void *const *>(dense_keys.data()), dense_keys_sizes.data(), dense_keys.size());\n    \n    TFE_OpSetAttrTypeList(op.get(), \"sparse_types\", reinterpret_cast<const enum TF_DataType *>(sparse_types.data()), sparse_types.size());\n    TFE_OpSetAttrTypeList(op.get(), \"Tdense\", reinterpret_cast<const enum TF_DataType *>(Tdense.data()), Tdense.size());\n    \n    std::vector<const int64_t*> dense_shapes_values; dense_shapes_values.reserve(dense_shapes.size());\n    std::vector<int> dense_shapes_ndims; dense_shapes_ndims.reserve(dense_shapes.size());\n    std::transform(dense_shapes.begin(), dense_shapes.end(), std::back_inserter(dense_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(dense_shapes.begin(), dense_shapes.end(), std::back_inserter(dense_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"dense_shapes\", dense_shapes_values.data(), dense_shapes_ndims.data(), dense_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<std::size_t> ragged_keys_sizes; ragged_keys_sizes.reserve(ragged_keys.size());\n    std::transform(ragged_keys.begin(), ragged_keys.end(), std::back_inserter(ragged_keys_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"ragged_keys\", reinterpret_cast<const void *const *>(ragged_keys.data()), ragged_keys_sizes.data(), ragged_keys.size());\n    \n    TFE_OpSetAttrTypeList(op.get(), \"ragged_value_types\", reinterpret_cast<const enum TF_DataType *>(ragged_value_types.data()), ragged_value_types.size());\n    TFE_OpSetAttrTypeList(op.get(), \"ragged_split_types\", reinterpret_cast<const enum TF_DataType *>(ragged_split_types.data()), ragged_split_types.size());\n    TFE_OpSetAttrString(op.get(), \"deterministic\", (void*) deterministic.c_str(), deterministic.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor parse_tensor(const tensor& serialized, datatype out_type) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ParseTensor\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), serialized.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"out_type\", out_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor placeholder(datatype dtype, const std::vector<int64_t>& shape) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Placeholder\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor placeholder_v2(datatype dtype, const std::vector<int64_t>& shape) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"PlaceholderV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor placeholder_with_default(const tensor& input, datatype dtype, const std::vector<int64_t>& shape) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"PlaceholderWithDefault\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor polygamma(const tensor& a, const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Polygamma\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), a.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor population_count(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"PopulationCount\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor pow(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Pow\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor prefetch_dataset(const tensor& input_dataset, const tensor& buffer_size, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, int64_t slack_period=0, bool legacy_autotune=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"PrefetchDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), buffer_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"slack_period\", slack_period);\n    TFE_OpSetAttrBool(op.get(), \"legacy_autotune\", (unsigned char)legacy_autotune);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor prelinearize(const tensor& input, datatype dtype, const std::vector<int64_t>& shape, const std::vector<int64_t>& layout) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Prelinearize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrIntList(op.get(), \"layout\", layout.data(), layout.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor prelinearize_tuple(const std::vector<tensor>&inputs, const std::vector<datatype>& dtypes, const std::vector< std::vector<int64_t>>& shapes, const std::vector<int64_t>& layouts) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"PrelinearizeTuple\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> inputs_handles; inputs_handles.reserve(inputs.size());\n    std::transform(inputs.begin(), inputs.end(), std::back_inserter(inputs_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), inputs_handles.data(), inputs.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"dtypes\", reinterpret_cast<const enum TF_DataType *>(dtypes.data()), dtypes.size());\n    \n    std::vector<const int64_t*> shapes_values; shapes_values.reserve(shapes.size());\n    std::vector<int> shapes_ndims; shapes_ndims.reserve(shapes.size());\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_values), [](const auto& v) { return v.data();});\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"shapes\", shapes_values.data(), shapes_ndims.data(), shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrIntList(op.get(), \"layouts\", layouts.data(), layouts.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor prevent_gradient(const tensor& input, const std::string& message=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"PreventGradient\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"message\", (void*) message.c_str(), message.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor print(const tensor& input, const std::vector<tensor>&data, const std::vector<datatype>& U, const std::string& message=\"\", int64_t first_n=-1, int64_t summarize=3) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Print\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> data_handles; data_handles.reserve(data.size());\n    std::transform(data.begin(), data.end(), std::back_inserter(data_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), data_handles.data(), data.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"U\", reinterpret_cast<const enum TF_DataType *>(U.data()), U.size());\n    TFE_OpSetAttrString(op.get(), \"message\", (void*) message.c_str(), message.size());\n    TFE_OpSetAttrInt(op.get(), \"first_n\", first_n);\n    TFE_OpSetAttrInt(op.get(), \"summarize\", summarize);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor priority_queue(const std::vector<datatype>& component_types, const std::vector< std::vector<int64_t>>& shapes, int64_t capacity=-1, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"PriorityQueue\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"component_types\", reinterpret_cast<const enum TF_DataType *>(component_types.data()), component_types.size());\n    \n    std::vector<const int64_t*> shapes_values; shapes_values.reserve(shapes.size());\n    std::vector<int> shapes_ndims; shapes_ndims.reserve(shapes.size());\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_values), [](const auto& v) { return v.data();});\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"shapes\", shapes_values.data(), shapes_ndims.data(), shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor priority_queue_v2(const std::vector<datatype>& component_types, const std::vector< std::vector<int64_t>>& shapes, int64_t capacity=-1, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"PriorityQueueV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"component_types\", reinterpret_cast<const enum TF_DataType *>(component_types.data()), component_types.size());\n    \n    std::vector<const int64_t*> shapes_values; shapes_values.reserve(shapes.size());\n    std::vector<int> shapes_ndims; shapes_ndims.reserve(shapes.size());\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_values), [](const auto& v) { return v.data();});\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"shapes\", shapes_values.data(), shapes_ndims.data(), shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor private_thread_pool_dataset(const tensor& input_dataset, const tensor& num_threads, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"PrivateThreadPoolDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_threads.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor prod(const tensor& input, const tensor& reduction_indices, bool keep_dims=false, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Prod\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), reduction_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"keep_dims\", (unsigned char)keep_dims);\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor py_func(const std::vector<tensor>&input, const std::string& token, const std::vector<datatype>& Tin, const std::vector<datatype>& Tout) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"PyFunc\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> input_handles; input_handles.reserve(input.size());\n    std::transform(input.begin(), input.end(), std::back_inserter(input_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), input_handles.data(), input.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"token\", (void*) token.c_str(), token.size());\n    TFE_OpSetAttrTypeList(op.get(), \"Tin\", reinterpret_cast<const enum TF_DataType *>(Tin.data()), Tin.size());\n    TFE_OpSetAttrTypeList(op.get(), \"Tout\", reinterpret_cast<const enum TF_DataType *>(Tout.data()), Tout.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor py_func_stateless(const std::vector<tensor>&input, const std::string& token, const std::vector<datatype>& Tin, const std::vector<datatype>& Tout) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"PyFuncStateless\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> input_handles; input_handles.reserve(input.size());\n    std::transform(input.begin(), input.end(), std::back_inserter(input_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), input_handles.data(), input.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"token\", (void*) token.c_str(), token.size());\n    TFE_OpSetAttrTypeList(op.get(), \"Tin\", reinterpret_cast<const enum TF_DataType *>(Tin.data()), Tin.size());\n    TFE_OpSetAttrTypeList(op.get(), \"Tout\", reinterpret_cast<const enum TF_DataType *>(Tout.data()), Tout.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor quantize_and_dequantize(const tensor& input, bool signed_input=true, int64_t num_bits=8, bool range_given=false, float input_min=0.0000e+00, float input_max=0.0000e+00) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"QuantizeAndDequantize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"signed_input\", (unsigned char)signed_input);\n    TFE_OpSetAttrInt(op.get(), \"num_bits\", num_bits);\n    TFE_OpSetAttrBool(op.get(), \"range_given\", (unsigned char)range_given);\n    TFE_OpSetAttrFloat(op.get(), \"input_min\", input_min);\n    TFE_OpSetAttrFloat(op.get(), \"input_max\", input_max);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor quantize_and_dequantize_v2(const tensor& input, const tensor& input_min, const tensor& input_max, bool signed_input=true, int64_t num_bits=8, bool range_given=false, const std::string& round_mode=\"HALF_TO_EVEN\", bool narrow_range=false, int64_t axis=-1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"QuantizeAndDequantizeV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_min.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_max.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"signed_input\", (unsigned char)signed_input);\n    TFE_OpSetAttrInt(op.get(), \"num_bits\", num_bits);\n    TFE_OpSetAttrBool(op.get(), \"range_given\", (unsigned char)range_given);\n    TFE_OpSetAttrString(op.get(), \"round_mode\", (void*) round_mode.c_str(), round_mode.size());\n    TFE_OpSetAttrBool(op.get(), \"narrow_range\", (unsigned char)narrow_range);\n    TFE_OpSetAttrInt(op.get(), \"axis\", axis);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor quantize_and_dequantize_v3(const tensor& input, const tensor& input_min, const tensor& input_max, const tensor& num_bits, bool signed_input=true, bool range_given=true, bool narrow_range=false, int64_t axis=-1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"QuantizeAndDequantizeV3\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_min.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_max.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_bits.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"signed_input\", (unsigned char)signed_input);\n    TFE_OpSetAttrBool(op.get(), \"range_given\", (unsigned char)range_given);\n    TFE_OpSetAttrBool(op.get(), \"narrow_range\", (unsigned char)narrow_range);\n    TFE_OpSetAttrInt(op.get(), \"axis\", axis);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor quantized_mat_mul_with_bias_and_dequantize(const tensor& a, const tensor& b, const tensor& bias, const tensor& min_a, const tensor& max_a, const tensor& min_b, const tensor& max_b, const tensor& min_freezed_output, const tensor& max_freezed_output, datatype T1, datatype T2, datatype Tbias, datatype Toutput, bool transpose_a=false, bool transpose_b=false, const std::string& input_quant_mode=\"MIN_FIRST\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"QuantizedMatMulWithBiasAndDequantize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), a.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), b.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), bias.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), min_a.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), max_a.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), min_b.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), max_b.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), min_freezed_output.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), max_freezed_output.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"T1\", T1);\n    TFE_OpSetAttrType(op.get(), \"T2\", T2);\n    TFE_OpSetAttrType(op.get(), \"Tbias\", Tbias);\n    TFE_OpSetAttrType(op.get(), \"Toutput\", Toutput);\n    TFE_OpSetAttrBool(op.get(), \"transpose_a\", (unsigned char)transpose_a);\n    TFE_OpSetAttrBool(op.get(), \"transpose_b\", (unsigned char)transpose_b);\n    TFE_OpSetAttrString(op.get(), \"input_quant_mode\", (void*) input_quant_mode.c_str(), input_quant_mode.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor queue_dequeue(const tensor& handle, const std::vector<datatype>& component_types, int64_t timeout_ms=-1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"QueueDequeue\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"component_types\", reinterpret_cast<const enum TF_DataType *>(component_types.data()), component_types.size());\n    TFE_OpSetAttrInt(op.get(), \"timeout_ms\", timeout_ms);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor queue_dequeue_many(const tensor& handle, const tensor& n, const std::vector<datatype>& component_types, int64_t timeout_ms=-1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"QueueDequeueMany\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), n.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"component_types\", reinterpret_cast<const enum TF_DataType *>(component_types.data()), component_types.size());\n    TFE_OpSetAttrInt(op.get(), \"timeout_ms\", timeout_ms);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor queue_dequeue_many_v2(const tensor& handle, const tensor& n, const std::vector<datatype>& component_types, int64_t timeout_ms=-1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"QueueDequeueManyV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), n.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"component_types\", reinterpret_cast<const enum TF_DataType *>(component_types.data()), component_types.size());\n    TFE_OpSetAttrInt(op.get(), \"timeout_ms\", timeout_ms);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor queue_dequeue_up_to(const tensor& handle, const tensor& n, const std::vector<datatype>& component_types, int64_t timeout_ms=-1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"QueueDequeueUpTo\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), n.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"component_types\", reinterpret_cast<const enum TF_DataType *>(component_types.data()), component_types.size());\n    TFE_OpSetAttrInt(op.get(), \"timeout_ms\", timeout_ms);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor queue_dequeue_up_to_v2(const tensor& handle, const tensor& n, const std::vector<datatype>& component_types, int64_t timeout_ms=-1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"QueueDequeueUpToV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), n.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"component_types\", reinterpret_cast<const enum TF_DataType *>(component_types.data()), component_types.size());\n    TFE_OpSetAttrInt(op.get(), \"timeout_ms\", timeout_ms);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor queue_dequeue_v2(const tensor& handle, const std::vector<datatype>& component_types, int64_t timeout_ms=-1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"QueueDequeueV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"component_types\", reinterpret_cast<const enum TF_DataType *>(component_types.data()), component_types.size());\n    TFE_OpSetAttrInt(op.get(), \"timeout_ms\", timeout_ms);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor queue_is_closed(const tensor& handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"QueueIsClosed\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor queue_is_closed_v2(const tensor& handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"QueueIsClosedV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor queue_size(const tensor& handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"QueueSize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor queue_size_v2(const tensor& handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"QueueSizeV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor r_f_f_t(const tensor& input, const tensor& fft_length, datatype Treal=static_cast<datatype>(1), datatype Tcomplex=static_cast<datatype>(8)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RFFT\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), fft_length.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Treal\", Treal);\n    TFE_OpSetAttrType(op.get(), \"Tcomplex\", Tcomplex);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor r_f_f_t2_d(const tensor& input, const tensor& fft_length, datatype Treal=static_cast<datatype>(1), datatype Tcomplex=static_cast<datatype>(8)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RFFT2D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), fft_length.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Treal\", Treal);\n    TFE_OpSetAttrType(op.get(), \"Tcomplex\", Tcomplex);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor r_f_f_t3_d(const tensor& input, const tensor& fft_length, datatype Treal=static_cast<datatype>(1), datatype Tcomplex=static_cast<datatype>(8)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RFFT3D\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), fft_length.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Treal\", Treal);\n    TFE_OpSetAttrType(op.get(), \"Tcomplex\", Tcomplex);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor r_g_b_to_h_s_v(const tensor& images) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RGBToHSV\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), images.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor ragged_bincount(const tensor& splits, const tensor& values, const tensor& size, const tensor& weights, datatype Tidx, bool binary_output=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RaggedBincount\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), splits.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), weights.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n    TFE_OpSetAttrBool(op.get(), \"binary_output\", (unsigned char)binary_output);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor ragged_tensor_to_tensor(const tensor& shape, const tensor& values, const tensor& default_value, const std::vector<tensor>&row_partition_tensors, datatype Tindex, datatype Tshape, const std::vector< std::string>& row_partition_types) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RaggedTensorToTensor\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), default_value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> row_partition_tensors_handles; row_partition_tensors_handles.reserve(row_partition_tensors.size());\n    std::transform(row_partition_tensors.begin(), row_partition_tensors.end(), std::back_inserter(row_partition_tensors_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), row_partition_tensors_handles.data(), row_partition_tensors.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindex\", Tindex);\n    TFE_OpSetAttrType(op.get(), \"Tshape\", Tshape);\n    TFE_OpSetAttrInt(op.get(), \"num_row_partition_tensors\", row_partition_tensors.size());\n    \n    std::vector<std::size_t> row_partition_types_sizes; row_partition_types_sizes.reserve(row_partition_types.size());\n    std::transform(row_partition_types.begin(), row_partition_types.end(), std::back_inserter(row_partition_types_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"row_partition_types\", reinterpret_cast<const void *const *>(row_partition_types.data()), row_partition_types_sizes.data(), row_partition_types.size());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor ragged_tensor_to_variant(const std::vector<tensor>&rt_nested_splits, const tensor& rt_dense_values, datatype Tvalues, bool batched_input, datatype Tsplits=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RaggedTensorToVariant\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> rt_nested_splits_handles; rt_nested_splits_handles.reserve(rt_nested_splits.size());\n    std::transform(rt_nested_splits.begin(), rt_nested_splits.end(), std::back_inserter(rt_nested_splits_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), rt_nested_splits_handles.data(), rt_nested_splits.size(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rt_dense_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"RAGGED_RANK\", rt_nested_splits.size());\n    TFE_OpSetAttrType(op.get(), \"Tvalues\", Tvalues);\n    TFE_OpSetAttrBool(op.get(), \"batched_input\", (unsigned char)batched_input);\n    TFE_OpSetAttrType(op.get(), \"Tsplits\", Tsplits);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor random_crop(const tensor& image, const tensor& size, int64_t seed=0, int64_t seed2=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RandomCrop\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), image.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"seed\", seed);\n    TFE_OpSetAttrInt(op.get(), \"seed2\", seed2);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor random_dataset(const tensor& seed, const tensor& seed2, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RandomDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), seed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor random_gamma(const tensor& shape, const tensor& alpha, datatype S, int64_t seed=0, int64_t seed2=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RandomGamma\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), alpha.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"S\", S);\n    TFE_OpSetAttrInt(op.get(), \"seed\", seed);\n    TFE_OpSetAttrInt(op.get(), \"seed2\", seed2);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor random_gamma_grad(const tensor& alpha, const tensor& sample) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RandomGammaGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), alpha.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sample.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor random_poisson(const tensor& shape, const tensor& rate, datatype S, datatype dtype, int64_t seed=0, int64_t seed2=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RandomPoisson\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rate.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"S\", S);\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrInt(op.get(), \"seed\", seed);\n    TFE_OpSetAttrInt(op.get(), \"seed2\", seed2);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor random_poisson_v2(const tensor& shape, const tensor& rate, datatype S, int64_t seed=0, int64_t seed2=0, datatype R=static_cast<datatype>(2), datatype dtype=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RandomPoissonV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rate.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"S\", S);\n    TFE_OpSetAttrInt(op.get(), \"seed\", seed);\n    TFE_OpSetAttrInt(op.get(), \"seed2\", seed2);\n    TFE_OpSetAttrType(op.get(), \"R\", R);\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor random_shuffle(const tensor& value, int64_t seed=0, int64_t seed2=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RandomShuffle\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"seed\", seed);\n    TFE_OpSetAttrInt(op.get(), \"seed2\", seed2);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor random_shuffle_queue(const std::vector<datatype>& component_types, const std::vector< std::vector<int64_t>>& shapes, int64_t capacity=-1, int64_t min_after_dequeue=0, int64_t seed=0, int64_t seed2=0, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RandomShuffleQueue\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"component_types\", reinterpret_cast<const enum TF_DataType *>(component_types.data()), component_types.size());\n    \n    std::vector<const int64_t*> shapes_values; shapes_values.reserve(shapes.size());\n    std::vector<int> shapes_ndims; shapes_ndims.reserve(shapes.size());\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_values), [](const auto& v) { return v.data();});\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"shapes\", shapes_values.data(), shapes_ndims.data(), shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrInt(op.get(), \"min_after_dequeue\", min_after_dequeue);\n    TFE_OpSetAttrInt(op.get(), \"seed\", seed);\n    TFE_OpSetAttrInt(op.get(), \"seed2\", seed2);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor random_shuffle_queue_v2(const std::vector<datatype>& component_types, const std::vector< std::vector<int64_t>>& shapes, int64_t capacity=-1, int64_t min_after_dequeue=0, int64_t seed=0, int64_t seed2=0, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RandomShuffleQueueV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"component_types\", reinterpret_cast<const enum TF_DataType *>(component_types.data()), component_types.size());\n    \n    std::vector<const int64_t*> shapes_values; shapes_values.reserve(shapes.size());\n    std::vector<int> shapes_ndims; shapes_ndims.reserve(shapes.size());\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_values), [](const auto& v) { return v.data();});\n    std::transform(shapes.begin(), shapes.end(), std::back_inserter(shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"shapes\", shapes_values.data(), shapes_ndims.data(), shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrInt(op.get(), \"min_after_dequeue\", min_after_dequeue);\n    TFE_OpSetAttrInt(op.get(), \"seed\", seed);\n    TFE_OpSetAttrInt(op.get(), \"seed2\", seed2);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor random_standard_normal(const tensor& shape, datatype dtype, int64_t seed=0, int64_t seed2=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RandomStandardNormal\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrInt(op.get(), \"seed\", seed);\n    TFE_OpSetAttrInt(op.get(), \"seed2\", seed2);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor random_uniform(const tensor& shape, datatype dtype, int64_t seed=0, int64_t seed2=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RandomUniform\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrInt(op.get(), \"seed\", seed);\n    TFE_OpSetAttrInt(op.get(), \"seed2\", seed2);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor random_uniform_int(const tensor& shape, const tensor& minval, const tensor& maxval, datatype Tout, int64_t seed=0, int64_t seed2=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RandomUniformInt\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), minval.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), maxval.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tout\", Tout);\n    TFE_OpSetAttrInt(op.get(), \"seed\", seed);\n    TFE_OpSetAttrInt(op.get(), \"seed2\", seed2);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor range(const tensor& start, const tensor& limit, const tensor& delta, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Range\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), start.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), limit.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), delta.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor range_dataset(const tensor& start, const tensor& stop, const tensor& step, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RangeDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), start.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), stop.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), step.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor rank(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Rank\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor read_file(const tensor& filename) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ReadFile\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), filename.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor read_variable_op(const tensor& resource, datatype dtype) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ReadVariableOp\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), resource.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor reader_num_records_produced(const tensor& reader_handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ReaderNumRecordsProduced\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), reader_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor reader_num_records_produced_v2(const tensor& reader_handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ReaderNumRecordsProducedV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), reader_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor reader_num_work_units_completed(const tensor& reader_handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ReaderNumWorkUnitsCompleted\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), reader_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor reader_num_work_units_completed_v2(const tensor& reader_handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ReaderNumWorkUnitsCompletedV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), reader_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor reader_serialize_state(const tensor& reader_handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ReaderSerializeState\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), reader_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor reader_serialize_state_v2(const tensor& reader_handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ReaderSerializeStateV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), reader_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor real(const tensor& input, datatype Tout=static_cast<datatype>(1)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Real\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tout\", Tout);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor real_div(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RealDiv\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor rebatch_dataset(const tensor& input_dataset, const tensor& num_replicas, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, bool use_fallback=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RebatchDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_replicas.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrBool(op.get(), \"use_fallback\", (unsigned char)use_fallback);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor reciprocal(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Reciprocal\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor reciprocal_grad(const tensor& y, const tensor& dy) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ReciprocalGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dy.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor record_input(const std::string& file_pattern, int64_t file_random_seed=301, float file_shuffle_shift_ratio=0.0000e+00, int64_t file_buffer_size=10000, int64_t file_parallelism=16, int64_t batch_size=32, const std::string& compression_type=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RecordInput\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"file_pattern\", (void*) file_pattern.c_str(), file_pattern.size());\n    TFE_OpSetAttrInt(op.get(), \"file_random_seed\", file_random_seed);\n    TFE_OpSetAttrFloat(op.get(), \"file_shuffle_shift_ratio\", file_shuffle_shift_ratio);\n    TFE_OpSetAttrInt(op.get(), \"file_buffer_size\", file_buffer_size);\n    TFE_OpSetAttrInt(op.get(), \"file_parallelism\", file_parallelism);\n    TFE_OpSetAttrInt(op.get(), \"batch_size\", batch_size);\n    TFE_OpSetAttrString(op.get(), \"compression_type\", (void*) compression_type.c_str(), compression_type.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor recv(datatype tensor_type, const std::string& tensor_name, const std::string& send_device, int64_t send_device_incarnation, const std::string& recv_device, bool client_terminated=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Recv\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"tensor_type\", tensor_type);\n    TFE_OpSetAttrString(op.get(), \"tensor_name\", (void*) tensor_name.c_str(), tensor_name.size());\n    TFE_OpSetAttrString(op.get(), \"send_device\", (void*) send_device.c_str(), send_device.size());\n    TFE_OpSetAttrInt(op.get(), \"send_device_incarnation\", send_device_incarnation);\n    TFE_OpSetAttrString(op.get(), \"recv_device\", (void*) recv_device.c_str(), recv_device.size());\n    TFE_OpSetAttrBool(op.get(), \"client_terminated\", (unsigned char)client_terminated);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor recv_t_p_u_embedding_activations(int64_t num_outputs, const std::string& config) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RecvTPUEmbeddingActivations\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_outputs\", num_outputs);\n    TFE_OpSetAttrString(op.get(), \"config\", (void*) config.c_str(), config.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor reduce_join(const tensor& inputs, const tensor& reduction_indices, bool keep_dims=false, const std::string& separator=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ReduceJoin\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), inputs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), reduction_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"keep_dims\", (unsigned char)keep_dims);\n    TFE_OpSetAttrString(op.get(), \"separator\", (void*) separator.c_str(), separator.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor ref_enter(const tensor& data, const std::string& frame_name, bool is_constant=false, int64_t parallel_iterations=10) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RefEnter\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"frame_name\", (void*) frame_name.c_str(), frame_name.size());\n    TFE_OpSetAttrBool(op.get(), \"is_constant\", (unsigned char)is_constant);\n    TFE_OpSetAttrInt(op.get(), \"parallel_iterations\", parallel_iterations);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor ref_exit(const tensor& data) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RefExit\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor ref_identity(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RefIdentity\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor ref_next_iteration(const tensor& data) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RefNextIteration\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor ref_select(const tensor& index, const std::vector<tensor>&inputs) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RefSelect\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), index.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<TFE_TensorHandle*> inputs_handles; inputs_handles.reserve(inputs.size());\n    std::transform(inputs.begin(), inputs.end(), std::back_inserter(inputs_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), inputs_handles.data(), inputs.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"N\", inputs.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor regex_full_match(const tensor& input, const tensor& pattern) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RegexFullMatch\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), pattern.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor regex_replace(const tensor& input, const tensor& pattern, const tensor& rewrite, bool replace_global=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RegexReplace\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), pattern.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rewrite.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"replace_global\", (unsigned char)replace_global);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor register_dataset(const tensor& dataset, const tensor& address, const tensor& protocol, int64_t external_state_policy) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RegisterDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), address.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), protocol.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"external_state_policy\", external_state_policy);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor relu(const tensor& features) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Relu\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), features.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor relu6(const tensor& features) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Relu6\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), features.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor relu6_grad(const tensor& gradients, const tensor& features) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Relu6Grad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), gradients.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), features.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor relu_grad(const tensor& gradients, const tensor& features) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ReluGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), gradients.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), features.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor repeat_dataset(const tensor& input_dataset, const tensor& count, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RepeatDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), count.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor reshape(const tensor& input_tensor, const tensor& shape, datatype Tshape=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Reshape\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tshape\", Tshape);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor resize_area(const tensor& images, const tensor& size, bool align_corners=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ResizeArea\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), images.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"align_corners\", (unsigned char)align_corners);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor resize_bicubic(const tensor& images, const tensor& size, bool align_corners=false, bool half_pixel_centers=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ResizeBicubic\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), images.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"align_corners\", (unsigned char)align_corners);\n    TFE_OpSetAttrBool(op.get(), \"half_pixel_centers\", (unsigned char)half_pixel_centers);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor resize_bicubic_grad(const tensor& grads, const tensor& original_image, bool align_corners=false, bool half_pixel_centers=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ResizeBicubicGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), grads.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), original_image.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"align_corners\", (unsigned char)align_corners);\n    TFE_OpSetAttrBool(op.get(), \"half_pixel_centers\", (unsigned char)half_pixel_centers);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor resize_bilinear(const tensor& images, const tensor& size, bool align_corners=false, bool half_pixel_centers=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ResizeBilinear\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), images.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"align_corners\", (unsigned char)align_corners);\n    TFE_OpSetAttrBool(op.get(), \"half_pixel_centers\", (unsigned char)half_pixel_centers);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor resize_bilinear_grad(const tensor& grads, const tensor& original_image, bool align_corners=false, bool half_pixel_centers=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ResizeBilinearGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), grads.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), original_image.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"align_corners\", (unsigned char)align_corners);\n    TFE_OpSetAttrBool(op.get(), \"half_pixel_centers\", (unsigned char)half_pixel_centers);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor resize_nearest_neighbor(const tensor& images, const tensor& size, bool align_corners=false, bool half_pixel_centers=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ResizeNearestNeighbor\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), images.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"align_corners\", (unsigned char)align_corners);\n    TFE_OpSetAttrBool(op.get(), \"half_pixel_centers\", (unsigned char)half_pixel_centers);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor resize_nearest_neighbor_grad(const tensor& grads, const tensor& size, bool align_corners=false, bool half_pixel_centers=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ResizeNearestNeighborGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), grads.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"align_corners\", (unsigned char)align_corners);\n    TFE_OpSetAttrBool(op.get(), \"half_pixel_centers\", (unsigned char)half_pixel_centers);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor resource_accumulator_num_accumulated(const tensor& handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ResourceAccumulatorNumAccumulated\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor resource_accumulator_take_gradient(const tensor& handle, const tensor& num_required, datatype dtype) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ResourceAccumulatorTakeGradient\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_required.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor resource_conditional_accumulator(datatype dtype, const std::vector<int64_t>& shape, const std::string& container=\"\", const std::string& shared_name=\"\", const std::string& reduction_type=\"MEAN\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ResourceConditionalAccumulator\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrString(op.get(), \"reduction_type\", (void*) reduction_type.c_str(), reduction_type.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor resource_count_up_to(const tensor& resource, int64_t limit) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ResourceCountUpTo\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), resource.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"limit\", limit);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor resource_gather(const tensor& resource, const tensor& indices, datatype dtype, datatype Tindices, int64_t batch_dims=0, bool validate_indices=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ResourceGather\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), resource.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrInt(op.get(), \"batch_dims\", batch_dims);\n    TFE_OpSetAttrBool(op.get(), \"validate_indices\", (unsigned char)validate_indices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor resource_gather_nd(const tensor& resource, const tensor& indices, datatype dtype, datatype Tindices) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ResourceGatherNd\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), resource.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor restore(const tensor& file_pattern, const tensor& input_tensor_name, datatype dt, int64_t preferred_shard=-1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Restore\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), file_pattern.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_tensor_name.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dt\", dt);\n    TFE_OpSetAttrInt(op.get(), \"preferred_shard\", preferred_shard);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor restore_slice(const tensor& file_pattern, const tensor& input_tensor_name, const tensor& shape_and_slice, datatype dt, int64_t preferred_shard=-1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RestoreSlice\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), file_pattern.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_tensor_name.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), shape_and_slice.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dt\", dt);\n    TFE_OpSetAttrInt(op.get(), \"preferred_shard\", preferred_shard);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor restore_v2(const tensor& prefix, const tensor& input_tensor_names, const tensor& shape_and_slices, const std::vector<datatype>& dtypes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RestoreV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), prefix.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_tensor_names.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), shape_and_slices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"dtypes\", reinterpret_cast<const enum TF_DataType *>(dtypes.data()), dtypes.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor retrieve_t_p_u_embedding_stochastic_gradient_descent_parameters(int64_t num_shards, int64_t shard_id, int64_t table_id=-1, const std::string& table_name=\"\", const std::string& config=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RetrieveTPUEmbeddingStochasticGradientDescentParameters\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_shards\", num_shards);\n    TFE_OpSetAttrInt(op.get(), \"shard_id\", shard_id);\n    TFE_OpSetAttrInt(op.get(), \"table_id\", table_id);\n    TFE_OpSetAttrString(op.get(), \"table_name\", (void*) table_name.c_str(), table_name.size());\n    TFE_OpSetAttrString(op.get(), \"config\", (void*) config.c_str(), config.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor reverse(const tensor& input_tensor, const tensor& dims) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Reverse\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dims.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor reverse_sequence(const tensor& input, const tensor& seq_lengths, int64_t seq_dim, int64_t batch_dim=0, datatype Tlen=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ReverseSequence\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seq_lengths.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"seq_dim\", seq_dim);\n    TFE_OpSetAttrInt(op.get(), \"batch_dim\", batch_dim);\n    TFE_OpSetAttrType(op.get(), \"Tlen\", Tlen);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor reverse_v2(const tensor& input_tensor, const tensor& axis, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ReverseV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), axis.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor right_shift(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RightShift\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor rint(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Rint\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor roll(const tensor& input, const tensor& shift, const tensor& axis, datatype Tshift, datatype Taxis) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Roll\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), shift.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), axis.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tshift\", Tshift);\n    TFE_OpSetAttrType(op.get(), \"Taxis\", Taxis);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor round(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Round\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor rsqrt(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Rsqrt\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor rsqrt_grad(const tensor& y, const tensor& dy) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"RsqrtGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dy.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sampling_dataset(const tensor& input_dataset, const tensor& rate, const tensor& seed, const tensor& seed2, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SamplingDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rate.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor scalar_summary(const tensor& tags, const tensor& values) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ScalarSummary\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), tags.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor scale_and_translate(const tensor& images, const tensor& size, const tensor& scale, const tensor& translation, const std::string& kernel_type=\"lanczos3\", bool antialias=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ScaleAndTranslate\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), images.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), scale.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), translation.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"kernel_type\", (void*) kernel_type.c_str(), kernel_type.size());\n    TFE_OpSetAttrBool(op.get(), \"antialias\", (unsigned char)antialias);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor scale_and_translate_grad(const tensor& grads, const tensor& original_image, const tensor& scale, const tensor& translation, const std::string& kernel_type=\"lanczos3\", bool antialias=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ScaleAndTranslateGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), grads.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), original_image.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), scale.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), translation.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"kernel_type\", (void*) kernel_type.c_str(), kernel_type.size());\n    TFE_OpSetAttrBool(op.get(), \"antialias\", (unsigned char)antialias);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor scatter_add(const tensor& ref, const tensor& indices, const tensor& updates, datatype Tindices, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ScatterAdd\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor scatter_div(const tensor& ref, const tensor& indices, const tensor& updates, datatype Tindices, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ScatterDiv\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor scatter_max(const tensor& ref, const tensor& indices, const tensor& updates, datatype Tindices, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ScatterMax\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor scatter_min(const tensor& ref, const tensor& indices, const tensor& updates, datatype Tindices, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ScatterMin\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor scatter_mul(const tensor& ref, const tensor& indices, const tensor& updates, datatype Tindices, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ScatterMul\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor scatter_nd(const tensor& indices, const tensor& updates, const tensor& shape, datatype Tindices) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ScatterNd\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor scatter_nd_add(const tensor& ref, const tensor& indices, const tensor& updates, datatype Tindices, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ScatterNdAdd\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor scatter_nd_max(const tensor& ref, const tensor& indices, const tensor& updates, datatype Tindices, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ScatterNdMax\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor scatter_nd_min(const tensor& ref, const tensor& indices, const tensor& updates, datatype Tindices, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ScatterNdMin\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor scatter_nd_non_aliasing_add(const tensor& input, const tensor& indices, const tensor& updates, datatype Tindices) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ScatterNdNonAliasingAdd\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor scatter_nd_sub(const tensor& ref, const tensor& indices, const tensor& updates, datatype Tindices, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ScatterNdSub\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor scatter_nd_update(const tensor& ref, const tensor& indices, const tensor& updates, datatype Tindices, bool use_locking=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ScatterNdUpdate\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor scatter_sub(const tensor& ref, const tensor& indices, const tensor& updates, datatype Tindices, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ScatterSub\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor scatter_update(const tensor& ref, const tensor& indices, const tensor& updates, datatype Tindices, bool use_locking=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ScatterUpdate\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sdca_fprint(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SdcaFprint\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor segment_max(const tensor& data, const tensor& segment_ids, datatype Tindices) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SegmentMax\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor segment_mean(const tensor& data, const tensor& segment_ids, datatype Tindices) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SegmentMean\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor segment_min(const tensor& data, const tensor& segment_ids, datatype Tindices) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SegmentMin\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor segment_prod(const tensor& data, const tensor& segment_ids, datatype Tindices) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SegmentProd\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor segment_sum(const tensor& data, const tensor& segment_ids, datatype Tindices) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SegmentSum\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor select(const tensor& condition, const tensor& t, const tensor& e) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Select\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), condition.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), t.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), e.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor select_v2(const tensor& condition, const tensor& t, const tensor& e) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SelectV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), condition.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), t.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), e.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor self_adjoint_eig(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SelfAdjointEig\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor selu(const tensor& features) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Selu\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), features.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor selu_grad(const tensor& gradients, const tensor& outputs) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SeluGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), gradients.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), outputs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor serialize_iterator(const tensor& resource_handle, int64_t external_state_policy=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SerializeIterator\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), resource_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"external_state_policy\", external_state_policy);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor serialize_many_sparse(const tensor& sparse_indices, const tensor& sparse_values, const tensor& sparse_shape, datatype out_type=static_cast<datatype>(7)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SerializeManySparse\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), sparse_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sparse_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sparse_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"out_type\", out_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor serialize_sparse(const tensor& sparse_indices, const tensor& sparse_values, const tensor& sparse_shape, datatype out_type=static_cast<datatype>(7)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SerializeSparse\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), sparse_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sparse_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sparse_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"out_type\", out_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor serialize_tensor(const tensor& input_tensor) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SerializeTensor\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor set_size(const tensor& set_indices, const tensor& set_values, const tensor& set_shape, bool validate_indices=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SetSize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), set_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), set_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), set_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"validate_indices\", (unsigned char)validate_indices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor set_stats_aggregator_dataset(const tensor& input_dataset, const tensor& stats_aggregator, const tensor& tag, const tensor& counter_prefix, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SetStatsAggregatorDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), stats_aggregator.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), tag.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), counter_prefix.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor shape(const tensor& input, datatype out_type=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Shape\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"out_type\", out_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor shape_n(const std::vector<tensor>&input, datatype out_type=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ShapeN\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> input_handles; input_handles.reserve(input.size());\n    std::transform(input.begin(), input.end(), std::back_inserter(input_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), input_handles.data(), input.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"N\", input.size());\n    TFE_OpSetAttrType(op.get(), \"out_type\", out_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor shard_dataset(const tensor& input_dataset, const tensor& num_shards, const tensor& index, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, bool require_non_empty=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ShardDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_shards.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), index.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrBool(op.get(), \"require_non_empty\", (unsigned char)require_non_empty);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sharded_filename(const tensor& basename, const tensor& shard, const tensor& num_shards) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ShardedFilename\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), basename.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), shard.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_shards.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sharded_filespec(const tensor& basename, const tensor& num_shards) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ShardedFilespec\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), basename.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_shards.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor shuffle_and_repeat_dataset(const tensor& input_dataset, const tensor& buffer_size, const tensor& seed, const tensor& seed2, const tensor& count, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, bool reshuffle_each_iteration=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ShuffleAndRepeatDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), buffer_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), count.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrBool(op.get(), \"reshuffle_each_iteration\", (unsigned char)reshuffle_each_iteration);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor shuffle_and_repeat_dataset_v2(const tensor& input_dataset, const tensor& buffer_size, const tensor& seed, const tensor& seed2, const tensor& count, const tensor& seed_generator, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, bool reshuffle_each_iteration=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ShuffleAndRepeatDatasetV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), buffer_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), count.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed_generator.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrBool(op.get(), \"reshuffle_each_iteration\", (unsigned char)reshuffle_each_iteration);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor shuffle_dataset(const tensor& input_dataset, const tensor& buffer_size, const tensor& seed, const tensor& seed2, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, bool reshuffle_each_iteration=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ShuffleDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), buffer_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrBool(op.get(), \"reshuffle_each_iteration\", (unsigned char)reshuffle_each_iteration);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor shuffle_dataset_v2(const tensor& input_dataset, const tensor& buffer_size, const tensor& seed_generator, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ShuffleDatasetV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), buffer_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed_generator.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor shuffle_dataset_v3(const tensor& input_dataset, const tensor& buffer_size, const tensor& seed, const tensor& seed2, const tensor& seed_generator, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, bool reshuffle_each_iteration=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ShuffleDatasetV3\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), buffer_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed_generator.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrBool(op.get(), \"reshuffle_each_iteration\", (unsigned char)reshuffle_each_iteration);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sigmoid(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Sigmoid\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sigmoid_grad(const tensor& y, const tensor& dy) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SigmoidGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dy.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sign(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Sign\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sin(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Sin\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sinh(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Sinh\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor size(const tensor& input, datatype out_type=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Size\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"out_type\", out_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor skip_dataset(const tensor& input_dataset, const tensor& count, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SkipDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), count.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sleep_dataset(const tensor& input_dataset, const tensor& sleep_microseconds, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SleepDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sleep_microseconds.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor slice(const tensor& input, const tensor& begin, const tensor& size, datatype Index) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Slice\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), begin.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Index\", Index);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sliding_window_dataset(const tensor& input_dataset, const tensor& window_size, const tensor& window_shift, const tensor& window_stride, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SlidingWindowDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), window_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), window_shift.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), window_stride.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor snapshot(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Snapshot\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor snapshot_dataset(const tensor& input_dataset, const tensor& path, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes, const std::string& compression=\"\", const std::string& reader_path_prefix=\"\", const std::string& writer_path_prefix=\"\", int64_t shard_size_bytes=10737418240, int64_t pending_snapshot_expiry_seconds=86400, int64_t num_reader_threads=1, int64_t reader_buffer_size=1, int64_t num_writer_threads=1, int64_t writer_buffer_size=1, bool shuffle_on_read=false, int64_t seed=0, int64_t seed2=0, const std::string& mode=\"auto\", const std::string& snapshot_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SnapshotDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), path.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrString(op.get(), \"compression\", (void*) compression.c_str(), compression.size());\n    TFE_OpSetAttrString(op.get(), \"reader_path_prefix\", (void*) reader_path_prefix.c_str(), reader_path_prefix.size());\n    TFE_OpSetAttrString(op.get(), \"writer_path_prefix\", (void*) writer_path_prefix.c_str(), writer_path_prefix.size());\n    TFE_OpSetAttrInt(op.get(), \"shard_size_bytes\", shard_size_bytes);\n    TFE_OpSetAttrInt(op.get(), \"pending_snapshot_expiry_seconds\", pending_snapshot_expiry_seconds);\n    TFE_OpSetAttrInt(op.get(), \"num_reader_threads\", num_reader_threads);\n    TFE_OpSetAttrInt(op.get(), \"reader_buffer_size\", reader_buffer_size);\n    TFE_OpSetAttrInt(op.get(), \"num_writer_threads\", num_writer_threads);\n    TFE_OpSetAttrInt(op.get(), \"writer_buffer_size\", writer_buffer_size);\n    TFE_OpSetAttrBool(op.get(), \"shuffle_on_read\", (unsigned char)shuffle_on_read);\n    TFE_OpSetAttrInt(op.get(), \"seed\", seed);\n    TFE_OpSetAttrInt(op.get(), \"seed2\", seed2);\n    TFE_OpSetAttrString(op.get(), \"mode\", (void*) mode.c_str(), mode.size());\n    TFE_OpSetAttrString(op.get(), \"snapshot_name\", (void*) snapshot_name.c_str(), snapshot_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sobol_sample(const tensor& dim, const tensor& num_results, const tensor& skip, datatype dtype=static_cast<datatype>(1)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SobolSample\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), dim.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_results.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), skip.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor softmax(const tensor& logits) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Softmax\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), logits.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor softplus(const tensor& features) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Softplus\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), features.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor softplus_grad(const tensor& gradients, const tensor& features) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SoftplusGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), gradients.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), features.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor softsign(const tensor& features) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Softsign\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), features.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor softsign_grad(const tensor& gradients, const tensor& features) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SoftsignGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), gradients.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), features.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor space_to_batch(const tensor& input, const tensor& paddings, int64_t block_size, datatype Tpaddings=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SpaceToBatch\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), paddings.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"block_size\", block_size);\n    TFE_OpSetAttrType(op.get(), \"Tpaddings\", Tpaddings);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor space_to_batch_n_d(const tensor& input, const tensor& block_shape, const tensor& paddings, datatype Tblock_shape=static_cast<datatype>(3), datatype Tpaddings=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SpaceToBatchND\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), block_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), paddings.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tblock_shape\", Tblock_shape);\n    TFE_OpSetAttrType(op.get(), \"Tpaddings\", Tpaddings);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor space_to_depth(const tensor& input, int64_t block_size, const std::string& data_format=\"NHWC\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SpaceToDepth\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"block_size\", block_size);\n    TFE_OpSetAttrString(op.get(), \"data_format\", (void*) data_format.c_str(), data_format.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_apply_adadelta(const tensor& var, const tensor& accum, const tensor& accum_update, const tensor& lr, const tensor& rho, const tensor& epsilon, const tensor& grad, const tensor& indices, datatype Tindices, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseApplyAdadelta\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), accum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), accum_update.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rho.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), epsilon.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_apply_adagrad(const tensor& var, const tensor& accum, const tensor& lr, const tensor& grad, const tensor& indices, datatype Tindices, bool use_locking=false, bool update_slots=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseApplyAdagrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), accum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n    TFE_OpSetAttrBool(op.get(), \"update_slots\", (unsigned char)update_slots);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_apply_adagrad_d_a(const tensor& var, const tensor& gradient_accumulator, const tensor& gradient_squared_accumulator, const tensor& grad, const tensor& indices, const tensor& lr, const tensor& l1, const tensor& l2, const tensor& global_step, datatype Tindices, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseApplyAdagradDA\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), gradient_accumulator.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), gradient_squared_accumulator.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l1.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), global_step.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_apply_adagrad_v2(const tensor& var, const tensor& accum, const tensor& lr, const tensor& epsilon, const tensor& grad, const tensor& indices, datatype Tindices, bool use_locking=false, bool update_slots=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseApplyAdagradV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), accum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), epsilon.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n    TFE_OpSetAttrBool(op.get(), \"update_slots\", (unsigned char)update_slots);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_apply_centered_r_m_s_prop(const tensor& var, const tensor& mg, const tensor& ms, const tensor& mom, const tensor& lr, const tensor& rho, const tensor& momentum, const tensor& epsilon, const tensor& grad, const tensor& indices, datatype Tindices, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseApplyCenteredRMSProp\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), mg.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), ms.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), mom.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rho.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), momentum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), epsilon.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_apply_ftrl(const tensor& var, const tensor& accum, const tensor& linear, const tensor& grad, const tensor& indices, const tensor& lr, const tensor& l1, const tensor& l2, const tensor& lr_power, datatype Tindices, bool use_locking=false, bool multiply_linear_by_lr=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseApplyFtrl\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), accum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), linear.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l1.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr_power.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n    TFE_OpSetAttrBool(op.get(), \"multiply_linear_by_lr\", (unsigned char)multiply_linear_by_lr);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_apply_ftrl_v2(const tensor& var, const tensor& accum, const tensor& linear, const tensor& grad, const tensor& indices, const tensor& lr, const tensor& l1, const tensor& l2, const tensor& l2_shrinkage, const tensor& lr_power, datatype Tindices, bool use_locking=false, bool multiply_linear_by_lr=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseApplyFtrlV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), accum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), linear.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l1.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l2_shrinkage.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr_power.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n    TFE_OpSetAttrBool(op.get(), \"multiply_linear_by_lr\", (unsigned char)multiply_linear_by_lr);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_apply_momentum(const tensor& var, const tensor& accum, const tensor& lr, const tensor& grad, const tensor& indices, const tensor& momentum, datatype Tindices, bool use_locking=false, bool use_nesterov=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseApplyMomentum\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), accum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), momentum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n    TFE_OpSetAttrBool(op.get(), \"use_nesterov\", (unsigned char)use_nesterov);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_apply_proximal_adagrad(const tensor& var, const tensor& accum, const tensor& lr, const tensor& l1, const tensor& l2, const tensor& grad, const tensor& indices, datatype Tindices, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseApplyProximalAdagrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), accum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l1.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_apply_proximal_gradient_descent(const tensor& var, const tensor& alpha, const tensor& l1, const tensor& l2, const tensor& grad, const tensor& indices, datatype Tindices, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseApplyProximalGradientDescent\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), alpha.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l1.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), l2.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_apply_r_m_s_prop(const tensor& var, const tensor& ms, const tensor& mom, const tensor& lr, const tensor& rho, const tensor& momentum, const tensor& epsilon, const tensor& grad, const tensor& indices, datatype Tindices, bool use_locking=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseApplyRMSProp\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), var.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), ms.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), mom.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lr.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rho.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), momentum.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), epsilon.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"use_locking\", (unsigned char)use_locking);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_bincount(const tensor& indices, const tensor& values, const tensor& dense_shape, const tensor& size, const tensor& weights, datatype Tidx, bool binary_output=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseBincount\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dense_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), weights.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n    TFE_OpSetAttrBool(op.get(), \"binary_output\", (unsigned char)binary_output);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_conditional_accumulator(datatype dtype, const std::vector<int64_t>& shape, const std::string& container=\"\", const std::string& shared_name=\"\", const std::string& reduction_type=\"MEAN\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseConditionalAccumulator\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrString(op.get(), \"reduction_type\", (void*) reduction_type.c_str(), reduction_type.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_dense_cwise_add(const tensor& sp_indices, const tensor& sp_values, const tensor& sp_shape, const tensor& dense) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseDenseCwiseAdd\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), sp_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sp_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sp_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dense.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_dense_cwise_div(const tensor& sp_indices, const tensor& sp_values, const tensor& sp_shape, const tensor& dense) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseDenseCwiseDiv\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), sp_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sp_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sp_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dense.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_dense_cwise_mul(const tensor& sp_indices, const tensor& sp_values, const tensor& sp_shape, const tensor& dense) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseDenseCwiseMul\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), sp_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sp_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sp_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dense.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_mat_mul(const tensor& a, const tensor& b, bool transpose_a=false, bool transpose_b=false, bool a_is_sparse=false, bool b_is_sparse=false, datatype Ta=static_cast<datatype>(1), datatype Tb=static_cast<datatype>(1)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseMatMul\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), a.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), b.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"transpose_a\", (unsigned char)transpose_a);\n    TFE_OpSetAttrBool(op.get(), \"transpose_b\", (unsigned char)transpose_b);\n    TFE_OpSetAttrBool(op.get(), \"a_is_sparse\", (unsigned char)a_is_sparse);\n    TFE_OpSetAttrBool(op.get(), \"b_is_sparse\", (unsigned char)b_is_sparse);\n    TFE_OpSetAttrType(op.get(), \"Ta\", Ta);\n    TFE_OpSetAttrType(op.get(), \"Tb\", Tb);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_matrix_add(const tensor& a, const tensor& b, const tensor& alpha, const tensor& beta) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseMatrixAdd\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), a.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), b.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), alpha.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), beta.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_matrix_mat_mul(const tensor& a, const tensor& b, bool transpose_a=false, bool transpose_b=false, bool adjoint_a=false, bool adjoint_b=false, bool transpose_output=false, bool conjugate_output=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseMatrixMatMul\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), a.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), b.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"transpose_a\", (unsigned char)transpose_a);\n    TFE_OpSetAttrBool(op.get(), \"transpose_b\", (unsigned char)transpose_b);\n    TFE_OpSetAttrBool(op.get(), \"adjoint_a\", (unsigned char)adjoint_a);\n    TFE_OpSetAttrBool(op.get(), \"adjoint_b\", (unsigned char)adjoint_b);\n    TFE_OpSetAttrBool(op.get(), \"transpose_output\", (unsigned char)transpose_output);\n    TFE_OpSetAttrBool(op.get(), \"conjugate_output\", (unsigned char)conjugate_output);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_matrix_mul(const tensor& a, const tensor& b) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseMatrixMul\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), a.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), b.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_matrix_n_n_z(const tensor& sparse_matrix) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseMatrixNNZ\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), sparse_matrix.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_matrix_ordering_a_m_d(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseMatrixOrderingAMD\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_matrix_softmax(const tensor& logits, datatype type) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseMatrixSoftmax\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), logits.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"type\", type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_matrix_softmax_grad(const tensor& softmax, const tensor& grad_softmax, datatype type) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseMatrixSoftmaxGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), softmax.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad_softmax.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"type\", type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_matrix_sparse_cholesky(const tensor& input, const tensor& permutation, datatype type) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseMatrixSparseCholesky\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), permutation.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"type\", type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_matrix_sparse_mat_mul(const tensor& a, const tensor& b, datatype type, bool transpose_a=false, bool transpose_b=false, bool adjoint_a=false, bool adjoint_b=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseMatrixSparseMatMul\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), a.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), b.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"type\", type);\n    TFE_OpSetAttrBool(op.get(), \"transpose_a\", (unsigned char)transpose_a);\n    TFE_OpSetAttrBool(op.get(), \"transpose_b\", (unsigned char)transpose_b);\n    TFE_OpSetAttrBool(op.get(), \"adjoint_a\", (unsigned char)adjoint_a);\n    TFE_OpSetAttrBool(op.get(), \"adjoint_b\", (unsigned char)adjoint_b);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_matrix_transpose(const tensor& input, datatype type, bool conjugate=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseMatrixTranspose\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"type\", type);\n    TFE_OpSetAttrBool(op.get(), \"conjugate\", (unsigned char)conjugate);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_matrix_zeros(const tensor& dense_shape, datatype type) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseMatrixZeros\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), dense_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"type\", type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_reduce_max(const tensor& input_indices, const tensor& input_values, const tensor& input_shape, const tensor& reduction_axes, bool keep_dims=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseReduceMax\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), reduction_axes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"keep_dims\", (unsigned char)keep_dims);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_reduce_sum(const tensor& input_indices, const tensor& input_values, const tensor& input_shape, const tensor& reduction_axes, bool keep_dims=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseReduceSum\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), reduction_axes.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"keep_dims\", (unsigned char)keep_dims);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_segment_mean(const tensor& data, const tensor& indices, const tensor& segment_ids, datatype Tidx=static_cast<datatype>(3), datatype Tsegmentids=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseSegmentMean\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n    TFE_OpSetAttrType(op.get(), \"Tsegmentids\", Tsegmentids);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_segment_mean_grad(const tensor& grad, const tensor& indices, const tensor& segment_ids, const tensor& output_dim0, datatype Tidx=static_cast<datatype>(3), datatype Tsegmentids=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseSegmentMeanGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), output_dim0.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n    TFE_OpSetAttrType(op.get(), \"Tsegmentids\", Tsegmentids);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_segment_mean_with_num_segments(const tensor& data, const tensor& indices, const tensor& segment_ids, const tensor& num_segments, datatype Tidx=static_cast<datatype>(3), datatype Tnumsegments=static_cast<datatype>(3), datatype Tsegmentids=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseSegmentMeanWithNumSegments\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_segments.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n    TFE_OpSetAttrType(op.get(), \"Tnumsegments\", Tnumsegments);\n    TFE_OpSetAttrType(op.get(), \"Tsegmentids\", Tsegmentids);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_segment_sqrt_n(const tensor& data, const tensor& indices, const tensor& segment_ids, datatype Tidx=static_cast<datatype>(3), datatype Tsegmentids=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseSegmentSqrtN\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n    TFE_OpSetAttrType(op.get(), \"Tsegmentids\", Tsegmentids);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_segment_sqrt_n_grad(const tensor& grad, const tensor& indices, const tensor& segment_ids, const tensor& output_dim0, datatype Tidx=static_cast<datatype>(3), datatype Tsegmentids=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseSegmentSqrtNGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), output_dim0.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n    TFE_OpSetAttrType(op.get(), \"Tsegmentids\", Tsegmentids);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_segment_sqrt_n_with_num_segments(const tensor& data, const tensor& indices, const tensor& segment_ids, const tensor& num_segments, datatype Tidx=static_cast<datatype>(3), datatype Tnumsegments=static_cast<datatype>(3), datatype Tsegmentids=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseSegmentSqrtNWithNumSegments\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_segments.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n    TFE_OpSetAttrType(op.get(), \"Tnumsegments\", Tnumsegments);\n    TFE_OpSetAttrType(op.get(), \"Tsegmentids\", Tsegmentids);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_segment_sum(const tensor& data, const tensor& indices, const tensor& segment_ids, datatype Tidx=static_cast<datatype>(3), datatype Tsegmentids=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseSegmentSum\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n    TFE_OpSetAttrType(op.get(), \"Tsegmentids\", Tsegmentids);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_segment_sum_with_num_segments(const tensor& data, const tensor& indices, const tensor& segment_ids, const tensor& num_segments, datatype Tidx=static_cast<datatype>(3), datatype Tnumsegments=static_cast<datatype>(3), datatype Tsegmentids=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseSegmentSumWithNumSegments\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_segments.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n    TFE_OpSetAttrType(op.get(), \"Tnumsegments\", Tnumsegments);\n    TFE_OpSetAttrType(op.get(), \"Tsegmentids\", Tsegmentids);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_slice_grad(const tensor& backprop_val_grad, const tensor& input_indices, const tensor& input_start, const tensor& output_indices) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseSliceGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), backprop_val_grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_start.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), output_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_softmax(const tensor& sp_indices, const tensor& sp_values, const tensor& sp_shape) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseSoftmax\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), sp_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sp_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sp_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_tensor_dense_add(const tensor& a_indices, const tensor& a_values, const tensor& a_shape, const tensor& b, datatype Tindices) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseTensorDenseAdd\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), a_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), a_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), a_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), b.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_tensor_dense_mat_mul(const tensor& a_indices, const tensor& a_values, const tensor& a_shape, const tensor& b, datatype Tindices=static_cast<datatype>(9), bool adjoint_a=false, bool adjoint_b=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseTensorDenseMatMul\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), a_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), a_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), a_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), b.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"adjoint_a\", (unsigned char)adjoint_a);\n    TFE_OpSetAttrBool(op.get(), \"adjoint_b\", (unsigned char)adjoint_b);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_tensor_slice_dataset(const tensor& indices, const tensor& values, const tensor& dense_shape, datatype Tvalues) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseTensorSliceDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dense_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tvalues\", Tvalues);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_tensor_to_c_s_r_sparse_matrix(const tensor& indices, const tensor& values, const tensor& dense_shape) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseTensorToCSRSparseMatrix\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dense_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sparse_to_dense(const tensor& sparse_indices, const tensor& output_shape, const tensor& sparse_values, const tensor& default_value, datatype Tindices, bool validate_indices=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SparseToDense\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), sparse_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), output_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sparse_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), default_value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrBool(op.get(), \"validate_indices\", (unsigned char)validate_indices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor spence(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Spence\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor split(const tensor& split_dim, const tensor& value, int64_t num_split) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Split\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), split_dim.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_split\", num_split);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor split_v(const tensor& value, const tensor& size_splits, const tensor& split_dim, int64_t num_split, datatype Tlen=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SplitV\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size_splits.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), split_dim.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_split\", num_split);\n    TFE_OpSetAttrType(op.get(), \"Tlen\", Tlen);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sql_dataset(const tensor& driver_name, const tensor& data_source_name, const tensor& query, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SqlDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), driver_name.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), data_source_name.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), query.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sqrt(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Sqrt\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sqrt_grad(const tensor& y, const tensor& dy) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SqrtGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dy.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor square(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Square\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor squared_difference(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SquaredDifference\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor squeeze(const tensor& input, const std::vector<int64_t>& squeeze_dims) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Squeeze\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrIntList(op.get(), \"squeeze_dims\", squeeze_dims.data(), squeeze_dims.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stack(datatype elem_type, const std::string& stack_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Stack\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"elem_type\", elem_type);\n    TFE_OpSetAttrString(op.get(), \"stack_name\", (void*) stack_name.c_str(), stack_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stack_pop(const tensor& handle, datatype elem_type) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StackPop\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"elem_type\", elem_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stack_pop_v2(const tensor& handle, datatype elem_type) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StackPopV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"elem_type\", elem_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stack_push(const tensor& handle, const tensor& elem, bool swap_memory=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StackPush\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), elem.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"swap_memory\", (unsigned char)swap_memory);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stack_push_v2(const tensor& handle, const tensor& elem, bool swap_memory=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StackPushV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), elem.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"swap_memory\", (unsigned char)swap_memory);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stack_v2(const tensor& max_size, datatype elem_type, const std::string& stack_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StackV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), max_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"elem_type\", elem_type);\n    TFE_OpSetAttrString(op.get(), \"stack_name\", (void*) stack_name.c_str(), stack_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stage_peek(const tensor& index, const std::vector<datatype>& dtypes, int64_t capacity=0, int64_t memory_limit=0, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StagePeek\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), index.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"dtypes\", reinterpret_cast<const enum TF_DataType *>(dtypes.data()), dtypes.size());\n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrInt(op.get(), \"memory_limit\", memory_limit);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stage_size(const std::vector<datatype>& dtypes, int64_t capacity=0, int64_t memory_limit=0, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StageSize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"dtypes\", reinterpret_cast<const enum TF_DataType *>(dtypes.data()), dtypes.size());\n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrInt(op.get(), \"memory_limit\", memory_limit);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stateful_random_binomial(const tensor& resource, const tensor& algorithm, const tensor& shape, const tensor& counts, const tensor& probs, datatype S, datatype dtype=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatefulRandomBinomial\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), resource.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), algorithm.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), counts.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), probs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"S\", S);\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stateful_standard_normal(const tensor& resource, const tensor& shape, datatype dtype=static_cast<datatype>(1), datatype shape_dtype=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatefulStandardNormal\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), resource.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrType(op.get(), \"shape_dtype\", shape_dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stateful_standard_normal_v2(const tensor& resource, const tensor& algorithm, const tensor& shape, datatype dtype=static_cast<datatype>(1), datatype shape_dtype=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatefulStandardNormalV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), resource.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), algorithm.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrType(op.get(), \"shape_dtype\", shape_dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stateful_truncated_normal(const tensor& resource, const tensor& algorithm, const tensor& shape, datatype dtype=static_cast<datatype>(1), datatype shape_dtype=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatefulTruncatedNormal\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), resource.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), algorithm.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrType(op.get(), \"shape_dtype\", shape_dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stateful_uniform(const tensor& resource, const tensor& algorithm, const tensor& shape, datatype dtype=static_cast<datatype>(1), datatype shape_dtype=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatefulUniform\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), resource.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), algorithm.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrType(op.get(), \"shape_dtype\", shape_dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stateful_uniform_full_int(const tensor& resource, const tensor& algorithm, const tensor& shape, datatype dtype=static_cast<datatype>(23), datatype shape_dtype=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatefulUniformFullInt\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), resource.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), algorithm.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrType(op.get(), \"shape_dtype\", shape_dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stateful_uniform_int(const tensor& resource, const tensor& algorithm, const tensor& shape, const tensor& minval, const tensor& maxval, datatype dtype=static_cast<datatype>(9), datatype shape_dtype=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatefulUniformInt\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), resource.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), algorithm.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), minval.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), maxval.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrType(op.get(), \"shape_dtype\", shape_dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stateless_multinomial(const tensor& logits, const tensor& num_samples, const tensor& seed, datatype Tseed=static_cast<datatype>(9), datatype output_dtype=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatelessMultinomial\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), logits.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_samples.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tseed\", Tseed);\n    TFE_OpSetAttrType(op.get(), \"output_dtype\", output_dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stateless_parameterized_truncated_normal(const tensor& shape, const tensor& seed, const tensor& means, const tensor& stddevs, const tensor& minvals, const tensor& maxvals, datatype S, datatype dtype, datatype Tseed=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatelessParameterizedTruncatedNormal\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), means.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), stddevs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), minvals.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), maxvals.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"S\", S);\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrType(op.get(), \"Tseed\", Tseed);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stateless_random_binomial(const tensor& shape, const tensor& seed, const tensor& counts, const tensor& probs, datatype S, datatype Tseed=static_cast<datatype>(9), datatype dtype=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatelessRandomBinomial\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), counts.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), probs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"S\", S);\n    TFE_OpSetAttrType(op.get(), \"Tseed\", Tseed);\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stateless_random_gamma_v2(const tensor& shape, const tensor& seed, const tensor& alpha, datatype dtype, datatype Tseed=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatelessRandomGammaV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), alpha.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrType(op.get(), \"Tseed\", Tseed);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stateless_random_normal(const tensor& shape, const tensor& seed, datatype dtype=static_cast<datatype>(1), datatype Tseed=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatelessRandomNormal\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrType(op.get(), \"Tseed\", Tseed);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stateless_random_poisson(const tensor& shape, const tensor& seed, const tensor& lam, datatype Rtype, datatype dtype, datatype Tseed=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatelessRandomPoisson\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lam.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Rtype\", Rtype);\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrType(op.get(), \"Tseed\", Tseed);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stateless_random_uniform(const tensor& shape, const tensor& seed, datatype dtype=static_cast<datatype>(1), datatype Tseed=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatelessRandomUniform\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrType(op.get(), \"Tseed\", Tseed);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stateless_random_uniform_full_int(const tensor& shape, const tensor& seed, datatype dtype=static_cast<datatype>(23), datatype Tseed=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatelessRandomUniformFullInt\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrType(op.get(), \"Tseed\", Tseed);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stateless_random_uniform_int(const tensor& shape, const tensor& seed, const tensor& minval, const tensor& maxval, datatype dtype, datatype Tseed=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatelessRandomUniformInt\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), minval.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), maxval.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrType(op.get(), \"Tseed\", Tseed);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stateless_truncated_normal(const tensor& shape, const tensor& seed, datatype dtype=static_cast<datatype>(1), datatype Tseed=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatelessTruncatedNormal\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), seed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrType(op.get(), \"Tseed\", Tseed);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor static_regex_full_match(const tensor& input, const std::string& pattern) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StaticRegexFullMatch\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"pattern\", (void*) pattern.c_str(), pattern.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor static_regex_replace(const tensor& input, const std::string& pattern, const std::string& rewrite, bool replace_global=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StaticRegexReplace\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"pattern\", (void*) pattern.c_str(), pattern.size());\n    TFE_OpSetAttrString(op.get(), \"rewrite\", (void*) rewrite.c_str(), rewrite.size());\n    TFE_OpSetAttrBool(op.get(), \"replace_global\", (unsigned char)replace_global);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stats_aggregator_handle(const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatsAggregatorHandle\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stats_aggregator_handle_v2(const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatsAggregatorHandleV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stats_aggregator_summary(const tensor& iterator) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StatsAggregatorSummary\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), iterator.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor stop_gradient(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StopGradient\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor strided_slice(const tensor& input, const tensor& begin, const tensor& end, const tensor& strides, datatype Index, int64_t begin_mask=0, int64_t end_mask=0, int64_t ellipsis_mask=0, int64_t new_axis_mask=0, int64_t shrink_axis_mask=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StridedSlice\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), begin.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), end.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), strides.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Index\", Index);\n    TFE_OpSetAttrInt(op.get(), \"begin_mask\", begin_mask);\n    TFE_OpSetAttrInt(op.get(), \"end_mask\", end_mask);\n    TFE_OpSetAttrInt(op.get(), \"ellipsis_mask\", ellipsis_mask);\n    TFE_OpSetAttrInt(op.get(), \"new_axis_mask\", new_axis_mask);\n    TFE_OpSetAttrInt(op.get(), \"shrink_axis_mask\", shrink_axis_mask);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor strided_slice_assign(const tensor& ref, const tensor& begin, const tensor& end, const tensor& strides, const tensor& value, datatype Index, int64_t begin_mask=0, int64_t end_mask=0, int64_t ellipsis_mask=0, int64_t new_axis_mask=0, int64_t shrink_axis_mask=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StridedSliceAssign\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), ref.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), begin.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), end.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), strides.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Index\", Index);\n    TFE_OpSetAttrInt(op.get(), \"begin_mask\", begin_mask);\n    TFE_OpSetAttrInt(op.get(), \"end_mask\", end_mask);\n    TFE_OpSetAttrInt(op.get(), \"ellipsis_mask\", ellipsis_mask);\n    TFE_OpSetAttrInt(op.get(), \"new_axis_mask\", new_axis_mask);\n    TFE_OpSetAttrInt(op.get(), \"shrink_axis_mask\", shrink_axis_mask);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor strided_slice_grad(const tensor& shape, const tensor& begin, const tensor& end, const tensor& strides, const tensor& dy, datatype Index, int64_t begin_mask=0, int64_t end_mask=0, int64_t ellipsis_mask=0, int64_t new_axis_mask=0, int64_t shrink_axis_mask=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StridedSliceGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), begin.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), end.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), strides.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dy.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Index\", Index);\n    TFE_OpSetAttrInt(op.get(), \"begin_mask\", begin_mask);\n    TFE_OpSetAttrInt(op.get(), \"end_mask\", end_mask);\n    TFE_OpSetAttrInt(op.get(), \"ellipsis_mask\", ellipsis_mask);\n    TFE_OpSetAttrInt(op.get(), \"new_axis_mask\", new_axis_mask);\n    TFE_OpSetAttrInt(op.get(), \"shrink_axis_mask\", shrink_axis_mask);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor string_format(const std::vector<tensor>&inputs, const std::string& template_arg=\"%s\", const std::string& placeholder=\"%s\", int64_t summarize=3) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StringFormat\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> inputs_handles; inputs_handles.reserve(inputs.size());\n    std::transform(inputs.begin(), inputs.end(), std::back_inserter(inputs_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), inputs_handles.data(), inputs.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"template\", (void*) template_arg.c_str(), template_arg.size());\n    TFE_OpSetAttrString(op.get(), \"placeholder\", (void*) placeholder.c_str(), placeholder.size());\n    TFE_OpSetAttrInt(op.get(), \"summarize\", summarize);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor string_join(const std::vector<tensor>&inputs, const std::string& separator=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StringJoin\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> inputs_handles; inputs_handles.reserve(inputs.size());\n    std::transform(inputs.begin(), inputs.end(), std::back_inserter(inputs_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), inputs_handles.data(), inputs.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"N\", inputs.size());\n    TFE_OpSetAttrString(op.get(), \"separator\", (void*) separator.c_str(), separator.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor string_length(const tensor& input, const std::string& unit=\"BYTE\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StringLength\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"unit\", (void*) unit.c_str(), unit.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor string_lower(const tensor& input, const std::string& encoding=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StringLower\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"encoding\", (void*) encoding.c_str(), encoding.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor string_strip(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StringStrip\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor string_to_hash_bucket(const tensor& string_input_tensor, int64_t num_buckets) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StringToHashBucket\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), string_input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_buckets\", num_buckets);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor string_to_hash_bucket_fast(const tensor& input, int64_t num_buckets) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StringToHashBucketFast\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_buckets\", num_buckets);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor string_to_hash_bucket_strong(const tensor& input, int64_t num_buckets, const std::vector<int64_t>& key) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StringToHashBucketStrong\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_buckets\", num_buckets);\n    TFE_OpSetAttrIntList(op.get(), \"key\", key.data(), key.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor string_to_number(const tensor& string_input_tensor, datatype out_type=static_cast<datatype>(1)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StringToNumber\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), string_input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"out_type\", out_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor string_upper(const tensor& input, const std::string& encoding=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"StringUpper\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"encoding\", (void*) encoding.c_str(), encoding.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sub(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Sub\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor substr(const tensor& input, const tensor& pos, const tensor& len, const std::string& unit=\"BYTE\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Substr\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), pos.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), len.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"unit\", (void*) unit.c_str(), unit.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor sum(const tensor& input, const tensor& reduction_indices, bool keep_dims=false, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Sum\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), reduction_indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"keep_dims\", (unsigned char)keep_dims);\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor summary_writer(const std::string& shared_name=\"\", const std::string& container=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"SummaryWriter\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor t_f_record_dataset(const tensor& filenames, const tensor& compression_type, const tensor& buffer_size) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TFRecordDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), filenames.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), compression_type.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), buffer_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor t_f_record_reader(const std::string& container=\"\", const std::string& shared_name=\"\", const std::string& compression_type=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TFRecordReader\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrString(op.get(), \"compression_type\", (void*) compression_type.c_str(), compression_type.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor t_f_record_reader_v2(const std::string& container=\"\", const std::string& shared_name=\"\", const std::string& compression_type=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TFRecordReaderV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n    TFE_OpSetAttrString(op.get(), \"compression_type\", (void*) compression_type.c_str(), compression_type.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor t_p_u_compilation_result() {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TPUCompilationResult\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor t_p_u_embedding_activations(const tensor& embedding_variable, const tensor& sliced_activations, int64_t table_id, int64_t lookup_id) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TPUEmbeddingActivations\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), embedding_variable.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), sliced_activations.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"table_id\", table_id);\n    TFE_OpSetAttrInt(op.get(), \"lookup_id\", lookup_id);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor t_p_u_ordinal_selector() {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TPUOrdinalSelector\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor t_p_u_replicated_input(const std::vector<tensor>&inputs, bool is_mirrored_variable=false, int64_t index=-1, bool is_packed=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TPUReplicatedInput\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> inputs_handles; inputs_handles.reserve(inputs.size());\n    std::transform(inputs.begin(), inputs.end(), std::back_inserter(inputs_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), inputs_handles.data(), inputs.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"N\", inputs.size());\n    TFE_OpSetAttrBool(op.get(), \"is_mirrored_variable\", (unsigned char)is_mirrored_variable);\n    TFE_OpSetAttrInt(op.get(), \"index\", index);\n    TFE_OpSetAttrBool(op.get(), \"is_packed\", (unsigned char)is_packed);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor t_p_u_replicated_output(const tensor& input, int64_t num_replicas) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TPUReplicatedOutput\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_replicas\", num_replicas);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor take_dataset(const tensor& input_dataset, const tensor& count, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TakeDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), count.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tan(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Tan\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tanh(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Tanh\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tanh_grad(const tensor& y, const tensor& dy) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TanhGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dy.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor temporary_variable(const std::vector<int64_t>& shape, datatype dtype, const std::string& var_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TemporaryVariable\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrString(op.get(), \"var_name\", (void*) var_name.c_str(), var_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array(const tensor& size, datatype dtype, const std::vector<int64_t>& element_shape, bool dynamic_size=false, bool clear_after_read=true, const std::string& tensor_array_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArray\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"element_shape\", element_shape.data(), element_shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrBool(op.get(), \"dynamic_size\", (unsigned char)dynamic_size);\n    TFE_OpSetAttrBool(op.get(), \"clear_after_read\", (unsigned char)clear_after_read);\n    TFE_OpSetAttrString(op.get(), \"tensor_array_name\", (void*) tensor_array_name.c_str(), tensor_array_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_gather(const tensor& handle, const tensor& indices, const tensor& flow_in, datatype dtype, const std::vector<int64_t>& element_shape) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArrayGather\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"element_shape\", element_shape.data(), element_shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_gather_v2(const tensor& handle, const tensor& indices, const tensor& flow_in, datatype dtype, const std::vector<int64_t>& element_shape) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArrayGatherV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"element_shape\", element_shape.data(), element_shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_gather_v3(const tensor& handle, const tensor& indices, const tensor& flow_in, datatype dtype, const std::vector<int64_t>& element_shape) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArrayGatherV3\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"element_shape\", element_shape.data(), element_shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_grad(const tensor& handle, const tensor& flow_in, const std::string& source) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArrayGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"source\", (void*) source.c_str(), source.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_grad_v2(const tensor& handle, const tensor& flow_in, const std::string& source) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArrayGradV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"source\", (void*) source.c_str(), source.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_pack(const tensor& handle, const tensor& flow_in, datatype dtype, const std::vector<int64_t>& element_shape) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArrayPack\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"element_shape\", element_shape.data(), element_shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_read(const tensor& handle, const tensor& index, const tensor& flow_in, datatype dtype) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArrayRead\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), index.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_read_v2(const tensor& handle, const tensor& index, const tensor& flow_in, datatype dtype) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArrayReadV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), index.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_read_v3(const tensor& handle, const tensor& index, const tensor& flow_in, datatype dtype) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArrayReadV3\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), index.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_scatter(const tensor& handle, const tensor& indices, const tensor& value, const tensor& flow_in) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArrayScatter\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_scatter_v2(const tensor& handle, const tensor& indices, const tensor& value, const tensor& flow_in) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArrayScatterV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_scatter_v3(const tensor& handle, const tensor& indices, const tensor& value, const tensor& flow_in) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArrayScatterV3\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_size(const tensor& handle, const tensor& flow_in) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArraySize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_size_v2(const tensor& handle, const tensor& flow_in) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArraySizeV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_size_v3(const tensor& handle, const tensor& flow_in) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArraySizeV3\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_split(const tensor& handle, const tensor& value, const tensor& lengths, const tensor& flow_in) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArraySplit\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lengths.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_split_v2(const tensor& handle, const tensor& value, const tensor& lengths, const tensor& flow_in) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArraySplitV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lengths.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_split_v3(const tensor& handle, const tensor& value, const tensor& lengths, const tensor& flow_in) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArraySplitV3\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lengths.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_unpack(const tensor& handle, const tensor& value, const tensor& flow_in) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArrayUnpack\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_v2(const tensor& size, datatype dtype, const std::vector<int64_t>& element_shape, bool dynamic_size=false, bool clear_after_read=true, const std::string& tensor_array_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArrayV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"element_shape\", element_shape.data(), element_shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrBool(op.get(), \"dynamic_size\", (unsigned char)dynamic_size);\n    TFE_OpSetAttrBool(op.get(), \"clear_after_read\", (unsigned char)clear_after_read);\n    TFE_OpSetAttrString(op.get(), \"tensor_array_name\", (void*) tensor_array_name.c_str(), tensor_array_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_write(const tensor& handle, const tensor& index, const tensor& value, const tensor& flow_in) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArrayWrite\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), index.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_write_v2(const tensor& handle, const tensor& index, const tensor& value, const tensor& flow_in) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArrayWriteV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), index.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_array_write_v3(const tensor& handle, const tensor& index, const tensor& value, const tensor& flow_in) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorArrayWriteV3\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), index.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), flow_in.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_dataset(const std::vector<tensor>&components, const std::vector<datatype>& Toutput_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> components_handles; components_handles.reserve(components.size());\n    std::transform(components.begin(), components.end(), std::back_inserter(components_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), components_handles.data(), components.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"Toutput_types\", reinterpret_cast<const enum TF_DataType *>(Toutput_types.data()), Toutput_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_list_concat_lists(const tensor& input_a, const tensor& input_b, datatype element_dtype) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorListConcatLists\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_a.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_b.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"element_dtype\", element_dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_list_element_shape(const tensor& input_handle, datatype shape_type) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorListElementShape\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"shape_type\", shape_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_list_from_tensor(const tensor& input_tensor, const tensor& element_shape, datatype element_dtype, datatype shape_type) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorListFromTensor\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), element_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"element_dtype\", element_dtype);\n    TFE_OpSetAttrType(op.get(), \"shape_type\", shape_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_list_gather(const tensor& input_handle, const tensor& indices, const tensor& element_shape, datatype element_dtype) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorListGather\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), element_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"element_dtype\", element_dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_list_get_item(const tensor& input_handle, const tensor& index, const tensor& element_shape, datatype element_dtype) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorListGetItem\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), index.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), element_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"element_dtype\", element_dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_list_length(const tensor& input_handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorListLength\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_list_push_back(const tensor& input_handle, const tensor& input_tensor, datatype element_dtype) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorListPushBack\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"element_dtype\", element_dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_list_push_back_batch(const tensor& input_handles, const tensor& input_tensor, datatype element_dtype) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorListPushBackBatch\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_handles.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"element_dtype\", element_dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_list_reserve(const tensor& element_shape, const tensor& num_elements, datatype element_dtype, datatype shape_type) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorListReserve\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), element_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_elements.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"element_dtype\", element_dtype);\n    TFE_OpSetAttrType(op.get(), \"shape_type\", shape_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_list_resize(const tensor& input_handle, const tensor& size) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorListResize\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_list_scatter(const tensor& input_tensor, const tensor& indices, const tensor& element_shape, datatype element_dtype, datatype shape_type) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorListScatter\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), element_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"element_dtype\", element_dtype);\n    TFE_OpSetAttrType(op.get(), \"shape_type\", shape_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_list_scatter_into_existing_list(const tensor& input_handle, const tensor& input_tensor, const tensor& indices, datatype element_dtype) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorListScatterIntoExistingList\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"element_dtype\", element_dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_list_scatter_v2(const tensor& input_tensor, const tensor& indices, const tensor& element_shape, const tensor& num_elements, datatype element_dtype, datatype shape_type) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorListScatterV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), element_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_elements.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"element_dtype\", element_dtype);\n    TFE_OpSetAttrType(op.get(), \"shape_type\", shape_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_list_set_item(const tensor& input_handle, const tensor& index, const tensor& item, datatype element_dtype) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorListSetItem\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), index.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), item.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"element_dtype\", element_dtype);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_list_split(const tensor& input_tensor, const tensor& element_shape, const tensor& lengths, datatype element_dtype, datatype shape_type) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorListSplit\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), element_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), lengths.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"element_dtype\", element_dtype);\n    TFE_OpSetAttrType(op.get(), \"shape_type\", shape_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_list_stack(const tensor& input_handle, const tensor& element_shape, datatype element_dtype, int64_t num_elements=-1) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorListStack\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), element_shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"element_dtype\", element_dtype);\n    TFE_OpSetAttrInt(op.get(), \"num_elements\", num_elements);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_scatter_add(const tensor& input_tensor, const tensor& indices, const tensor& updates, datatype Tindices) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorScatterAdd\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_scatter_max(const tensor& input_tensor, const tensor& indices, const tensor& updates, datatype Tindices) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorScatterMax\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_scatter_min(const tensor& input_tensor, const tensor& indices, const tensor& updates, datatype Tindices) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorScatterMin\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_scatter_sub(const tensor& input_tensor, const tensor& indices, const tensor& updates, datatype Tindices) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorScatterSub\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_scatter_update(const tensor& input_tensor, const tensor& indices, const tensor& updates, datatype Tindices) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorScatterUpdate\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), updates.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_slice_dataset(const std::vector<tensor>&components, const std::vector<datatype>& Toutput_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorSliceDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> components_handles; components_handles.reserve(components.size());\n    std::transform(components.begin(), components.end(), std::back_inserter(components_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), components_handles.data(), components.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"Toutput_types\", reinterpret_cast<const enum TF_DataType *>(Toutput_types.data()), Toutput_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_strided_slice_update(const tensor& input, const tensor& begin, const tensor& end, const tensor& strides, const tensor& value, datatype Index, int64_t begin_mask=0, int64_t end_mask=0, int64_t ellipsis_mask=0, int64_t new_axis_mask=0, int64_t shrink_axis_mask=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorStridedSliceUpdate\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), begin.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), end.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), strides.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Index\", Index);\n    TFE_OpSetAttrInt(op.get(), \"begin_mask\", begin_mask);\n    TFE_OpSetAttrInt(op.get(), \"end_mask\", end_mask);\n    TFE_OpSetAttrInt(op.get(), \"ellipsis_mask\", ellipsis_mask);\n    TFE_OpSetAttrInt(op.get(), \"new_axis_mask\", new_axis_mask);\n    TFE_OpSetAttrInt(op.get(), \"shrink_axis_mask\", shrink_axis_mask);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_summary(const tensor& input_tensor, const std::vector< std::string>& labels, const std::string& description=\"\", const std::string& display_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorSummary\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n    std::vector<std::size_t> labels_sizes; labels_sizes.reserve(labels.size());\n    std::transform(labels.begin(), labels.end(), std::back_inserter(labels_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"labels\", reinterpret_cast<const void *const *>(labels.data()), labels_sizes.data(), labels.size());\n    \n    TFE_OpSetAttrString(op.get(), \"description\", (void*) description.c_str(), description.size());\n    TFE_OpSetAttrString(op.get(), \"display_name\", (void*) display_name.c_str(), display_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tensor_summary_v2(const tensor& tag, const tensor& input_tensor, const tensor& serialized_summary_metadata) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TensorSummaryV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), tag.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), serialized_summary_metadata.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor text_line_dataset(const tensor& filenames, const tensor& compression_type, const tensor& buffer_size) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TextLineDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), filenames.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), compression_type.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), buffer_size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor text_line_reader(int64_t skip_header_lines=0, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TextLineReader\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"skip_header_lines\", skip_header_lines);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor text_line_reader_v2(int64_t skip_header_lines=0, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TextLineReaderV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"skip_header_lines\", skip_header_lines);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor thread_pool_dataset(const tensor& input_dataset, const tensor& thread_pool, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ThreadPoolDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), thread_pool.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor thread_pool_handle(int64_t num_threads, const std::string& display_name, int64_t max_intra_op_parallelism=1, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ThreadPoolHandle\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num_threads\", num_threads);\n    TFE_OpSetAttrString(op.get(), \"display_name\", (void*) display_name.c_str(), display_name.size());\n    TFE_OpSetAttrInt(op.get(), \"max_intra_op_parallelism\", max_intra_op_parallelism);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tile(const tensor& input, const tensor& multiples, datatype Tmultiples=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Tile\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), multiples.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tmultiples\", Tmultiples);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tile_grad(const tensor& input, const tensor& multiples) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TileGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), multiples.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor timestamp() {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Timestamp\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor to_bool(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ToBool\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor transpose(const tensor& x, const tensor& perm, datatype Tperm=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Transpose\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), perm.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tperm\", Tperm);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tridiagonal_mat_mul(const tensor& superdiag, const tensor& maindiag, const tensor& subdiag, const tensor& rhs) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TridiagonalMatMul\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), superdiag.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), maindiag.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), subdiag.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rhs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor tridiagonal_solve(const tensor& diagonals, const tensor& rhs, bool partial_pivoting=true) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TridiagonalSolve\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), diagonals.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), rhs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrBool(op.get(), \"partial_pivoting\", (unsigned char)partial_pivoting);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor truncate_div(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TruncateDiv\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor truncate_mod(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TruncateMod\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor truncated_normal(const tensor& shape, datatype dtype, int64_t seed=0, int64_t seed2=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"TruncatedNormal\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), shape.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrInt(op.get(), \"seed\", seed);\n    TFE_OpSetAttrInt(op.get(), \"seed2\", seed2);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor unbatch(const tensor& batched_input_tensor, const tensor& batch_index, const tensor& id, int64_t timeout_micros, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Unbatch\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), batched_input_tensor.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), batch_index.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), id.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"timeout_micros\", timeout_micros);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor unbatch_dataset(const tensor& input_dataset, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"UnbatchDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor unbatch_grad(const tensor& original_input, const tensor& batch_index, const tensor& grad, const tensor& id, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"UnbatchGrad\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), original_input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), batch_index.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), grad.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), id.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor uncompress_element(const tensor& compressed, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"UncompressElement\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), compressed.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor unicode_encode(const tensor& input_values, const tensor& input_splits, const std::string& output_encoding, const std::string& errors=\"replace\", int64_t replacement_char=65533, datatype Tsplits=static_cast<datatype>(9)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"UnicodeEncode\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), input_splits.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"output_encoding\", (void*) output_encoding.c_str(), output_encoding.size());\n    TFE_OpSetAttrString(op.get(), \"errors\", (void*) errors.c_str(), errors.size());\n    TFE_OpSetAttrInt(op.get(), \"replacement_char\", replacement_char);\n    TFE_OpSetAttrType(op.get(), \"Tsplits\", Tsplits);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor unicode_script(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"UnicodeScript\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor unicode_transcode(const tensor& input, const std::string& input_encoding, const std::string& output_encoding, const std::string& errors=\"replace\", int64_t replacement_char=65533, bool replace_control_characters=false) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"UnicodeTranscode\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"input_encoding\", (void*) input_encoding.c_str(), input_encoding.size());\n    TFE_OpSetAttrString(op.get(), \"output_encoding\", (void*) output_encoding.c_str(), output_encoding.size());\n    TFE_OpSetAttrString(op.get(), \"errors\", (void*) errors.c_str(), errors.size());\n    TFE_OpSetAttrInt(op.get(), \"replacement_char\", replacement_char);\n    TFE_OpSetAttrBool(op.get(), \"replace_control_characters\", (unsigned char)replace_control_characters);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor unique_dataset(const tensor& input_dataset, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"UniqueDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor unpack(const tensor& value, int64_t num, int64_t axis=0) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Unpack\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), value.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrInt(op.get(), \"num\", num);\n    TFE_OpSetAttrInt(op.get(), \"axis\", axis);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor unravel_index(const tensor& indices, const tensor& dims, datatype Tidx=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"UnravelIndex\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), indices.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), dims.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tidx\", Tidx);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor unsorted_segment_join(const tensor& inputs, const tensor& segment_ids, const tensor& num_segments, datatype Tindices, const std::string& separator=\"\", datatype Tnumsegments=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"UnsortedSegmentJoin\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), inputs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_segments.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrString(op.get(), \"separator\", (void*) separator.c_str(), separator.size());\n    TFE_OpSetAttrType(op.get(), \"Tnumsegments\", Tnumsegments);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor unsorted_segment_max(const tensor& data, const tensor& segment_ids, const tensor& num_segments, datatype Tindices, datatype Tnumsegments=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"UnsortedSegmentMax\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_segments.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrType(op.get(), \"Tnumsegments\", Tnumsegments);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor unsorted_segment_min(const tensor& data, const tensor& segment_ids, const tensor& num_segments, datatype Tindices, datatype Tnumsegments=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"UnsortedSegmentMin\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_segments.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrType(op.get(), \"Tnumsegments\", Tnumsegments);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor unsorted_segment_prod(const tensor& data, const tensor& segment_ids, const tensor& num_segments, datatype Tindices, datatype Tnumsegments=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"UnsortedSegmentProd\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_segments.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrType(op.get(), \"Tnumsegments\", Tnumsegments);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor unsorted_segment_sum(const tensor& data, const tensor& segment_ids, const tensor& num_segments, datatype Tindices, datatype Tnumsegments=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"UnsortedSegmentSum\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), data.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), segment_ids.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), num_segments.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"Tindices\", Tindices);\n    TFE_OpSetAttrType(op.get(), \"Tnumsegments\", Tnumsegments);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor unstage(const std::vector<datatype>& dtypes, int64_t capacity=0, int64_t memory_limit=0, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Unstage\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"dtypes\", reinterpret_cast<const enum TF_DataType *>(dtypes.data()), dtypes.size());\n    TFE_OpSetAttrInt(op.get(), \"capacity\", capacity);\n    TFE_OpSetAttrInt(op.get(), \"memory_limit\", memory_limit);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor unwrap_dataset_variant(const tensor& input_handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"UnwrapDatasetVariant\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor upper_bound(const tensor& sorted_inputs, const tensor& values, datatype out_type=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"UpperBound\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), sorted_inputs.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), values.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"out_type\", out_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor var_handle_op(datatype dtype, const std::vector<int64_t>& shape, const std::vector< std::string>& allowed_devices, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"VarHandleOp\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    std::vector<std::size_t> allowed_devices_sizes; allowed_devices_sizes.reserve(allowed_devices.size());\n    std::transform(allowed_devices.begin(), allowed_devices.end(), std::back_inserter(allowed_devices_sizes), [](const auto& s) { return s.size();});\n    TFE_OpSetAttrStringList(op.get(), \"allowed_devices\", reinterpret_cast<const void *const *>(allowed_devices.data()), allowed_devices_sizes.data(), allowed_devices.size());\n    \n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor var_is_initialized_op(const tensor& resource) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"VarIsInitializedOp\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), resource.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor variable(const std::vector<int64_t>& shape, datatype dtype, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Variable\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor variable_shape(const tensor& input, datatype out_type=static_cast<datatype>(3)) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"VariableShape\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrType(op.get(), \"out_type\", out_type);\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor variable_v2(const std::vector<int64_t>& shape, datatype dtype, const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"VariableV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    \n    TFE_OpSetAttrShape(op.get(), \"shape\", shape.data(), shape.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrType(op.get(), \"dtype\", dtype);\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor where(const tensor& input) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Where\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor whole_file_reader(const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"WholeFileReader\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor whole_file_reader_v2(const std::string& container=\"\", const std::string& shared_name=\"\") {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"WholeFileReaderV2\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n\n    // Attributes\n    TFE_OpSetAttrString(op.get(), \"container\", (void*) container.c_str(), container.size());\n    TFE_OpSetAttrString(op.get(), \"shared_name\", (void*) shared_name.c_str(), shared_name.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor window_dataset(const tensor& input_dataset, const tensor& size, const tensor& shift, const tensor& stride, const tensor& drop_remainder, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"WindowDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_dataset.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), size.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), shift.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), stride.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), drop_remainder.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor worker_heartbeat(const tensor& request) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"WorkerHeartbeat\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), request.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor wrap_dataset_variant(const tensor& input_handle) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"WrapDatasetVariant\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), input_handle.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor xdivy(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Xdivy\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor xlog1py(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Xlog1py\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor xlogy(const tensor& x, const tensor& y) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Xlogy\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), y.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor zeros_like(const tensor& x) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ZerosLike\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor zeta(const tensor& x, const tensor& q) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"Zeta\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    TFE_OpAddInput(op.get(), x.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n    \n    TFE_OpAddInput(op.get(), q.tfe_handle.get(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    \n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\ninline tensor zip_dataset(const std::vector<tensor>&input_datasets, const std::vector<datatype>& output_types, const std::vector< std::vector<int64_t>>& output_shapes) {\n\n    // Define Op\n    std::unique_ptr<TFE_Op, decltype(&TFE_DeleteOp)> op(TFE_NewOp(context::get_context(), \"ZipDataset\", context::get_status()), &TFE_DeleteOp);\n    status_check(context::get_status());\n\n    // Required input arguments\n    \n    std::vector<TFE_TensorHandle*> input_datasets_handles; input_datasets_handles.reserve(input_datasets.size());\n    std::transform(input_datasets.begin(), input_datasets.end(), std::back_inserter(input_datasets_handles), [](const auto& t) { return t.tfe_handle.get();});\n    TFE_OpAddInputList(op.get(), input_datasets_handles.data(), input_datasets.size(), context::get_status());\n    status_check(context::get_status());\n    \n\n    // Attributes\n    TFE_OpSetAttrTypeList(op.get(), \"output_types\", reinterpret_cast<const enum TF_DataType *>(output_types.data()), output_types.size());\n    \n    std::vector<const int64_t*> output_shapes_values; output_shapes_values.reserve(output_shapes.size());\n    std::vector<int> output_shapes_ndims; output_shapes_ndims.reserve(output_shapes.size());\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_values), [](const auto& v) { return v.data();});\n    std::transform(output_shapes.begin(), output_shapes.end(), std::back_inserter(output_shapes_ndims), [](const auto& v) { return v.size();});\n    TFE_OpSetAttrShapeList(op.get(), \"output_shapes\", output_shapes_values.data(), output_shapes_ndims.data(), output_shapes.size(), context::get_status());\n    status_check(context::get_status());\n    \n    TFE_OpSetAttrInt(op.get(), \"N\", input_datasets.size());\n\n    // Execute Op\n    int num_outputs_op = 1;\n    TFE_TensorHandle* res[1] = {nullptr};\n    TFE_Execute(op.get(), res, &num_outputs_op, context::get_status());\n    status_check(context::get_status());\n    return tensor(res[0]);\n}\n\n\n} // cppflow\n\n#endif\n\n"
  },
  {
    "path": "ext/CppFlow/tensor.h",
    "content": "//\n// Created by serizba on 27/6/20.\n//\n\n#ifndef CPPFLOW2_TENSOR_H\n#define CPPFLOW2_TENSOR_H\n\n#include <memory>\n#include <vector>\n#include <cstring>\n#include <string>\n#include <tensorflow/c/tf_tensor.h>\n#include <tensorflow/c/eager/c_api.h>\n\n#include \"context.h\"\n#include \"datatype.h\"\n\nnamespace cppflow {\n\n    /**\n     * @class tensor\n     * @brief A TensorFlow eager tensor wrapper\n     *\n     */\n    class tensor {\n    public:\n        tensor()= default;\n\n        /**\n        * Creates a tensor with the given values and specified shape\n        * @tparam T A type that can be convertible into a tensor\n        * @param values The values to be converted (in a flattened version)\n        * @param shape The shape of the converted tensor\n        */\n        template<typename T>\n        tensor(const std::vector<T>& values, const std::vector<int64_t>& shape);\n\n        /**\n        * Creates a flat tensor with the given values\n        * @tparam T A type that can be convertible into a tensor\n        * @param values The values to be converted\n        */\n        template<typename T>\n        tensor(const std::initializer_list<T>& values);\n\n        /**\n         * Creates a tensor with the given value\n         * @tparam T A type that can be convertible into a tensor\n         * @param value The value to be converted\n         */\n        template<typename T>\n        tensor(const T& value);\n\n        /**\n         * @return Shape of the tensor\n         */\n        tensor shape() const;\n\n        /**\n         * @param on_memory If false, the function will return the name of the device that produced the tensor.\n         * If true, the function will return the name of the device in whose memory the tensor resides\n         * @return Returns the name of the device of the tensor\n         */\n        std::string device(bool on_memory=false) const;\n\n\n        /**\n         * @return The tensor datatype\n         */\n        datatype dtype() const;\n\n        /**\n         * Converts the tensor into a C++ vector\n         * @tparam T The c++ type (must be equivalent to the tensor type)\n         * @return A vector representing the flat tensor\n         */\n        template<typename T>\n        std::vector<T> get_data() const;\n\n\n        ~tensor() = default;\n        tensor(const tensor &tensor) = default;\n        tensor(tensor &&tensor) = default;\n        tensor &operator=(const tensor &other) = default;\n        tensor &operator=(tensor &&other) = default;\n\n        explicit tensor(TFE_TensorHandle* handle);\n        explicit tensor(TF_Tensor* t);\n\n        // NOTE: Usually, one should not call get_eager_handle() or get_tensor() below.\n        //       They are designed for implementation details in cppflow.\n        //       If you are calling them directly, it is likely that you are using some\n        //       tenforflow APIs not supported in cppflow.\n        \n        // Additional NOTE: TF_Tensor is an immutable tensor inside tensorflow.\n        // TFE_TensorHandle is a TF_Tensor and the associated device, plus some data cache\n        \n        // TODO: Need to determine if we can mark the return value or *this as const\n        std::shared_ptr<TFE_TensorHandle> get_eager_handle() const { return tfe_handle; }\n\n        // Get the TF_Tensor data from the eager handle\n        // Call `get_data<T>()` instead if possible\n        // NOTE: Changes to the returned TF_Tensor may not be reflected in the actual device memory!\n        //       Do *NOT* modify the returned TF_Tensor!\n        //       See comments of `tf_tensor` for more details.\n        std::shared_ptr<TF_Tensor> get_tensor() const;\n        \n        // DO NOT directly access this member, call get_eager_handle() instead\n        // TODO: This is kept as public to be compatible with existing code and should be mark as private\n        std::shared_ptr<TFE_TensorHandle> tfe_handle;\n\n    private:\n        // This member serves as a local cache of the data in tfe_handle.\n        // It refers to `local_mirrors_` if on device, or `data_` if on host CPU.\n        // Changes to this variable may not be reflected in the actual device memory,\n        // e.g. on GPUs or on remote nodes.\n        // Access it via get_tensor() if not in constructor\n        mutable std::shared_ptr<TF_Tensor> tf_tensor;\n\n        tensor(enum TF_DataType type, const void* data, size_t len, const std::vector<int64_t>& shape);\n    };\n}\n\n\n/******************************\n *   IMPLEMENTATION DETAILS   *\n ******************************/\n\n\nnamespace cppflow {\n\n    inline tensor::tensor(enum TF_DataType type, const void *data, size_t len, const std::vector<int64_t> &shape) {\n        this->tf_tensor = {TF_AllocateTensor(type, shape.data(), shape.size(), len), TF_DeleteTensor};\n        memcpy(TF_TensorData(this->tf_tensor.get()), data, TF_TensorByteSize(this->tf_tensor.get()));\n        this->tfe_handle = {TFE_NewTensorHandle(this->tf_tensor.get(), context::get_status()), TFE_DeleteTensorHandle};\n        status_check(context::get_status());\n    }\n\n    template<typename T>\n    tensor::tensor(const std::vector<T>& values, const std::vector<int64_t>& shape) :\n        tensor(deduce_tf_type<T>(), values.data(), values.size() * sizeof(T), shape) {}\n\n    template<typename T>\n    tensor::tensor(const std::initializer_list<T>& values) :\n            tensor(std::vector<T>(values), {(int64_t) values.size()}) {}\n\n    template<typename T>\n    tensor::tensor(const T& value) :\n            tensor(std::vector<T>({value}), {}) {}\n\n#ifdef TENSORFLOW_C_TF_TSTRING_H_\n    // For future version TensorFlow 2.4\n    template<>\n    inline tensor::tensor(const std::string& value) {\n        TF_TString tstr[1];\n        TF_TString_Init(&tstr[0]);\n        TF_TString_Copy(&tstr[0], value.c_str(), value.size());\n\n        *this = tensor(static_cast<enum TF_DataType>(TF_STRING), (void *) tstr, sizeof(tstr), {});\n    }\n#else\n    template<>\n    inline tensor::tensor(const std::string& value) {\n        size_t size = 8 + TF_StringEncodedSize(value.length());\n        char* data = new char[value.size() + 8];\n        for (int i=0; i<8; i++) {data[i]=0;}\n        TF_StringEncode(value.c_str(), value.size(), data + 8, size - 8, context::get_status());\n        status_check(context::get_status());\n\n        *this = tensor(static_cast<enum TF_DataType>(TF_STRING), (void *) data, size, {});\n        delete [] data;\n    }\n#endif // TENSORFLOW_C_TF_TSTRING_H_\n\n    inline tensor::tensor(TFE_TensorHandle* handle) {\n            this->tfe_handle = {handle, TFE_DeleteTensorHandle};\n    }\n\n    inline tensor::tensor(TF_Tensor* t) {\n        this->tf_tensor = {t, TF_DeleteTensor};\n        this->tfe_handle = {TFE_NewTensorHandle(this->tf_tensor.get(), context::get_status()), TFE_DeleteTensorHandle};\n        status_check(context::get_status());\n    }\n\n    inline tensor tensor::shape() const {\n        auto op = TFE_NewOp(context::get_context(), \"Shape\", context::get_status());\n        status_check(context::get_status());\n\n        TFE_OpAddInput(op, this->tfe_handle.get(), context::get_status());\n        status_check(context::get_status());\n\n        // Output type should be int64_t\n        TFE_OpSetAttrType(op, \"out_type\", cppflow::datatype::TF_INT64);\n\n        // EXECUTE\n        int n = 1;\n        TFE_TensorHandle* res[1] = { nullptr };\n        TFE_Execute(op, res, &n, context::get_status());\n        status_check(context::get_status());\n        TFE_DeleteOp(op);\n\n        return tensor(res[0]);\n    }\n\n    inline std::string tensor::device(bool on_memory) const {\n        std::string res;\n        if (on_memory)\n            res = TFE_TensorHandleBackingDeviceName(this->tfe_handle.get(), context::get_status());\n        else\n            res = std::string(TFE_TensorHandleDeviceName(this->tfe_handle.get(), context::get_status()));\n\n        status_check(context::get_status());\n        return res;\n    }\n\n    template<typename T>\n    std::vector<T> tensor::get_data() const {\n\n        // Check if asked datatype and tensor datatype match\n        if (this->dtype() != deduce_tf_type<T>()) {\n            auto type1 = cppflow::to_string(deduce_tf_type<T>());\n            auto type2 = cppflow::to_string(this->dtype());\n            auto error = \"Datatype in function get_data (\" + type1 + \") does not match tensor datatype (\" + type2 + \")\";\n            throw std::runtime_error(error);\n        }\n\n\n        auto res_tensor = get_tensor();\n\n        // Check tensor data is not empty\n        auto raw_data = TF_TensorData(res_tensor.get());\n        //this->error_check(raw_data != nullptr, \"Tensor data is empty\");\n\n        size_t size = TF_TensorByteSize(res_tensor.get()) / TF_DataTypeSize(TF_TensorType(res_tensor.get()));\n\n        // Convert to correct type\n        const auto T_data = static_cast<T*>(raw_data);\n        std::vector<T> r(T_data, T_data + size);\n\n        return r;\n    }\n\n    inline datatype tensor::dtype() const {\n        return TFE_TensorHandleDataType(this->tfe_handle.get());\n    }\n    \n    // NOTE: Changes to the returned TF_Tensor are not reflected in the actual device memory!\n    inline std::shared_ptr<TF_Tensor> tensor::get_tensor() const {\n        if(!tf_tensor) {\n            tf_tensor = {TFE_TensorHandleResolve(tfe_handle.get(), context::get_status()), TF_DeleteTensor};\n            status_check(context::get_status());\n        }\n        return tf_tensor;\n    }\n}\n\n#endif //CPPFLOW2_TENSOR_H\n"
  },
  {
    "path": "ext/Qt-Frameless-Window-DarkStyle-master/.gitignore",
    "content": "# C++ objects and libs\n\n*.slo\n*.lo\n*.o\n*.a\n*.la\n*.lai\n*.so\n*.dll\n*.dylib\n\n# Qt-es\n\n/.qmake.cache\n/.qmake.stash\n*.pro.user\n*.pro.user.*\n*.qbs.user\n*.qbs.user.*\n*.moc\nmoc_*.cpp\nmoc_*.h\nqrc_*.cpp\nui_*.h\nMakefile*\n*build-*\n\n# QtCreator\n\n*.autosave\n\n# QtCtreator Qml\n*.qmlproject.user\n*.qmlproject.user.*\n\n# QtCtreator CMake\nCMakeLists.txt.user*\n\n"
  },
  {
    "path": "ext/Qt-Frameless-Window-DarkStyle-master/DarkStyle.cpp",
    "content": "/*\n###############################################################################\n#                                                                             #\n# The MIT License                                                             #\n#                                                                             #\n# Copyright (C) 2017 by Juergen Skrotzky (JorgenVikingGod@gmail.com)          #\n#               >> https://github.com/Jorgen-VikingGod                        #\n#                                                                             #\n# Sources: https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle  #\n#                                                                             #\n###############################################################################\n*/\n\n#include \"DarkStyle.h\"\n\nDarkStyle::DarkStyle():\n  DarkStyle(styleBase())\n{ }\n\nDarkStyle::DarkStyle(QStyle *style):\n  QProxyStyle(style)\n{ }\n\nQStyle *DarkStyle::styleBase(QStyle *style) const {\n  static QStyle *base = !style ? QStyleFactory::create(QStringLiteral(\"Fusion\")) : style;\n  return base;\n}\n\nQStyle *DarkStyle::baseStyle() const\n{\n  return styleBase();\n}\n\nvoid DarkStyle::polish(QPalette &palette)\n{\n  // modify palette to dark\n  palette.setColor(QPalette::Window,QColor(53,53,53));\n  palette.setColor(QPalette::WindowText,Qt::white);\n  palette.setColor(QPalette::Disabled,QPalette::WindowText,QColor(127,127,127));\n  palette.setColor(QPalette::Base,QColor(42,42,42));\n  palette.setColor(QPalette::AlternateBase,QColor(66,66,66));\n  palette.setColor(QPalette::ToolTipBase,Qt::white);\n  palette.setColor(QPalette::ToolTipText,QColor(53,53,53));\n  palette.setColor(QPalette::Text,Qt::white);\n  palette.setColor(QPalette::Disabled,QPalette::Text,QColor(127,127,127));\n  palette.setColor(QPalette::Dark,QColor(35,35,35));\n  palette.setColor(QPalette::Shadow,QColor(20,20,20));\n  palette.setColor(QPalette::Button,QColor(53,53,53));\n  palette.setColor(QPalette::ButtonText,Qt::white);\n  palette.setColor(QPalette::Disabled,QPalette::ButtonText,QColor(127,127,127));\n  palette.setColor(QPalette::BrightText,Qt::red);\n  palette.setColor(QPalette::Link,QColor(42,130,218));\n  palette.setColor(QPalette::Highlight,QColor(42,130,218));\n  palette.setColor(QPalette::Disabled,QPalette::Highlight,QColor(80,80,80));\n  palette.setColor(QPalette::HighlightedText,Qt::white);\n  palette.setColor(QPalette::Disabled,QPalette::HighlightedText,QColor(127,127,127));\n}\n\nvoid DarkStyle::polish(QApplication *app)\n{\n  if (!app) return;\n\n  // increase font size for better reading,\n  // setPointSize was reduced from +2 because when applied this way in Qt5, the font is larger than intended for some reason\n  QFont defaultFont = QApplication::font();\n  defaultFont.setPointSize(defaultFont.pointSize()+1);\n  app->setFont(defaultFont);\n\n  // loadstylesheet\n  QFile qfDarkstyle(QStringLiteral(\":/darkstyle/darkstyle.qss\"));\n  if (qfDarkstyle.open(QIODevice::ReadOnly | QIODevice::Text))\n  {\n    // set stylesheet\n    QString qsStylesheet = QString::fromLatin1(qfDarkstyle.readAll());\n    app->setStyleSheet(qsStylesheet);\n    qfDarkstyle.close();\n  }\n}\n"
  },
  {
    "path": "ext/Qt-Frameless-Window-DarkStyle-master/DarkStyle.h",
    "content": "/*\n###############################################################################\n#                                                                             #\n# The MIT License                                                             #\n#                                                                             #\n# Copyright (C) 2017 by Juergen Skrotzky (JorgenVikingGod@gmail.com)          #\n#               >> https://github.com/Jorgen-VikingGod                        #\n#                                                                             #\n# Sources: https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle  #\n#                                                                             #\n###############################################################################\n*/\n\n#ifndef _DarkStyle_HPP\n#define _DarkStyle_HPP\n\n#include <QApplication>\n#include <QProxyStyle>\n#include <QStyleFactory>\n#include <QFont>\n#include <QFile>\n\nclass DarkStyle : public QProxyStyle\n{\n  Q_OBJECT\n\npublic:\n  DarkStyle();\n  explicit DarkStyle(QStyle *style);\n\n  QStyle *baseStyle() const;\n\n  void polish(QPalette &palette) override;\n  void polish(QApplication *app) override;\n\nprivate:\n  QStyle *styleBase(QStyle *style = Q_NULLPTR) const;\n};\n\n#endif  // _DarkStyle_HPP\n"
  },
  {
    "path": "ext/Qt-Frameless-Window-DarkStyle-master/README.md",
    "content": "# Qt Frameless Window with DarkStyle\nsimple MainWindow class implementation with frameless window and custom dark style. \n\nIt adds also support for titlebar and buttons (minimize, maximize, close)\n\nLook is based on the VS2013 application window (flat and frameless window)\n\n<table>\n  <tr><th colspan=\"2\">Screenshots</th></tr>\n  <tr><th>mac enabled</th><th>mac disabled</th></tr>\n  <tr>\n    <td><img src=\"https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle/blob/master/screenshot_mac_frameless_window_qt_dark_style_enabled.png\" title=\"screenshot mac frameless window qt dark style enabled\" /></td>\n    <td><img src=\"https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle/blob/master/screenshot_mac_frameless_window_qt_dark_style_disabled.png\" title=\"screenshot mac frameless window qt dark style disabled\" /></td>\n  </tr>\n</table>\n\n\n## Qt and OS\n* tested with Qt5.5.0, Qt5.9.0 and Qt5.10.0\n* tested on Windows 7, Windows 10,MacOSX 10.12.5 and MacOS 10.13.2\n\n## PyQt5\nHere is an [unofficial Python port](https://github.com/gmarull/qtmodern) of my implementation.\n\n## How to use\n* add additional include plath to **framelesswindow**\n* add resources **framelesswindow.qrc** and **darkstyle.qrc**\n* add ``#include \"framelesswindow.h\"`` into **main.cpp**, create window ``FramelessWindow framelessWindow;`` and assign your mainwindow object as content ``framelessWindow.setContent(mainWindow);`` and show it ``framelessWindow.show();``\n* add ``#include \"DarkStyle.h\"`` into **main.cpp** and call ``a.setStyle(new DarkStyle);``\n\n\n```qt\n#include <QApplication>\n#include \"DarkStyle.h\"\n#include \"framelesswindow.h\"\n#include \"mainwindow.h\"\n\nint main(int argc, char *argv[])\n{\n  QApplication a(argc, argv);\n\n  // style our application with custom dark style\n  a.setStyle(new DarkStyle);\n\n  // create frameless window (and set windowState or title)\n  FramelessWindow framelessWindow;\n  //framelessWindow.setWindowState(Qt::WindowMaximized);\n  //framelessWindow.setWindowTitle(\"test title\");\n  //framelessWindow.setWindowIcon(a.style()->standardIcon(QStyle::SP_DesktopIcon));\n  \n  // create our mainwindow instance\n  MainWindow *mainWindow = new MainWindow;\n\n  // add the mainwindow to our custom frameless window\n  framelessWindow.setContent(mainWindow);\n  framelessWindow.show();\n\n  return a.exec();\n}\n```\n\n\n## features\n* frameless window\n* custom dark style (based on **Fusion style** with dark palette and custom stylesheets)\n* title bar\n* buttons (minimize | restore | maximize | close)\n* move window by drag the title bar\n* dobule click title bar to toggle between window styte (maximize and normal)\n* use of native events, like minimizing or system menu\n\n\n## todo\n* [resize window on each corner [#1]](https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle/issues/1)\n* [snap on screen edges [#3]](https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle/issues/3)\n\n\n## thanks\nMany thanks goes to the [Qt Forum](https://forum.qt.io/topic/80654/how-to-create-vs2013-like-frameless-window-with-dark-style) and especially to [Chris Kawa](https://forum.qt.io/user/chris-kawa) for pointing me to some usual issues and hints of great must have features. \n\n\n## Licence\n> The MIT License\n>\n> Copyright (c) 2018, Juergen Skrotzky (https://github.com/Jorgen-VikingGod, JorgenVikingGod@gmail.com)\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"
  },
  {
    "path": "ext/Qt-Frameless-Window-DarkStyle-master/darkstyle/darkstyle.qss",
    "content": "QToolTip{\n  color:#ffffff;\n  background-color:palette(base);\n  border:1px solid palette(highlight);\n  border-radius:4px;\n}\nQStatusBar{\n  background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n  color:palette(mid);\n}\nQMenuBar{\n  background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n  border-bottom:2px solid rgba(25,25,25,75);\n}\nQMenuBar::item{\n  spacing:2px;\n  padding:3px 4px;\n  background:transparent;\n}\nQMenuBar::item:selected{\n  background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(106,106,106,255),stop:1 rgba(106,106,106,75));\n  border-left:1px solid rgba(106,106,106,127);\n  border-right:1px solid rgba(106,106,106,127);\n}\nQMenuBar::item:pressed{\n  background-color:palette(highlight);\n  border-left:1px solid rgba(25,25,25,127);\n  border-right:1px solid rgba(25,25,25,127);\n}\nQMenu{\n  background-color:palette(window);\n  border:1px solid palette(shadow);\n}\nQMenu::item{\n  padding:3px 25px 3px 25px;\n  border:1px solid transparent;\n}\nQMenu::item:disabled{\n  background-color:rgba(35,35,35,127);\n  color:palette(disabled);\n}\nQMenu::item:selected{\n  border-color:rgba(147,191,236,127);\n  background:palette(highlight);\n}\nQMenu::icon:checked{\n  background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n  border:1px solid palette(highlight);\n  border-radius:2px;\n}\nQMenu::separator{\n  height:1px;\n  background:palette(alternate-base);\n  margin-left:5px;\n  margin-right:5px;\n}\nQMenu::indicator{\n  width:18px;\n  height:18px;\n}\nQMenu::indicator:non-exclusive:checked{\n  image:url(:/darkstyle/icon_checkbox_checked.png);\n  padding-left:2px;\n}\nQMenu::indicator:non-exclusive:unchecked{\n  image:url(:/darkstyle/icon_checkbox_unchecked.png);\n  padding-left:2px;\n}\nQMenu::indicator:exclusive:checked{\n  image:url(:/darkstyle/icon_radiobutton_checked.png);\n  padding-left:2px;\n}\nQMenu::indicator:exclusive:unchecked{\n  image:url(:/darkstyle/icon_radiobutton_unchecked.png);\n  padding-left:2px;\n}\nQToolBar::top{\n  background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n  border-bottom:3px solid qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n}\nQToolBar::bottom{\n  background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n  border-top:3px solid qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n}\nQToolBar::left{\n  background-color:qlineargradient(x1:0,y1:0,x2:1,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n  border-right:3px solid qlineargradient(x1:0,y1:0,x2:1,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n}\nQToolBar::right{\n  background-color:qlineargradient(x1:1,y1:0,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n  border-left:3px solid qlineargradient(x1:1,y1:0,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n}\nQMainWindow::separator{\n  width:6px;\n  height:5px;\n  padding:2px;\n}\nQSplitter::handle:horizontal{\n  width:10px;\n}\nQSplitter::handle:vertical{\n  height:10px;\n}\nQMainWindow::separator:hover,QSplitter::handle:hover{\n  background:palette(highlight);\n}\nQDockWidget::title{\n  padding:4px;\n  background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n  border:1px solid rgba(25,25,25,75);\n  border-bottom:2px solid rgba(25,25,25,75);\n}\nQDockWidget{\n  titlebar-close-icon:url(:/darkstyle/icon_close.png);\n  titlebar-normal-icon:url(:/darkstyle/icon_restore.png);\n}\nQDockWidget::close-button,QDockWidget::float-button{\n  subcontrol-position:top right;\n  subcontrol-origin:margin;\n  position:absolute;\n  top:3px;\n  bottom:0px;\n  width:20px;\n  height:20px;\n}\nQDockWidget::close-button{\n  right:3px;\n}\nQDockWidget::float-button{\n  right:25px;\n}\nQGroupBox{\n  background-color:rgba(66,66,66,50%);\n  margin-top:27px;\n  border:1px solid rgba(25,25,25,127);\n  border-radius:4px;\n}\nQGroupBox::title{\n  subcontrol-origin:margin;\n  subcontrol-position:left top;\n  padding:4px 6px;\n  margin-left:3px;\n  background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n  border:1px solid rgba(25,25,25,75);\n  border-bottom:2px solid rgb(127,127,127);\n  border-top-left-radius:4px;\n  border-top-right-radius:4px;\n}\nQTabWidget::pane{\n  background-color:rgba(66,66,66,50%);\n  border-top:1px solid rgba(25,25,25,50%);\n}\nQTabWidget::tab-bar{\n  left:3px;\n  top:1px;\n}\nQTabBar{\n  background-color:transparent;\n  qproperty-drawBase:0;\n  border-bottom:1px solid rgba(25,25,25,50%);\n}\nQTabBar::tab{\n  padding:4px 6px;\n  background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n  border:1px solid rgba(25,25,25,75);\n  border-top-left-radius:4px;\n  border-top-right-radius:4px;\n}\nQTabBar::tab:selected,QTabBar::tab:hover{\n  background-color:qlineargradient(x1:0,y1:0,x2:0,y2:1,stop:0 rgba(53,53,53,127),stop:1 rgba(66,66,66,50%));\n  border-bottom-color:rgba(66,66,66,75%);\n}\nQTabBar::tab:selected{\n  border-bottom:2px solid palette(highlight);\n}\nQTabBar::tab::selected:disabled{\n  border-bottom:2px solid rgb(127,127,127);\n}\nQTabBar::tab:!selected{\n  margin-top:2px;\n}\nQTabBar::close-button {\n    image:url(:/darkstyle/icon_tbclose.png);\n    subcontrol-position:right;\n}\nQTabBar::close-button:hover {\n    image:url(:/darkstyle/icon_tbclose_hover.png);\n}\nQCheckBox::indicator{\n  width:18px;\n  height:18px;\n}\nQCheckBox::indicator:checked,QTreeView::indicator:checked,QTableView::indicator:checked,QGroupBox::indicator:checked{\n  image:url(:/darkstyle/icon_checkbox_checked.png);\n}\nQCheckBox::indicator:checked:pressed,QTreeView::indicator:checked:pressed,QTableView::indicator:checked:pressed,QGroupBox::indicator:checked:pressed{\n  image:url(:/darkstyle/icon_checkbox_checked_pressed.png);\n}\nQCheckBox::indicator:checked:disabled,QTreeView::indicator:checked:disabled,QTableView::indicator:checked:disabled,QGroupBox::indicator:checked:disabled{\n  image:url(:/darkstyle/icon_checkbox_checked_disabled.png);\n}\nQCheckBox::indicator:unchecked,QTreeView::indicator:unchecked,QTableView::indicator:unchecked,QGroupBox::indicator:unchecked{\n  image:url(:/darkstyle/icon_checkbox_unchecked.png);\n}\nQCheckBox::indicator:unchecked:pressed,QTreeView::indicator:unchecked:pressed,QTableView::indicator:unchecked:pressed,QGroupBox::indicator:unchecked:pressed{\n  image:url(:/darkstyle/icon_checkbox_unchecked_pressed.png);\n}\nQCheckBox::indicator:unchecked:disabled,QTreeView::indicator:unchecked:disabled,QTableView::indicator:unchecked:disabled,QGroupBox::indicator:unchecked:disabled{\n  image:url(:/darkstyle/icon_checkbox_unchecked_disabled.png);\n}\nQCheckBox::indicator:indeterminate,QTreeView::indicator:indeterminate,QTableView::indicator:indeterminate,QGroupBox::indicator:indeterminate{\n  image:url(:/darkstyle/icon_checkbox_indeterminate.png);\n}\nQCheckBox::indicator:indeterminate:pressed,QTreeView::indicator:indeterminate:pressed,QTableView::indicator:indeterminate:pressed,QGroupBox::indicator:indeterminate:pressed{\n  image:url(:/darkstyle/icon_checkbox_indeterminate_pressed.png);\n}\nQCheckBox::indicator:indeterminate:disabled,QTreeView::indicator:indeterminate:disabled,QTableView::indicator:indeterminate:disabled,QGroupBox::indicator:indeterminate:disabled{\n  image:url(:/darkstyle/icon_checkbox_indeterminate_disabled.png);\n}\nQRadioButton::indicator{\n  width:18px;\n  height:18px;\n}\nQRadioButton::indicator:checked{\n  image:url(:/darkstyle/icon_radiobutton_checked.png);\n}\nQRadioButton::indicator:checked:pressed{\n  image:url(:/darkstyle/icon_radiobutton_checked_pressed.png);\n}\nQRadioButton::indicator:checked:disabled{\n  image:url(:/darkstyle/icon_radiobutton_checked_disabled.png);\n}\nQRadioButton::indicator:unchecked{\n  image:url(:/darkstyle/icon_radiobutton_unchecked.png);\n}\nQRadioButton::indicator:unchecked:pressed{\n  image:url(:/darkstyle/icon_radiobutton_unchecked_pressed.png);\n}\nQRadioButton::indicator:unchecked:disabled{\n  image:url(:/darkstyle/icon_radiobutton_unchecked_disabled.png);\n}\nQTreeView, QTableView{\n  alternate-background-color:palette(window);\n  background:palette(base);\n}\nQToolBar::separator:vertical {\n    height:3px;\n    image:url(:/darkstyle/icon_sepvline.png);\n}\nQToolBar::separator:horizontal {\n    width:3px;\n    image:url(:/darkstyle/icon_sepvline.png);\n}\nQTreeView QHeaderView::section, QTableView QHeaderView::section{\n  background-color:qlineargradient(x1:0,y1:1,x2:0,y2:0,stop:0 rgba(25,25,25,127),stop:1 rgba(53,53,53,75));\n  border-style:none;\n  border-bottom:1px solid palette(dark);\n  padding-left:5px;\n  padding-right:5px;\n}\nQTreeView::item:selected:disabled, QTableView::item:selected:disabled{\n  background:rgb(80,80,80);\n}\nQTreeView::branch{\n  background-color:palette(base);\n}\nQTreeView::branch:has-siblings:!adjoins-item{\n  border-image:url(:/darkstyle/icon_vline.png) 0;\n}\nQTreeView::branch:has-siblings:adjoins-item{\n  border-image:url(:/darkstyle/icon_branch_more.png) 0;\n}\nQTreeView::branch:!has-children:!has-siblings:adjoins-item{\n  border-image:url(:/darkstyle/icon_branch_end.png) 0;\n}\nQTreeView::branch:has-children:!has-siblings:closed,\nQTreeView::branch:closed:has-children:has-siblings{\n  border-image:none;\n  image:url(:/darkstyle/icon_branch_closed.png);\n}\nQTreeView::branch:open:has-children:!has-siblings,\nQTreeView::branch:open:has-children:has-siblings{\n  border-image:none;\n  image:url(:/darkstyle/icon_branch_open.png);\n}\nQScrollBar:vertical{\n  background:palette(base);\n  border-top-right-radius:2px;\n  border-bottom-right-radius:2px;\n  width:16px;\n  margin:0px;\n}\nQScrollBar::handle:vertical{\n  background-color:palette(alternate-base);\n  border-radius:2px;\n  min-height:20px;\n  margin:2px 4px 2px 4px;\n}\nQScrollBar::handle:vertical:hover{\n  background-color:palette(highlight);\n}\nQScrollBar::add-line:vertical{\n  background:none;\n  height:0px;\n  subcontrol-position:right;\n  subcontrol-origin:margin;\n}\nQScrollBar::sub-line:vertical{\n  background:none;\n  height:0px;\n  subcontrol-position:left;\n  subcontrol-origin:margin;\n}\nQScrollBar:horizontal{\n  background:palette(base);\n  height:16px;\n  margin:0px;\n}\nQScrollBar::handle:horizontal{\n  background-color:palette(alternate-base);\n  border-radius:2px;\n  min-width:20px;\n  margin:4px 2px 4px 2px;\n}\nQScrollBar::handle:horizontal:hover{\n  background-color:palette(highlight);\n}\nQScrollBar::add-line:horizontal{\n  background:none;\n  width:0px;\n  subcontrol-position:bottom;\n  subcontrol-origin:margin;\n}\nQScrollBar::sub-line:horizontal{\n  background:none;\n  width:0px;\n  subcontrol-position:top;\n  subcontrol-origin:margin;\n}\nQSlider::handle:horizontal{\n  border-radius:4px;\n  border:1px solid rgba(25,25,25,255);\n  background-color:palette(alternate-base);\n  min-height:20px;\n  margin:0 -4px;\n}\nQSlider::handle:horizontal:hover{\n  background:palette(highlight);\n}\nQSlider::add-page:horizontal{\n  background:palette(base);\n}\nQSlider::sub-page:horizontal{\n  background:palette(highlight);\n}\nQSlider::sub-page:horizontal:disabled{\n  background:rgb(80,80,80);\n}\n"
  },
  {
    "path": "ext/Qt-Frameless-Window-DarkStyle-master/darkstyle.qrc",
    "content": "<RCC>\n    <qresource prefix=\"/\">\n        <file>darkstyle/darkstyle.qss</file>\n        <file>darkstyle/icon_close.png</file>\n        <file>darkstyle/icon_restore.png</file>\n        <file>darkstyle/icon_undock.png</file>\n        <file>darkstyle/icon_branch_closed.png</file>\n        <file>darkstyle/icon_branch_end.png</file>\n        <file>darkstyle/icon_branch_more.png</file>\n        <file>darkstyle/icon_branch_open.png</file>\n        <file>darkstyle/icon_vline.png</file>\n        <file>darkstyle/icon_checkbox_checked.png</file>\n        <file>darkstyle/icon_checkbox_indeterminate.png</file>\n        <file>darkstyle/icon_checkbox_unchecked.png</file>\n        <file>darkstyle/icon_checkbox_checked_pressed.png</file>\n        <file>darkstyle/icon_checkbox_indeterminate_pressed.png</file>\n        <file>darkstyle/icon_checkbox_unchecked_pressed.png</file>\n        <file>darkstyle/icon_checkbox_checked_disabled.png</file>\n        <file>darkstyle/icon_checkbox_indeterminate_disabled.png</file>\n        <file>darkstyle/icon_checkbox_unchecked_disabled.png</file>\n        <file>darkstyle/icon_radiobutton_checked.png</file>\n        <file>darkstyle/icon_radiobutton_unchecked.png</file>\n        <file>darkstyle/icon_radiobutton_checked_pressed.png</file>\n        <file>darkstyle/icon_radiobutton_unchecked_pressed.png</file>\n        <file>darkstyle/icon_radiobutton_checked_disabled.png</file>\n        <file>darkstyle/icon_radiobutton_unchecked_disabled.png</file>\n        <file>darkstyle/icon_tbclose.png</file>\n        <file>darkstyle/icon_tbclose_hover.png</file>\n        <file>darkstyle/icon_sepvline.png</file>\n    </qresource>\n</RCC>\n"
  },
  {
    "path": "ext/Qt-Frameless-Window-DarkStyle-master/frameless_window_dark.pro",
    "content": "###############################################################################\n#                                                                             #\n# The MIT License                                                             #\n#                                                                             #\n# Copyright (C) 2017 by Juergen Skrotzky (JorgenVikingGod@gmail.com)          #\n#               >> https://github.com/Jorgen-VikingGod                        #\n#                                                                             #\n# Sources: https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle  #\n#                                                                             #\n###############################################################################\n\nQT       += core gui\n\ngreaterThan(QT_MAJOR_VERSION, 4): QT += widgets\n\nINCLUDEPATH +=\"framelesswindow\"\n\nTARGET      =  QtFramelessWindowDarkStyle\nTEMPLATE    =  app\n\nSOURCES     += main.cpp\\\n               mainwindow.cpp \\\n               framelesswindow/framelesswindow.cpp \\\n               framelesswindow/windowdragger.cpp \\\n               DarkStyle.cpp\n\n\nHEADERS     += mainwindow.h \\\n               framelesswindow/framelesswindow.h \\\n               framelesswindow/windowdragger.h \\\n               DarkStyle.h\n\n\nFORMS       += mainwindow.ui \\\n               framelesswindow/framelesswindow.ui\n\nRESOURCES   += darkstyle.qrc \\\n               framelesswindow.qrc\n"
  },
  {
    "path": "ext/Qt-Frameless-Window-DarkStyle-master/framelesswindow/framelesswindow.cpp",
    "content": "/*\n###############################################################################\n#                                                                             #\n# The MIT License                                                             #\n#                                                                             #\n# Copyright (C) 2017 by Juergen Skrotzky (JorgenVikingGod@gmail.com)          #\n#               >> https://github.com/Jorgen-VikingGod                        #\n#                                                                             #\n# Sources: https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle  #\n#                                                                             #\n###############################################################################\n*/\n\n#include <QGraphicsDropShadowEffect>\n#include \"framelesswindow.h\"\n#include \"ui_framelesswindow.h\"\n\nFramelessWindow::FramelessWindow(QWidget *parent): QWidget(parent), ui(new Ui::FramelessWindow)\n{\n  ui->setupUi(this);\n\n  ContDlg = false;\n  setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);\n  // append minimize button flag in case of windows,\n  // for correct windows native handling of minimize function\n#if defined(Q_OS_WIN)\n  setWindowFlags(windowFlags() | Qt::WindowMinimizeButtonHint);\n#endif\n  setAttribute(Qt::WA_TranslucentBackground);\n\n  ui->restoreButton->setVisible(false);\n\n  // add window shadow\n  if (   QSysInfo::productType().toLower() != \"windows\"\n     || (QSysInfo::productType().toLower() == \"windows\" && QSysInfo::productVersion().toInt() > 7) ) {\n\n     //shadow under window title text\n     QGraphicsDropShadowEffect *textShadow = new QGraphicsDropShadowEffect;\n     textShadow->setBlurRadius(4.0);\n     textShadow->setColor(QColor(0,0,0));\n     textShadow->setOffset(0.0);\n     ui->titleText->setGraphicsEffect(textShadow);\n\n     //window shadow\n     QGraphicsDropShadowEffect *windowShadow = new QGraphicsDropShadowEffect;\n     windowShadow->setBlurRadius(9.0);\n     windowShadow->setColor(palette().color(QPalette::Highlight));\n     windowShadow->setOffset(0.0);\n     ui->windowFrame->setGraphicsEffect(windowShadow);\n  }\n\n  // watch mouse clicks on icon label and fire own signals\n  MouseButtonSignaler signaler;\n  signaler.installOn(ui->icon);\n  QObject::connect(&signaler, &MouseButtonSignaler::mouseButtonEvent, [this](QWidget*, QMouseEvent * event) {\n    if (event->type() == QEvent::MouseButtonPress) {\n      QMouseEvent *mouse = static_cast<QMouseEvent*>(event);\n      if (mouse->button() == Qt::LeftButton) {\n        emit windowIconLeftClicked();\n      } else if (mouse->button() == Qt::RightButton) {\n        emit windowIconRightClicked();\n      }\n    } else if (event->type() == QEvent::MouseButtonDblClick) {\n      emit windowIconDblClick();\n    }\n  });\n\n  QObject::connect(qApp, &QGuiApplication::applicationStateChanged, this, &FramelessWindow::on_applicationStateChanged);\n}\n\nvoid FramelessWindow::changeEvent(QEvent *event)\n{\n  if (event->type() == QEvent::WindowStateChange) {\n    if (windowState().testFlag(Qt::WindowNoState)) {\n      on_restoreButton_clicked();\n      event->ignore();\n    } else if (windowState().testFlag(Qt::WindowFullScreen)) {\n      on_maximizeButton_clicked();\n      event->ignore();\n    }\n  }\n  event->accept();\n}\n\nvoid FramelessWindow::setContent(QWidget *w)\n{\n  contentLayout.setContentsMargins(0,0,0,0);\n  contentLayout.addWidget(w);\n  ui->windowContent->setLayout(&contentLayout);\n}\n\n\nvoid FramelessWindow::ContentDlg(QDialog *indlg)\n{\n    ContDlg = true;\n    dlgCont = indlg;\n}\n\nvoid FramelessWindow::SetTitleBarBtns(bool Maximize, bool Minimize, bool Close)\n{\n    ui->closeButton->setVisible(Close);\n    ui->maximizeButton->setVisible(Maximize);\n    ui->minimizeButton->setVisible(Minimize);\n}\n\nvoid FramelessWindow::setMargins(int size)\n{\n    layout()->setMargin(size);\n //   layout()->setContentsMargins(size, size, size, size);\n\n}\n\nvoid FramelessWindow::setWindowTitle(const QString &text)\n{\n\n  ui->titleText->setText(text);\n}\n\nvoid FramelessWindow::setWindowIcon(const QIcon &ico)\n{\n  ui->icon->setPixmap(ico.pixmap(16,16));\n}\n\nvoid FramelessWindow::styleWindow(bool bActive, bool bNoState)\n{\n  if (bActive) {\n    if (bNoState) {\n      setMargins(15);\n      ui->windowTitlebar->setStyleSheet(\"#windowTitlebar{border: 0px none palette(shadow); border-top-left-radius:5px; border-top-right-radius:5px; background-color:palette(shadow); height:20px;}\");\n      ui->windowFrame->setStyleSheet(\"#windowFrame{border:1px solid palette(highlight); border-radius:5px 5px 5px 5px; background-color:palette(Window);}\");\n      QGraphicsEffect *oldShadow = ui->windowFrame->graphicsEffect();\n      if (oldShadow)\n        delete oldShadow;\n      QGraphicsDropShadowEffect *windowShadow = new QGraphicsDropShadowEffect;\n      windowShadow->setBlurRadius(9.0);\n      windowShadow->setColor(palette().color(QPalette::Highlight));\n      windowShadow->setOffset(0.0);\n      ui->windowFrame->setGraphicsEffect(windowShadow);\n    } else {\n      setMargins(0);\n      ui->windowTitlebar->setStyleSheet(\"#windowTitlebar{border: 0px none palette(shadow); border-top-left-radius:0px; border-top-right-radius:0px; background-color:palette(shadow); height:20px;}\");\n      ui->windowFrame->setStyleSheet(\"#windowFrame{border:1px solid palette(dark); border-radius:0px 0px 0px 0px; background-color:palette(Window);}\");\n      QGraphicsEffect *oldShadow = ui->windowFrame->graphicsEffect();\n      if (oldShadow)\n        delete oldShadow;\n      ui->windowFrame->setGraphicsEffect(nullptr);\n    } // if (bNoState) else maximize\n  } else {\n    if (bNoState) {\n      setMargins(15);\n      ui->windowTitlebar->setStyleSheet(\"#windowTitlebar{border: 0px none palette(shadow); border-top-left-radius:5px; border-top-right-radius:5px; background-color:palette(dark); height:20px;}\");\n      ui->windowFrame->setStyleSheet(\"#windowFrame{border:1px solid #000000; border-radius:5px 5px 5px 5px; background-color:palette(Window);}\");\n      QGraphicsEffect *oldShadow = ui->windowFrame->graphicsEffect();\n      if (oldShadow)\n        delete oldShadow;\n      QGraphicsDropShadowEffect *windowShadow = new QGraphicsDropShadowEffect;\n      windowShadow->setBlurRadius(9.0);\n      windowShadow->setColor(palette().color(QPalette::Shadow));\n      windowShadow->setOffset(0.0);\n      ui->windowFrame->setGraphicsEffect(windowShadow);\n    } else {\n      setMargins(0);\n      ui->windowTitlebar->setStyleSheet(\"#titlebarWidget{border: 0px none palette(shadow); border-top-left-radius:0px; border-top-right-radius:0px; background-color:palette(dark); height:20px;}\");\n      ui->windowFrame->setStyleSheet(\"#windowFrame{border:1px solid palette(shadow); border-radius:0px 0px 0px 0px; background-color:palette(Window);}\");\n      QGraphicsEffect *oldShadow = ui->windowFrame->graphicsEffect();\n      if (oldShadow)\n        delete oldShadow;\n      ui->windowFrame->setGraphicsEffect(nullptr);\n    } // if (bNoState) { else maximize\n  } // if (bActive) { else no focus\n}\n\nvoid FramelessWindow::on_applicationStateChanged(Qt::ApplicationState state)\n{\n  if (windowState().testFlag(Qt::WindowNoState)) {\n    if (state == Qt::ApplicationActive) {\n      styleWindow(true, true);\n    } else {\n      styleWindow(false, true);\n    }\n  } else if (windowState().testFlag(Qt::WindowFullScreen)) {\n    if (state == Qt::ApplicationActive) {\n      styleWindow(true, false);\n    } else {\n      styleWindow(false, false);\n    }\n  }\n}\n\nvoid FramelessWindow::on_minimizeButton_clicked()\n{\n  setWindowState(Qt::WindowMinimized);\n}\n\nvoid FramelessWindow::on_restoreButton_clicked() {\n  setMargins(15);\n  ui->restoreButton->setVisible(false);\n  ui->maximizeButton->setVisible(true);\n  setWindowState(Qt::WindowNoState);\n  styleWindow(true, true);\n}\nvoid FramelessWindow::on_maximizeButton_clicked()\n{\n  setMargins(0);\n  ui->restoreButton->setVisible(true);\n  ui->maximizeButton->setVisible(false);\n  setWindowState(Qt::WindowFullScreen);\n  styleWindow(true, false);\n}\nvoid FramelessWindow::on_closeButton_clicked()\n{\n  close();\n  if (ContDlg)\n      dlgCont->reject();\n}\n\nvoid FramelessWindow::on_windowTitlebar_doubleClicked()\n{\n    if (!ui->maximizeButton->isVisible())\n        return;\n\n  if (windowState().testFlag(Qt::WindowNoState)) {\n    on_maximizeButton_clicked();\n  } else if (windowState().testFlag(Qt::WindowFullScreen)) {\n    on_restoreButton_clicked();\n  }\n}\n"
  },
  {
    "path": "ext/Qt-Frameless-Window-DarkStyle-master/framelesswindow/framelesswindow.h",
    "content": "/*\n###############################################################################\n#                                                                             #\n# The MIT License                                                             #\n#                                                                             #\n# Copyright (C) 2017 by Juergen Skrotzky (JorgenVikingGod@gmail.com)          #\n#               >> https://github.com/Jorgen-VikingGod                        #\n#                                                                             #\n# Sources: https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle  #\n#                                                                             #\n###############################################################################\n*/\n\n#ifndef FRAMELESSWINDOW_H\n#define FRAMELESSWINDOW_H\n\n#include <QEvent>\n#include <QtWidgets>\n\nnamespace Ui {\n  class FramelessWindow;\n}\n\nclass MouseButtonSignaler: public QObject\n{\n  Q_OBJECT\n\npublic:\n  MouseButtonSignaler(QObject * parent = 0) : QObject(parent) {}\n  void installOn(QWidget * widget) { widget->installEventFilter(this); }\n\nprotected:\n  virtual bool eventFilter(QObject * obj, QEvent * ev) Q_DECL_OVERRIDE {\n    if ((   ev->type() == QEvent::MouseButtonPress\n         || ev->type() == QEvent::MouseButtonRelease\n         || ev->type() == QEvent::MouseButtonDblClick)\n        && obj->isWidgetType()) {\n      emit mouseButtonEvent(static_cast<QWidget*>(obj),\n                            static_cast<QMouseEvent*>(ev));\n    }\n    return false;\n  }\nsignals:\n  void mouseButtonEvent(QWidget *, QMouseEvent *);\n};\n\nclass FramelessWindow: public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit FramelessWindow(QWidget *parent = 0);\n  void setContent(QWidget *w);\n\n  // Set a content dialog which if the close button is done, it sends a cancel signal.\n  void ContentDlg(QDialog* indlg);\n  void SetTitleBarBtns(bool Maximize,bool Minimize,bool Close);\nprivate:\n  void setMargins(int size);\n  void styleWindow(bool bActive, bool bNoState);\n\n  bool ContDlg;\n  QDialog* dlgCont;\n\nsignals:\n  void windowIconLeftClicked();\n  void windowIconRightClicked();\n  void windowIconDblClick();\n\npublic slots:\n  void setWindowTitle(const QString &text);\n  void setWindowIcon(const QIcon &ico);\n\nprivate slots:\n  void on_applicationStateChanged(Qt::ApplicationState state);\n  void on_minimizeButton_clicked();\n  void on_restoreButton_clicked();\n  void on_maximizeButton_clicked();\n  void on_closeButton_clicked();\n  void on_windowTitlebar_doubleClicked();\n\nprotected:\n  virtual void changeEvent(QEvent *event);\n\nprivate:\n  Ui::FramelessWindow *ui;\n\nprotected:\n  QHBoxLayout contentLayout;\n};\n\n#endif // FRAMELESSWINDOW_H\n"
  },
  {
    "path": "ext/Qt-Frameless-Window-DarkStyle-master/framelesswindow/framelesswindow.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>FramelessWindow</class>\n <widget class=\"QWidget\" name=\"FramelessWindow\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>958</width>\n    <height>621</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string/>\n  </property>\n  <property name=\"autoFillBackground\">\n   <bool>false</bool>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n   <property name=\"spacing\">\n    <number>0</number>\n   </property>\n   <property name=\"sizeConstraint\">\n    <enum>QLayout::SetNoConstraint</enum>\n   </property>\n   <property name=\"leftMargin\">\n    <number>15</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>15</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>15</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>15</number>\n   </property>\n   <item>\n    <widget class=\"QWidget\" name=\"windowFrame\" native=\"true\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"autoFillBackground\">\n      <bool>false</bool>\n     </property>\n     <property name=\"styleSheet\">\n      <string notr=\"true\">#windowFrame{border:1px solid palette(highlight); border-radius:5px 5px 5px 5px; background-color:palette(Window);}</string>\n     </property>\n     <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n      <property name=\"spacing\">\n       <number>0</number>\n      </property>\n      <property name=\"sizeConstraint\">\n       <enum>QLayout::SetNoConstraint</enum>\n      </property>\n      <property name=\"leftMargin\">\n       <number>1</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>1</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>1</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>1</number>\n      </property>\n      <item>\n       <widget class=\"WindowDragger\" name=\"windowTitlebar\" native=\"true\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"minimumSize\">\n         <size>\n          <width>0</width>\n          <height>0</height>\n         </size>\n        </property>\n        <property name=\"autoFillBackground\">\n         <bool>false</bool>\n        </property>\n        <property name=\"styleSheet\">\n         <string notr=\"true\">#windowTitlebar{border: 0px none palette(base); border-top-left-radius:5px; border-top-right-radius:5px; background-color:palette(shadow); height:20px;}</string>\n        </property>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout\" stretch=\"0,0,1,0,0,0,0\">\n         <property name=\"spacing\">\n          <number>0</number>\n         </property>\n         <property name=\"leftMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <widget class=\"QLabel\" name=\"spacer\">\n           <property name=\"minimumSize\">\n            <size>\n             <width>4</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"maximumSize\">\n            <size>\n             <width>4</width>\n             <height>16777215</height>\n            </size>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLabel\" name=\"icon\">\n           <property name=\"minimumSize\">\n            <size>\n             <width>16</width>\n             <height>16</height>\n            </size>\n           </property>\n           <property name=\"maximumSize\">\n            <size>\n             <width>16</width>\n             <height>16</height>\n            </size>\n           </property>\n           <property name=\"contextMenuPolicy\">\n            <enum>Qt::NoContextMenu</enum>\n           </property>\n           <property name=\"styleSheet\">\n            <string notr=\"true\">#icon {background-color:palette(shadow);}</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLabel\" name=\"titleText\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"font\">\n            <font>\n             <weight>75</weight>\n             <bold>true</bold>\n            </font>\n           </property>\n           <property name=\"styleSheet\">\n            <string notr=\"true\">  padding-left:5px;\n  color:rgb(153,153,153);</string>\n           </property>\n           <property name=\"frameShape\">\n            <enum>QFrame::StyledPanel</enum>\n           </property>\n           <property name=\"text\">\n            <string>String Table Editor</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QToolButton\" name=\"minimizeButton\">\n           <property name=\"font\">\n            <font>\n             <weight>75</weight>\n             <bold>true</bold>\n            </font>\n           </property>\n           <property name=\"styleSheet\">\n            <string notr=\"true\">#minimizeButton{\n  background-color:none;\n  border:none;\n  width:16px;\n  height:16px;\n  padding:4px;\n  image:url(:/images/icon_window_minimize.png);\n}\n#minimizeButton:hover{\n  background-color:palette(alternate-base);\n}\n#minimizeButton:pressed{\n  background-color:palette(highlight);\n}</string>\n           </property>\n           <property name=\"text\">\n            <string/>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QToolButton\" name=\"restoreButton\">\n           <property name=\"styleSheet\">\n            <string notr=\"true\">#restoreButton{\n  background-color:none;\n  border:none;\n  width:16px;\n  height:16px;\n  padding:4px;\n  image:url(:/images/icon_window_restore.png);\n}\n#restoreButton:hover{\n  background-color:palette(alternate-base);\n}\n#restoreButton:pressed{\n  background-color:palette(highlight);\n}</string>\n           </property>\n           <property name=\"text\">\n            <string/>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QToolButton\" name=\"maximizeButton\">\n           <property name=\"styleSheet\">\n            <string notr=\"true\">#maximizeButton{\n  background-color:none;\n  border:none;\n  width:16px;\n  height:16px;\n  padding:4px;\n  image:url(:/images/icon_window_maximize.png);\n}\n#maximizeButton:hover{\n  background-color:palette(alternate-base);\n}\n##maximizeButton:pressed{\n  background-color:palette(highlight);\n}</string>\n           </property>\n           <property name=\"text\">\n            <string/>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QToolButton\" name=\"closeButton\">\n           <property name=\"font\">\n            <font>\n             <weight>75</weight>\n             <bold>true</bold>\n            </font>\n           </property>\n           <property name=\"styleSheet\">\n            <string notr=\"true\">#closeButton{\n  background-color:none;\n  border:none;\n  width:16px;\n  height:16px;\n  padding:4px;\n  image:url(:/images/icon_window_close.png);\n  border-top-right-radius: 5px;\n}\n#closeButton:hover{\n  background-color:palette(alternate-base);\n}\n##closeButton:pressed{\n  background-color:palette(highlight);\n}</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QWidget\" name=\"windowContent\" native=\"true\">\n        <property name=\"autoFillBackground\">\n         <bool>false</bool>\n        </property>\n        <property name=\"styleSheet\">\n         <string notr=\"true\">#windowContent{\n  border: 0px none palette(base);\n  border-radius:0px 0px 5px 5px;\n}</string>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <layoutdefault spacing=\"6\" margin=\"11\"/>\n <customwidgets>\n  <customwidget>\n   <class>WindowDragger</class>\n   <extends>QWidget</extends>\n   <header>windowdragger.h</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "ext/Qt-Frameless-Window-DarkStyle-master/framelesswindow/windowdragger.cpp",
    "content": "/*\n###############################################################################\n#                                                                             #\n# The MIT License                                                             #\n#                                                                             #\n# Copyright (C) 2017 by Juergen Skrotzky (JorgenVikingGod@gmail.com)          #\n#               >> https://github.com/Jorgen-VikingGod                        #\n#                                                                             #\n# Sources: https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle  #\n#                                                                             #\n###############################################################################\n*/\n\n#include <QStyleOption>\n#include <QPainter>\n#include \"windowdragger.h\"\n\nWindowDragger::WindowDragger(QWidget *parent): QWidget(parent)\n{\n  mousePressed = false;\n}\n\nvoid WindowDragger::mousePressEvent(QMouseEvent *event)\n{\n  mousePressed = true;\n  mousePos = event->globalPos();\n\n  QWidget *parent = parentWidget();\n  if (parent)\n    parent = parent->parentWidget();\n\n  if (parent)\n    wndPos = parent->pos();\n}\n\nvoid WindowDragger::mouseMoveEvent(QMouseEvent *event)\n{\n  QWidget *parent = parentWidget();\n  if (parent)\n    parent = parent->parentWidget();\n\n  if (parent && mousePressed)\n    parent->move(wndPos + (event->globalPos() - mousePos));\n}\n\nvoid WindowDragger::mouseReleaseEvent(QMouseEvent *event)\n{\n  Q_UNUSED(event);\n  mousePressed = false;\n}\n\nvoid WindowDragger::paintEvent(QPaintEvent *event)\n{\n  Q_UNUSED(event);\n  QStyleOption styleOption;\n  styleOption.initFrom(this);\n  QPainter painter(this);\n  style()->drawPrimitive(QStyle::PE_Widget, &styleOption, &painter, this);\n}\n\nvoid WindowDragger::mouseDoubleClickEvent(QMouseEvent *event)\n{\n  Q_UNUSED(event);\n  emit doubleClicked();\n}\n\n"
  },
  {
    "path": "ext/Qt-Frameless-Window-DarkStyle-master/framelesswindow/windowdragger.h",
    "content": "/*\n###############################################################################\n#                                                                             #\n# The MIT License                                                             #\n#                                                                             #\n# Copyright (C) 2017 by Juergen Skrotzky (JorgenVikingGod@gmail.com)          #\n#               >> https://github.com/Jorgen-VikingGod                        #\n#                                                                             #\n# Sources: https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle  #\n#                                                                             #\n###############################################################################\n*/\n\n#ifndef WINDOWDRAGGER_H\n#define WINDOWDRAGGER_H\n\n#include <QWidget>\n#include <QMouseEvent>\n\nclass WindowDragger : public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit WindowDragger(QWidget *parent = 0);\n\nsignals:\n  void doubleClicked();\n\nprotected:\n  void mousePressEvent(QMouseEvent *event);\n  void mouseMoveEvent(QMouseEvent *event);\n  void mouseReleaseEvent(QMouseEvent *event);\n  void mouseDoubleClickEvent(QMouseEvent *event);\n  void paintEvent(QPaintEvent *event);\n\nprotected:\n  bool   mousePressed;\n  QPoint mousePos;\n  QPoint wndPos;\n};\n\n#endif // WINDOWDRAGGER_H\n"
  },
  {
    "path": "ext/Qt-Frameless-Window-DarkStyle-master/framelesswindow.qrc",
    "content": "<RCC>\n    <qresource prefix=\"/\">\n        <file>images/icon_window_minimize.png</file>\n        <file>images/icon_window_restore.png</file>\n        <file>images/icon_window_maximize.png</file>\n        <file>images/icon_window_close.png</file>\n    </qresource>\n</RCC>\n"
  },
  {
    "path": "ext/ZCharScanner.cpp",
    "content": "#include \"ZCharScanner.h\"\nusing namespace std;\n#include <stdexcept>\n\nint ZStringDelimiter::key_search(const GString& s, const GString& key)\n{\n\tint count = 0;\n\tsize_t pos = 0;\n\twhile ((pos = s.find(key, pos)) != GString::npos) {\n\t\t++count;\n\t\t++pos;\n\t}\n\treturn count;\n}\nvoid ZStringDelimiter::UpdateTokens()\n{\n    if (!m_vDelimiters.size() || m_sString == \"\")\n\t\treturn;\n\n\tm_vTokens.clear();\n\n\n\tvector<GString>::iterator dIt = m_vDelimiters.begin();\n\twhile (dIt != m_vDelimiters.end())\n\t{\n\t\tGString delimiter = *dIt;\n\t\n\n\t\tDelimStr(m_sString, delimiter, true);\n\t\t\n\t\n\t\t++dIt;\n\t}\n\t\n\t\n\n}\n\n\nvoid ZStringDelimiter::DelimStr(const GString & s, const GString & delimiter, const bool & removeEmptyEntries)\n{\n\tBarRange(0, s.length());\n\tfor (size_t start = 0, end; start < s.length(); start = end + delimiter.length())\n\t{\n\t\tsize_t position = s.find(delimiter, start);\n\t\tend = position != GString::npos ? position : s.length();\n\n\t\tGString token = s.substr(start, end - start);\n\t\tif (!removeEmptyEntries || !token.empty())\n\t\t{\n\t\t\tif (token != s)\n\t\t\t\tm_vTokens.push_back(token);\n\n\t\t}\n\t\tBar(position);\n\t}\n\n\t// dadwwdawdaawdwadwd\n}\n\nvoid ZStringDelimiter::BarRange(const int & min, const int & max)\n{\n#ifdef _AFX_ALL_WARNINGS\n\tif (PgBar)\n\t\tm_pBar->SetRange32(min, max);\n\n\n#endif\n}\n\nvoid ZStringDelimiter::Bar(const int & pos)\n{\n#ifdef _AFX_ALL_WARNINGS\n\tif (PgBar)\n\t\tm_pBar->SetPos(pos);\n\n\n#endif\n}\n\nZStringDelimiter::ZStringDelimiter()\n{\n    m_sString = \"\";\n\ttokenIndex = 0;\n\tPgBar = false;\n}\n\n\nbool ZStringDelimiter::GetFirstToken(GString & in_out)\n{\n\tif (m_vTokens.size() >= 1) {\n\t\tin_out = m_vTokens[0];\n\t\treturn true;\n\t}\n\telse {\n        return false;\n\t}\n}\n\nbool ZStringDelimiter::GetNextToken(GString & in_sOut)\n{\n\tif (tokenIndex > m_vTokens.size() - 1)\n\t\treturn false;\n\n\tin_sOut = m_vTokens[tokenIndex];\n\t++tokenIndex;\n\n\treturn true;\n}\n\nGString ZStringDelimiter::operator[](const size_t & in_index)\n{\n\tif (in_index > m_vTokens.size())\n\t\tthrow std::out_of_range(\"ZStringDelimiter tried to access token higher than size\");\n\n\treturn m_vTokens[in_index];\n\n}\nGString ZStringDelimiter::Reassemble(const GString& delim, const int& nelem)\n{\n    GString Result = \"\";\n\tTokenIterator RasIt = m_vTokens.begin();\n\tint r = 0;\n\tif (nelem == -1) {\n\t\twhile (RasIt != m_vTokens.end())\n\t\t{\n\n\t\t\tif (r != 0)\n\t\t\t\tResult.append(delim);\n\n\t\t\tResult.append(*RasIt);\n\n\t\t\t++r;\n\n\n\t\t\t++RasIt;\n\t\t}\n\t}\n\telse {\n\t\twhile (RasIt != m_vTokens.end() && r < nelem)\n\t\t{\n\t\t\n\t\t\tif (r != 0)\n\t\t\t\tResult.append(delim);\n\n\t\t\tResult.append(*RasIt);\n\n\t\t\t++r;\n\t\t\t++RasIt;\n\t\t}\n\t}\n\t\n\treturn Result;\n\n}\n\nGString ZStringDelimiter::Reassemble(const GString & delim, const std::vector<GString>& Strs,int nelem)\n{\n    GString Result = \"\";\n\tTokenIterator RasIt = Strs.begin();\n\tint r = 0;\n\tif (nelem == -1) {\n\t\twhile (RasIt != Strs.end())\n\t\t{\n\n\t\t\tif (r != 0)\n\t\t\t\tResult.append(delim);\n\n\t\t\tResult.append(*RasIt);\n\n\t\t\t++r;\n\n\n\t\t\t++RasIt;\n\t\t}\n\t}\n\telse {\n\t\twhile (RasIt != Strs.end() && r < nelem)\n\t\t{\n\n\t\t\tif (r != 0)\n\t\t\t\tResult.append(delim);\n\n\t\t\tResult.append(*RasIt);\n\n\t\t\t++r;\n\t\t\t++RasIt;\n\t\t}\n\t}\n\n\treturn Result;\n}\n\nvoid ZStringDelimiter::AddDelimiter(const GString & in_Delim)\n{\n\tm_vDelimiters.push_back(in_Delim);\n\tUpdateTokens();\n\n}\n\nvoid ZStringDelimiter::SetDelimiters(const std::vector<GString> &Delims)\n{\n    m_vDelimiters.assign(Delims.begin(),Delims.end());\n    UpdateTokens();\n\n}\n\nZStringDelimiter::~ZStringDelimiter()\n{\n}\n"
  },
  {
    "path": "ext/ZCharScanner.h",
    "content": "#pragma once\n\n#define GBasicCharScanner ZStringDelimiter\n\n#include <vector>\n#include <string>\n\n#ifndef ZSDEL_USE_WSTRING\n#define GString std::string\n#else\n#define GString std::wstring\n#endif\n\ntypedef std::vector<GString>::const_iterator TokenIterator;\n\n// ZStringDelimiter\n// ==============\n// Simple class to delimit and split strings.\n// You can use operator[] to access them\n// Or you can use the itBegin() and itEnd() to get some iterators\n// =================\nclass ZStringDelimiter\n{\nprivate:\n\tint key_search(const GString & s, const GString & key);\n\tvoid UpdateTokens();\n\tstd::vector<GString> m_vTokens;\n\tstd::vector<GString> m_vDelimiters;\n\n\tGString m_sString;\n\n\tvoid DelimStr(const GString& s, const GString& delimiter, const bool& removeEmptyEntries = false);\n\tvoid BarRange(const int& min, const int& max);\n\tvoid Bar(const int& pos);\n\tsize_t tokenIndex;\npublic:\n\tZStringDelimiter();\n\tbool PgBar;\n\n#ifdef _AFX_ALL_WARNINGS\n\tCProgressCtrl* m_pBar;\n#endif\n\n\tZStringDelimiter(const GString& in_iStr) {\n\t\tm_sString = in_iStr;\n\t\tPgBar = false;\n\n\t}\n\n\tbool GetFirstToken(GString& in_out);\n\tbool GetNextToken(GString& in_sOut);\n\n\t// std::String alts\n\n\tsize_t szTokens() { return m_vTokens.size(); }\n\tGString operator[](const size_t& in_index);\n\n\tGString Reassemble(const GString & delim, const int & nelem = -1);\n\n\t// Override to reassemble provided tokens.\n\tGString Reassemble(const GString & delim, const std::vector<GString>& Strs,int nelem = -1);\n\n\t// Get a const reference to the tokens\n\tconst std::vector<GString>& GetTokens() { return m_vTokens; }\n\n\tTokenIterator itBegin() { return m_vTokens.begin(); }\n\tTokenIterator itEnd() { return m_vTokens.end(); }\n\n\tvoid SetText(const GString& in_Txt) { \n\t\tm_sString = in_Txt; \n\t\tif (m_vDelimiters.size())\n\t\t\tUpdateTokens();\n\t}\n\tvoid AddDelimiter(const GString& in_Delim);\n    void SetDelimiters(const std::vector<GString>& Delims);\n\n\t~ZStringDelimiter();\n};\n\n"
  },
  {
    "path": "ext/ZCharScannerWide.cpp",
    "content": "#include \"ZCharScannerWide.h\"\nusing namespace std;\n#include <stdexcept>\n\nint ZStringDelimiterWide::key_search(const std::wstring& s, const std::wstring& key)\n{\n\tint count = 0;\n\tsize_t pos = 0;\n    while ((pos = s.find(key, pos)) != std::wstring::npos) {\n\t\t++count;\n\t\t++pos;\n\t}\n\treturn count;\n}\nvoid ZStringDelimiterWide::UpdateTokens()\n{\n    if (!m_vDelimiters.size() || m_sString == L\"\")\n\t\treturn;\n\n\tm_vTokens.clear();\n\n\n    vector<std::wstring>::iterator dIt = m_vDelimiters.begin();\n\twhile (dIt != m_vDelimiters.end())\n\t{\n        std::wstring delimiter = *dIt;\n\t\n\n\t\tDelimStr(m_sString, delimiter, true);\n\t\t\n\t\n\t\t++dIt;\n\t}\n\t\n\t\n\n}\n\n\nvoid ZStringDelimiterWide::DelimStr(const std::wstring & s, const std::wstring & delimiter, const bool & removeEmptyEntries)\n{\n\tBarRange(0, s.length());\n\tfor (size_t start = 0, end; start < s.length(); start = end + delimiter.length())\n\t{\n\t\tsize_t position = s.find(delimiter, start);\n        end = position != std::wstring::npos ? position : s.length();\n\n        std::wstring token = s.substr(start, end - start);\n\t\tif (!removeEmptyEntries || !token.empty())\n\t\t{\n\t\t\tif (token != s)\n\t\t\t\tm_vTokens.push_back(token);\n\n\t\t}\n\t\tBar(position);\n\t}\n\n\t// dadwwdawdaawdwadwd\n}\n\nvoid ZStringDelimiterWide::BarRange(const int & min, const int & max)\n{\n#ifdef _AFX_ALL_WARNINGS\n\tif (PgBar)\n\t\tm_pBar->SetRange32(min, max);\n\n\n#endif\n}\n\nvoid ZStringDelimiterWide::Bar(const int & pos)\n{\n#ifdef _AFX_ALL_WARNINGS\n\tif (PgBar)\n\t\tm_pBar->SetPos(pos);\n\n\n#endif\n}\n\nZStringDelimiterWide::ZStringDelimiterWide()\n{\n    m_sString = L\"\";\n\ttokenIndex = 0;\n\tPgBar = false;\n}\n\n\nbool ZStringDelimiterWide::GetFirstToken(std::wstring & in_out)\n{\n\tif (m_vTokens.size() >= 1) {\n\t\tin_out = m_vTokens[0];\n\t\treturn true;\n\t}\n\telse {\n        return false;\n\t}\n}\n\nbool ZStringDelimiterWide::GetNextToken(std::wstring & in_sOut)\n{\n\tif (tokenIndex > m_vTokens.size() - 1)\n\t\treturn false;\n\n\tin_sOut = m_vTokens[tokenIndex];\n\t++tokenIndex;\n\n\treturn true;\n}\n\nstd::wstring ZStringDelimiterWide::operator[](const size_t & in_index)\n{\n\tif (in_index > m_vTokens.size())\n\t\tthrow std::out_of_range(\"ZStringDelimiter tried to access token higher than size\");\n\n\treturn m_vTokens[in_index];\n\n}\nstd::wstring ZStringDelimiterWide::Reassemble(const std::wstring& delim, const int& nelem)\n{\n    std::wstring Result = L\"\";\n\tTokenIterator RasIt = m_vTokens.begin();\n\tint r = 0;\n\tif (nelem == -1) {\n\t\twhile (RasIt != m_vTokens.end())\n\t\t{\n\n\t\t\tif (r != 0)\n\t\t\t\tResult.append(delim);\n\n\t\t\tResult.append(*RasIt);\n\n\t\t\t++r;\n\n\n\t\t\t++RasIt;\n\t\t}\n\t}\n\telse {\n\t\twhile (RasIt != m_vTokens.end() && r < nelem)\n\t\t{\n\t\t\n\t\t\tif (r != 0)\n\t\t\t\tResult.append(delim);\n\n\t\t\tResult.append(*RasIt);\n\n\t\t\t++r;\n\t\t\t++RasIt;\n\t\t}\n\t}\n\t\n\treturn Result;\n\n}\n\nstd::wstring ZStringDelimiterWide::Reassemble(const std::wstring & delim, const std::vector<std::wstring>& Strs,int nelem)\n{\n    std::wstring Result = L\"\";\n\tTokenIterator RasIt = Strs.begin();\n\tint r = 0;\n\tif (nelem == -1) {\n\t\twhile (RasIt != Strs.end())\n\t\t{\n\n\t\t\tif (r != 0)\n\t\t\t\tResult.append(delim);\n\n\t\t\tResult.append(*RasIt);\n\n\t\t\t++r;\n\n\n\t\t\t++RasIt;\n\t\t}\n\t}\n\telse {\n\t\twhile (RasIt != Strs.end() && r < nelem)\n\t\t{\n\n\t\t\tif (r != 0)\n\t\t\t\tResult.append(delim);\n\n\t\t\tResult.append(*RasIt);\n\n\t\t\t++r;\n\t\t\t++RasIt;\n\t\t}\n\t}\n\n\treturn Result;\n}\n\nvoid ZStringDelimiterWide::AddDelimiter(const std::wstring & in_Delim)\n{\n\tm_vDelimiters.push_back(in_Delim);\n\tUpdateTokens();\n\n}\n\nvoid ZStringDelimiterWide::SetDelimiters(const std::vector<std::wstring> &Delims)\n{\n    m_vDelimiters.assign(Delims.begin(),Delims.end());\n    UpdateTokens();\n\n}\n\nZStringDelimiterWide::~ZStringDelimiterWide()\n{\n}\n"
  },
  {
    "path": "ext/ZCharScannerWide.h",
    "content": "#pragma once\n\n#define GBasicCharScanner ZStringDelimiter\n\n#include <vector>\n#include <string>\n\n// We need ZCharScanner but for wstrings. I copy class, fastest way\ntypedef std::vector<std::wstring>::const_iterator TokenIterator;\n\n// ZStringDelimiter\n// ==============\n// Simple class to delimit and split strings.\n// You can use operator[] to access them\n// Or you can use the itBegin() and itEnd() to get some iterators\n// =================\nclass ZStringDelimiterWide\n{\nprivate:\n    int key_search(const std::wstring & s, const std::wstring & key);\n\tvoid UpdateTokens();\n    std::vector<std::wstring> m_vTokens;\n    std::vector<std::wstring> m_vDelimiters;\n\n    std::wstring m_sString;\n\n    void DelimStr(const std::wstring& s, const std::wstring& delimiter, const bool& removeEmptyEntries = false);\n\tvoid BarRange(const int& min, const int& max);\n\tvoid Bar(const int& pos);\n\tsize_t tokenIndex;\npublic:\n    ZStringDelimiterWide();\n\tbool PgBar;\n\n#ifdef _AFX_ALL_WARNINGS\n\tCProgressCtrl* m_pBar;\n#endif\n\n    ZStringDelimiterWide(const std::wstring& in_iStr) {\n\t\tm_sString = in_iStr;\n\t\tPgBar = false;\n\n\t}\n\n    bool GetFirstToken(std::wstring& in_out);\n    bool GetNextToken(std::wstring& in_sOut);\n\n\t// std::String alts\n\n\tsize_t szTokens() { return m_vTokens.size(); }\n    std::wstring operator[](const size_t& in_index);\n\n    std::wstring Reassemble(const std::wstring & delim, const int & nelem = -1);\n\n\t// Override to reassemble provided tokens.\n    std::wstring Reassemble(const std::wstring & delim, const std::vector<std::wstring>& Strs,int nelem = -1);\n\n\t// Get a const reference to the tokens\n    const std::vector<std::wstring>& GetTokens() { return m_vTokens; }\n\n\tTokenIterator itBegin() { return m_vTokens.begin(); }\n\tTokenIterator itEnd() { return m_vTokens.end(); }\n\n    void SetText(const std::wstring& in_Txt) {\n\t\tm_sString = in_Txt; \n\t\tif (m_vDelimiters.size())\n\t\t\tUpdateTokens();\n\t}\n    void AddDelimiter(const std::wstring& in_Delim);\n    void SetDelimiters(const std::vector<std::wstring>& Delims);\n\n    ~ZStringDelimiterWide();\n};\n\n"
  },
  {
    "path": "ext/ZFile.cpp",
    "content": "#include \"ZFile.h\"\n\nusing namespace std;\nint ZFile::EZFOpenModeToIos(const EZFOpenMode::Enum & input)\n{\n\t/*\n\thehe wall of ifs\n\tyanderedev amirite???\n\t*/\n\tif (input == EZFOpenMode::BinaryRead)\n\t\treturn ios::in | ios::binary;\n\telse if (input == EZFOpenMode::BinaryWrite)\n\t\treturn ios::out | ios::binary;\n\telse if (input == EZFOpenMode::TextRead)\n\t\treturn ios::in;\n\telse if (input == EZFOpenMode::TextWrite)\n\t\treturn ios::out;\n\n\tSysEndian = ZFUtil::GetSysEndianness();\n\n\treturn ios::in | ios::binary;\n\n}\n\nZFile::ZFile(const std::string & coFName, const EZFOpenMode::Enum & coMode)\n{\n\tOpen(coFName, coMode);\n}\n\nbool ZFile::Open(const std::string & in_sFileName, const EZFOpenMode::Enum & in_Mode)\n{\n\tOpenMode = in_Mode;\n\n    Stream.open(in_sFileName,(ios_base::openmode)EZFOpenModeToIos(in_Mode));\n\treturn Stream.good();\n\n}\n\n\n\nvoid ZFile::Seek(const INT64 & in_Pos)\n{\n\tif (OpenMode == EZFOpenMode::BinaryRead || OpenMode == EZFOpenMode::TextRead)\n\t\tStream.seekg(in_Pos, ios::beg);\n\telse if (OpenMode == EZFOpenMode::BinaryWrite || OpenMode == EZFOpenMode::TextWrite)\n\t\tStream.seekp(in_Pos, ios::beg);\n}\n\nINT64 ZFile::GetPos()\n{\n\tif (OpenMode == EZFOpenMode::BinaryRead || OpenMode == EZFOpenMode::TextRead)\n\t\treturn Stream.tellg();\n\telse if (OpenMode == EZFOpenMode::BinaryWrite || OpenMode == EZFOpenMode::TextWrite)\n\t\treturn Stream.tellp();\n\n\t// NO TYPE?????????????\n    return -1;\n}\n\nvoid ZFile::SeekToEnd()\n{\n    if (OpenMode == EZFOpenMode::BinaryRead || OpenMode == EZFOpenMode::TextRead)\n        Stream.seekg(0, Stream.end);\n    else if (OpenMode == EZFOpenMode::BinaryWrite || OpenMode == EZFOpenMode::TextWrite)\n        Stream.seekp(0, Stream.end);\n}\n\nINT64 ZFile::GetFileLength()\n{\n\tstd::streampos lpos = GetPos();\n\n\tif (OpenMode == EZFOpenMode::BinaryRead || OpenMode == EZFOpenMode::TextRead)\n\t\tStream.seekg(0, Stream.end);\n\telse if (OpenMode == EZFOpenMode::BinaryWrite || OpenMode == EZFOpenMode::TextWrite)\n\t\tStream.seekp(0, Stream.end);\n\n\tconst INT64 Len = GetPos();\n\tSeek(lpos);\n\n\treturn Len;\n\n}\n\nvoid ZFile::Read(void * out, const INT64 & count)\n{\n\tStream.read((BYTE*)out, count);\n\n}\n\nvoid ZFile::Write(void * in, const INT64 & incount)\n{\n\tStream.write((BYTE*)in, incount);\n\t\n}\n\nByteArr ZFile::ReadEntireFile()\n{\n\n\tByteArr ArrRet;\n\n\tStream.seekg(0, Stream.end);\n\tINT64 length = Stream.tellg();\n\tStream.seekg(0, Stream.beg);\n\tArrRet.CAlloc(length);\n\n\tStream.read(ArrRet.GetData(), length);\n\t\n\treturn ArrRet;\n\n}\n\nvoid ZFile::WriteLine(const string &inLi)\n{\n    std::string Line = inLi + \"\\n\";\n\n    Write((void*)Line.data(),Line.size() * sizeof(char));\n\n}\n\nvoid ZFile::Write(const ByteArr & BrDat)\n{\n\tStream.write(BrDat.CoData(), BrDat.Size());\n}\n\nvoid ZFile::Close()\n{\n\tStream.close();\n}\n\nvoid ZFile::operator>>(ByteArr& BarDat) {\n\tsize_t BaSz = 0;\n\tRead(BaSz);\n\tBarDat.CAlloc(BaSz);\n\tStream.read(BarDat.GetData(), BaSz);\n\n}\n\n\nZFile::ZFile()\n{\n}\n\n\nZFile::~ZFile()\n{\n}\n"
  },
  {
    "path": "ext/ZFile.h",
    "content": "#pragma once\n\n/*\n######################################\n#\n#\n  ____________ _ _      \n |___  /  ____(_) |     \n    / /| |__   _| | ___ \n   / / |  __| | | |/ _ \\\n  / /__| |    | | |  __/\n /_____|_|    |_|_|\\___|\n                        \n                        \n########################################\n# Description: Defines ZFile class, one meant for easy serialization and writing of binary types,\n# including commonly used std containers without much problem\n#\n# Author: ZDisket\n# Copyright (C) 2018 YOUR MOM GAY LOLOLOL\n#######################################\n*/\n\n#include <fstream>\n#include <string>\n#include <vector>\n\n#include \"ByteArr.h\"\n\n#define ZFILE_IOVR(cla,n) ZFile& operator<<(ZFile& right,const cla& n)\n#define ZFILE_OOVR(cla,n) ZFile& operator>>(ZFile& right,cla& n)\n// FStream that works with bytes\ntypedef std::basic_fstream<BYTE,std::char_traits<BYTE>> ufstream;\n\n\n\nnamespace EZFOpenMode {\n\tenum Enum {\n\t\tBinaryRead = 0,\n\t\tTextRead,\n\t\tBinaryWrite,\n\t\tTextWrite\n\t};\n}\n\nnamespace EZFEndian {\n\tenum Enum {\n\t\tBig = 0,\n\t\tLittle\n\t};\n}\n\n\n\nnamespace ZFUtil {\n\tinline EZFEndian::Enum GetSysEndianness()\n\t{\n\t\tconst int value{ 0x01 };\n\t\tconst void * address = static_cast<const void *>(&value);\n\t\tconst unsigned char * least_significant_address = static_cast<const unsigned char *>(address);\n\t\treturn (*least_significant_address == 0x01) ? EZFEndian::Little : EZFEndian::Big;\n\t}\n\n\ttemplate <typename T>\n\tvoid SwapEndian(T& var)\n\t{\n\t\tstatic_assert(std::is_pod<T>::value, \"Type must be POD type for safety\");\n\t\tstd::array<char, sizeof(T)> varArray;\n\t\tstd::memcpy(varArray.data(), &var, sizeof(T));\n\t\tfor (int i = 0; i < static_cast<int>(sizeof(var) / 2); i++)\n\t\t\tstd::swap(varArray[sizeof(var) - 1 - i], varArray[i]);\n\t\tstd::memcpy(&var, varArray.data(), sizeof(T));\n\t}\n}\n\n// ZFile: Class for (mostly binary) file handling.\n// Cannot be copied\nclass ZFile\n{\nprivate:\n\n\tZFile(const ZFile&);\n\n\tBYTE * m_pData;\n\tbool FileOpened;\n\tufstream Stream;\n\n\tEZFOpenMode::Enum OpenMode;\n\tEZFEndian::Enum SysEndian;\n\n\tint EZFOpenModeToIos(const EZFOpenMode::Enum& input);\n\npublic:\n\tZFile();\n\n\tZFile(const std::string& coFName, const EZFOpenMode::Enum& coMode);\n\n\tbool Open(const std::string& in_sFileName,const EZFOpenMode::Enum& in_Mode);\n\n\tvoid Seek(const INT64& in_Pos);\n\tINT64 GetPos();\n\n    void SeekToEnd();\n\t\n\t INT64 GetFileLength();\n\t// Reads from the file\n\t// Please pass a pointer to this\n\tvoid Read(void* out, const INT64& count);\n\t// Writes to the file\n\t// Please pass a pointer\n\tvoid Write(void* in, const INT64& incount);\n\n\t\n\t// Read the entire file into a byte array\n\tByteArr ReadEntireFile();\n\t\n\t// Write with template argument to not pass size.\n\t// Only works with regular datatypes\n    template <typename Dat>\n\tvoid Write(const Dat& dta)\n\t{\n\t\tStream.write((BYTE*)&dta, sizeof(dta));\n\t\t\n\t\n\t}\n\n    void WriteLine(const std::string& inLi);\n\n\t// Read with template argument to not pass size.\n   // Only works with regular datatypes\n\ttemplate <typename Dat>\n\tvoid Read(Dat& dta)\n\t{\n\t\tStream.read((BYTE*)&dta, sizeof(dta));\n\n\n\t}\n\n\t\n\n\t// Write a string\n\ttemplate<typename chardat>\n\tvoid Write(const std::basic_string<chardat>& Str) {\n\t\t// Get total len in bytes.\n\t\tconst size_t LenInBytes = Str.length() * sizeof(chardat);\n\n\t\t// Write the string length (NOT in bytes)\n\t\tWrite(Str.length());\n\t\tStream.write((BYTE*)Str.data(),LenInBytes);\n\t\t\n\t\t\n\t\n\t}\n\n\t// Read a string\n\ttemplate<typename chardat>\n\tvoid Read(std::basic_string<chardat>& Str) {\n\n\t\tsize_t StrLen = 0;\n\t\tRead(StrLen);\n\t\tchardat* dpBuffer = new chardat[StrLen];\n\n\n\t\tStream.read((BYTE*)dpBuffer, sizeof(chardat) * StrLen);\n\n\t\t// For some reason (witchcraft?) our buffer has more chars in it than we actually allocated, which should be impossible.\n\t\t// Thankfully, std::string's assign function allows for cutting.\n\t\tStr.assign(dpBuffer,0,StrLen);\n\n\n\t\tdelete[] dpBuffer;\n\n\t}\n\n\t// Write a vector\n\ttemplate<typename vdat>\n\tvoid Write(const std::vector<vdat>& Vec) {\n\t\t// Write size in bytes then vector size.\n\t\tWrite(Vec.size());\n\n\t\t// Write vector size.\n\n\t\tauto It = Vec.begin();\n\t\t\n\t\twhile (It != Vec.end()) {\n\t\t\t(*this) << *It;\n\t\t\t++It;\n\t\t}\n\t\t\n\n\t\n\t}\n\n\t// Read a vector\n\ttemplate<typename vdat>\n\tvoid Read(std::vector<vdat>& Vec) {\n\t\tsize_t vSz = 0;\n\t\tRead(vSz);\n\t\t\n\t\tVec.resize(vSz);\n\n\t\tsize_t i = 0;\n\n\t\twhile (i != vSz) {\n\t\t\t(*this) >> Vec[i];\n\t\t\n\t\t\t++i;\n\t\t}\n\n\n\t}\n\n\ttemplate <typename N>\n\tvoid Write(const N& Num, EZFEndian::Enum TargetEndian);\n\n\t// Write some stuff\n\ttemplate<typename Ty>\n\tvoid operator<<(const Ty& In) {\n\t\tWrite(In);\n\t\n\t}\n\n\t// Write a Byte Array RAW into the file, without the size. Useful for exporting\n\tvoid Write(const ByteArr& BrDat);\n\n\tvoid operator<<(const ByteArr& BarDat) {\n\t\tif (BarDat.CoData() == NULL) {\n\t\t\tthrow new std::invalid_argument(\"ZFile tried to write invalid byte array!!\");\n\t\t}\n\n\t\tWrite(BarDat.Size());\n\t\tStream.write(BarDat.CoData(), BarDat.Size());\n\t\n\t}\n\t// Read to a byte array. Note: DELETES AND REPLACES THE ALREADY EXISTING CONTENTS THERE!!\n\tvoid operator>>(ByteArr& BarDat);\n\n\n\ttemplate<typename MTy>\n\tvoid operator>>(MTy& mIn) {\n\t\tRead(mIn);\n\t}\n\n\tvoid Close();\n\n\n\t~ZFile();\n};\n\ntemplate<typename N>\n// Function to write a value with target endianness.\ninline void ZFile::Write(const N & Num, EZFEndian::Enum TargetEndian)\n{\n\tif (SysEndian == TargetEndian)\n\t\tWrite(Num);\n\telse\n\t\tWrite(ZFUtil::SwapEndian(Num));\n\n\n}\n"
  },
  {
    "path": "ext/json.hpp",
    "content": "/*\n    __ _____ _____ _____\n __|  |   __|     |   | |  JSON for Modern C++\n|  |  |__   |  |  | | | |  version 3.9.1\n|_____|_____|_____|_|___|  https://github.com/nlohmann/json\n\nLicensed under the MIT License <http://opensource.org/licenses/MIT>.\nSPDX-License-Identifier: MIT\nCopyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.\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*/\n\n#ifndef INCLUDE_NLOHMANN_JSON_HPP_\n#define INCLUDE_NLOHMANN_JSON_HPP_\n\n#define NLOHMANN_JSON_VERSION_MAJOR 3\n#define NLOHMANN_JSON_VERSION_MINOR 9\n#define NLOHMANN_JSON_VERSION_PATCH 1\n\n#include <algorithm> // all_of, find, for_each\n#include <cstddef> // nullptr_t, ptrdiff_t, size_t\n#include <functional> // hash, less\n#include <initializer_list> // initializer_list\n#include <iosfwd> // istream, ostream\n#include <iterator> // random_access_iterator_tag\n#include <memory> // unique_ptr\n#include <numeric> // accumulate\n#include <string> // string, stoi, to_string\n#include <utility> // declval, forward, move, pair, swap\n#include <vector> // vector\n\n// #include <nlohmann/adl_serializer.hpp>\n\n\n#include <utility>\n\n// #include <nlohmann/detail/conversions/from_json.hpp>\n\n\n#include <algorithm> // transform\n#include <array> // array\n#include <forward_list> // forward_list\n#include <iterator> // inserter, front_inserter, end\n#include <map> // map\n#include <string> // string\n#include <tuple> // tuple, make_tuple\n#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible\n#include <unordered_map> // unordered_map\n#include <utility> // pair, declval\n#include <valarray> // valarray\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n\n#include <exception> // exception\n#include <stdexcept> // runtime_error\n#include <string> // to_string\n\n// #include <nlohmann/detail/input/position_t.hpp>\n\n\n#include <cstddef> // size_t\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// struct to capture the start position of the current token\nstruct position_t\n{\n    /// the total number of characters read\n    std::size_t chars_read_total = 0;\n    /// the number of characters read in the current line\n    std::size_t chars_read_current_line = 0;\n    /// the number of lines read\n    std::size_t lines_read = 0;\n\n    /// conversion to size_t to preserve SAX interface\n    constexpr operator size_t() const\n    {\n        return chars_read_total;\n    }\n};\n\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\n#include <utility> // pair\n// #include <nlohmann/thirdparty/hedley/hedley.hpp>\n/* Hedley - https://nemequ.github.io/hedley\n * Created by Evan Nemerson <evan@nemerson.com>\n *\n * To the extent possible under law, the author(s) have dedicated all\n * copyright and related and neighboring rights to this software to\n * the public domain worldwide. This software is distributed without\n * any warranty.\n *\n * For details, see <http://creativecommons.org/publicdomain/zero/1.0/>.\n * SPDX-License-Identifier: CC0-1.0\n */\n\n#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 13)\n#if defined(JSON_HEDLEY_VERSION)\n    #undef JSON_HEDLEY_VERSION\n#endif\n#define JSON_HEDLEY_VERSION 13\n\n#if defined(JSON_HEDLEY_STRINGIFY_EX)\n    #undef JSON_HEDLEY_STRINGIFY_EX\n#endif\n#define JSON_HEDLEY_STRINGIFY_EX(x) #x\n\n#if defined(JSON_HEDLEY_STRINGIFY)\n    #undef JSON_HEDLEY_STRINGIFY\n#endif\n#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x)\n\n#if defined(JSON_HEDLEY_CONCAT_EX)\n    #undef JSON_HEDLEY_CONCAT_EX\n#endif\n#define JSON_HEDLEY_CONCAT_EX(a,b) a##b\n\n#if defined(JSON_HEDLEY_CONCAT)\n    #undef JSON_HEDLEY_CONCAT\n#endif\n#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b)\n\n#if defined(JSON_HEDLEY_CONCAT3_EX)\n    #undef JSON_HEDLEY_CONCAT3_EX\n#endif\n#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c\n\n#if defined(JSON_HEDLEY_CONCAT3)\n    #undef JSON_HEDLEY_CONCAT3\n#endif\n#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c)\n\n#if defined(JSON_HEDLEY_VERSION_ENCODE)\n    #undef JSON_HEDLEY_VERSION_ENCODE\n#endif\n#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision))\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR)\n    #undef JSON_HEDLEY_VERSION_DECODE_MAJOR\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000)\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR)\n    #undef JSON_HEDLEY_VERSION_DECODE_MINOR\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000)\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION)\n    #undef JSON_HEDLEY_VERSION_DECODE_REVISION\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000)\n\n#if defined(JSON_HEDLEY_GNUC_VERSION)\n    #undef JSON_HEDLEY_GNUC_VERSION\n#endif\n#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__)\n    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)\n#elif defined(__GNUC__)\n    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK)\n    #undef JSON_HEDLEY_GNUC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_GNUC_VERSION)\n    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_MSVC_VERSION)\n    #undef JSON_HEDLEY_MSVC_VERSION\n#endif\n#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100)\n#elif defined(_MSC_FULL_VER)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10)\n#elif defined(_MSC_VER)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK)\n    #undef JSON_HEDLEY_MSVC_VERSION_CHECK\n#endif\n#if !defined(_MSC_VER)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0)\n#elif defined(_MSC_VER) && (_MSC_VER >= 1400)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch)))\n#elif defined(_MSC_VER) && (_MSC_VER >= 1200)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch)))\n#else\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor)))\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_VERSION)\n    #undef JSON_HEDLEY_INTEL_VERSION\n#endif\n#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE)\n    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE)\n#elif defined(__INTEL_COMPILER)\n    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK)\n    #undef JSON_HEDLEY_INTEL_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_INTEL_VERSION)\n    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_PGI_VERSION)\n    #undef JSON_HEDLEY_PGI_VERSION\n#endif\n#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__)\n    #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__)\n#endif\n\n#if defined(JSON_HEDLEY_PGI_VERSION_CHECK)\n    #undef JSON_HEDLEY_PGI_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_PGI_VERSION)\n    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_SUNPRO_VERSION)\n    #undef JSON_HEDLEY_SUNPRO_VERSION\n#endif\n#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10)\n#elif defined(__SUNPRO_C)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf)\n#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10)\n#elif defined(__SUNPRO_CC)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf)\n#endif\n\n#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK)\n    #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_SUNPRO_VERSION)\n    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)\n    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION\n#endif\n#if defined(__EMSCRIPTEN__)\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__)\n#endif\n\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK)\n    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_ARM_VERSION)\n    #undef JSON_HEDLEY_ARM_VERSION\n#endif\n#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100)\n#elif defined(__CC_ARM) && defined(__ARMCC_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100)\n#endif\n\n#if defined(JSON_HEDLEY_ARM_VERSION_CHECK)\n    #undef JSON_HEDLEY_ARM_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_ARM_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_IBM_VERSION)\n    #undef JSON_HEDLEY_IBM_VERSION\n#endif\n#if defined(__ibmxl__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__)\n#elif defined(__xlC__) && defined(__xlC_ver__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff)\n#elif defined(__xlC__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0)\n#endif\n\n#if defined(JSON_HEDLEY_IBM_VERSION_CHECK)\n    #undef JSON_HEDLEY_IBM_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_IBM_VERSION)\n    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_VERSION)\n    #undef JSON_HEDLEY_TI_VERSION\n#endif\n#if \\\n    defined(__TI_COMPILER_VERSION__) && \\\n    ( \\\n      defined(__TMS470__) || defined(__TI_ARM__) || \\\n      defined(__MSP430__) || \\\n      defined(__TMS320C2000__) \\\n    )\n#if (__TI_COMPILER_VERSION__ >= 16000000)\n    #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n#endif\n\n#if defined(JSON_HEDLEY_TI_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_VERSION)\n    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION)\n    #undef JSON_HEDLEY_TI_CL2000_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__)\n    #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION)\n    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL430_VERSION)\n    #undef JSON_HEDLEY_TI_CL430_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__)\n    #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL430_VERSION)\n    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)\n    #undef JSON_HEDLEY_TI_ARMCL_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__))\n    #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)\n    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION)\n    #undef JSON_HEDLEY_TI_CL6X_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__)\n    #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION)\n    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION)\n    #undef JSON_HEDLEY_TI_CL7X_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__)\n    #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION)\n    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)\n    #undef JSON_HEDLEY_TI_CLPRU_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__)\n    #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)\n    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_CRAY_VERSION)\n    #undef JSON_HEDLEY_CRAY_VERSION\n#endif\n#if defined(_CRAYC)\n    #if defined(_RELEASE_PATCHLEVEL)\n        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL)\n    #else\n        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0)\n    #endif\n#endif\n\n#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK)\n    #undef JSON_HEDLEY_CRAY_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_CRAY_VERSION)\n    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_IAR_VERSION)\n    #undef JSON_HEDLEY_IAR_VERSION\n#endif\n#if defined(__IAR_SYSTEMS_ICC__)\n    #if __VER__ > 1000\n        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000))\n    #else\n        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(VER / 100, __VER__ % 100, 0)\n    #endif\n#endif\n\n#if defined(JSON_HEDLEY_IAR_VERSION_CHECK)\n    #undef JSON_HEDLEY_IAR_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_IAR_VERSION)\n    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TINYC_VERSION)\n    #undef JSON_HEDLEY_TINYC_VERSION\n#endif\n#if defined(__TINYC__)\n    #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100)\n#endif\n\n#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK)\n    #undef JSON_HEDLEY_TINYC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TINYC_VERSION)\n    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_DMC_VERSION)\n    #undef JSON_HEDLEY_DMC_VERSION\n#endif\n#if defined(__DMC__)\n    #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf)\n#endif\n\n#if defined(JSON_HEDLEY_DMC_VERSION_CHECK)\n    #undef JSON_HEDLEY_DMC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_DMC_VERSION)\n    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_COMPCERT_VERSION)\n    #undef JSON_HEDLEY_COMPCERT_VERSION\n#endif\n#if defined(__COMPCERT_VERSION__)\n    #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100)\n#endif\n\n#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK)\n    #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_COMPCERT_VERSION)\n    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_PELLES_VERSION)\n    #undef JSON_HEDLEY_PELLES_VERSION\n#endif\n#if defined(__POCC__)\n    #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK)\n    #undef JSON_HEDLEY_PELLES_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_PELLES_VERSION)\n    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_VERSION)\n    #undef JSON_HEDLEY_GCC_VERSION\n#endif\n#if \\\n    defined(JSON_HEDLEY_GNUC_VERSION) && \\\n    !defined(__clang__) && \\\n    !defined(JSON_HEDLEY_INTEL_VERSION) && \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_ARM_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL430_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \\\n    !defined(__COMPCERT__)\n    #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION\n#endif\n\n#if defined(JSON_HEDLEY_GCC_VERSION_CHECK)\n    #undef JSON_HEDLEY_GCC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_GCC_VERSION)\n    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_ATTRIBUTE\n#endif\n#if defined(__has_attribute)\n    #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute)\n#else\n    #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE\n#endif\n#if defined(__has_attribute)\n    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE\n#endif\n#if defined(__has_attribute)\n    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE\n#endif\n#if \\\n    defined(__has_cpp_attribute) && \\\n    defined(__cplusplus) && \\\n    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0))\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS)\n    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS\n#endif\n#if !defined(__cplusplus) || !defined(__has_cpp_attribute)\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)\n#elif \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_IAR_VERSION) && \\\n    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \\\n    (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0))\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute)\n#else\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE\n#endif\n#if defined(__has_cpp_attribute) && defined(__cplusplus)\n    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE\n#endif\n#if defined(__has_cpp_attribute) && defined(__cplusplus)\n    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_BUILTIN)\n    #undef JSON_HEDLEY_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN)\n    #undef JSON_HEDLEY_GNUC_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN)\n    #undef JSON_HEDLEY_GCC_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_FEATURE)\n    #undef JSON_HEDLEY_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_HAS_FEATURE(feature) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE)\n    #undef JSON_HEDLEY_GNUC_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_FEATURE)\n    #undef JSON_HEDLEY_GCC_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_EXTENSION)\n    #undef JSON_HEDLEY_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_HAS_EXTENSION(extension) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION)\n    #undef JSON_HEDLEY_GNUC_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION)\n    #undef JSON_HEDLEY_GCC_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_WARNING)\n    #undef JSON_HEDLEY_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_HAS_WARNING(warning) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_WARNING)\n    #undef JSON_HEDLEY_GNUC_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_WARNING)\n    #undef JSON_HEDLEY_GCC_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for\n   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_\n#endif\n#if defined(__cplusplus)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wc++98-compat\")\n#    if JSON_HEDLEY_HAS_WARNING(\"-Wc++17-extensions\")\n#      define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++98-compat\\\"\") \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++17-extensions\\\"\") \\\n    xpr \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#    else\n#      define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++98-compat\\\"\") \\\n    xpr \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#    endif\n#  endif\n#endif\n#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x\n#endif\n\n#if defined(JSON_HEDLEY_CONST_CAST)\n    #undef JSON_HEDLEY_CONST_CAST\n#endif\n#if defined(__cplusplus)\n#  define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast<T>(expr))\n#elif \\\n  JSON_HEDLEY_HAS_WARNING(\"-Wcast-qual\") || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \\\n        JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n        JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \\\n        ((T) (expr)); \\\n        JSON_HEDLEY_DIAGNOSTIC_POP \\\n    }))\n#else\n#  define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_REINTERPRET_CAST)\n    #undef JSON_HEDLEY_REINTERPRET_CAST\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast<T>(expr))\n#else\n    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_STATIC_CAST)\n    #undef JSON_HEDLEY_STATIC_CAST\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast<T>(expr))\n#else\n    #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_CPP_CAST)\n    #undef JSON_HEDLEY_CPP_CAST\n#endif\n#if defined(__cplusplus)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wold-style-cast\")\n#    define JSON_HEDLEY_CPP_CAST(T, expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wold-style-cast\\\"\") \\\n    ((T) (expr)) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#  elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0)\n#    define JSON_HEDLEY_CPP_CAST(T, expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"diag_suppress=Pe137\") \\\n    JSON_HEDLEY_DIAGNOSTIC_POP \\\n#  else\n#    define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr))\n#  endif\n#else\n#  define JSON_HEDLEY_CPP_CAST(T, expr) (expr)\n#endif\n\n#if \\\n    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \\\n    defined(__clang__) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \\\n    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \\\n    (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR))\n    #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value)\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_PRAGMA(value) __pragma(value)\n#else\n    #define JSON_HEDLEY_PRAGMA(value)\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH)\n    #undef JSON_HEDLEY_DIAGNOSTIC_PUSH\n#endif\n#if defined(JSON_HEDLEY_DIAGNOSTIC_POP)\n    #undef JSON_HEDLEY_DIAGNOSTIC_POP\n#endif\n#if defined(__clang__)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"clang diagnostic push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"clang diagnostic pop\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"warning(push)\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"warning(pop)\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"GCC diagnostic push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"GCC diagnostic pop\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push))\n    #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop))\n#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"pop\")\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"diag_push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"diag_pop\")\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"warning(push)\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"warning(pop)\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH\n    #define JSON_HEDLEY_DIAGNOSTIC_POP\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wdeprecated-declarations\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"clang diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"warning(disable:1478 1786)\")\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1215,1444\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"GCC diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996))\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1291,1718\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"error_messages(off,symdeprecated,symdeprecated2)\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress=Pe1444,Pe1215\")\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"warn(disable:2241)\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"clang diagnostic ignored \\\"-Wunknown-pragmas\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"warning(disable:161)\")\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 1675\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"GCC diagnostic ignored \\\"-Wunknown-pragmas\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068))\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 163\")\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 163\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress=Pe161\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-attributes\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"clang diagnostic ignored \\\"-Wunknown-attributes\\\"\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"GCC diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"warning(disable:1292)\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030))\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1097\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"error_messages(off,attrskipunsup)\")\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1173\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress=Pe1097\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wcast-qual\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"clang diagnostic ignored \\\"-Wcast-qual\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"warning(disable:2203 2331)\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"GCC diagnostic ignored \\\"-Wcast-qual\\\"\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#endif\n\n#if defined(JSON_HEDLEY_DEPRECATED)\n    #undef JSON_HEDLEY_DEPRECATED\n#endif\n#if defined(JSON_HEDLEY_DEPRECATED_FOR)\n    #undef JSON_HEDLEY_DEPRECATED_FOR\n#endif\n#if JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated(\"Since \" # since))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated(\"Since \" #since \"; use \" #replacement))\n#elif defined(__cplusplus) && (__cplusplus >= 201402L)\n    #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated(\"Since \" #since)]])\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated(\"Since \" #since \"; use \" #replacement)]])\n#elif \\\n    JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__(\"Since \" #since)))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__(\"Since \" #since \"; use \" #replacement)))\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated)\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated)\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DEPRECATED(since) _Pragma(\"deprecated\")\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma(\"deprecated\")\n#else\n    #define JSON_HEDLEY_DEPRECATED(since)\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement)\n#endif\n\n#if defined(JSON_HEDLEY_UNAVAILABLE)\n    #undef JSON_HEDLEY_UNAVAILABLE\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__(\"Not available until \" #available_since)))\n#else\n    #define JSON_HEDLEY_UNAVAILABLE(available_since)\n#endif\n\n#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT)\n    #undef JSON_HEDLEY_WARN_UNUSED_RESULT\n#endif\n#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG)\n    #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG\n#endif\n#if (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]])\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__))\n#elif defined(_Check_return_) /* SAL */\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_\n#else\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg)\n#endif\n\n#if defined(JSON_HEDLEY_SENTINEL)\n    #undef JSON_HEDLEY_SENTINEL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0)\n    #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position)))\n#else\n    #define JSON_HEDLEY_SENTINEL(position)\n#endif\n\n#if defined(JSON_HEDLEY_NO_RETURN)\n    #undef JSON_HEDLEY_NO_RETURN\n#endif\n#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_NO_RETURN __noreturn\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))\n#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L\n    #define JSON_HEDLEY_NO_RETURN _Noreturn\n#elif defined(__cplusplus) && (__cplusplus >= 201103L)\n    #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]])\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_NO_RETURN _Pragma(\"does_not_return\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0)\n    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_NO_RETURN _Pragma(\"FUNC_NEVER_RETURNS;\")\n#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)\n    #define JSON_HEDLEY_NO_RETURN __attribute((noreturn))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)\n    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)\n#else\n    #define JSON_HEDLEY_NO_RETURN\n#endif\n\n#if defined(JSON_HEDLEY_NO_ESCAPE)\n    #undef JSON_HEDLEY_NO_ESCAPE\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape)\n    #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__))\n#else\n    #define JSON_HEDLEY_NO_ESCAPE\n#endif\n\n#if defined(JSON_HEDLEY_UNREACHABLE)\n    #undef JSON_HEDLEY_UNREACHABLE\n#endif\n#if defined(JSON_HEDLEY_UNREACHABLE_RETURN)\n    #undef JSON_HEDLEY_UNREACHABLE_RETURN\n#endif\n#if defined(JSON_HEDLEY_ASSUME)\n    #undef JSON_HEDLEY_ASSUME\n#endif\n#if \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_ASSUME(expr) __assume(expr)\n#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume)\n    #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr)\n#elif \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)\n    #if defined(__cplusplus)\n        #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr)\n    #else\n        #define JSON_HEDLEY_ASSUME(expr) _nassert(expr)\n    #endif\n#endif\n#if \\\n    (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5)\n    #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable()\n#elif defined(JSON_HEDLEY_ASSUME)\n    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)\n#endif\n#if !defined(JSON_HEDLEY_ASSUME)\n    #if defined(JSON_HEDLEY_UNREACHABLE)\n        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1)))\n    #else\n        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr)\n    #endif\n#endif\n#if defined(JSON_HEDLEY_UNREACHABLE)\n    #if  \\\n        JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)\n        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value))\n    #else\n        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE()\n    #endif\n#else\n    #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value)\n#endif\n#if !defined(JSON_HEDLEY_UNREACHABLE)\n    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)\n#endif\n\nJSON_HEDLEY_DIAGNOSTIC_PUSH\n#if JSON_HEDLEY_HAS_WARNING(\"-Wpedantic\")\n    #pragma clang diagnostic ignored \"-Wpedantic\"\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wc++98-compat-pedantic\") && defined(__cplusplus)\n    #pragma clang diagnostic ignored \"-Wc++98-compat-pedantic\"\n#endif\n#if JSON_HEDLEY_GCC_HAS_WARNING(\"-Wvariadic-macros\",4,0,0)\n    #if defined(__clang__)\n        #pragma clang diagnostic ignored \"-Wvariadic-macros\"\n    #elif defined(JSON_HEDLEY_GCC_VERSION)\n        #pragma GCC diagnostic ignored \"-Wvariadic-macros\"\n    #endif\n#endif\n#if defined(JSON_HEDLEY_NON_NULL)\n    #undef JSON_HEDLEY_NON_NULL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)\n    #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__)))\n#else\n    #define JSON_HEDLEY_NON_NULL(...)\n#endif\nJSON_HEDLEY_DIAGNOSTIC_POP\n\n#if defined(JSON_HEDLEY_PRINTF_FORMAT)\n    #undef JSON_HEDLEY_PRINTF_FORMAT\n#endif\n#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check)))\n#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check)))\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(format) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check)))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check))\n#else\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check)\n#endif\n\n#if defined(JSON_HEDLEY_CONSTEXPR)\n    #undef JSON_HEDLEY_CONSTEXPR\n#endif\n#if defined(__cplusplus)\n    #if __cplusplus >= 201103L\n        #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr)\n    #endif\n#endif\n#if !defined(JSON_HEDLEY_CONSTEXPR)\n    #define JSON_HEDLEY_CONSTEXPR\n#endif\n\n#if defined(JSON_HEDLEY_PREDICT)\n    #undef JSON_HEDLEY_PREDICT\n#endif\n#if defined(JSON_HEDLEY_LIKELY)\n    #undef JSON_HEDLEY_LIKELY\n#endif\n#if defined(JSON_HEDLEY_UNLIKELY)\n    #undef JSON_HEDLEY_UNLIKELY\n#endif\n#if defined(JSON_HEDLEY_UNPREDICTABLE)\n    #undef JSON_HEDLEY_UNPREDICTABLE\n#endif\n#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable)\n    #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr))\n#endif\n#if \\\n  JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0)\n#  define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(  (expr), (value), (probability))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability)   __builtin_expect_with_probability(!!(expr),    1   , (probability))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability)  __builtin_expect_with_probability(!!(expr),    0   , (probability))\n#  define JSON_HEDLEY_LIKELY(expr)                      __builtin_expect                 (!!(expr),    1                  )\n#  define JSON_HEDLEY_UNLIKELY(expr)                    __builtin_expect                 (!!(expr),    0                  )\n#elif \\\n  JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n  JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \\\n  JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0)\n#  define JSON_HEDLEY_PREDICT(expr, expected, probability) \\\n    (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \\\n    (__extension__ ({ \\\n        double hedley_probability_ = (probability); \\\n        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \\\n    }))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \\\n    (__extension__ ({ \\\n        double hedley_probability_ = (probability); \\\n        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \\\n    }))\n#  define JSON_HEDLEY_LIKELY(expr)   __builtin_expect(!!(expr), 1)\n#  define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0)\n#else\n#  define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr))\n#  define JSON_HEDLEY_LIKELY(expr) (!!(expr))\n#  define JSON_HEDLEY_UNLIKELY(expr) (!!(expr))\n#endif\n#if !defined(JSON_HEDLEY_UNPREDICTABLE)\n    #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5)\n#endif\n\n#if defined(JSON_HEDLEY_MALLOC)\n    #undef JSON_HEDLEY_MALLOC\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_MALLOC __attribute__((__malloc__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_MALLOC _Pragma(\"returns_new_memory\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0)\n    #define JSON_HEDLEY_MALLOC __declspec(restrict)\n#else\n    #define JSON_HEDLEY_MALLOC\n#endif\n\n#if defined(JSON_HEDLEY_PURE)\n    #undef JSON_HEDLEY_PURE\n#endif\n#if \\\n  JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n  JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n#  define JSON_HEDLEY_PURE __attribute__((__pure__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n#  define JSON_HEDLEY_PURE _Pragma(\"does_not_write_global_data\")\n#elif defined(__cplusplus) && \\\n    ( \\\n      JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \\\n      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \\\n      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \\\n    )\n#  define JSON_HEDLEY_PURE _Pragma(\"FUNC_IS_PURE;\")\n#else\n#  define JSON_HEDLEY_PURE\n#endif\n\n#if defined(JSON_HEDLEY_CONST)\n    #undef JSON_HEDLEY_CONST\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(const) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_CONST __attribute__((__const__))\n#elif \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_CONST _Pragma(\"no_side_effect\")\n#else\n    #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE\n#endif\n\n#if defined(JSON_HEDLEY_RESTRICT)\n    #undef JSON_HEDLEY_RESTRICT\n#endif\n#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus)\n    #define JSON_HEDLEY_RESTRICT restrict\n#elif \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \\\n    defined(__clang__)\n    #define JSON_HEDLEY_RESTRICT __restrict\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus)\n    #define JSON_HEDLEY_RESTRICT _Restrict\n#else\n    #define JSON_HEDLEY_RESTRICT\n#endif\n\n#if defined(JSON_HEDLEY_INLINE)\n    #undef JSON_HEDLEY_INLINE\n#endif\n#if \\\n    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \\\n    (defined(__cplusplus) && (__cplusplus >= 199711L))\n    #define JSON_HEDLEY_INLINE inline\n#elif \\\n    defined(JSON_HEDLEY_GCC_VERSION) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0)\n    #define JSON_HEDLEY_INLINE __inline__\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_INLINE __inline\n#else\n    #define JSON_HEDLEY_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_ALWAYS_INLINE)\n    #undef JSON_HEDLEY_ALWAYS_INLINE\n#endif\n#if \\\n  JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE __forceinline\n#elif defined(__cplusplus) && \\\n    ( \\\n      JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n      JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n      JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n      JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \\\n    )\n#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma(\"FUNC_ALWAYS_INLINE;\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma(\"inline=forced\")\n#else\n#  define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_NEVER_INLINE)\n    #undef JSON_HEDLEY_NEVER_INLINE\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__))\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0)\n    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"noinline\")\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"FUNC_CANNOT_INLINE;\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"inline=never\")\n#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)\n    #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)\n    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)\n#else\n    #define JSON_HEDLEY_NEVER_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_PRIVATE)\n    #undef JSON_HEDLEY_PRIVATE\n#endif\n#if defined(JSON_HEDLEY_PUBLIC)\n    #undef JSON_HEDLEY_PUBLIC\n#endif\n#if defined(JSON_HEDLEY_IMPORT)\n    #undef JSON_HEDLEY_IMPORT\n#endif\n#if defined(_WIN32) || defined(__CYGWIN__)\n#  define JSON_HEDLEY_PRIVATE\n#  define JSON_HEDLEY_PUBLIC   __declspec(dllexport)\n#  define JSON_HEDLEY_IMPORT   __declspec(dllimport)\n#else\n#  if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n    ( \\\n      defined(__TI_EABI__) && \\\n      ( \\\n        (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \\\n      ) \\\n    )\n#    define JSON_HEDLEY_PRIVATE __attribute__((__visibility__(\"hidden\")))\n#    define JSON_HEDLEY_PUBLIC  __attribute__((__visibility__(\"default\")))\n#  else\n#    define JSON_HEDLEY_PRIVATE\n#    define JSON_HEDLEY_PUBLIC\n#  endif\n#  define JSON_HEDLEY_IMPORT    extern\n#endif\n\n#if defined(JSON_HEDLEY_NO_THROW)\n    #undef JSON_HEDLEY_NO_THROW\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)\n    #define JSON_HEDLEY_NO_THROW __declspec(nothrow)\n#else\n    #define JSON_HEDLEY_NO_THROW\n#endif\n\n#if defined(JSON_HEDLEY_FALL_THROUGH)\n    #undef JSON_HEDLEY_FALL_THROUGH\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0)\n    #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__))\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough)\n    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]])\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough)\n    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]])\n#elif defined(__fallthrough) /* SAL */\n    #define JSON_HEDLEY_FALL_THROUGH __fallthrough\n#else\n    #define JSON_HEDLEY_FALL_THROUGH\n#endif\n\n#if defined(JSON_HEDLEY_RETURNS_NON_NULL)\n    #undef JSON_HEDLEY_RETURNS_NON_NULL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)\n    #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__))\n#elif defined(_Ret_notnull_) /* SAL */\n    #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_\n#else\n    #define JSON_HEDLEY_RETURNS_NON_NULL\n#endif\n\n#if defined(JSON_HEDLEY_ARRAY_PARAM)\n    #undef JSON_HEDLEY_ARRAY_PARAM\n#endif\n#if \\\n    defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \\\n    !defined(__STDC_NO_VLA__) && \\\n    !defined(__cplusplus) && \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_TINYC_VERSION)\n    #define JSON_HEDLEY_ARRAY_PARAM(name) (name)\n#else\n    #define JSON_HEDLEY_ARRAY_PARAM(name)\n#endif\n\n#if defined(JSON_HEDLEY_IS_CONSTANT)\n    #undef JSON_HEDLEY_IS_CONSTANT\n#endif\n#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR)\n    #undef JSON_HEDLEY_REQUIRE_CONSTEXPR\n#endif\n/* JSON_HEDLEY_IS_CONSTEXPR_ is for\n   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */\n#if defined(JSON_HEDLEY_IS_CONSTEXPR_)\n    #undef JSON_HEDLEY_IS_CONSTEXPR_\n#endif\n#if \\\n    JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0)\n    #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr)\n#endif\n#if !defined(__cplusplus)\n#  if \\\n       JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \\\n       JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n       JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n       JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n       JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \\\n       JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \\\n       JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24)\n#if defined(__INTPTR_TYPE__)\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*)\n#else\n    #include <stdint.h>\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*)\n#endif\n#  elif \\\n       ( \\\n          defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \\\n          !defined(JSON_HEDLEY_SUNPRO_VERSION) && \\\n          !defined(JSON_HEDLEY_PGI_VERSION) && \\\n          !defined(JSON_HEDLEY_IAR_VERSION)) || \\\n       JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) || \\\n       JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \\\n       JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \\\n       JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \\\n       JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0)\n#if defined(__INTPTR_TYPE__)\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0)\n#else\n    #include <stdint.h>\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0)\n#endif\n#  elif \\\n       defined(JSON_HEDLEY_GCC_VERSION) || \\\n       defined(JSON_HEDLEY_INTEL_VERSION) || \\\n       defined(JSON_HEDLEY_TINYC_VERSION) || \\\n       defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \\\n       JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \\\n       defined(JSON_HEDLEY_TI_CL2000_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CL6X_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CL7X_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \\\n       defined(__clang__)\n#    define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \\\n        sizeof(void) != \\\n        sizeof(*( \\\n                  1 ? \\\n                  ((void*) ((expr) * 0L) ) : \\\n((struct { char v[sizeof(void) * 2]; } *) 1) \\\n                ) \\\n              ) \\\n                                            )\n#  endif\n#endif\n#if defined(JSON_HEDLEY_IS_CONSTEXPR_)\n    #if !defined(JSON_HEDLEY_IS_CONSTANT)\n        #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr)\n    #endif\n    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1))\n#else\n    #if !defined(JSON_HEDLEY_IS_CONSTANT)\n        #define JSON_HEDLEY_IS_CONSTANT(expr) (0)\n    #endif\n    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr)\n#endif\n\n#if defined(JSON_HEDLEY_BEGIN_C_DECLS)\n    #undef JSON_HEDLEY_BEGIN_C_DECLS\n#endif\n#if defined(JSON_HEDLEY_END_C_DECLS)\n    #undef JSON_HEDLEY_END_C_DECLS\n#endif\n#if defined(JSON_HEDLEY_C_DECL)\n    #undef JSON_HEDLEY_C_DECL\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_BEGIN_C_DECLS extern \"C\" {\n    #define JSON_HEDLEY_END_C_DECLS }\n    #define JSON_HEDLEY_C_DECL extern \"C\"\n#else\n    #define JSON_HEDLEY_BEGIN_C_DECLS\n    #define JSON_HEDLEY_END_C_DECLS\n    #define JSON_HEDLEY_C_DECL\n#endif\n\n#if defined(JSON_HEDLEY_STATIC_ASSERT)\n    #undef JSON_HEDLEY_STATIC_ASSERT\n#endif\n#if \\\n  !defined(__cplusplus) && ( \\\n      (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \\\n      JSON_HEDLEY_HAS_FEATURE(c_static_assert) || \\\n      JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \\\n      JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n      defined(_Static_assert) \\\n    )\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message)\n#elif \\\n  (defined(__cplusplus) && (__cplusplus >= 201103L)) || \\\n  JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0)\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message))\n#else\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message)\n#endif\n\n#if defined(JSON_HEDLEY_NULL)\n    #undef JSON_HEDLEY_NULL\n#endif\n#if defined(__cplusplus)\n    #if __cplusplus >= 201103L\n        #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr)\n    #elif defined(NULL)\n        #define JSON_HEDLEY_NULL NULL\n    #else\n        #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0)\n    #endif\n#elif defined(NULL)\n    #define JSON_HEDLEY_NULL NULL\n#else\n    #define JSON_HEDLEY_NULL ((void*) 0)\n#endif\n\n#if defined(JSON_HEDLEY_MESSAGE)\n    #undef JSON_HEDLEY_MESSAGE\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n#  define JSON_HEDLEY_MESSAGE(msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \\\n    JSON_HEDLEY_PRAGMA(message msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#elif \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg)\n#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg)\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#else\n#  define JSON_HEDLEY_MESSAGE(msg)\n#endif\n\n#if defined(JSON_HEDLEY_WARNING)\n    #undef JSON_HEDLEY_WARNING\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n#  define JSON_HEDLEY_WARNING(msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \\\n    JSON_HEDLEY_PRAGMA(clang warning msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#elif \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \\\n  JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg)\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#else\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg)\n#endif\n\n#if defined(JSON_HEDLEY_REQUIRE)\n    #undef JSON_HEDLEY_REQUIRE\n#endif\n#if defined(JSON_HEDLEY_REQUIRE_MSG)\n    #undef JSON_HEDLEY_REQUIRE_MSG\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wgcc-compat\")\n#    define JSON_HEDLEY_REQUIRE(expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wgcc-compat\\\"\") \\\n    __attribute__((diagnose_if(!(expr), #expr, \"error\"))) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wgcc-compat\\\"\") \\\n    __attribute__((diagnose_if(!(expr), msg, \"error\"))) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#  else\n#    define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, \"error\")))\n#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, \"error\")))\n#  endif\n#else\n#  define JSON_HEDLEY_REQUIRE(expr)\n#  define JSON_HEDLEY_REQUIRE_MSG(expr,msg)\n#endif\n\n#if defined(JSON_HEDLEY_FLAGS)\n    #undef JSON_HEDLEY_FLAGS\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum)\n    #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__))\n#endif\n\n#if defined(JSON_HEDLEY_FLAGS_CAST)\n    #undef JSON_HEDLEY_FLAGS_CAST\n#endif\n#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0)\n#  define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \\\n        JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n        _Pragma(\"warning(disable:188)\") \\\n        ((T) (expr)); \\\n        JSON_HEDLEY_DIAGNOSTIC_POP \\\n    }))\n#else\n#  define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr)\n#endif\n\n#if defined(JSON_HEDLEY_EMPTY_BASES)\n    #undef JSON_HEDLEY_EMPTY_BASES\n#endif\n#if JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)\n    #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases)\n#else\n    #define JSON_HEDLEY_EMPTY_BASES\n#endif\n\n/* Remaining macros are deprecated. */\n\n#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK)\n    #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK\n#endif\n#if defined(__clang__)\n    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0)\n#else\n    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN)\n    #undef JSON_HEDLEY_CLANG_HAS_BUILTIN\n#endif\n#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE)\n    #undef JSON_HEDLEY_CLANG_HAS_FEATURE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION)\n    #undef JSON_HEDLEY_CLANG_HAS_EXTENSION\n#endif\n#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_WARNING)\n    #undef JSON_HEDLEY_CLANG_HAS_WARNING\n#endif\n#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning)\n\n#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */\n\n\n// This file contains all internal macro definitions\n// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them\n\n// exclude unsupported compilers\n#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)\n    #if defined(__clang__)\n        #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400\n            #error \"unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers\"\n        #endif\n    #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))\n        #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800\n            #error \"unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers\"\n        #endif\n    #endif\n#endif\n\n// C++ language standard detection\n#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)\n    #define JSON_HAS_CPP_20\n    #define JSON_HAS_CPP_17\n    #define JSON_HAS_CPP_14\n#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464\n    #define JSON_HAS_CPP_17\n    #define JSON_HAS_CPP_14\n#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)\n    #define JSON_HAS_CPP_14\n#endif\n\n// disable float-equal warnings on GCC/clang\n#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#endif\n\n// disable documentation warnings on clang\n#if defined(__clang__)\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Wdocumentation\"\n#endif\n\n// allow to disable exceptions\n#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)\n    #define JSON_THROW(exception) throw exception\n    #define JSON_TRY try\n    #define JSON_CATCH(exception) catch(exception)\n    #define JSON_INTERNAL_CATCH(exception) catch(exception)\n#else\n    #include <cstdlib>\n    #define JSON_THROW(exception) std::abort()\n    #define JSON_TRY if(true)\n    #define JSON_CATCH(exception) if(false)\n    #define JSON_INTERNAL_CATCH(exception) if(false)\n#endif\n\n// override exception macros\n#if defined(JSON_THROW_USER)\n    #undef JSON_THROW\n    #define JSON_THROW JSON_THROW_USER\n#endif\n#if defined(JSON_TRY_USER)\n    #undef JSON_TRY\n    #define JSON_TRY JSON_TRY_USER\n#endif\n#if defined(JSON_CATCH_USER)\n    #undef JSON_CATCH\n    #define JSON_CATCH JSON_CATCH_USER\n    #undef JSON_INTERNAL_CATCH\n    #define JSON_INTERNAL_CATCH JSON_CATCH_USER\n#endif\n#if defined(JSON_INTERNAL_CATCH_USER)\n    #undef JSON_INTERNAL_CATCH\n    #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER\n#endif\n\n// allow to override assert\n#if !defined(JSON_ASSERT)\n    #include <cassert> // assert\n    #define JSON_ASSERT(x) assert(x)\n#endif\n\n/*!\n@brief macro to briefly define a mapping between an enum and JSON\n@def NLOHMANN_JSON_SERIALIZE_ENUM\n@since version 3.4.0\n*/\n#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...)                                            \\\n    template<typename BasicJsonType>                                                            \\\n    inline void to_json(BasicJsonType& j, const ENUM_TYPE& e)                                   \\\n    {                                                                                           \\\n        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE \" must be an enum!\");          \\\n        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \\\n        auto it = std::find_if(std::begin(m), std::end(m),                                      \\\n                               [e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool  \\\n        {                                                                                       \\\n            return ej_pair.first == e;                                                          \\\n        });                                                                                     \\\n        j = ((it != std::end(m)) ? it : std::begin(m))->second;                                 \\\n    }                                                                                           \\\n    template<typename BasicJsonType>                                                            \\\n    inline void from_json(const BasicJsonType& j, ENUM_TYPE& e)                                 \\\n    {                                                                                           \\\n        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE \" must be an enum!\");          \\\n        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \\\n        auto it = std::find_if(std::begin(m), std::end(m),                                      \\\n                               [&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \\\n        {                                                                                       \\\n            return ej_pair.second == j;                                                         \\\n        });                                                                                     \\\n        e = ((it != std::end(m)) ? it : std::begin(m))->first;                                  \\\n    }\n\n// Ugly macros to avoid uglier copy-paste when specializing basic_json. They\n// may be removed in the future once the class is split.\n\n#define NLOHMANN_BASIC_JSON_TPL_DECLARATION                                \\\n    template<template<typename, typename, typename...> class ObjectType,   \\\n             template<typename, typename...> class ArrayType,              \\\n             class StringType, class BooleanType, class NumberIntegerType, \\\n             class NumberUnsignedType, class NumberFloatType,              \\\n             template<typename> class AllocatorType,                       \\\n             template<typename, typename = void> class JSONSerializer,     \\\n             class BinaryType>\n\n#define NLOHMANN_BASIC_JSON_TPL                                            \\\n    basic_json<ObjectType, ArrayType, StringType, BooleanType,             \\\n    NumberIntegerType, NumberUnsignedType, NumberFloatType,                \\\n    AllocatorType, JSONSerializer, BinaryType>\n\n// Macros to simplify conversion from/to types\n\n#define NLOHMANN_JSON_EXPAND( x ) x\n#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME\n#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \\\n        NLOHMANN_JSON_PASTE64, \\\n        NLOHMANN_JSON_PASTE63, \\\n        NLOHMANN_JSON_PASTE62, \\\n        NLOHMANN_JSON_PASTE61, \\\n        NLOHMANN_JSON_PASTE60, \\\n        NLOHMANN_JSON_PASTE59, \\\n        NLOHMANN_JSON_PASTE58, \\\n        NLOHMANN_JSON_PASTE57, \\\n        NLOHMANN_JSON_PASTE56, \\\n        NLOHMANN_JSON_PASTE55, \\\n        NLOHMANN_JSON_PASTE54, \\\n        NLOHMANN_JSON_PASTE53, \\\n        NLOHMANN_JSON_PASTE52, \\\n        NLOHMANN_JSON_PASTE51, \\\n        NLOHMANN_JSON_PASTE50, \\\n        NLOHMANN_JSON_PASTE49, \\\n        NLOHMANN_JSON_PASTE48, \\\n        NLOHMANN_JSON_PASTE47, \\\n        NLOHMANN_JSON_PASTE46, \\\n        NLOHMANN_JSON_PASTE45, \\\n        NLOHMANN_JSON_PASTE44, \\\n        NLOHMANN_JSON_PASTE43, \\\n        NLOHMANN_JSON_PASTE42, \\\n        NLOHMANN_JSON_PASTE41, \\\n        NLOHMANN_JSON_PASTE40, \\\n        NLOHMANN_JSON_PASTE39, \\\n        NLOHMANN_JSON_PASTE38, \\\n        NLOHMANN_JSON_PASTE37, \\\n        NLOHMANN_JSON_PASTE36, \\\n        NLOHMANN_JSON_PASTE35, \\\n        NLOHMANN_JSON_PASTE34, \\\n        NLOHMANN_JSON_PASTE33, \\\n        NLOHMANN_JSON_PASTE32, \\\n        NLOHMANN_JSON_PASTE31, \\\n        NLOHMANN_JSON_PASTE30, \\\n        NLOHMANN_JSON_PASTE29, \\\n        NLOHMANN_JSON_PASTE28, \\\n        NLOHMANN_JSON_PASTE27, \\\n        NLOHMANN_JSON_PASTE26, \\\n        NLOHMANN_JSON_PASTE25, \\\n        NLOHMANN_JSON_PASTE24, \\\n        NLOHMANN_JSON_PASTE23, \\\n        NLOHMANN_JSON_PASTE22, \\\n        NLOHMANN_JSON_PASTE21, \\\n        NLOHMANN_JSON_PASTE20, \\\n        NLOHMANN_JSON_PASTE19, \\\n        NLOHMANN_JSON_PASTE18, \\\n        NLOHMANN_JSON_PASTE17, \\\n        NLOHMANN_JSON_PASTE16, \\\n        NLOHMANN_JSON_PASTE15, \\\n        NLOHMANN_JSON_PASTE14, \\\n        NLOHMANN_JSON_PASTE13, \\\n        NLOHMANN_JSON_PASTE12, \\\n        NLOHMANN_JSON_PASTE11, \\\n        NLOHMANN_JSON_PASTE10, \\\n        NLOHMANN_JSON_PASTE9, \\\n        NLOHMANN_JSON_PASTE8, \\\n        NLOHMANN_JSON_PASTE7, \\\n        NLOHMANN_JSON_PASTE6, \\\n        NLOHMANN_JSON_PASTE5, \\\n        NLOHMANN_JSON_PASTE4, \\\n        NLOHMANN_JSON_PASTE3, \\\n        NLOHMANN_JSON_PASTE2, \\\n        NLOHMANN_JSON_PASTE1)(__VA_ARGS__))\n#define NLOHMANN_JSON_PASTE2(func, v1) func(v1)\n#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2)\n#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3)\n#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4)\n#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5)\n#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6)\n#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7)\n#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8)\n#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9)\n#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10)\n#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)\n#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12)\n#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13)\n#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14)\n#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)\n#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16)\n#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17)\n#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18)\n#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19)\n#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)\n#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21)\n#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22)\n#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23)\n#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24)\n#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25)\n#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26)\n#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27)\n#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28)\n#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29)\n#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30)\n#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31)\n#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32)\n#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33)\n#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34)\n#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35)\n#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36)\n#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37)\n#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38)\n#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39)\n#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40)\n#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41)\n#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42)\n#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43)\n#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44)\n#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45)\n#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46)\n#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47)\n#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48)\n#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49)\n#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50)\n#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51)\n#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52)\n#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53)\n#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54)\n#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55)\n#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56)\n#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57)\n#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58)\n#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59)\n#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60)\n#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61)\n#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62)\n#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63)\n\n#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;\n#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);\n\n/*!\n@brief macro\n@def NLOHMANN_DEFINE_TYPE_INTRUSIVE\n@since version 3.9.0\n*/\n#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...)  \\\n    friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \\\n    friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }\n\n/*!\n@brief macro\n@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE\n@since version 3.9.0\n*/\n#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...)  \\\n    inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \\\n    inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }\n\n#ifndef JSON_USE_IMPLICIT_CONVERSIONS\n    #define JSON_USE_IMPLICIT_CONVERSIONS 1\n#endif\n\n#if JSON_USE_IMPLICIT_CONVERSIONS\n    #define JSON_EXPLICIT\n#else\n    #define JSON_EXPLICIT explicit\n#endif\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n////////////////\n// exceptions //\n////////////////\n\n/*!\n@brief general exception of the @ref basic_json class\n\nThis class is an extension of `std::exception` objects with a member @a id for\nexception ids. It is used as the base class for all exceptions thrown by the\n@ref basic_json class. This class can hence be used as \"wildcard\" to catch\nexceptions.\n\nSubclasses:\n- @ref parse_error for exceptions indicating a parse error\n- @ref invalid_iterator for exceptions indicating errors with iterators\n- @ref type_error for exceptions indicating executing a member function with\n                  a wrong type\n- @ref out_of_range for exceptions indicating access out of the defined range\n- @ref other_error for exceptions indicating other library errors\n\n@internal\n@note To have nothrow-copy-constructible exceptions, we internally use\n      `std::runtime_error` which can cope with arbitrary-length error messages.\n      Intermediate strings are built with static functions and then passed to\n      the actual constructor.\n@endinternal\n\n@liveexample{The following code shows how arbitrary library exceptions can be\ncaught.,exception}\n\n@since version 3.0.0\n*/\nclass exception : public std::exception\n{\n  public:\n    /// returns the explanatory string\n    JSON_HEDLEY_RETURNS_NON_NULL\n    const char* what() const noexcept override\n    {\n        return m.what();\n    }\n\n    /// the id of the exception\n    const int id;\n\n  protected:\n    JSON_HEDLEY_NON_NULL(3)\n    exception(int id_, const char* what_arg) : id(id_), m(what_arg) {}\n\n    static std::string name(const std::string& ename, int id_)\n    {\n        return \"[json.exception.\" + ename + \".\" + std::to_string(id_) + \"] \";\n    }\n\n  private:\n    /// an exception object as storage for error messages\n    std::runtime_error m;\n};\n\n/*!\n@brief exception indicating a parse error\n\nThis exception is thrown by the library when a parse error occurs. Parse errors\ncan occur during the deserialization of JSON text, CBOR, MessagePack, as well\nas when using JSON Patch.\n\nMember @a byte holds the byte index of the last read character in the input\nfile.\n\nExceptions have ids 1xx.\n\nname / id                      | example message | description\n------------------------------ | --------------- | -------------------------\njson.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position.\njson.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\\uxxxx` entries (\"surrogate pairs\"). This error indicates that the surrogate pair is incomplete or contains an invalid code point.\njson.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid.\njson.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects.\njson.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one \"op\" member, whose value indicates the operation to perform. Its value must be one of \"add\", \"remove\", \"replace\", \"move\", \"copy\", or \"test\"; other values are errors.\njson.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`.\njson.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character.\njson.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences.\njson.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number.\njson.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read.\njson.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read.\njson.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read.\njson.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet).\njson.exception.parse_error.115 | parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A | A UBJSON high-precision number could not be parsed.\n\n@note For an input with n bytes, 1 is the index of the first character and n+1\n      is the index of the terminating null byte or the end of file. This also\n      holds true when reading a byte vector (CBOR or MessagePack).\n\n@liveexample{The following code shows how a `parse_error` exception can be\ncaught.,parse_error}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass parse_error : public exception\n{\n  public:\n    /*!\n    @brief create a parse error exception\n    @param[in] id_       the id of the exception\n    @param[in] pos       the position where the error occurred (or with\n                         chars_read_total=0 if the position cannot be\n                         determined)\n    @param[in] what_arg  the explanatory string\n    @return parse_error object\n    */\n    static parse_error create(int id_, const position_t& pos, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"parse_error\", id_) + \"parse error\" +\n                        position_string(pos) + \": \" + what_arg;\n        return parse_error(id_, pos.chars_read_total, w.c_str());\n    }\n\n    static parse_error create(int id_, std::size_t byte_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"parse_error\", id_) + \"parse error\" +\n                        (byte_ != 0 ? (\" at byte \" + std::to_string(byte_)) : \"\") +\n                        \": \" + what_arg;\n        return parse_error(id_, byte_, w.c_str());\n    }\n\n    /*!\n    @brief byte index of the parse error\n\n    The byte index of the last read character in the input file.\n\n    @note For an input with n bytes, 1 is the index of the first character and\n          n+1 is the index of the terminating null byte or the end of file.\n          This also holds true when reading a byte vector (CBOR or MessagePack).\n    */\n    const std::size_t byte;\n\n  private:\n    parse_error(int id_, std::size_t byte_, const char* what_arg)\n        : exception(id_, what_arg), byte(byte_) {}\n\n    static std::string position_string(const position_t& pos)\n    {\n        return \" at line \" + std::to_string(pos.lines_read + 1) +\n               \", column \" + std::to_string(pos.chars_read_current_line);\n    }\n};\n\n/*!\n@brief exception indicating errors with iterators\n\nThis exception is thrown if iterators passed to a library function do not match\nthe expected semantics.\n\nExceptions have ids 2xx.\n\nname / id                           | example message | description\n----------------------------------- | --------------- | -------------------------\njson.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.\njson.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion.\njson.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from.\njson.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid.\njson.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid.\njson.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range.\njson.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key.\njson.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.\njson.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.\njson.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.\njson.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to.\njson.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container.\njson.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered.\njson.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin().\n\n@liveexample{The following code shows how an `invalid_iterator` exception can be\ncaught.,invalid_iterator}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass invalid_iterator : public exception\n{\n  public:\n    static invalid_iterator create(int id_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"invalid_iterator\", id_) + what_arg;\n        return invalid_iterator(id_, w.c_str());\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    invalid_iterator(int id_, const char* what_arg)\n        : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating executing a member function with a wrong type\n\nThis exception is thrown in case of a type error; that is, a library function is\nexecuted on a JSON value whose type does not match the expected semantics.\n\nExceptions have ids 3xx.\n\nname / id                     | example message | description\n----------------------------- | --------------- | -------------------------\njson.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead.\njson.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types.\njson.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &.\njson.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types.\njson.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types.\njson.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types.\njson.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types.\njson.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types.\njson.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types.\njson.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types.\njson.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types.\njson.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types.\njson.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined.\njson.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.\njson.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.\njson.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |\njson.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) |\n\n@liveexample{The following code shows how a `type_error` exception can be\ncaught.,type_error}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass type_error : public exception\n{\n  public:\n    static type_error create(int id_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"type_error\", id_) + what_arg;\n        return type_error(id_, w.c_str());\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating access out of the defined range\n\nThis exception is thrown in case a library function is called on an input\nparameter that exceeds the expected range, for instance in case of array\nindices or nonexisting object keys.\n\nExceptions have ids 4xx.\n\nname / id                       | example message | description\n------------------------------- | --------------- | -------------------------\njson.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1.\njson.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it.\njson.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object.\njson.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.\njson.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.\njson.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.\njson.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. (until version 3.8.0) |\njson.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. |\njson.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string |\n\n@liveexample{The following code shows how an `out_of_range` exception can be\ncaught.,out_of_range}\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass out_of_range : public exception\n{\n  public:\n    static out_of_range create(int id_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"out_of_range\", id_) + what_arg;\n        return out_of_range(id_, w.c_str());\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating other library errors\n\nThis exception is thrown in case of errors that cannot be classified with the\nother exception types.\n\nExceptions have ids 5xx.\n\nname / id                      | example message | description\n------------------------------ | --------------- | -------------------------\njson.exception.other_error.501 | unsuccessful: {\"op\":\"test\",\"path\":\"/baz\", \"value\":\"bar\"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed.\n\n@sa - @ref exception for the base class of the library exceptions\n@sa - @ref parse_error for exceptions indicating a parse error\n@sa - @ref invalid_iterator for exceptions indicating errors with iterators\n@sa - @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa - @ref out_of_range for exceptions indicating access out of the defined range\n\n@liveexample{The following code shows how an `other_error` exception can be\ncaught.,other_error}\n\n@since version 3.0.0\n*/\nclass other_error : public exception\n{\n  public:\n    static other_error create(int id_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"other_error\", id_) + what_arg;\n        return other_error(id_, w.c_str());\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n\n#include <cstddef> // size_t\n#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type\n\nnamespace nlohmann\n{\nnamespace detail\n{\n// alias templates to reduce boilerplate\ntemplate<bool B, typename T = void>\nusing enable_if_t = typename std::enable_if<B, T>::type;\n\ntemplate<typename T>\nusing uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;\n\n// implementation of C++14 index_sequence and affiliates\n// source: https://stackoverflow.com/a/32223343\ntemplate<std::size_t... Ints>\nstruct index_sequence\n{\n    using type = index_sequence;\n    using value_type = std::size_t;\n    static constexpr std::size_t size() noexcept\n    {\n        return sizeof...(Ints);\n    }\n};\n\ntemplate<class Sequence1, class Sequence2>\nstruct merge_and_renumber;\n\ntemplate<std::size_t... I1, std::size_t... I2>\nstruct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>\n        : index_sequence < I1..., (sizeof...(I1) + I2)... > {};\n\ntemplate<std::size_t N>\nstruct make_index_sequence\n    : merge_and_renumber < typename make_index_sequence < N / 2 >::type,\n      typename make_index_sequence < N - N / 2 >::type > {};\n\ntemplate<> struct make_index_sequence<0> : index_sequence<> {};\ntemplate<> struct make_index_sequence<1> : index_sequence<0> {};\n\ntemplate<typename... Ts>\nusing index_sequence_for = make_index_sequence<sizeof...(Ts)>;\n\n// dispatch utility (taken from ranges-v3)\ntemplate<unsigned N> struct priority_tag : priority_tag < N - 1 > {};\ntemplate<> struct priority_tag<0> {};\n\n// taken from ranges-v3\ntemplate<typename T>\nstruct static_const\n{\n    static constexpr T value{};\n};\n\ntemplate<typename T>\nconstexpr T static_const<T>::value;\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\n#include <limits> // numeric_limits\n#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type\n#include <utility> // declval\n\n// #include <nlohmann/detail/iterators/iterator_traits.hpp>\n\n\n#include <iterator> // random_access_iterator_tag\n\n// #include <nlohmann/detail/meta/void_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename ...Ts> struct make_void\n{\n    using type = void;\n};\ntemplate<typename ...Ts> using void_t = typename make_void<Ts...>::type;\n} // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename It, typename = void>\nstruct iterator_types {};\n\ntemplate<typename It>\nstruct iterator_types <\n    It,\n    void_t<typename It::difference_type, typename It::value_type, typename It::pointer,\n    typename It::reference, typename It::iterator_category >>\n{\n    using difference_type = typename It::difference_type;\n    using value_type = typename It::value_type;\n    using pointer = typename It::pointer;\n    using reference = typename It::reference;\n    using iterator_category = typename It::iterator_category;\n};\n\n// This is required as some compilers implement std::iterator_traits in a way that\n// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.\ntemplate<typename T, typename = void>\nstruct iterator_traits\n{\n};\n\ntemplate<typename T>\nstruct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>\n            : iterator_types<T>\n{\n};\n\ntemplate<typename T>\nstruct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>\n{\n    using iterator_category = std::random_access_iterator_tag;\n    using value_type = T;\n    using difference_type = ptrdiff_t;\n    using pointer = T*;\n    using reference = T&;\n};\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/detected.hpp>\n\n\n#include <type_traits>\n\n// #include <nlohmann/detail/meta/void_t.hpp>\n\n\n// https://en.cppreference.com/w/cpp/experimental/is_detected\nnamespace nlohmann\n{\nnamespace detail\n{\nstruct nonesuch\n{\n    nonesuch() = delete;\n    ~nonesuch() = delete;\n    nonesuch(nonesuch const&) = delete;\n    nonesuch(nonesuch const&&) = delete;\n    void operator=(nonesuch const&) = delete;\n    void operator=(nonesuch&&) = delete;\n};\n\ntemplate<class Default,\n         class AlwaysVoid,\n         template<class...> class Op,\n         class... Args>\nstruct detector\n{\n    using value_t = std::false_type;\n    using type = Default;\n};\n\ntemplate<class Default, template<class...> class Op, class... Args>\nstruct detector<Default, void_t<Op<Args...>>, Op, Args...>\n{\n    using value_t = std::true_type;\n    using type = Op<Args...>;\n};\n\ntemplate<template<class...> class Op, class... Args>\nusing is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;\n\ntemplate<template<class...> class Op, class... Args>\nusing detected_t = typename detector<nonesuch, void, Op, Args...>::type;\n\ntemplate<class Default, template<class...> class Op, class... Args>\nusing detected_or = detector<Default, void, Op, Args...>;\n\ntemplate<class Default, template<class...> class Op, class... Args>\nusing detected_or_t = typename detected_or<Default, Op, Args...>::type;\n\ntemplate<class Expected, template<class...> class Op, class... Args>\nusing is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;\n\ntemplate<class To, template<class...> class Op, class... Args>\nusing is_detected_convertible =\n    std::is_convertible<detected_t<Op, Args...>, To>;\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/json_fwd.hpp>\n#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_\n#define INCLUDE_NLOHMANN_JSON_FWD_HPP_\n\n#include <cstdint> // int64_t, uint64_t\n#include <map> // map\n#include <memory> // allocator\n#include <string> // string\n#include <vector> // vector\n\n/*!\n@brief namespace for Niels Lohmann\n@see https://github.com/nlohmann\n@since version 1.0.0\n*/\nnamespace nlohmann\n{\n/*!\n@brief default JSONSerializer template argument\n\nThis serializer ignores the template arguments and uses ADL\n([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))\nfor serialization.\n*/\ntemplate<typename T = void, typename SFINAE = void>\nstruct adl_serializer;\n\ntemplate<template<typename U, typename V, typename... Args> class ObjectType =\n         std::map,\n         template<typename U, typename... Args> class ArrayType = std::vector,\n         class StringType = std::string, class BooleanType = bool,\n         class NumberIntegerType = std::int64_t,\n         class NumberUnsignedType = std::uint64_t,\n         class NumberFloatType = double,\n         template<typename U> class AllocatorType = std::allocator,\n         template<typename T, typename SFINAE = void> class JSONSerializer =\n         adl_serializer,\n         class BinaryType = std::vector<std::uint8_t>>\nclass basic_json;\n\n/*!\n@brief JSON Pointer\n\nA JSON pointer defines a string syntax for identifying a specific value\nwithin a JSON document. It can be used with functions `at` and\n`operator[]`. Furthermore, JSON pointers are the base for JSON patches.\n\n@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)\n\n@since version 2.0.0\n*/\ntemplate<typename BasicJsonType>\nclass json_pointer;\n\n/*!\n@brief default JSON class\n\nThis type is the default specialization of the @ref basic_json class which\nuses the standard template types.\n\n@since version 1.0.0\n*/\nusing json = basic_json<>;\n\ntemplate<class Key, class T, class IgnoredLess, class Allocator>\nstruct ordered_map;\n\n/*!\n@brief ordered JSON class\n\nThis type preserves the insertion order of object keys.\n\n@since version 3.9.0\n*/\nusing ordered_json = basic_json<nlohmann::ordered_map>;\n\n}  // namespace nlohmann\n\n#endif  // INCLUDE_NLOHMANN_JSON_FWD_HPP_\n\n\nnamespace nlohmann\n{\n/*!\n@brief detail namespace with internal helper functions\n\nThis namespace collects functions that should not be exposed,\nimplementations of some @ref basic_json methods, and meta-programming helpers.\n\n@since version 2.1.0\n*/\nnamespace detail\n{\n/////////////\n// helpers //\n/////////////\n\n// Note to maintainers:\n//\n// Every trait in this file expects a non CV-qualified type.\n// The only exceptions are in the 'aliases for detected' section\n// (i.e. those of the form: decltype(T::member_function(std::declval<T>())))\n//\n// In this case, T has to be properly CV-qualified to constraint the function arguments\n// (e.g. to_json(BasicJsonType&, const T&))\n\ntemplate<typename> struct is_basic_json : std::false_type {};\n\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nstruct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};\n\n//////////////////////\n// json_ref helpers //\n//////////////////////\n\ntemplate<typename>\nclass json_ref;\n\ntemplate<typename>\nstruct is_json_ref : std::false_type {};\n\ntemplate<typename T>\nstruct is_json_ref<json_ref<T>> : std::true_type {};\n\n//////////////////////////\n// aliases for detected //\n//////////////////////////\n\ntemplate<typename T>\nusing mapped_type_t = typename T::mapped_type;\n\ntemplate<typename T>\nusing key_type_t = typename T::key_type;\n\ntemplate<typename T>\nusing value_type_t = typename T::value_type;\n\ntemplate<typename T>\nusing difference_type_t = typename T::difference_type;\n\ntemplate<typename T>\nusing pointer_t = typename T::pointer;\n\ntemplate<typename T>\nusing reference_t = typename T::reference;\n\ntemplate<typename T>\nusing iterator_category_t = typename T::iterator_category;\n\ntemplate<typename T>\nusing iterator_t = typename T::iterator;\n\ntemplate<typename T, typename... Args>\nusing to_json_function = decltype(T::to_json(std::declval<Args>()...));\n\ntemplate<typename T, typename... Args>\nusing from_json_function = decltype(T::from_json(std::declval<Args>()...));\n\ntemplate<typename T, typename U>\nusing get_template_function = decltype(std::declval<T>().template get<U>());\n\n// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_from_json : std::false_type {};\n\n// trait checking if j.get<T> is valid\n// use this trait instead of std::is_constructible or std::is_convertible,\n// both rely on, or make use of implicit conversions, and thus fail when T\n// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)\ntemplate <typename BasicJsonType, typename T>\nstruct is_getable\n{\n    static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;\n};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_from_json < BasicJsonType, T,\n           enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<void, from_json_function, serializer,\n        const BasicJsonType&, T&>::value;\n};\n\n// This trait checks if JSONSerializer<T>::from_json(json const&) exists\n// this overload is used for non-default-constructible user-defined-types\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_non_default_from_json : std::false_type {};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<T, from_json_function, serializer,\n        const BasicJsonType&>::value;\n};\n\n// This trait checks if BasicJsonType::json_serializer<T>::to_json exists\n// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_to_json : std::false_type {};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<void, to_json_function, serializer, BasicJsonType&,\n        T>::value;\n};\n\n\n///////////////////\n// is_ functions //\n///////////////////\n\ntemplate<typename T, typename = void>\nstruct is_iterator_traits : std::false_type {};\n\ntemplate<typename T>\nstruct is_iterator_traits<iterator_traits<T>>\n{\n  private:\n    using traits = iterator_traits<T>;\n\n  public:\n    static constexpr auto value =\n        is_detected<value_type_t, traits>::value &&\n        is_detected<difference_type_t, traits>::value &&\n        is_detected<pointer_t, traits>::value &&\n        is_detected<iterator_category_t, traits>::value &&\n        is_detected<reference_t, traits>::value;\n};\n\n// source: https://stackoverflow.com/a/37193089/4116453\n\ntemplate<typename T, typename = void>\nstruct is_complete_type : std::false_type {};\n\ntemplate<typename T>\nstruct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType,\n         typename = void>\nstruct is_compatible_object_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType>\nstruct is_compatible_object_type_impl <\n    BasicJsonType, CompatibleObjectType,\n    enable_if_t < is_detected<mapped_type_t, CompatibleObjectType>::value&&\n    is_detected<key_type_t, CompatibleObjectType>::value >>\n{\n\n    using object_t = typename BasicJsonType::object_t;\n\n    // macOS's is_constructible does not play well with nonesuch...\n    static constexpr bool value =\n        std::is_constructible<typename object_t::key_type,\n        typename CompatibleObjectType::key_type>::value &&\n        std::is_constructible<typename object_t::mapped_type,\n        typename CompatibleObjectType::mapped_type>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType>\nstruct is_compatible_object_type\n    : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType,\n         typename = void>\nstruct is_constructible_object_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType>\nstruct is_constructible_object_type_impl <\n    BasicJsonType, ConstructibleObjectType,\n    enable_if_t < is_detected<mapped_type_t, ConstructibleObjectType>::value&&\n    is_detected<key_type_t, ConstructibleObjectType>::value >>\n{\n    using object_t = typename BasicJsonType::object_t;\n\n    static constexpr bool value =\n        (std::is_default_constructible<ConstructibleObjectType>::value &&\n         (std::is_move_assignable<ConstructibleObjectType>::value ||\n          std::is_copy_assignable<ConstructibleObjectType>::value) &&\n         (std::is_constructible<typename ConstructibleObjectType::key_type,\n          typename object_t::key_type>::value &&\n          std::is_same <\n          typename object_t::mapped_type,\n          typename ConstructibleObjectType::mapped_type >::value)) ||\n        (has_from_json<BasicJsonType,\n         typename ConstructibleObjectType::mapped_type>::value ||\n         has_non_default_from_json <\n         BasicJsonType,\n         typename ConstructibleObjectType::mapped_type >::value);\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType>\nstruct is_constructible_object_type\n    : is_constructible_object_type_impl<BasicJsonType,\n      ConstructibleObjectType> {};\n\ntemplate<typename BasicJsonType, typename CompatibleStringType,\n         typename = void>\nstruct is_compatible_string_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleStringType>\nstruct is_compatible_string_type_impl <\n    BasicJsonType, CompatibleStringType,\n    enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type,\n    value_type_t, CompatibleStringType>::value >>\n{\n    static constexpr auto value =\n        std::is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleStringType>\nstruct is_compatible_string_type\n    : is_compatible_string_type_impl<BasicJsonType, ConstructibleStringType> {};\n\ntemplate<typename BasicJsonType, typename ConstructibleStringType,\n         typename = void>\nstruct is_constructible_string_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleStringType>\nstruct is_constructible_string_type_impl <\n    BasicJsonType, ConstructibleStringType,\n    enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type,\n    value_type_t, ConstructibleStringType>::value >>\n{\n    static constexpr auto value =\n        std::is_constructible<ConstructibleStringType,\n        typename BasicJsonType::string_t>::value;\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleStringType>\nstruct is_constructible_string_type\n    : is_constructible_string_type_impl<BasicJsonType, ConstructibleStringType> {};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType, typename = void>\nstruct is_compatible_array_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType>\nstruct is_compatible_array_type_impl <\n    BasicJsonType, CompatibleArrayType,\n    enable_if_t < is_detected<value_type_t, CompatibleArrayType>::value&&\n    is_detected<iterator_t, CompatibleArrayType>::value&&\n// This is needed because json_reverse_iterator has a ::iterator type...\n// Therefore it is detected as a CompatibleArrayType.\n// The real fix would be to have an Iterable concept.\n    !is_iterator_traits <\n    iterator_traits<CompatibleArrayType >>::value >>\n{\n    static constexpr bool value =\n        std::is_constructible<BasicJsonType,\n        typename CompatibleArrayType::value_type>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType>\nstruct is_compatible_array_type\n    : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType, typename = void>\nstruct is_constructible_array_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type_impl <\n    BasicJsonType, ConstructibleArrayType,\n    enable_if_t<std::is_same<ConstructibleArrayType,\n    typename BasicJsonType::value_type>::value >>\n            : std::true_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type_impl <\n    BasicJsonType, ConstructibleArrayType,\n    enable_if_t < !std::is_same<ConstructibleArrayType,\n    typename BasicJsonType::value_type>::value&&\n    std::is_default_constructible<ConstructibleArrayType>::value&&\n(std::is_move_assignable<ConstructibleArrayType>::value ||\n std::is_copy_assignable<ConstructibleArrayType>::value)&&\nis_detected<value_type_t, ConstructibleArrayType>::value&&\nis_detected<iterator_t, ConstructibleArrayType>::value&&\nis_complete_type <\ndetected_t<value_type_t, ConstructibleArrayType >>::value >>\n{\n    static constexpr bool value =\n        // This is needed because json_reverse_iterator has a ::iterator type,\n        // furthermore, std::back_insert_iterator (and other iterators) have a\n        // base class `iterator`... Therefore it is detected as a\n        // ConstructibleArrayType. The real fix would be to have an Iterable\n        // concept.\n        !is_iterator_traits<iterator_traits<ConstructibleArrayType>>::value &&\n\n        (std::is_same<typename ConstructibleArrayType::value_type,\n         typename BasicJsonType::array_t::value_type>::value ||\n         has_from_json<BasicJsonType,\n         typename ConstructibleArrayType::value_type>::value ||\n         has_non_default_from_json <\n         BasicJsonType, typename ConstructibleArrayType::value_type >::value);\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type\n    : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType,\n         typename = void>\nstruct is_compatible_integer_type_impl : std::false_type {};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType>\nstruct is_compatible_integer_type_impl <\n    RealIntegerType, CompatibleNumberIntegerType,\n    enable_if_t < std::is_integral<RealIntegerType>::value&&\n    std::is_integral<CompatibleNumberIntegerType>::value&&\n    !std::is_same<bool, CompatibleNumberIntegerType>::value >>\n{\n    // is there an assert somewhere on overflows?\n    using RealLimits = std::numeric_limits<RealIntegerType>;\n    using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;\n\n    static constexpr auto value =\n        std::is_constructible<RealIntegerType,\n        CompatibleNumberIntegerType>::value &&\n        CompatibleLimits::is_integer &&\n        RealLimits::is_signed == CompatibleLimits::is_signed;\n};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType>\nstruct is_compatible_integer_type\n    : is_compatible_integer_type_impl<RealIntegerType,\n      CompatibleNumberIntegerType> {};\n\ntemplate<typename BasicJsonType, typename CompatibleType, typename = void>\nstruct is_compatible_type_impl: std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleType>\nstruct is_compatible_type_impl <\n    BasicJsonType, CompatibleType,\n    enable_if_t<is_complete_type<CompatibleType>::value >>\n{\n    static constexpr bool value =\n        has_to_json<BasicJsonType, CompatibleType>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleType>\nstruct is_compatible_type\n    : is_compatible_type_impl<BasicJsonType, CompatibleType> {};\n\n// https://en.cppreference.com/w/cpp/types/conjunction\ntemplate<class...> struct conjunction : std::true_type { };\ntemplate<class B1> struct conjunction<B1> : B1 { };\ntemplate<class B1, class... Bn>\nstruct conjunction<B1, Bn...>\n: std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};\n\ntemplate<typename T1, typename T2>\nstruct is_constructible_tuple : std::false_type {};\n\ntemplate<typename T1, typename... Args>\nstruct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<std::is_constructible<T1, Args>...> {};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\n#include <array> // array\n#include <cstddef> // size_t\n#include <cstdint> // uint8_t\n#include <string> // string\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////////////\n// JSON type enumeration //\n///////////////////////////\n\n/*!\n@brief the JSON type enumeration\n\nThis enumeration collects the different JSON types. It is internally used to\ndistinguish the stored values, and the functions @ref basic_json::is_null(),\n@ref basic_json::is_object(), @ref basic_json::is_array(),\n@ref basic_json::is_string(), @ref basic_json::is_boolean(),\n@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),\n@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),\n@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and\n@ref basic_json::is_structured() rely on it.\n\n@note There are three enumeration entries (number_integer, number_unsigned, and\nnumber_float), because the library distinguishes these three types for numbers:\n@ref basic_json::number_unsigned_t is used for unsigned integers,\n@ref basic_json::number_integer_t is used for signed integers, and\n@ref basic_json::number_float_t is used for floating-point numbers or to\napproximate integers which do not fit in the limits of their respective type.\n\n@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON\nvalue with the default value for a given type\n\n@since version 1.0.0\n*/\nenum class value_t : std::uint8_t\n{\n    null,             ///< null value\n    object,           ///< object (unordered set of name/value pairs)\n    array,            ///< array (ordered collection of values)\n    string,           ///< string value\n    boolean,          ///< boolean value\n    number_integer,   ///< number value (signed integer)\n    number_unsigned,  ///< number value (unsigned integer)\n    number_float,     ///< number value (floating-point)\n    binary,           ///< binary array (ordered collection of bytes)\n    discarded         ///< discarded by the parser callback function\n};\n\n/*!\n@brief comparison operator for JSON types\n\nReturns an ordering that is similar to Python:\n- order: null < boolean < number < object < array < string < binary\n- furthermore, each type is not smaller than itself\n- discarded values are not comparable\n- binary is represented as a b\"\" string in python and directly comparable to a\n  string; however, making a binary array directly comparable with a string would\n  be surprising behavior in a JSON file.\n\n@since version 1.0.0\n*/\ninline bool operator<(const value_t lhs, const value_t rhs) noexcept\n{\n    static constexpr std::array<std::uint8_t, 9> order = {{\n            0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,\n            1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */,\n            6 /* binary */\n        }\n    };\n\n    const auto l_index = static_cast<std::size_t>(lhs);\n    const auto r_index = static_cast<std::size_t>(rhs);\n    return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];\n}\n}  // namespace detail\n}  // namespace nlohmann\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename std::nullptr_t& n)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_null()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be null, but is \" + std::string(j.type_name())));\n    }\n    n = nullptr;\n}\n\n// overloads for basic_json template parameters\ntemplate < typename BasicJsonType, typename ArithmeticType,\n           enable_if_t < std::is_arithmetic<ArithmeticType>::value&&\n                         !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,\n                         int > = 0 >\nvoid get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)\n{\n    switch (static_cast<value_t>(j))\n    {\n        case value_t::number_unsigned:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());\n            break;\n        }\n        case value_t::number_integer:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());\n            break;\n        }\n        case value_t::number_float:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());\n            break;\n        }\n\n        default:\n            JSON_THROW(type_error::create(302, \"type must be number, but is \" + std::string(j.type_name())));\n    }\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be boolean, but is \" + std::string(j.type_name())));\n    }\n    b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be string, but is \" + std::string(j.type_name())));\n    }\n    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n\ntemplate <\n    typename BasicJsonType, typename ConstructibleStringType,\n    enable_if_t <\n        is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value&&\n        !std::is_same<typename BasicJsonType::string_t,\n                      ConstructibleStringType>::value,\n        int > = 0 >\nvoid from_json(const BasicJsonType& j, ConstructibleStringType& s)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be string, but is \" + std::string(j.type_name())));\n    }\n\n    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType, typename EnumType,\n         enable_if_t<std::is_enum<EnumType>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, EnumType& e)\n{\n    typename std::underlying_type<EnumType>::type val;\n    get_arithmetic_value(j, val);\n    e = static_cast<EnumType>(val);\n}\n\n// forward_list doesn't have an insert method\ntemplate<typename BasicJsonType, typename T, typename Allocator,\n         enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name())));\n    }\n    l.clear();\n    std::transform(j.rbegin(), j.rend(),\n                   std::front_inserter(l), [](const BasicJsonType & i)\n    {\n        return i.template get<T>();\n    });\n}\n\n// valarray doesn't have an insert method\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, std::valarray<T>& l)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name())));\n    }\n    l.resize(j.size());\n    std::transform(j.begin(), j.end(), std::begin(l),\n                   [](const BasicJsonType & elem)\n    {\n        return elem.template get<T>();\n    });\n}\n\ntemplate<typename BasicJsonType, typename T, std::size_t N>\nauto from_json(const BasicJsonType& j, T (&arr)[N])\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i = 0; i < N; ++i)\n    {\n        arr[i] = j.at(i).template get<T>();\n    }\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)\n{\n    arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();\n}\n\ntemplate<typename BasicJsonType, typename T, std::size_t N>\nauto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,\n                          priority_tag<2> /*unused*/)\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i = 0; i < N; ++i)\n    {\n        arr[i] = j.at(i).template get<T>();\n    }\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nauto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)\n-> decltype(\n    arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),\n    j.template get<typename ConstructibleArrayType::value_type>(),\n    void())\n{\n    using std::end;\n\n    ConstructibleArrayType ret;\n    ret.reserve(j.size());\n    std::transform(j.begin(), j.end(),\n                   std::inserter(ret, end(ret)), [](const BasicJsonType & i)\n    {\n        // get<BasicJsonType>() returns *this, this won't call a from_json\n        // method when value_type is BasicJsonType\n        return i.template get<typename ConstructibleArrayType::value_type>();\n    });\n    arr = std::move(ret);\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nvoid from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,\n                          priority_tag<0> /*unused*/)\n{\n    using std::end;\n\n    ConstructibleArrayType ret;\n    std::transform(\n        j.begin(), j.end(), std::inserter(ret, end(ret)),\n        [](const BasicJsonType & i)\n    {\n        // get<BasicJsonType>() returns *this, this won't call a from_json\n        // method when value_type is BasicJsonType\n        return i.template get<typename ConstructibleArrayType::value_type>();\n    });\n    arr = std::move(ret);\n}\n\ntemplate < typename BasicJsonType, typename ConstructibleArrayType,\n           enable_if_t <\n               is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&\n               !is_basic_json<ConstructibleArrayType>::value,\n               int > = 0 >\nauto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)\n-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),\nj.template get<typename ConstructibleArrayType::value_type>(),\nvoid())\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" +\n                                      std::string(j.type_name())));\n    }\n\n    from_json_array_impl(j, arr, priority_tag<3> {});\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be binary, but is \" + std::string(j.type_name())));\n    }\n\n    bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType,\n         enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, ConstructibleObjectType& obj)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_object()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be object, but is \" + std::string(j.type_name())));\n    }\n\n    ConstructibleObjectType ret;\n    auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();\n    using value_type = typename ConstructibleObjectType::value_type;\n    std::transform(\n        inner_object->begin(), inner_object->end(),\n        std::inserter(ret, ret.begin()),\n        [](typename BasicJsonType::object_t::value_type const & p)\n    {\n        return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());\n    });\n    obj = std::move(ret);\n}\n\n// overload for arithmetic types, not chosen for basic_json template arguments\n// (BooleanType, etc..); note: Is it really necessary to provide explicit\n// overloads for boolean_t etc. in case of a custom BooleanType which is not\n// an arithmetic type?\ntemplate < typename BasicJsonType, typename ArithmeticType,\n           enable_if_t <\n               std::is_arithmetic<ArithmeticType>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,\n               int > = 0 >\nvoid from_json(const BasicJsonType& j, ArithmeticType& val)\n{\n    switch (static_cast<value_t>(j))\n    {\n        case value_t::number_unsigned:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());\n            break;\n        }\n        case value_t::number_integer:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());\n            break;\n        }\n        case value_t::number_float:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());\n            break;\n        }\n        case value_t::boolean:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());\n            break;\n        }\n\n        default:\n            JSON_THROW(type_error::create(302, \"type must be number, but is \" + std::string(j.type_name())));\n    }\n}\n\ntemplate<typename BasicJsonType, typename A1, typename A2>\nvoid from_json(const BasicJsonType& j, std::pair<A1, A2>& p)\n{\n    p = {j.at(0).template get<A1>(), j.at(1).template get<A2>()};\n}\n\ntemplate<typename BasicJsonType, typename Tuple, std::size_t... Idx>\nvoid from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence<Idx...> /*unused*/)\n{\n    t = std::make_tuple(j.at(Idx).template get<typename std::tuple_element<Idx, Tuple>::type>()...);\n}\n\ntemplate<typename BasicJsonType, typename... Args>\nvoid from_json(const BasicJsonType& j, std::tuple<Args...>& t)\n{\n    from_json_tuple_impl(j, t, index_sequence_for<Args...> {});\n}\n\ntemplate < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,\n           typename = enable_if_t < !std::is_constructible <\n                                        typename BasicJsonType::string_t, Key >::value >>\nvoid from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name())));\n    }\n    m.clear();\n    for (const auto& p : j)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))\n        {\n            JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(p.type_name())));\n        }\n        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());\n    }\n}\n\ntemplate < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,\n           typename = enable_if_t < !std::is_constructible <\n                                        typename BasicJsonType::string_t, Key >::value >>\nvoid from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name())));\n    }\n    m.clear();\n    for (const auto& p : j)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))\n        {\n            JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(p.type_name())));\n        }\n        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());\n    }\n}\n\nstruct from_json_fn\n{\n    template<typename BasicJsonType, typename T>\n    auto operator()(const BasicJsonType& j, T& val) const\n    noexcept(noexcept(from_json(j, val)))\n    -> decltype(from_json(j, val), void())\n    {\n        return from_json(j, val);\n    }\n};\n}  // namespace detail\n\n/// namespace to hold default `from_json` function\n/// to see why this is required:\n/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html\nnamespace\n{\nconstexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value;\n} // namespace\n} // namespace nlohmann\n\n// #include <nlohmann/detail/conversions/to_json.hpp>\n\n\n#include <algorithm> // copy\n#include <iterator> // begin, end\n#include <string> // string\n#include <tuple> // tuple, get\n#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type\n#include <utility> // move, forward, declval, pair\n#include <valarray> // valarray\n#include <vector> // vector\n\n// #include <nlohmann/detail/iterators/iteration_proxy.hpp>\n\n\n#include <cstddef> // size_t\n#include <iterator> // input_iterator_tag\n#include <string> // string, to_string\n#include <tuple> // tuple_size, get, tuple_element\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename string_type>\nvoid int_to_string( string_type& target, std::size_t value )\n{\n    // For ADL\n    using std::to_string;\n    target = to_string(value);\n}\ntemplate<typename IteratorType> class iteration_proxy_value\n{\n  public:\n    using difference_type = std::ptrdiff_t;\n    using value_type = iteration_proxy_value;\n    using pointer = value_type * ;\n    using reference = value_type & ;\n    using iterator_category = std::input_iterator_tag;\n    using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;\n\n  private:\n    /// the iterator\n    IteratorType anchor;\n    /// an index for arrays (used to create key names)\n    std::size_t array_index = 0;\n    /// last stringified array index\n    mutable std::size_t array_index_last = 0;\n    /// a string representation of the array index\n    mutable string_type array_index_str = \"0\";\n    /// an empty string (to return a reference for primitive values)\n    const string_type empty_str = \"\";\n\n  public:\n    explicit iteration_proxy_value(IteratorType it) noexcept : anchor(it) {}\n\n    /// dereference operator (needed for range-based for)\n    iteration_proxy_value& operator*()\n    {\n        return *this;\n    }\n\n    /// increment operator (needed for range-based for)\n    iteration_proxy_value& operator++()\n    {\n        ++anchor;\n        ++array_index;\n\n        return *this;\n    }\n\n    /// equality operator (needed for InputIterator)\n    bool operator==(const iteration_proxy_value& o) const\n    {\n        return anchor == o.anchor;\n    }\n\n    /// inequality operator (needed for range-based for)\n    bool operator!=(const iteration_proxy_value& o) const\n    {\n        return anchor != o.anchor;\n    }\n\n    /// return key of the iterator\n    const string_type& key() const\n    {\n        JSON_ASSERT(anchor.m_object != nullptr);\n\n        switch (anchor.m_object->type())\n        {\n            // use integer array index as key\n            case value_t::array:\n            {\n                if (array_index != array_index_last)\n                {\n                    int_to_string( array_index_str, array_index );\n                    array_index_last = array_index;\n                }\n                return array_index_str;\n            }\n\n            // use key from the object\n            case value_t::object:\n                return anchor.key();\n\n            // use an empty key for all primitive types\n            default:\n                return empty_str;\n        }\n    }\n\n    /// return value of the iterator\n    typename IteratorType::reference value() const\n    {\n        return anchor.value();\n    }\n};\n\n/// proxy class for the items() function\ntemplate<typename IteratorType> class iteration_proxy\n{\n  private:\n    /// the container to iterate\n    typename IteratorType::reference container;\n\n  public:\n    /// construct iteration proxy from a container\n    explicit iteration_proxy(typename IteratorType::reference cont) noexcept\n        : container(cont) {}\n\n    /// return iterator begin (needed for range-based for)\n    iteration_proxy_value<IteratorType> begin() noexcept\n    {\n        return iteration_proxy_value<IteratorType>(container.begin());\n    }\n\n    /// return iterator end (needed for range-based for)\n    iteration_proxy_value<IteratorType> end() noexcept\n    {\n        return iteration_proxy_value<IteratorType>(container.end());\n    }\n};\n// Structured Bindings Support\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\ntemplate<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>\nauto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())\n{\n    return i.key();\n}\n// Structured Bindings Support\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\ntemplate<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>\nauto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())\n{\n    return i.value();\n}\n}  // namespace detail\n}  // namespace nlohmann\n\n// The Addition to the STD Namespace is required to add\n// Structured Bindings Support to the iteration_proxy_value class\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\nnamespace std\n{\n#if defined(__clang__)\n    // Fix: https://github.com/nlohmann/json/issues/1401\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wmismatched-tags\"\n#endif\ntemplate<typename IteratorType>\nclass tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>>\n            : public std::integral_constant<std::size_t, 2> {};\n\ntemplate<std::size_t N, typename IteratorType>\nclass tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>\n{\n  public:\n    using type = decltype(\n                     get<N>(std::declval <\n                            ::nlohmann::detail::iteration_proxy_value<IteratorType >> ()));\n};\n#if defined(__clang__)\n    #pragma clang diagnostic pop\n#endif\n} // namespace std\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n//////////////////\n// constructors //\n//////////////////\n\ntemplate<value_t> struct external_constructor;\n\ntemplate<>\nstruct external_constructor<value_t::boolean>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept\n    {\n        j.m_type = value_t::boolean;\n        j.m_value = b;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::string>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)\n    {\n        j.m_type = value_t::string;\n        j.m_value = s;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)\n    {\n        j.m_type = value_t::string;\n        j.m_value = std::move(s);\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleStringType,\n               enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,\n                             int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleStringType& str)\n    {\n        j.m_type = value_t::string;\n        j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::binary>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)\n    {\n        j.m_type = value_t::binary;\n        typename BasicJsonType::binary_t value{b};\n        j.m_value = value;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)\n    {\n        j.m_type = value_t::binary;\n        typename BasicJsonType::binary_t value{std::move(b)};\n        j.m_value = value;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_float>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept\n    {\n        j.m_type = value_t::number_float;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_unsigned>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept\n    {\n        j.m_type = value_t::number_unsigned;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_integer>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept\n    {\n        j.m_type = value_t::number_integer;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::array>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)\n    {\n        j.m_type = value_t::array;\n        j.m_value = arr;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)\n    {\n        j.m_type = value_t::array;\n        j.m_value = std::move(arr);\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleArrayType,\n               enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,\n                             int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleArrayType& arr)\n    {\n        using std::begin;\n        using std::end;\n        j.m_type = value_t::array;\n        j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const std::vector<bool>& arr)\n    {\n        j.m_type = value_t::array;\n        j.m_value = value_t::array;\n        j.m_value.array->reserve(arr.size());\n        for (const bool x : arr)\n        {\n            j.m_value.array->push_back(x);\n        }\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType, typename T,\n             enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>\n    static void construct(BasicJsonType& j, const std::valarray<T>& arr)\n    {\n        j.m_type = value_t::array;\n        j.m_value = value_t::array;\n        j.m_value.array->resize(arr.size());\n        if (arr.size() > 0)\n        {\n            std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());\n        }\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::object>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)\n    {\n        j.m_type = value_t::object;\n        j.m_value = obj;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)\n    {\n        j.m_type = value_t::object;\n        j.m_value = std::move(obj);\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleObjectType,\n               enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleObjectType& obj)\n    {\n        using std::begin;\n        using std::end;\n\n        j.m_type = value_t::object;\n        j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));\n        j.assert_invariant();\n    }\n};\n\n/////////////\n// to_json //\n/////////////\n\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>\nvoid to_json(BasicJsonType& j, T b) noexcept\n{\n    external_constructor<value_t::boolean>::construct(j, b);\n}\n\ntemplate<typename BasicJsonType, typename CompatibleString,\n         enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const CompatibleString& s)\n{\n    external_constructor<value_t::string>::construct(j, s);\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)\n{\n    external_constructor<value_t::string>::construct(j, std::move(s));\n}\n\ntemplate<typename BasicJsonType, typename FloatType,\n         enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, FloatType val) noexcept\n{\n    external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleNumberUnsignedType,\n         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept\n{\n    external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleNumberIntegerType,\n         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept\n{\n    external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename EnumType,\n         enable_if_t<std::is_enum<EnumType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, EnumType e) noexcept\n{\n    using underlying_type = typename std::underlying_type<EnumType>::type;\n    external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, const std::vector<bool>& e)\n{\n    external_constructor<value_t::array>::construct(j, e);\n}\n\ntemplate < typename BasicJsonType, typename CompatibleArrayType,\n           enable_if_t < is_compatible_array_type<BasicJsonType,\n                         CompatibleArrayType>::value&&\n                         !is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&\n                         !is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&\n                         !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&\n                         !is_basic_json<CompatibleArrayType>::value,\n                         int > = 0 >\nvoid to_json(BasicJsonType& j, const CompatibleArrayType& arr)\n{\n    external_constructor<value_t::array>::construct(j, arr);\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)\n{\n    external_constructor<value_t::binary>::construct(j, bin);\n}\n\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const std::valarray<T>& arr)\n{\n    external_constructor<value_t::array>::construct(j, std::move(arr));\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)\n{\n    external_constructor<value_t::array>::construct(j, std::move(arr));\n}\n\ntemplate < typename BasicJsonType, typename CompatibleObjectType,\n           enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >\nvoid to_json(BasicJsonType& j, const CompatibleObjectType& obj)\n{\n    external_constructor<value_t::object>::construct(j, obj);\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)\n{\n    external_constructor<value_t::object>::construct(j, std::move(obj));\n}\n\ntemplate <\n    typename BasicJsonType, typename T, std::size_t N,\n    enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,\n                  const T(&)[N]>::value,\n                  int > = 0 >\nvoid to_json(BasicJsonType& j, const T(&arr)[N])\n{\n    external_constructor<value_t::array>::construct(j, arr);\n}\n\ntemplate < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >\nvoid to_json(BasicJsonType& j, const std::pair<T1, T2>& p)\n{\n    j = { p.first, p.second };\n}\n\n// for https://github.com/nlohmann/json/pull/1134\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const T& b)\n{\n    j = { {b.key(), b.value()} };\n}\n\ntemplate<typename BasicJsonType, typename Tuple, std::size_t... Idx>\nvoid to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)\n{\n    j = { std::get<Idx>(t)... };\n}\n\ntemplate<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>\nvoid to_json(BasicJsonType& j, const T& t)\n{\n    to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});\n}\n\nstruct to_json_fn\n{\n    template<typename BasicJsonType, typename T>\n    auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))\n    -> decltype(to_json(j, std::forward<T>(val)), void())\n    {\n        return to_json(j, std::forward<T>(val));\n    }\n};\n}  // namespace detail\n\n/// namespace to hold default `to_json` function\nnamespace\n{\nconstexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value;\n} // namespace\n} // namespace nlohmann\n\n\nnamespace nlohmann\n{\n\ntemplate<typename, typename>\nstruct adl_serializer\n{\n    /*!\n    @brief convert a JSON value to any value type\n\n    This function is usually called by the `get()` function of the\n    @ref basic_json class (either explicit or via conversion operators).\n\n    @param[in] j        JSON value to read from\n    @param[in,out] val  value to write to\n    */\n    template<typename BasicJsonType, typename ValueType>\n    static auto from_json(BasicJsonType&& j, ValueType& val) noexcept(\n        noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))\n    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())\n    {\n        ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);\n    }\n\n    /*!\n    @brief convert any value type to a JSON value\n\n    This function is usually called by the constructors of the @ref basic_json\n    class.\n\n    @param[in,out] j  JSON value to write to\n    @param[in] val    value to read from\n    */\n    template<typename BasicJsonType, typename ValueType>\n    static auto to_json(BasicJsonType& j, ValueType&& val) noexcept(\n        noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val))))\n    -> decltype(::nlohmann::to_json(j, std::forward<ValueType>(val)), void())\n    {\n        ::nlohmann::to_json(j, std::forward<ValueType>(val));\n    }\n};\n\n}  // namespace nlohmann\n\n// #include <nlohmann/byte_container_with_subtype.hpp>\n\n\n#include <cstdint> // uint8_t\n#include <tuple> // tie\n#include <utility> // move\n\nnamespace nlohmann\n{\n\n/*!\n@brief an internal type for a backed binary type\n\nThis type extends the template parameter @a BinaryType provided to `basic_json`\nwith a subtype used by BSON and MessagePack. This type exists so that the user\ndoes not have to specify a type themselves with a specific naming scheme in\norder to override the binary type.\n\n@tparam BinaryType container to store bytes (`std::vector<std::uint8_t>` by\n                   default)\n\n@since version 3.8.0\n*/\ntemplate<typename BinaryType>\nclass byte_container_with_subtype : public BinaryType\n{\n  public:\n    /// the type of the underlying container\n    using container_type = BinaryType;\n\n    byte_container_with_subtype() noexcept(noexcept(container_type()))\n        : container_type()\n    {}\n\n    byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b)))\n        : container_type(b)\n    {}\n\n    byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b))))\n        : container_type(std::move(b))\n    {}\n\n    byte_container_with_subtype(const container_type& b, std::uint8_t subtype) noexcept(noexcept(container_type(b)))\n        : container_type(b)\n        , m_subtype(subtype)\n        , m_has_subtype(true)\n    {}\n\n    byte_container_with_subtype(container_type&& b, std::uint8_t subtype) noexcept(noexcept(container_type(std::move(b))))\n        : container_type(std::move(b))\n        , m_subtype(subtype)\n        , m_has_subtype(true)\n    {}\n\n    bool operator==(const byte_container_with_subtype& rhs) const\n    {\n        return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==\n               std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);\n    }\n\n    bool operator!=(const byte_container_with_subtype& rhs) const\n    {\n        return !(rhs == *this);\n    }\n\n    /*!\n    @brief sets the binary subtype\n\n    Sets the binary subtype of the value, also flags a binary JSON value as\n    having a subtype, which has implications for serialization.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @sa @ref subtype() -- return the binary subtype\n    @sa @ref clear_subtype() -- clears the binary subtype\n    @sa @ref has_subtype() -- returns whether or not the binary value has a\n    subtype\n\n    @since version 3.8.0\n    */\n    void set_subtype(std::uint8_t subtype) noexcept\n    {\n        m_subtype = subtype;\n        m_has_subtype = true;\n    }\n\n    /*!\n    @brief return the binary subtype\n\n    Returns the numerical subtype of the value if it has a subtype. If it does\n    not have a subtype, this function will return size_t(-1) as a sentinel\n    value.\n\n    @return the numerical subtype of the binary value\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @sa @ref set_subtype() -- sets the binary subtype\n    @sa @ref clear_subtype() -- clears the binary subtype\n    @sa @ref has_subtype() -- returns whether or not the binary value has a\n    subtype\n\n    @since version 3.8.0\n    */\n    constexpr std::uint8_t subtype() const noexcept\n    {\n        return m_subtype;\n    }\n\n    /*!\n    @brief return whether the value has a subtype\n\n    @return whether the value has a subtype\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @sa @ref subtype() -- return the binary subtype\n    @sa @ref set_subtype() -- sets the binary subtype\n    @sa @ref clear_subtype() -- clears the binary subtype\n\n    @since version 3.8.0\n    */\n    constexpr bool has_subtype() const noexcept\n    {\n        return m_has_subtype;\n    }\n\n    /*!\n    @brief clears the binary subtype\n\n    Clears the binary subtype and flags the value as not having a subtype, which\n    has implications for serialization; for instance MessagePack will prefer the\n    bin family over the ext family.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @sa @ref subtype() -- return the binary subtype\n    @sa @ref set_subtype() -- sets the binary subtype\n    @sa @ref has_subtype() -- returns whether or not the binary value has a\n    subtype\n\n    @since version 3.8.0\n    */\n    void clear_subtype() noexcept\n    {\n        m_subtype = 0;\n        m_has_subtype = false;\n    }\n\n  private:\n    std::uint8_t m_subtype = 0;\n    bool m_has_subtype = false;\n};\n\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/conversions/from_json.hpp>\n\n// #include <nlohmann/detail/conversions/to_json.hpp>\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/hash.hpp>\n\n\n#include <cstddef> // size_t, uint8_t\n#include <functional> // hash\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\n// boost::hash_combine\ninline std::size_t combine(std::size_t seed, std::size_t h) noexcept\n{\n    seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U);\n    return seed;\n}\n\n/*!\n@brief hash a JSON value\n\nThe hash function tries to rely on std::hash where possible. Furthermore, the\ntype of the JSON value is taken into account to have different hash values for\nnull, 0, 0U, and false, etc.\n\n@tparam BasicJsonType basic_json specialization\n@param j JSON value to hash\n@return hash value of j\n*/\ntemplate<typename BasicJsonType>\nstd::size_t hash(const BasicJsonType& j)\n{\n    using string_t = typename BasicJsonType::string_t;\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n\n    const auto type = static_cast<std::size_t>(j.type());\n    switch (j.type())\n    {\n        case BasicJsonType::value_t::null:\n        case BasicJsonType::value_t::discarded:\n        {\n            return combine(type, 0);\n        }\n\n        case BasicJsonType::value_t::object:\n        {\n            auto seed = combine(type, j.size());\n            for (const auto& element : j.items())\n            {\n                const auto h = std::hash<string_t> {}(element.key());\n                seed = combine(seed, h);\n                seed = combine(seed, hash(element.value()));\n            }\n            return seed;\n        }\n\n        case BasicJsonType::value_t::array:\n        {\n            auto seed = combine(type, j.size());\n            for (const auto& element : j)\n            {\n                seed = combine(seed, hash(element));\n            }\n            return seed;\n        }\n\n        case BasicJsonType::value_t::string:\n        {\n            const auto h = std::hash<string_t> {}(j.template get_ref<const string_t&>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::boolean:\n        {\n            const auto h = std::hash<bool> {}(j.template get<bool>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::number_integer:\n        {\n            const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());\n            return combine(type, h);\n        }\n\n        case nlohmann::detail::value_t::number_unsigned:\n        {\n            const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());\n            return combine(type, h);\n        }\n\n        case nlohmann::detail::value_t::number_float:\n        {\n            const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());\n            return combine(type, h);\n        }\n\n        case nlohmann::detail::value_t::binary:\n        {\n            auto seed = combine(type, j.get_binary().size());\n            const auto h = std::hash<bool> {}(j.get_binary().has_subtype());\n            seed = combine(seed, h);\n            seed = combine(seed, j.get_binary().subtype());\n            for (const auto byte : j.get_binary())\n            {\n                seed = combine(seed, std::hash<std::uint8_t> {}(byte));\n            }\n            return seed;\n        }\n\n        default: // LCOV_EXCL_LINE\n            JSON_ASSERT(false); // LCOV_EXCL_LINE\n    }\n}\n\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/binary_reader.hpp>\n\n\n#include <algorithm> // generate_n\n#include <array> // array\n#include <cmath> // ldexp\n#include <cstddef> // size_t\n#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t\n#include <cstdio> // snprintf\n#include <cstring> // memcpy\n#include <iterator> // back_inserter\n#include <limits> // numeric_limits\n#include <string> // char_traits, string\n#include <utility> // make_pair, move\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n\n#include <array> // array\n#include <cstddef> // size_t\n#include <cstdio> //FILE *\n#include <cstring> // strlen\n#include <istream> // istream\n#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next\n#include <memory> // shared_ptr, make_shared, addressof\n#include <numeric> // accumulate\n#include <string> // string, char_traits\n#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer\n#include <utility> // pair, declval\n\n// #include <nlohmann/detail/iterators/iterator_traits.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// the supported input formats\nenum class input_format_t { json, cbor, msgpack, ubjson, bson };\n\n////////////////////\n// input adapters //\n////////////////////\n\n/*!\nInput adapter for stdio file access. This adapter read only 1 byte and do not use any\n buffer. This adapter is a very low level adapter.\n*/\nclass file_input_adapter\n{\n  public:\n    using char_type = char;\n\n    JSON_HEDLEY_NON_NULL(2)\n    explicit file_input_adapter(std::FILE* f) noexcept\n        : m_file(f)\n    {}\n\n    // make class move-only\n    file_input_adapter(const file_input_adapter&) = delete;\n    file_input_adapter(file_input_adapter&&) = default;\n    file_input_adapter& operator=(const file_input_adapter&) = delete;\n    file_input_adapter& operator=(file_input_adapter&&) = delete;\n\n    std::char_traits<char>::int_type get_character() noexcept\n    {\n        return std::fgetc(m_file);\n    }\n\n  private:\n    /// the file pointer to read from\n    std::FILE* m_file;\n};\n\n\n/*!\nInput adapter for a (caching) istream. Ignores a UFT Byte Order Mark at\nbeginning of input. Does not support changing the underlying std::streambuf\nin mid-input. Maintains underlying std::istream and std::streambuf to support\nsubsequent use of standard std::istream operations to process any input\ncharacters following those used in parsing the JSON input.  Clears the\nstd::istream flags; any input errors (e.g., EOF) will be detected by the first\nsubsequent call for input from the std::istream.\n*/\nclass input_stream_adapter\n{\n  public:\n    using char_type = char;\n\n    ~input_stream_adapter()\n    {\n        // clear stream flags; we use underlying streambuf I/O, do not\n        // maintain ifstream flags, except eof\n        if (is != nullptr)\n        {\n            is->clear(is->rdstate() & std::ios::eofbit);\n        }\n    }\n\n    explicit input_stream_adapter(std::istream& i)\n        : is(&i), sb(i.rdbuf())\n    {}\n\n    // delete because of pointer members\n    input_stream_adapter(const input_stream_adapter&) = delete;\n    input_stream_adapter& operator=(input_stream_adapter&) = delete;\n    input_stream_adapter& operator=(input_stream_adapter&& rhs) = delete;\n\n    input_stream_adapter(input_stream_adapter&& rhs) noexcept : is(rhs.is), sb(rhs.sb)\n    {\n        rhs.is = nullptr;\n        rhs.sb = nullptr;\n    }\n\n    // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to\n    // ensure that std::char_traits<char>::eof() and the character 0xFF do not\n    // end up as the same value, eg. 0xFFFFFFFF.\n    std::char_traits<char>::int_type get_character()\n    {\n        auto res = sb->sbumpc();\n        // set eof manually, as we don't use the istream interface.\n        if (JSON_HEDLEY_UNLIKELY(res == EOF))\n        {\n            is->clear(is->rdstate() | std::ios::eofbit);\n        }\n        return res;\n    }\n\n  private:\n    /// the associated input stream\n    std::istream* is = nullptr;\n    std::streambuf* sb = nullptr;\n};\n\n// General-purpose iterator-based adapter. It might not be as fast as\n// theoretically possible for some containers, but it is extremely versatile.\ntemplate<typename IteratorType>\nclass iterator_input_adapter\n{\n  public:\n    using char_type = typename std::iterator_traits<IteratorType>::value_type;\n\n    iterator_input_adapter(IteratorType first, IteratorType last)\n        : current(std::move(first)), end(std::move(last)) {}\n\n    typename std::char_traits<char_type>::int_type get_character()\n    {\n        if (JSON_HEDLEY_LIKELY(current != end))\n        {\n            auto result = std::char_traits<char_type>::to_int_type(*current);\n            std::advance(current, 1);\n            return result;\n        }\n        else\n        {\n            return std::char_traits<char_type>::eof();\n        }\n    }\n\n  private:\n    IteratorType current;\n    IteratorType end;\n\n    template<typename BaseInputAdapter, size_t T>\n    friend struct wide_string_input_helper;\n\n    bool empty() const\n    {\n        return current == end;\n    }\n\n};\n\n\ntemplate<typename BaseInputAdapter, size_t T>\nstruct wide_string_input_helper;\n\ntemplate<typename BaseInputAdapter>\nstruct wide_string_input_helper<BaseInputAdapter, 4>\n{\n    // UTF-32\n    static void fill_buffer(BaseInputAdapter& input,\n                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,\n                            size_t& utf8_bytes_index,\n                            size_t& utf8_bytes_filled)\n    {\n        utf8_bytes_index = 0;\n\n        if (JSON_HEDLEY_UNLIKELY(input.empty()))\n        {\n            utf8_bytes[0] = std::char_traits<char>::eof();\n            utf8_bytes_filled = 1;\n        }\n        else\n        {\n            // get the current character\n            const auto wc = input.get_character();\n\n            // UTF-32 to UTF-8 encoding\n            if (wc < 0x80)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n            else if (wc <= 0x7FF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u) & 0x1Fu));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 2;\n            }\n            else if (wc <= 0xFFFF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u) & 0x0Fu));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 3;\n            }\n            else if (wc <= 0x10FFFF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((static_cast<unsigned int>(wc) >> 18u) & 0x07u));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 12u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 4;\n            }\n            else\n            {\n                // unknown character\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n        }\n    }\n};\n\ntemplate<typename BaseInputAdapter>\nstruct wide_string_input_helper<BaseInputAdapter, 2>\n{\n    // UTF-16\n    static void fill_buffer(BaseInputAdapter& input,\n                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,\n                            size_t& utf8_bytes_index,\n                            size_t& utf8_bytes_filled)\n    {\n        utf8_bytes_index = 0;\n\n        if (JSON_HEDLEY_UNLIKELY(input.empty()))\n        {\n            utf8_bytes[0] = std::char_traits<char>::eof();\n            utf8_bytes_filled = 1;\n        }\n        else\n        {\n            // get the current character\n            const auto wc = input.get_character();\n\n            // UTF-16 to UTF-8 encoding\n            if (wc < 0x80)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n            else if (wc <= 0x7FF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u)));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 2;\n            }\n            else if (0xD800 > wc || wc >= 0xE000)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u)));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 3;\n            }\n            else\n            {\n                if (JSON_HEDLEY_UNLIKELY(!input.empty()))\n                {\n                    const auto wc2 = static_cast<unsigned int>(input.get_character());\n                    const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));\n                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));\n                    utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));\n                    utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));\n                    utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));\n                    utf8_bytes_filled = 4;\n                }\n                else\n                {\n                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                    utf8_bytes_filled = 1;\n                }\n            }\n        }\n    }\n};\n\n// Wraps another input apdater to convert wide character types into individual bytes.\ntemplate<typename BaseInputAdapter, typename WideCharType>\nclass wide_string_input_adapter\n{\n  public:\n    using char_type = char;\n\n    wide_string_input_adapter(BaseInputAdapter base)\n        : base_adapter(base) {}\n\n    typename std::char_traits<char>::int_type get_character() noexcept\n    {\n        // check if buffer needs to be filled\n        if (utf8_bytes_index == utf8_bytes_filled)\n        {\n            fill_buffer<sizeof(WideCharType)>();\n\n            JSON_ASSERT(utf8_bytes_filled > 0);\n            JSON_ASSERT(utf8_bytes_index == 0);\n        }\n\n        // use buffer\n        JSON_ASSERT(utf8_bytes_filled > 0);\n        JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);\n        return utf8_bytes[utf8_bytes_index++];\n    }\n\n  private:\n    BaseInputAdapter base_adapter;\n\n    template<size_t T>\n    void fill_buffer()\n    {\n        wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);\n    }\n\n    /// a buffer for UTF-8 bytes\n    std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};\n\n    /// index to the utf8_codes array for the next valid byte\n    std::size_t utf8_bytes_index = 0;\n    /// number of valid bytes in the utf8_codes array\n    std::size_t utf8_bytes_filled = 0;\n};\n\n\ntemplate<typename IteratorType, typename Enable = void>\nstruct iterator_input_adapter_factory\n{\n    using iterator_type = IteratorType;\n    using char_type = typename std::iterator_traits<iterator_type>::value_type;\n    using adapter_type = iterator_input_adapter<iterator_type>;\n\n    static adapter_type create(IteratorType first, IteratorType last)\n    {\n        return adapter_type(std::move(first), std::move(last));\n    }\n};\n\ntemplate<typename T>\nstruct is_iterator_of_multibyte\n{\n    using value_type = typename std::iterator_traits<T>::value_type;\n    enum\n    {\n        value = sizeof(value_type) > 1\n    };\n};\n\ntemplate<typename IteratorType>\nstruct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>\n{\n    using iterator_type = IteratorType;\n    using char_type = typename std::iterator_traits<iterator_type>::value_type;\n    using base_adapter_type = iterator_input_adapter<iterator_type>;\n    using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;\n\n    static adapter_type create(IteratorType first, IteratorType last)\n    {\n        return adapter_type(base_adapter_type(std::move(first), std::move(last)));\n    }\n};\n\n// General purpose iterator-based input\ntemplate<typename IteratorType>\ntypename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)\n{\n    using factory_type = iterator_input_adapter_factory<IteratorType>;\n    return factory_type::create(first, last);\n}\n\n// Convenience shorthand from container to iterator\ntemplate<typename ContainerType>\nauto input_adapter(const ContainerType& container) -> decltype(input_adapter(begin(container), end(container)))\n{\n    // Enable ADL\n    using std::begin;\n    using std::end;\n\n    return input_adapter(begin(container), end(container));\n}\n\n// Special cases with fast paths\ninline file_input_adapter input_adapter(std::FILE* file)\n{\n    return file_input_adapter(file);\n}\n\ninline input_stream_adapter input_adapter(std::istream& stream)\n{\n    return input_stream_adapter(stream);\n}\n\ninline input_stream_adapter input_adapter(std::istream&& stream)\n{\n    return input_stream_adapter(stream);\n}\n\nusing contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));\n\n// Null-delimited strings, and the like.\ntemplate < typename CharT,\n           typename std::enable_if <\n               std::is_pointer<CharT>::value&&\n               !std::is_array<CharT>::value&&\n               std::is_integral<typename std::remove_pointer<CharT>::type>::value&&\n               sizeof(typename std::remove_pointer<CharT>::type) == 1,\n               int >::type = 0 >\ncontiguous_bytes_input_adapter input_adapter(CharT b)\n{\n    auto length = std::strlen(reinterpret_cast<const char*>(b));\n    const auto* ptr = reinterpret_cast<const char*>(b);\n    return input_adapter(ptr, ptr + length);\n}\n\ntemplate<typename T, std::size_t N>\nauto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N))\n{\n    return input_adapter(array, array + N);\n}\n\n// This class only handles inputs of input_buffer_adapter type.\n// It's required so that expressions like {ptr, len} can be implicitely casted\n// to the correct adapter.\nclass span_input_adapter\n{\n  public:\n    template < typename CharT,\n               typename std::enable_if <\n                   std::is_pointer<CharT>::value&&\n                   std::is_integral<typename std::remove_pointer<CharT>::type>::value&&\n                   sizeof(typename std::remove_pointer<CharT>::type) == 1,\n                   int >::type = 0 >\n    span_input_adapter(CharT b, std::size_t l)\n        : ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {}\n\n    template<class IteratorType,\n             typename std::enable_if<\n                 std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,\n                 int>::type = 0>\n    span_input_adapter(IteratorType first, IteratorType last)\n        : ia(input_adapter(first, last)) {}\n\n    contiguous_bytes_input_adapter&& get()\n    {\n        return std::move(ia);\n    }\n\n  private:\n    contiguous_bytes_input_adapter ia;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/json_sax.hpp>\n\n\n#include <cstddef>\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\n\n/*!\n@brief SAX interface\n\nThis class describes the SAX interface used by @ref nlohmann::json::sax_parse.\nEach function is called in different situations while the input is parsed. The\nboolean return value informs the parser whether to continue processing the\ninput.\n*/\ntemplate<typename BasicJsonType>\nstruct json_sax\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n\n    /*!\n    @brief a null value was read\n    @return whether parsing should proceed\n    */\n    virtual bool null() = 0;\n\n    /*!\n    @brief a boolean value was read\n    @param[in] val  boolean value\n    @return whether parsing should proceed\n    */\n    virtual bool boolean(bool val) = 0;\n\n    /*!\n    @brief an integer number was read\n    @param[in] val  integer value\n    @return whether parsing should proceed\n    */\n    virtual bool number_integer(number_integer_t val) = 0;\n\n    /*!\n    @brief an unsigned integer number was read\n    @param[in] val  unsigned integer value\n    @return whether parsing should proceed\n    */\n    virtual bool number_unsigned(number_unsigned_t val) = 0;\n\n    /*!\n    @brief an floating-point number was read\n    @param[in] val  floating-point value\n    @param[in] s    raw token value\n    @return whether parsing should proceed\n    */\n    virtual bool number_float(number_float_t val, const string_t& s) = 0;\n\n    /*!\n    @brief a string was read\n    @param[in] val  string value\n    @return whether parsing should proceed\n    @note It is safe to move the passed string.\n    */\n    virtual bool string(string_t& val) = 0;\n\n    /*!\n    @brief a binary string was read\n    @param[in] val  binary value\n    @return whether parsing should proceed\n    @note It is safe to move the passed binary.\n    */\n    virtual bool binary(binary_t& val) = 0;\n\n    /*!\n    @brief the beginning of an object was read\n    @param[in] elements  number of object elements or -1 if unknown\n    @return whether parsing should proceed\n    @note binary formats may report the number of elements\n    */\n    virtual bool start_object(std::size_t elements) = 0;\n\n    /*!\n    @brief an object key was read\n    @param[in] val  object key\n    @return whether parsing should proceed\n    @note It is safe to move the passed string.\n    */\n    virtual bool key(string_t& val) = 0;\n\n    /*!\n    @brief the end of an object was read\n    @return whether parsing should proceed\n    */\n    virtual bool end_object() = 0;\n\n    /*!\n    @brief the beginning of an array was read\n    @param[in] elements  number of array elements or -1 if unknown\n    @return whether parsing should proceed\n    @note binary formats may report the number of elements\n    */\n    virtual bool start_array(std::size_t elements) = 0;\n\n    /*!\n    @brief the end of an array was read\n    @return whether parsing should proceed\n    */\n    virtual bool end_array() = 0;\n\n    /*!\n    @brief a parse error occurred\n    @param[in] position    the position in the input where the error occurs\n    @param[in] last_token  the last read token\n    @param[in] ex          an exception object describing the error\n    @return whether parsing should proceed (must return false)\n    */\n    virtual bool parse_error(std::size_t position,\n                             const std::string& last_token,\n                             const detail::exception& ex) = 0;\n\n    virtual ~json_sax() = default;\n};\n\n\nnamespace detail\n{\n/*!\n@brief SAX implementation to create a JSON value from SAX events\n\nThis class implements the @ref json_sax interface and processes the SAX events\nto create a JSON value which makes it basically a DOM parser. The structure or\nhierarchy of the JSON value is managed by the stack `ref_stack` which contains\na pointer to the respective array or object for each recursion depth.\n\nAfter successful parsing, the value that is passed by reference to the\nconstructor contains the parsed value.\n\n@tparam BasicJsonType  the JSON type\n*/\ntemplate<typename BasicJsonType>\nclass json_sax_dom_parser\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n\n    /*!\n    @param[in, out] r  reference to a JSON value that is manipulated while\n                       parsing\n    @param[in] allow_exceptions_  whether parse errors yield exceptions\n    */\n    explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)\n        : root(r), allow_exceptions(allow_exceptions_)\n    {}\n\n    // make class move-only\n    json_sax_dom_parser(const json_sax_dom_parser&) = delete;\n    json_sax_dom_parser(json_sax_dom_parser&&) = default;\n    json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete;\n    json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default;\n    ~json_sax_dom_parser() = default;\n\n    bool null()\n    {\n        handle_value(nullptr);\n        return true;\n    }\n\n    bool boolean(bool val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_integer(number_integer_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_float(number_float_t val, const string_t& /*unused*/)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool string(string_t& val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool binary(binary_t& val)\n    {\n        handle_value(std::move(val));\n        return true;\n    }\n\n    bool start_object(std::size_t len)\n    {\n        ref_stack.push_back(handle_value(BasicJsonType::value_t::object));\n\n        if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408,\n                                            \"excessive object size: \" + std::to_string(len)));\n        }\n\n        return true;\n    }\n\n    bool key(string_t& val)\n    {\n        // add null at given key and store the reference for later\n        object_element = &(ref_stack.back()->m_value.object->operator[](val));\n        return true;\n    }\n\n    bool end_object()\n    {\n        ref_stack.pop_back();\n        return true;\n    }\n\n    bool start_array(std::size_t len)\n    {\n        ref_stack.push_back(handle_value(BasicJsonType::value_t::array));\n\n        if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408,\n                                            \"excessive array size: \" + std::to_string(len)));\n        }\n\n        return true;\n    }\n\n    bool end_array()\n    {\n        ref_stack.pop_back();\n        return true;\n    }\n\n    template<class Exception>\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,\n                     const Exception& ex)\n    {\n        errored = true;\n        static_cast<void>(ex);\n        if (allow_exceptions)\n        {\n            JSON_THROW(ex);\n        }\n        return false;\n    }\n\n    constexpr bool is_errored() const\n    {\n        return errored;\n    }\n\n  private:\n    /*!\n    @invariant If the ref stack is empty, then the passed value will be the new\n               root.\n    @invariant If the ref stack contains a value, then it is an array or an\n               object to which we can add elements\n    */\n    template<typename Value>\n    JSON_HEDLEY_RETURNS_NON_NULL\n    BasicJsonType* handle_value(Value&& v)\n    {\n        if (ref_stack.empty())\n        {\n            root = BasicJsonType(std::forward<Value>(v));\n            return &root;\n        }\n\n        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());\n\n        if (ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v));\n            return &(ref_stack.back()->m_value.array->back());\n        }\n\n        JSON_ASSERT(ref_stack.back()->is_object());\n        JSON_ASSERT(object_element);\n        *object_element = BasicJsonType(std::forward<Value>(v));\n        return object_element;\n    }\n\n    /// the parsed JSON value\n    BasicJsonType& root;\n    /// stack to model hierarchy of values\n    std::vector<BasicJsonType*> ref_stack {};\n    /// helper to hold the reference for the next object element\n    BasicJsonType* object_element = nullptr;\n    /// whether a syntax error occurred\n    bool errored = false;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = false;\n};\n\ntemplate<typename BasicJsonType>\nclass json_sax_dom_callback_parser\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using parser_callback_t = typename BasicJsonType::parser_callback_t;\n    using parse_event_t = typename BasicJsonType::parse_event_t;\n\n    json_sax_dom_callback_parser(BasicJsonType& r,\n                                 const parser_callback_t cb,\n                                 const bool allow_exceptions_ = true)\n        : root(r), callback(cb), allow_exceptions(allow_exceptions_)\n    {\n        keep_stack.push_back(true);\n    }\n\n    // make class move-only\n    json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete;\n    json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default;\n    json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete;\n    json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default;\n    ~json_sax_dom_callback_parser() = default;\n\n    bool null()\n    {\n        handle_value(nullptr);\n        return true;\n    }\n\n    bool boolean(bool val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_integer(number_integer_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_float(number_float_t val, const string_t& /*unused*/)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool string(string_t& val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool binary(binary_t& val)\n    {\n        handle_value(std::move(val));\n        return true;\n    }\n\n    bool start_object(std::size_t len)\n    {\n        // check callback for object start\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);\n        keep_stack.push_back(keep);\n\n        auto val = handle_value(BasicJsonType::value_t::object, true);\n        ref_stack.push_back(val.second);\n\n        // check object limit\n        if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, \"excessive object size: \" + std::to_string(len)));\n        }\n\n        return true;\n    }\n\n    bool key(string_t& val)\n    {\n        BasicJsonType k = BasicJsonType(val);\n\n        // check callback for key\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);\n        key_keep_stack.push_back(keep);\n\n        // add discarded value at given key and store the reference for later\n        if (keep && ref_stack.back())\n        {\n            object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);\n        }\n\n        return true;\n    }\n\n    bool end_object()\n    {\n        if (ref_stack.back() && !callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))\n        {\n            // discard object\n            *ref_stack.back() = discarded;\n        }\n\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(!keep_stack.empty());\n        ref_stack.pop_back();\n        keep_stack.pop_back();\n\n        if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())\n        {\n            // remove discarded value\n            for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)\n            {\n                if (it->is_discarded())\n                {\n                    ref_stack.back()->erase(it);\n                    break;\n                }\n            }\n        }\n\n        return true;\n    }\n\n    bool start_array(std::size_t len)\n    {\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);\n        keep_stack.push_back(keep);\n\n        auto val = handle_value(BasicJsonType::value_t::array, true);\n        ref_stack.push_back(val.second);\n\n        // check array limit\n        if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, \"excessive array size: \" + std::to_string(len)));\n        }\n\n        return true;\n    }\n\n    bool end_array()\n    {\n        bool keep = true;\n\n        if (ref_stack.back())\n        {\n            keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());\n            if (!keep)\n            {\n                // discard array\n                *ref_stack.back() = discarded;\n            }\n        }\n\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(!keep_stack.empty());\n        ref_stack.pop_back();\n        keep_stack.pop_back();\n\n        // remove discarded value\n        if (!keep && !ref_stack.empty() && ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->pop_back();\n        }\n\n        return true;\n    }\n\n    template<class Exception>\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,\n                     const Exception& ex)\n    {\n        errored = true;\n        static_cast<void>(ex);\n        if (allow_exceptions)\n        {\n            JSON_THROW(ex);\n        }\n        return false;\n    }\n\n    constexpr bool is_errored() const\n    {\n        return errored;\n    }\n\n  private:\n    /*!\n    @param[in] v  value to add to the JSON value we build during parsing\n    @param[in] skip_callback  whether we should skip calling the callback\n               function; this is required after start_array() and\n               start_object() SAX events, because otherwise we would call the\n               callback function with an empty array or object, respectively.\n\n    @invariant If the ref stack is empty, then the passed value will be the new\n               root.\n    @invariant If the ref stack contains a value, then it is an array or an\n               object to which we can add elements\n\n    @return pair of boolean (whether value should be kept) and pointer (to the\n            passed value in the ref_stack hierarchy; nullptr if not kept)\n    */\n    template<typename Value>\n    std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)\n    {\n        JSON_ASSERT(!keep_stack.empty());\n\n        // do not handle this value if we know it would be added to a discarded\n        // container\n        if (!keep_stack.back())\n        {\n            return {false, nullptr};\n        }\n\n        // create value\n        auto value = BasicJsonType(std::forward<Value>(v));\n\n        // check callback\n        const bool keep = skip_callback || callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);\n\n        // do not handle this value if we just learnt it shall be discarded\n        if (!keep)\n        {\n            return {false, nullptr};\n        }\n\n        if (ref_stack.empty())\n        {\n            root = std::move(value);\n            return {true, &root};\n        }\n\n        // skip this value if we already decided to skip the parent\n        // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)\n        if (!ref_stack.back())\n        {\n            return {false, nullptr};\n        }\n\n        // we now only expect arrays and objects\n        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());\n\n        // array\n        if (ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->push_back(std::move(value));\n            return {true, &(ref_stack.back()->m_value.array->back())};\n        }\n\n        // object\n        JSON_ASSERT(ref_stack.back()->is_object());\n        // check if we should store an element for the current key\n        JSON_ASSERT(!key_keep_stack.empty());\n        const bool store_element = key_keep_stack.back();\n        key_keep_stack.pop_back();\n\n        if (!store_element)\n        {\n            return {false, nullptr};\n        }\n\n        JSON_ASSERT(object_element);\n        *object_element = std::move(value);\n        return {true, object_element};\n    }\n\n    /// the parsed JSON value\n    BasicJsonType& root;\n    /// stack to model hierarchy of values\n    std::vector<BasicJsonType*> ref_stack {};\n    /// stack to manage which values to keep\n    std::vector<bool> keep_stack {};\n    /// stack to manage which object keys to keep\n    std::vector<bool> key_keep_stack {};\n    /// helper to hold the reference for the next object element\n    BasicJsonType* object_element = nullptr;\n    /// whether a syntax error occurred\n    bool errored = false;\n    /// callback function\n    const parser_callback_t callback = nullptr;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n    /// a discarded value for the callback\n    BasicJsonType discarded = BasicJsonType::value_t::discarded;\n};\n\ntemplate<typename BasicJsonType>\nclass json_sax_acceptor\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n\n    bool null()\n    {\n        return true;\n    }\n\n    bool boolean(bool /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_integer(number_integer_t /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool string(string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool binary(binary_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool start_object(std::size_t /*unused*/ = std::size_t(-1))\n    {\n        return true;\n    }\n\n    bool key(string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool end_object()\n    {\n        return true;\n    }\n\n    bool start_array(std::size_t /*unused*/ = std::size_t(-1))\n    {\n        return true;\n    }\n\n    bool end_array()\n    {\n        return true;\n    }\n\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)\n    {\n        return false;\n    }\n};\n}  // namespace detail\n\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/lexer.hpp>\n\n\n#include <array> // array\n#include <clocale> // localeconv\n#include <cstddef> // size_t\n#include <cstdio> // snprintf\n#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull\n#include <initializer_list> // initializer_list\n#include <string> // char_traits, string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/position_t.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////\n// lexer //\n///////////\n\ntemplate<typename BasicJsonType>\nclass lexer_base\n{\n  public:\n    /// token types for the parser\n    enum class token_type\n    {\n        uninitialized,    ///< indicating the scanner is uninitialized\n        literal_true,     ///< the `true` literal\n        literal_false,    ///< the `false` literal\n        literal_null,     ///< the `null` literal\n        value_string,     ///< a string -- use get_string() for actual value\n        value_unsigned,   ///< an unsigned integer -- use get_number_unsigned() for actual value\n        value_integer,    ///< a signed integer -- use get_number_integer() for actual value\n        value_float,      ///< an floating point number -- use get_number_float() for actual value\n        begin_array,      ///< the character for array begin `[`\n        begin_object,     ///< the character for object begin `{`\n        end_array,        ///< the character for array end `]`\n        end_object,       ///< the character for object end `}`\n        name_separator,   ///< the name separator `:`\n        value_separator,  ///< the value separator `,`\n        parse_error,      ///< indicating a parse error\n        end_of_input,     ///< indicating the end of the input buffer\n        literal_or_value  ///< a literal or the begin of a value (only for diagnostics)\n    };\n\n    /// return name of values of type token_type (only used for errors)\n    JSON_HEDLEY_RETURNS_NON_NULL\n    JSON_HEDLEY_CONST\n    static const char* token_type_name(const token_type t) noexcept\n    {\n        switch (t)\n        {\n            case token_type::uninitialized:\n                return \"<uninitialized>\";\n            case token_type::literal_true:\n                return \"true literal\";\n            case token_type::literal_false:\n                return \"false literal\";\n            case token_type::literal_null:\n                return \"null literal\";\n            case token_type::value_string:\n                return \"string literal\";\n            case token_type::value_unsigned:\n            case token_type::value_integer:\n            case token_type::value_float:\n                return \"number literal\";\n            case token_type::begin_array:\n                return \"'['\";\n            case token_type::begin_object:\n                return \"'{'\";\n            case token_type::end_array:\n                return \"']'\";\n            case token_type::end_object:\n                return \"'}'\";\n            case token_type::name_separator:\n                return \"':'\";\n            case token_type::value_separator:\n                return \"','\";\n            case token_type::parse_error:\n                return \"<parse error>\";\n            case token_type::end_of_input:\n                return \"end of input\";\n            case token_type::literal_or_value:\n                return \"'[', '{', or a literal\";\n            // LCOV_EXCL_START\n            default: // catch non-enum values\n                return \"unknown token\";\n                // LCOV_EXCL_STOP\n        }\n    }\n};\n/*!\n@brief lexical analysis\n\nThis class organizes the lexical analysis during JSON deserialization.\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType>\nclass lexer : public lexer_base<BasicJsonType>\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using char_type = typename InputAdapterType::char_type;\n    using char_int_type = typename std::char_traits<char_type>::int_type;\n\n  public:\n    using token_type = typename lexer_base<BasicJsonType>::token_type;\n\n    explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false)\n        : ia(std::move(adapter))\n        , ignore_comments(ignore_comments_)\n        , decimal_point_char(static_cast<char_int_type>(get_decimal_point()))\n    {}\n\n    // delete because of pointer members\n    lexer(const lexer&) = delete;\n    lexer(lexer&&) = default;\n    lexer& operator=(lexer&) = delete;\n    lexer& operator=(lexer&&) = default;\n    ~lexer() = default;\n\n  private:\n    /////////////////////\n    // locales\n    /////////////////////\n\n    /// return the locale-dependent decimal point\n    JSON_HEDLEY_PURE\n    static char get_decimal_point() noexcept\n    {\n        const auto* loc = localeconv();\n        JSON_ASSERT(loc != nullptr);\n        return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);\n    }\n\n    /////////////////////\n    // scan functions\n    /////////////////////\n\n    /*!\n    @brief get codepoint from 4 hex characters following `\\u`\n\n    For input \"\\u c1 c2 c3 c4\" the codepoint is:\n      (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4\n    = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)\n\n    Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'\n    must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The\n    conversion is done by subtracting the offset (0x30, 0x37, and 0x57)\n    between the ASCII value of the character and the desired integer value.\n\n    @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or\n            non-hex character)\n    */\n    int get_codepoint()\n    {\n        // this function only makes sense after reading `\\u`\n        JSON_ASSERT(current == 'u');\n        int codepoint = 0;\n\n        const auto factors = { 12u, 8u, 4u, 0u };\n        for (const auto factor : factors)\n        {\n            get();\n\n            if (current >= '0' && current <= '9')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x30u) << factor);\n            }\n            else if (current >= 'A' && current <= 'F')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x37u) << factor);\n            }\n            else if (current >= 'a' && current <= 'f')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x57u) << factor);\n            }\n            else\n            {\n                return -1;\n            }\n        }\n\n        JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF);\n        return codepoint;\n    }\n\n    /*!\n    @brief check if the next byte(s) are inside a given range\n\n    Adds the current byte and, for each passed range, reads a new byte and\n    checks if it is inside the range. If a violation was detected, set up an\n    error message and return false. Otherwise, return true.\n\n    @param[in] ranges  list of integers; interpreted as list of pairs of\n                       inclusive lower and upper bound, respectively\n\n    @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,\n         1, 2, or 3 pairs. This precondition is enforced by an assertion.\n\n    @return true if and only if no range violation was detected\n    */\n    bool next_byte_in_range(std::initializer_list<char_int_type> ranges)\n    {\n        JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6);\n        add(current);\n\n        for (auto range = ranges.begin(); range != ranges.end(); ++range)\n        {\n            get();\n            if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range)))\n            {\n                add(current);\n            }\n            else\n            {\n                error_message = \"invalid string: ill-formed UTF-8 byte\";\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief scan a string literal\n\n    This function scans a string according to Sect. 7 of RFC 7159. While\n    scanning, bytes are escaped and copied into buffer token_buffer. Then the\n    function returns successfully, token_buffer is *not* null-terminated (as it\n    may contain \\0 bytes), and token_buffer.size() is the number of bytes in the\n    string.\n\n    @return token_type::value_string if string could be successfully scanned,\n            token_type::parse_error otherwise\n\n    @note In case of errors, variable error_message contains a textual\n          description.\n    */\n    token_type scan_string()\n    {\n        // reset token_buffer (ignore opening quote)\n        reset();\n\n        // we entered the function by reading an open quote\n        JSON_ASSERT(current == '\\\"');\n\n        while (true)\n        {\n            // get next character\n            switch (get())\n            {\n                // end of file while parsing string\n                case std::char_traits<char_type>::eof():\n                {\n                    error_message = \"invalid string: missing closing quote\";\n                    return token_type::parse_error;\n                }\n\n                // closing quote\n                case '\\\"':\n                {\n                    return token_type::value_string;\n                }\n\n                // escapes\n                case '\\\\':\n                {\n                    switch (get())\n                    {\n                        // quotation mark\n                        case '\\\"':\n                            add('\\\"');\n                            break;\n                        // reverse solidus\n                        case '\\\\':\n                            add('\\\\');\n                            break;\n                        // solidus\n                        case '/':\n                            add('/');\n                            break;\n                        // backspace\n                        case 'b':\n                            add('\\b');\n                            break;\n                        // form feed\n                        case 'f':\n                            add('\\f');\n                            break;\n                        // line feed\n                        case 'n':\n                            add('\\n');\n                            break;\n                        // carriage return\n                        case 'r':\n                            add('\\r');\n                            break;\n                        // tab\n                        case 't':\n                            add('\\t');\n                            break;\n\n                        // unicode escapes\n                        case 'u':\n                        {\n                            const int codepoint1 = get_codepoint();\n                            int codepoint = codepoint1; // start with codepoint1\n\n                            if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1))\n                            {\n                                error_message = \"invalid string: '\\\\u' must be followed by 4 hex digits\";\n                                return token_type::parse_error;\n                            }\n\n                            // check if code point is a high surrogate\n                            if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF)\n                            {\n                                // expect next \\uxxxx entry\n                                if (JSON_HEDLEY_LIKELY(get() == '\\\\' && get() == 'u'))\n                                {\n                                    const int codepoint2 = get_codepoint();\n\n                                    if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1))\n                                    {\n                                        error_message = \"invalid string: '\\\\u' must be followed by 4 hex digits\";\n                                        return token_type::parse_error;\n                                    }\n\n                                    // check if codepoint2 is a low surrogate\n                                    if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF))\n                                    {\n                                        // overwrite codepoint\n                                        codepoint = static_cast<int>(\n                                                        // high surrogate occupies the most significant 22 bits\n                                                        (static_cast<unsigned int>(codepoint1) << 10u)\n                                                        // low surrogate occupies the least significant 15 bits\n                                                        + static_cast<unsigned int>(codepoint2)\n                                                        // there is still the 0xD800, 0xDC00 and 0x10000 noise\n                                                        // in the result so we have to subtract with:\n                                                        // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00\n                                                        - 0x35FDC00u);\n                                    }\n                                    else\n                                    {\n                                        error_message = \"invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF\";\n                                        return token_type::parse_error;\n                                    }\n                                }\n                                else\n                                {\n                                    error_message = \"invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF\";\n                                    return token_type::parse_error;\n                                }\n                            }\n                            else\n                            {\n                                if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF))\n                                {\n                                    error_message = \"invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF\";\n                                    return token_type::parse_error;\n                                }\n                            }\n\n                            // result of the above calculation yields a proper codepoint\n                            JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);\n\n                            // translate codepoint into bytes\n                            if (codepoint < 0x80)\n                            {\n                                // 1-byte characters: 0xxxxxxx (ASCII)\n                                add(static_cast<char_int_type>(codepoint));\n                            }\n                            else if (codepoint <= 0x7FF)\n                            {\n                                // 2-byte characters: 110xxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n                            else if (codepoint <= 0xFFFF)\n                            {\n                                // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n                            else\n                            {\n                                // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n\n                            break;\n                        }\n\n                        // other characters after escape\n                        default:\n                            error_message = \"invalid string: forbidden character after backslash\";\n                            return token_type::parse_error;\n                    }\n\n                    break;\n                }\n\n                // invalid control characters\n                case 0x00:\n                {\n                    error_message = \"invalid string: control character U+0000 (NUL) must be escaped to \\\\u0000\";\n                    return token_type::parse_error;\n                }\n\n                case 0x01:\n                {\n                    error_message = \"invalid string: control character U+0001 (SOH) must be escaped to \\\\u0001\";\n                    return token_type::parse_error;\n                }\n\n                case 0x02:\n                {\n                    error_message = \"invalid string: control character U+0002 (STX) must be escaped to \\\\u0002\";\n                    return token_type::parse_error;\n                }\n\n                case 0x03:\n                {\n                    error_message = \"invalid string: control character U+0003 (ETX) must be escaped to \\\\u0003\";\n                    return token_type::parse_error;\n                }\n\n                case 0x04:\n                {\n                    error_message = \"invalid string: control character U+0004 (EOT) must be escaped to \\\\u0004\";\n                    return token_type::parse_error;\n                }\n\n                case 0x05:\n                {\n                    error_message = \"invalid string: control character U+0005 (ENQ) must be escaped to \\\\u0005\";\n                    return token_type::parse_error;\n                }\n\n                case 0x06:\n                {\n                    error_message = \"invalid string: control character U+0006 (ACK) must be escaped to \\\\u0006\";\n                    return token_type::parse_error;\n                }\n\n                case 0x07:\n                {\n                    error_message = \"invalid string: control character U+0007 (BEL) must be escaped to \\\\u0007\";\n                    return token_type::parse_error;\n                }\n\n                case 0x08:\n                {\n                    error_message = \"invalid string: control character U+0008 (BS) must be escaped to \\\\u0008 or \\\\b\";\n                    return token_type::parse_error;\n                }\n\n                case 0x09:\n                {\n                    error_message = \"invalid string: control character U+0009 (HT) must be escaped to \\\\u0009 or \\\\t\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0A:\n                {\n                    error_message = \"invalid string: control character U+000A (LF) must be escaped to \\\\u000A or \\\\n\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0B:\n                {\n                    error_message = \"invalid string: control character U+000B (VT) must be escaped to \\\\u000B\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0C:\n                {\n                    error_message = \"invalid string: control character U+000C (FF) must be escaped to \\\\u000C or \\\\f\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0D:\n                {\n                    error_message = \"invalid string: control character U+000D (CR) must be escaped to \\\\u000D or \\\\r\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0E:\n                {\n                    error_message = \"invalid string: control character U+000E (SO) must be escaped to \\\\u000E\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0F:\n                {\n                    error_message = \"invalid string: control character U+000F (SI) must be escaped to \\\\u000F\";\n                    return token_type::parse_error;\n                }\n\n                case 0x10:\n                {\n                    error_message = \"invalid string: control character U+0010 (DLE) must be escaped to \\\\u0010\";\n                    return token_type::parse_error;\n                }\n\n                case 0x11:\n                {\n                    error_message = \"invalid string: control character U+0011 (DC1) must be escaped to \\\\u0011\";\n                    return token_type::parse_error;\n                }\n\n                case 0x12:\n                {\n                    error_message = \"invalid string: control character U+0012 (DC2) must be escaped to \\\\u0012\";\n                    return token_type::parse_error;\n                }\n\n                case 0x13:\n                {\n                    error_message = \"invalid string: control character U+0013 (DC3) must be escaped to \\\\u0013\";\n                    return token_type::parse_error;\n                }\n\n                case 0x14:\n                {\n                    error_message = \"invalid string: control character U+0014 (DC4) must be escaped to \\\\u0014\";\n                    return token_type::parse_error;\n                }\n\n                case 0x15:\n                {\n                    error_message = \"invalid string: control character U+0015 (NAK) must be escaped to \\\\u0015\";\n                    return token_type::parse_error;\n                }\n\n                case 0x16:\n                {\n                    error_message = \"invalid string: control character U+0016 (SYN) must be escaped to \\\\u0016\";\n                    return token_type::parse_error;\n                }\n\n                case 0x17:\n                {\n                    error_message = \"invalid string: control character U+0017 (ETB) must be escaped to \\\\u0017\";\n                    return token_type::parse_error;\n                }\n\n                case 0x18:\n                {\n                    error_message = \"invalid string: control character U+0018 (CAN) must be escaped to \\\\u0018\";\n                    return token_type::parse_error;\n                }\n\n                case 0x19:\n                {\n                    error_message = \"invalid string: control character U+0019 (EM) must be escaped to \\\\u0019\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1A:\n                {\n                    error_message = \"invalid string: control character U+001A (SUB) must be escaped to \\\\u001A\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1B:\n                {\n                    error_message = \"invalid string: control character U+001B (ESC) must be escaped to \\\\u001B\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1C:\n                {\n                    error_message = \"invalid string: control character U+001C (FS) must be escaped to \\\\u001C\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1D:\n                {\n                    error_message = \"invalid string: control character U+001D (GS) must be escaped to \\\\u001D\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1E:\n                {\n                    error_message = \"invalid string: control character U+001E (RS) must be escaped to \\\\u001E\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1F:\n                {\n                    error_message = \"invalid string: control character U+001F (US) must be escaped to \\\\u001F\";\n                    return token_type::parse_error;\n                }\n\n                // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))\n                case 0x20:\n                case 0x21:\n                case 0x23:\n                case 0x24:\n                case 0x25:\n                case 0x26:\n                case 0x27:\n                case 0x28:\n                case 0x29:\n                case 0x2A:\n                case 0x2B:\n                case 0x2C:\n                case 0x2D:\n                case 0x2E:\n                case 0x2F:\n                case 0x30:\n                case 0x31:\n                case 0x32:\n                case 0x33:\n                case 0x34:\n                case 0x35:\n                case 0x36:\n                case 0x37:\n                case 0x38:\n                case 0x39:\n                case 0x3A:\n                case 0x3B:\n                case 0x3C:\n                case 0x3D:\n                case 0x3E:\n                case 0x3F:\n                case 0x40:\n                case 0x41:\n                case 0x42:\n                case 0x43:\n                case 0x44:\n                case 0x45:\n                case 0x46:\n                case 0x47:\n                case 0x48:\n                case 0x49:\n                case 0x4A:\n                case 0x4B:\n                case 0x4C:\n                case 0x4D:\n                case 0x4E:\n                case 0x4F:\n                case 0x50:\n                case 0x51:\n                case 0x52:\n                case 0x53:\n                case 0x54:\n                case 0x55:\n                case 0x56:\n                case 0x57:\n                case 0x58:\n                case 0x59:\n                case 0x5A:\n                case 0x5B:\n                case 0x5D:\n                case 0x5E:\n                case 0x5F:\n                case 0x60:\n                case 0x61:\n                case 0x62:\n                case 0x63:\n                case 0x64:\n                case 0x65:\n                case 0x66:\n                case 0x67:\n                case 0x68:\n                case 0x69:\n                case 0x6A:\n                case 0x6B:\n                case 0x6C:\n                case 0x6D:\n                case 0x6E:\n                case 0x6F:\n                case 0x70:\n                case 0x71:\n                case 0x72:\n                case 0x73:\n                case 0x74:\n                case 0x75:\n                case 0x76:\n                case 0x77:\n                case 0x78:\n                case 0x79:\n                case 0x7A:\n                case 0x7B:\n                case 0x7C:\n                case 0x7D:\n                case 0x7E:\n                case 0x7F:\n                {\n                    add(current);\n                    break;\n                }\n\n                // U+0080..U+07FF: bytes C2..DF 80..BF\n                case 0xC2:\n                case 0xC3:\n                case 0xC4:\n                case 0xC5:\n                case 0xC6:\n                case 0xC7:\n                case 0xC8:\n                case 0xC9:\n                case 0xCA:\n                case 0xCB:\n                case 0xCC:\n                case 0xCD:\n                case 0xCE:\n                case 0xCF:\n                case 0xD0:\n                case 0xD1:\n                case 0xD2:\n                case 0xD3:\n                case 0xD4:\n                case 0xD5:\n                case 0xD6:\n                case 0xD7:\n                case 0xD8:\n                case 0xD9:\n                case 0xDA:\n                case 0xDB:\n                case 0xDC:\n                case 0xDD:\n                case 0xDE:\n                case 0xDF:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF})))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+0800..U+0FFF: bytes E0 A0..BF 80..BF\n                case 0xE0:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF\n                // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF\n                case 0xE1:\n                case 0xE2:\n                case 0xE3:\n                case 0xE4:\n                case 0xE5:\n                case 0xE6:\n                case 0xE7:\n                case 0xE8:\n                case 0xE9:\n                case 0xEA:\n                case 0xEB:\n                case 0xEC:\n                case 0xEE:\n                case 0xEF:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+D000..U+D7FF: bytes ED 80..9F 80..BF\n                case 0xED:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF\n                case 0xF0:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF\n                case 0xF1:\n                case 0xF2:\n                case 0xF3:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF\n                case 0xF4:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // remaining bytes (80..C1 and F5..FF) are ill-formed\n                default:\n                {\n                    error_message = \"invalid string: ill-formed UTF-8 byte\";\n                    return token_type::parse_error;\n                }\n            }\n        }\n    }\n\n    /*!\n     * @brief scan a comment\n     * @return whether comment could be scanned successfully\n     */\n    bool scan_comment()\n    {\n        switch (get())\n        {\n            // single-line comments skip input until a newline or EOF is read\n            case '/':\n            {\n                while (true)\n                {\n                    switch (get())\n                    {\n                        case '\\n':\n                        case '\\r':\n                        case std::char_traits<char_type>::eof():\n                        case '\\0':\n                            return true;\n\n                        default:\n                            break;\n                    }\n                }\n            }\n\n            // multi-line comments skip input until */ is read\n            case '*':\n            {\n                while (true)\n                {\n                    switch (get())\n                    {\n                        case std::char_traits<char_type>::eof():\n                        case '\\0':\n                        {\n                            error_message = \"invalid comment; missing closing '*/'\";\n                            return false;\n                        }\n\n                        case '*':\n                        {\n                            switch (get())\n                            {\n                                case '/':\n                                    return true;\n\n                                default:\n                                {\n                                    unget();\n                                    continue;\n                                }\n                            }\n                        }\n\n                        default:\n                            continue;\n                    }\n                }\n            }\n\n            // unexpected character after reading '/'\n            default:\n            {\n                error_message = \"invalid comment; expecting '/' or '*' after '/'\";\n                return false;\n            }\n        }\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(float& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtof(str, endptr);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(double& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtod(str, endptr);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(long double& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtold(str, endptr);\n    }\n\n    /*!\n    @brief scan a number literal\n\n    This function scans a string according to Sect. 6 of RFC 7159.\n\n    The function is realized with a deterministic finite state machine derived\n    from the grammar described in RFC 7159. Starting in state \"init\", the\n    input is read and used to determined the next state. Only state \"done\"\n    accepts the number. State \"error\" is a trap state to model errors. In the\n    table below, \"anything\" means any character but the ones listed before.\n\n    state    | 0        | 1-9      | e E      | +       | -       | .        | anything\n    ---------|----------|----------|----------|---------|---------|----------|-----------\n    init     | zero     | any1     | [error]  | [error] | minus   | [error]  | [error]\n    minus    | zero     | any1     | [error]  | [error] | [error] | [error]  | [error]\n    zero     | done     | done     | exponent | done    | done    | decimal1 | done\n    any1     | any1     | any1     | exponent | done    | done    | decimal1 | done\n    decimal1 | decimal2 | decimal2 | [error]  | [error] | [error] | [error]  | [error]\n    decimal2 | decimal2 | decimal2 | exponent | done    | done    | done     | done\n    exponent | any2     | any2     | [error]  | sign    | sign    | [error]  | [error]\n    sign     | any2     | any2     | [error]  | [error] | [error] | [error]  | [error]\n    any2     | any2     | any2     | done     | done    | done    | done     | done\n\n    The state machine is realized with one label per state (prefixed with\n    \"scan_number_\") and `goto` statements between them. The state machine\n    contains cycles, but any cycle can be left when EOF is read. Therefore,\n    the function is guaranteed to terminate.\n\n    During scanning, the read bytes are stored in token_buffer. This string is\n    then converted to a signed integer, an unsigned integer, or a\n    floating-point number.\n\n    @return token_type::value_unsigned, token_type::value_integer, or\n            token_type::value_float if number could be successfully scanned,\n            token_type::parse_error otherwise\n\n    @note The scanner is independent of the current locale. Internally, the\n          locale's decimal point is used instead of `.` to work with the\n          locale-dependent converters.\n    */\n    token_type scan_number()  // lgtm [cpp/use-of-goto]\n    {\n        // reset token_buffer to store the number's bytes\n        reset();\n\n        // the type of the parsed number; initially set to unsigned; will be\n        // changed if minus sign, decimal point or exponent is read\n        token_type number_type = token_type::value_unsigned;\n\n        // state (init): we just found out we need to scan a number\n        switch (current)\n        {\n            case '-':\n            {\n                add(current);\n                goto scan_number_minus;\n            }\n\n            case '0':\n            {\n                add(current);\n                goto scan_number_zero;\n            }\n\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            // all other characters are rejected outside scan_number()\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false);  // LCOV_EXCL_LINE\n        }\n\nscan_number_minus:\n        // state: we just parsed a leading minus sign\n        number_type = token_type::value_integer;\n        switch (get())\n        {\n            case '0':\n            {\n                add(current);\n                goto scan_number_zero;\n            }\n\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after '-'\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_zero:\n        // state: we just parse a zero (maybe with a leading minus sign)\n        switch (get())\n        {\n            case '.':\n            {\n                add(decimal_point_char);\n                goto scan_number_decimal1;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_any1:\n        // state: we just parsed a number 0-9 (maybe with a leading minus sign)\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            case '.':\n            {\n                add(decimal_point_char);\n                goto scan_number_decimal1;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_decimal1:\n        // state: we just parsed a decimal point\n        number_type = token_type::value_float;\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_decimal2;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after '.'\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_decimal2:\n        // we just parsed at least one number after a decimal point\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_decimal2;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_exponent:\n        // we just parsed an exponent\n        number_type = token_type::value_float;\n        switch (get())\n        {\n            case '+':\n            case '-':\n            {\n                add(current);\n                goto scan_number_sign;\n            }\n\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n            {\n                error_message =\n                    \"invalid number; expected '+', '-', or digit after exponent\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_sign:\n        // we just parsed an exponent sign\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after exponent sign\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_any2:\n        // we just parsed a number after the exponent or exponent sign\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_done:\n        // unget the character after the number (we only read it to know that\n        // we are done scanning a number)\n        unget();\n\n        char* endptr = nullptr;\n        errno = 0;\n\n        // try to parse integers first and fall back to floats\n        if (number_type == token_type::value_unsigned)\n        {\n            const auto x = std::strtoull(token_buffer.data(), &endptr, 10);\n\n            // we checked the number format before\n            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n            if (errno == 0)\n            {\n                value_unsigned = static_cast<number_unsigned_t>(x);\n                if (value_unsigned == x)\n                {\n                    return token_type::value_unsigned;\n                }\n            }\n        }\n        else if (number_type == token_type::value_integer)\n        {\n            const auto x = std::strtoll(token_buffer.data(), &endptr, 10);\n\n            // we checked the number format before\n            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n            if (errno == 0)\n            {\n                value_integer = static_cast<number_integer_t>(x);\n                if (value_integer == x)\n                {\n                    return token_type::value_integer;\n                }\n            }\n        }\n\n        // this code is reached if we parse a floating-point number or if an\n        // integer conversion above failed\n        strtof(value_float, token_buffer.data(), &endptr);\n\n        // we checked the number format before\n        JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n        return token_type::value_float;\n    }\n\n    /*!\n    @param[in] literal_text  the literal text to expect\n    @param[in] length        the length of the passed literal text\n    @param[in] return_type   the token type to return on success\n    */\n    JSON_HEDLEY_NON_NULL(2)\n    token_type scan_literal(const char_type* literal_text, const std::size_t length,\n                            token_type return_type)\n    {\n        JSON_ASSERT(std::char_traits<char_type>::to_char_type(current) == literal_text[0]);\n        for (std::size_t i = 1; i < length; ++i)\n        {\n            if (JSON_HEDLEY_UNLIKELY(std::char_traits<char_type>::to_char_type(get()) != literal_text[i]))\n            {\n                error_message = \"invalid literal\";\n                return token_type::parse_error;\n            }\n        }\n        return return_type;\n    }\n\n    /////////////////////\n    // input management\n    /////////////////////\n\n    /// reset token_buffer; current character is beginning of token\n    void reset() noexcept\n    {\n        token_buffer.clear();\n        token_string.clear();\n        token_string.push_back(std::char_traits<char_type>::to_char_type(current));\n    }\n\n    /*\n    @brief get next character from the input\n\n    This function provides the interface to the used input adapter. It does\n    not throw in case the input reached EOF, but returns a\n    `std::char_traits<char>::eof()` in that case.  Stores the scanned characters\n    for use in error messages.\n\n    @return character read from the input\n    */\n    char_int_type get()\n    {\n        ++position.chars_read_total;\n        ++position.chars_read_current_line;\n\n        if (next_unget)\n        {\n            // just reset the next_unget variable and work with current\n            next_unget = false;\n        }\n        else\n        {\n            current = ia.get_character();\n        }\n\n        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))\n        {\n            token_string.push_back(std::char_traits<char_type>::to_char_type(current));\n        }\n\n        if (current == '\\n')\n        {\n            ++position.lines_read;\n            position.chars_read_current_line = 0;\n        }\n\n        return current;\n    }\n\n    /*!\n    @brief unget current character (read it again on next get)\n\n    We implement unget by setting variable next_unget to true. The input is not\n    changed - we just simulate ungetting by modifying chars_read_total,\n    chars_read_current_line, and token_string. The next call to get() will\n    behave as if the unget character is read again.\n    */\n    void unget()\n    {\n        next_unget = true;\n\n        --position.chars_read_total;\n\n        // in case we \"unget\" a newline, we have to also decrement the lines_read\n        if (position.chars_read_current_line == 0)\n        {\n            if (position.lines_read > 0)\n            {\n                --position.lines_read;\n            }\n        }\n        else\n        {\n            --position.chars_read_current_line;\n        }\n\n        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))\n        {\n            JSON_ASSERT(!token_string.empty());\n            token_string.pop_back();\n        }\n    }\n\n    /// add a character to token_buffer\n    void add(char_int_type c)\n    {\n        token_buffer.push_back(static_cast<typename string_t::value_type>(c));\n    }\n\n  public:\n    /////////////////////\n    // value getters\n    /////////////////////\n\n    /// return integer value\n    constexpr number_integer_t get_number_integer() const noexcept\n    {\n        return value_integer;\n    }\n\n    /// return unsigned integer value\n    constexpr number_unsigned_t get_number_unsigned() const noexcept\n    {\n        return value_unsigned;\n    }\n\n    /// return floating-point value\n    constexpr number_float_t get_number_float() const noexcept\n    {\n        return value_float;\n    }\n\n    /// return current string value (implicitly resets the token; useful only once)\n    string_t& get_string()\n    {\n        return token_buffer;\n    }\n\n    /////////////////////\n    // diagnostics\n    /////////////////////\n\n    /// return position of last read token\n    constexpr position_t get_position() const noexcept\n    {\n        return position;\n    }\n\n    /// return the last read token (for errors only).  Will never contain EOF\n    /// (an arbitrary value that is not a valid char value, often -1), because\n    /// 255 may legitimately occur.  May contain NUL, which should be escaped.\n    std::string get_token_string() const\n    {\n        // escape control characters\n        std::string result;\n        for (const auto c : token_string)\n        {\n            if (static_cast<unsigned char>(c) <= '\\x1F')\n            {\n                // escape control characters\n                std::array<char, 9> cs{{}};\n                (std::snprintf)(cs.data(), cs.size(), \"<U+%.4X>\", static_cast<unsigned char>(c));\n                result += cs.data();\n            }\n            else\n            {\n                // add character as is\n                result.push_back(static_cast<std::string::value_type>(c));\n            }\n        }\n\n        return result;\n    }\n\n    /// return syntax error message\n    JSON_HEDLEY_RETURNS_NON_NULL\n    constexpr const char* get_error_message() const noexcept\n    {\n        return error_message;\n    }\n\n    /////////////////////\n    // actual scanner\n    /////////////////////\n\n    /*!\n    @brief skip the UTF-8 byte order mark\n    @return true iff there is no BOM or the correct BOM has been skipped\n    */\n    bool skip_bom()\n    {\n        if (get() == 0xEF)\n        {\n            // check if we completely parse the BOM\n            return get() == 0xBB && get() == 0xBF;\n        }\n\n        // the first character is not the beginning of the BOM; unget it to\n        // process is later\n        unget();\n        return true;\n    }\n\n    void skip_whitespace()\n    {\n        do\n        {\n            get();\n        }\n        while (current == ' ' || current == '\\t' || current == '\\n' || current == '\\r');\n    }\n\n    token_type scan()\n    {\n        // initially, skip the BOM\n        if (position.chars_read_total == 0 && !skip_bom())\n        {\n            error_message = \"invalid BOM; must be 0xEF 0xBB 0xBF if given\";\n            return token_type::parse_error;\n        }\n\n        // read next character and ignore whitespace\n        skip_whitespace();\n\n        // ignore comments\n        while (ignore_comments && current == '/')\n        {\n            if (!scan_comment())\n            {\n                return token_type::parse_error;\n            }\n\n            // skip following whitespace\n            skip_whitespace();\n        }\n\n        switch (current)\n        {\n            // structural characters\n            case '[':\n                return token_type::begin_array;\n            case ']':\n                return token_type::end_array;\n            case '{':\n                return token_type::begin_object;\n            case '}':\n                return token_type::end_object;\n            case ':':\n                return token_type::name_separator;\n            case ',':\n                return token_type::value_separator;\n\n            // literals\n            case 't':\n            {\n                std::array<char_type, 4> true_literal = {{'t', 'r', 'u', 'e'}};\n                return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true);\n            }\n            case 'f':\n            {\n                std::array<char_type, 5> false_literal = {{'f', 'a', 'l', 's', 'e'}};\n                return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false);\n            }\n            case 'n':\n            {\n                std::array<char_type, 4> null_literal = {{'n', 'u', 'l', 'l'}};\n                return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null);\n            }\n\n            // string\n            case '\\\"':\n                return scan_string();\n\n            // number\n            case '-':\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n                return scan_number();\n\n            // end of input (the null byte is needed when parsing from\n            // string literals)\n            case '\\0':\n            case std::char_traits<char_type>::eof():\n                return token_type::end_of_input;\n\n            // error\n            default:\n                error_message = \"invalid literal\";\n                return token_type::parse_error;\n        }\n    }\n\n  private:\n    /// input adapter\n    InputAdapterType ia;\n\n    /// whether comments should be ignored (true) or signaled as errors (false)\n    const bool ignore_comments = false;\n\n    /// the current character\n    char_int_type current = std::char_traits<char_type>::eof();\n\n    /// whether the next get() call should just return current\n    bool next_unget = false;\n\n    /// the start position of the current token\n    position_t position {};\n\n    /// raw input token string (for error messages)\n    std::vector<char_type> token_string {};\n\n    /// buffer for variable-length tokens (numbers, strings)\n    string_t token_buffer {};\n\n    /// a description of occurred lexer errors\n    const char* error_message = \"\";\n\n    // number values\n    number_integer_t value_integer = 0;\n    number_unsigned_t value_unsigned = 0;\n    number_float_t value_float = 0;\n\n    /// the decimal point\n    const char_int_type decimal_point_char = '.';\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/is_sax.hpp>\n\n\n#include <cstdint> // size_t\n#include <utility> // declval\n#include <string> // string\n\n// #include <nlohmann/detail/meta/detected.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename T>\nusing null_function_t = decltype(std::declval<T&>().null());\n\ntemplate<typename T>\nusing boolean_function_t =\n    decltype(std::declval<T&>().boolean(std::declval<bool>()));\n\ntemplate<typename T, typename Integer>\nusing number_integer_function_t =\n    decltype(std::declval<T&>().number_integer(std::declval<Integer>()));\n\ntemplate<typename T, typename Unsigned>\nusing number_unsigned_function_t =\n    decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>()));\n\ntemplate<typename T, typename Float, typename String>\nusing number_float_function_t = decltype(std::declval<T&>().number_float(\n                                    std::declval<Float>(), std::declval<const String&>()));\n\ntemplate<typename T, typename String>\nusing string_function_t =\n    decltype(std::declval<T&>().string(std::declval<String&>()));\n\ntemplate<typename T, typename Binary>\nusing binary_function_t =\n    decltype(std::declval<T&>().binary(std::declval<Binary&>()));\n\ntemplate<typename T>\nusing start_object_function_t =\n    decltype(std::declval<T&>().start_object(std::declval<std::size_t>()));\n\ntemplate<typename T, typename String>\nusing key_function_t =\n    decltype(std::declval<T&>().key(std::declval<String&>()));\n\ntemplate<typename T>\nusing end_object_function_t = decltype(std::declval<T&>().end_object());\n\ntemplate<typename T>\nusing start_array_function_t =\n    decltype(std::declval<T&>().start_array(std::declval<std::size_t>()));\n\ntemplate<typename T>\nusing end_array_function_t = decltype(std::declval<T&>().end_array());\n\ntemplate<typename T, typename Exception>\nusing parse_error_function_t = decltype(std::declval<T&>().parse_error(\n        std::declval<std::size_t>(), std::declval<const std::string&>(),\n        std::declval<const Exception&>()));\n\ntemplate<typename SAX, typename BasicJsonType>\nstruct is_sax\n{\n  private:\n    static_assert(is_basic_json<BasicJsonType>::value,\n                  \"BasicJsonType must be of type basic_json<...>\");\n\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using exception_t = typename BasicJsonType::exception;\n\n  public:\n    static constexpr bool value =\n        is_detected_exact<bool, null_function_t, SAX>::value &&\n        is_detected_exact<bool, boolean_function_t, SAX>::value &&\n        is_detected_exact<bool, number_integer_function_t, SAX, number_integer_t>::value &&\n        is_detected_exact<bool, number_unsigned_function_t, SAX, number_unsigned_t>::value &&\n        is_detected_exact<bool, number_float_function_t, SAX, number_float_t, string_t>::value &&\n        is_detected_exact<bool, string_function_t, SAX, string_t>::value &&\n        is_detected_exact<bool, binary_function_t, SAX, binary_t>::value &&\n        is_detected_exact<bool, start_object_function_t, SAX>::value &&\n        is_detected_exact<bool, key_function_t, SAX, string_t>::value &&\n        is_detected_exact<bool, end_object_function_t, SAX>::value &&\n        is_detected_exact<bool, start_array_function_t, SAX>::value &&\n        is_detected_exact<bool, end_array_function_t, SAX>::value &&\n        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;\n};\n\ntemplate<typename SAX, typename BasicJsonType>\nstruct is_sax_static_asserts\n{\n  private:\n    static_assert(is_basic_json<BasicJsonType>::value,\n                  \"BasicJsonType must be of type basic_json<...>\");\n\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using exception_t = typename BasicJsonType::exception;\n\n  public:\n    static_assert(is_detected_exact<bool, null_function_t, SAX>::value,\n                  \"Missing/invalid function: bool null()\");\n    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,\n                  \"Missing/invalid function: bool boolean(bool)\");\n    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,\n                  \"Missing/invalid function: bool boolean(bool)\");\n    static_assert(\n        is_detected_exact<bool, number_integer_function_t, SAX,\n        number_integer_t>::value,\n        \"Missing/invalid function: bool number_integer(number_integer_t)\");\n    static_assert(\n        is_detected_exact<bool, number_unsigned_function_t, SAX,\n        number_unsigned_t>::value,\n        \"Missing/invalid function: bool number_unsigned(number_unsigned_t)\");\n    static_assert(is_detected_exact<bool, number_float_function_t, SAX,\n                  number_float_t, string_t>::value,\n                  \"Missing/invalid function: bool number_float(number_float_t, const string_t&)\");\n    static_assert(\n        is_detected_exact<bool, string_function_t, SAX, string_t>::value,\n        \"Missing/invalid function: bool string(string_t&)\");\n    static_assert(\n        is_detected_exact<bool, binary_function_t, SAX, binary_t>::value,\n        \"Missing/invalid function: bool binary(binary_t&)\");\n    static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value,\n                  \"Missing/invalid function: bool start_object(std::size_t)\");\n    static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value,\n                  \"Missing/invalid function: bool key(string_t&)\");\n    static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value,\n                  \"Missing/invalid function: bool end_object()\");\n    static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value,\n                  \"Missing/invalid function: bool start_array(std::size_t)\");\n    static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value,\n                  \"Missing/invalid function: bool end_array()\");\n    static_assert(\n        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value,\n        \"Missing/invalid function: bool parse_error(std::size_t, const \"\n        \"std::string&, const exception&)\");\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\n/// how to treat CBOR tags\nenum class cbor_tag_handler_t\n{\n    error,  ///< throw a parse_error exception in case of a tag\n    ignore   ///< ignore tags\n};\n\n/*!\n@brief determine system byte order\n\n@return true if and only if system's byte order is little endian\n\n@note from https://stackoverflow.com/a/1001328/266378\n*/\nstatic inline bool little_endianess(int num = 1) noexcept\n{\n    return *reinterpret_cast<char*>(&num) == 1;\n}\n\n\n///////////////////\n// binary reader //\n///////////////////\n\n/*!\n@brief deserialization of CBOR, MessagePack, and UBJSON values\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType>>\nclass binary_reader\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using json_sax_t = SAX;\n    using char_type = typename InputAdapterType::char_type;\n    using char_int_type = typename std::char_traits<char_type>::int_type;\n\n  public:\n    /*!\n    @brief create a binary reader\n\n    @param[in] adapter  input adapter to read from\n    */\n    explicit binary_reader(InputAdapterType&& adapter) : ia(std::move(adapter))\n    {\n        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};\n    }\n\n    // make class move-only\n    binary_reader(const binary_reader&) = delete;\n    binary_reader(binary_reader&&) = default;\n    binary_reader& operator=(const binary_reader&) = delete;\n    binary_reader& operator=(binary_reader&&) = default;\n    ~binary_reader() = default;\n\n    /*!\n    @param[in] format  the binary format to parse\n    @param[in] sax_    a SAX event processor\n    @param[in] strict  whether to expect the input to be consumed completed\n    @param[in] tag_handler  how to treat CBOR tags\n\n    @return\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    bool sax_parse(const input_format_t format,\n                   json_sax_t* sax_,\n                   const bool strict = true,\n                   const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        sax = sax_;\n        bool result = false;\n\n        switch (format)\n        {\n            case input_format_t::bson:\n                result = parse_bson_internal();\n                break;\n\n            case input_format_t::cbor:\n                result = parse_cbor_internal(true, tag_handler);\n                break;\n\n            case input_format_t::msgpack:\n                result = parse_msgpack_internal();\n                break;\n\n            case input_format_t::ubjson:\n                result = parse_ubjson_internal();\n                break;\n\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false);  // LCOV_EXCL_LINE\n        }\n\n        // strict mode: next byte must be EOF\n        if (result && strict)\n        {\n            if (format == input_format_t::ubjson)\n            {\n                get_ignore_noop();\n            }\n            else\n            {\n                get();\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(current != std::char_traits<char_type>::eof()))\n            {\n                return sax->parse_error(chars_read, get_token_string(),\n                                        parse_error::create(110, chars_read, exception_message(format, \"expected end of input; last byte: 0x\" + get_token_string(), \"value\")));\n            }\n        }\n\n        return result;\n    }\n\n  private:\n    //////////\n    // BSON //\n    //////////\n\n    /*!\n    @brief Reads in a BSON-object and passes it to the SAX-parser.\n    @return whether a valid BSON-value was passed to the SAX parser\n    */\n    bool parse_bson_internal()\n    {\n        std::int32_t document_size{};\n        get_number<std::int32_t, true>(input_format_t::bson, document_size);\n\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))\n        {\n            return false;\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false)))\n        {\n            return false;\n        }\n\n        return sax->end_object();\n    }\n\n    /*!\n    @brief Parses a C-style string from the BSON input.\n    @param[in, out] result  A reference to the string variable where the read\n                            string is to be stored.\n    @return `true` if the \\x00-byte indicating the end of the string was\n             encountered before the EOF; false` indicates an unexpected EOF.\n    */\n    bool get_bson_cstr(string_t& result)\n    {\n        auto out = std::back_inserter(result);\n        while (true)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, \"cstring\")))\n            {\n                return false;\n            }\n            if (current == 0x00)\n            {\n                return true;\n            }\n            *out++ = static_cast<typename string_t::value_type>(current);\n        }\n    }\n\n    /*!\n    @brief Parses a zero-terminated string of length @a len from the BSON\n           input.\n    @param[in] len  The length (including the zero-byte at the end) of the\n                    string to be read.\n    @param[in, out] result  A reference to the string variable where the read\n                            string is to be stored.\n    @tparam NumberType The type of the length @a len\n    @pre len >= 1\n    @return `true` if the string was successfully parsed\n    */\n    template<typename NumberType>\n    bool get_bson_string(const NumberType len, string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(len < 1))\n        {\n            auto last_token = get_token_string();\n            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, \"string length must be at least 1, is \" + std::to_string(len), \"string\")));\n        }\n\n        return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != std::char_traits<char_type>::eof();\n    }\n\n    /*!\n    @brief Parses a byte array input of length @a len from the BSON input.\n    @param[in] len  The length of the byte array to be read.\n    @param[in, out] result  A reference to the binary variable where the read\n                            array is to be stored.\n    @tparam NumberType The type of the length @a len\n    @pre len >= 0\n    @return `true` if the byte array was successfully parsed\n    */\n    template<typename NumberType>\n    bool get_bson_binary(const NumberType len, binary_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(len < 0))\n        {\n            auto last_token = get_token_string();\n            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, \"byte array length cannot be negative, is \" + std::to_string(len), \"binary\")));\n        }\n\n        // All BSON binary values have a subtype\n        std::uint8_t subtype{};\n        get_number<std::uint8_t>(input_format_t::bson, subtype);\n        result.set_subtype(subtype);\n\n        return get_binary(input_format_t::bson, len, result);\n    }\n\n    /*!\n    @brief Read a BSON document element of the given @a element_type.\n    @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html\n    @param[in] element_type_parse_position The position in the input stream,\n               where the `element_type` was read.\n    @warning Not all BSON element types are supported yet. An unsupported\n             @a element_type will give rise to a parse_error.114:\n             Unsupported BSON record type 0x...\n    @return whether a valid BSON-object/array was passed to the SAX parser\n    */\n    bool parse_bson_element_internal(const char_int_type element_type,\n                                     const std::size_t element_type_parse_position)\n    {\n        switch (element_type)\n        {\n            case 0x01: // double\n            {\n                double number{};\n                return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0x02: // string\n            {\n                std::int32_t len{};\n                string_t value;\n                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value);\n            }\n\n            case 0x03: // object\n            {\n                return parse_bson_internal();\n            }\n\n            case 0x04: // array\n            {\n                return parse_bson_array();\n            }\n\n            case 0x05: // binary\n            {\n                std::int32_t len{};\n                binary_t value;\n                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value);\n            }\n\n            case 0x08: // boolean\n            {\n                return sax->boolean(get() != 0);\n            }\n\n            case 0x0A: // null\n            {\n                return sax->null();\n            }\n\n            case 0x10: // int32\n            {\n                std::int32_t value{};\n                return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value);\n            }\n\n            case 0x12: // int64\n            {\n                std::int64_t value{};\n                return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);\n            }\n\n            default: // anything else not supported (yet)\n            {\n                std::array<char, 3> cr{{}};\n                (std::snprintf)(cr.data(), cr.size(), \"%.2hhX\", static_cast<unsigned char>(element_type));\n                return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, \"Unsupported BSON record type 0x\" + std::string(cr.data())));\n            }\n        }\n    }\n\n    /*!\n    @brief Read a BSON element list (as specified in the BSON-spec)\n\n    The same binary layout is used for objects and arrays, hence it must be\n    indicated with the argument @a is_array which one is expected\n    (true --> array, false --> object).\n\n    @param[in] is_array Determines if the element list being read is to be\n                        treated as an object (@a is_array == false), or as an\n                        array (@a is_array == true).\n    @return whether a valid BSON-object/array was passed to the SAX parser\n    */\n    bool parse_bson_element_list(const bool is_array)\n    {\n        string_t key;\n\n        while (auto element_type = get())\n        {\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, \"element list\")))\n            {\n                return false;\n            }\n\n            const std::size_t element_type_parse_position = chars_read;\n            if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key)))\n            {\n                return false;\n            }\n\n            if (!is_array && !sax->key(key))\n            {\n                return false;\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position)))\n            {\n                return false;\n            }\n\n            // get_bson_cstr only appends\n            key.clear();\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief Reads an array from the BSON input and passes it to the SAX-parser.\n    @return whether a valid BSON-array was passed to the SAX parser\n    */\n    bool parse_bson_array()\n    {\n        std::int32_t document_size{};\n        get_number<std::int32_t, true>(input_format_t::bson, document_size);\n\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1))))\n        {\n            return false;\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true)))\n        {\n            return false;\n        }\n\n        return sax->end_array();\n    }\n\n    //////////\n    // CBOR //\n    //////////\n\n    /*!\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true) or whether the last read character should\n                         be considered instead (false)\n    @param[in] tag_handler how CBOR tags should be treated\n\n    @return whether a valid CBOR value was passed to the SAX parser\n    */\n    bool parse_cbor_internal(const bool get_char,\n                             const cbor_tag_handler_t tag_handler)\n    {\n        switch (get_char ? get() : current)\n        {\n            // EOF\n            case std::char_traits<char_type>::eof():\n                return unexpect_eof(input_format_t::cbor, \"value\");\n\n            // Integer 0x00..0x17 (0..23)\n            case 0x00:\n            case 0x01:\n            case 0x02:\n            case 0x03:\n            case 0x04:\n            case 0x05:\n            case 0x06:\n            case 0x07:\n            case 0x08:\n            case 0x09:\n            case 0x0A:\n            case 0x0B:\n            case 0x0C:\n            case 0x0D:\n            case 0x0E:\n            case 0x0F:\n            case 0x10:\n            case 0x11:\n            case 0x12:\n            case 0x13:\n            case 0x14:\n            case 0x15:\n            case 0x16:\n            case 0x17:\n                return sax->number_unsigned(static_cast<number_unsigned_t>(current));\n\n            case 0x18: // Unsigned integer (one-byte uint8_t follows)\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x19: // Unsigned integer (two-byte uint16_t follows)\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x1A: // Unsigned integer (four-byte uint32_t follows)\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x1B: // Unsigned integer (eight-byte uint64_t follows)\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            // Negative integer -1-0x00..-1-0x17 (-1..-24)\n            case 0x20:\n            case 0x21:\n            case 0x22:\n            case 0x23:\n            case 0x24:\n            case 0x25:\n            case 0x26:\n            case 0x27:\n            case 0x28:\n            case 0x29:\n            case 0x2A:\n            case 0x2B:\n            case 0x2C:\n            case 0x2D:\n            case 0x2E:\n            case 0x2F:\n            case 0x30:\n            case 0x31:\n            case 0x32:\n            case 0x33:\n            case 0x34:\n            case 0x35:\n            case 0x36:\n            case 0x37:\n                return sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - current));\n\n            case 0x38: // Negative integer (one-byte uint8_t follows)\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x39: // Negative integer -1-n (two-byte uint16_t follows)\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1)\n                        - static_cast<number_integer_t>(number));\n            }\n\n            // Binary data (0x00..0x17 bytes follow)\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            case 0x58: // Binary data (one-byte uint8_t for n follows)\n            case 0x59: // Binary data (two-byte uint16_t for n follow)\n            case 0x5A: // Binary data (four-byte uint32_t for n follow)\n            case 0x5B: // Binary data (eight-byte uint64_t for n follow)\n            case 0x5F: // Binary data (indefinite length)\n            {\n                binary_t b;\n                return get_cbor_binary(b) && sax->binary(b);\n            }\n\n            // UTF-8 string (0x00..0x17 bytes follow)\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)\n            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)\n            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)\n            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)\n            case 0x7F: // UTF-8 string (indefinite length)\n            {\n                string_t s;\n                return get_cbor_string(s) && sax->string(s);\n            }\n\n            // array (0x00..0x17 data items follow)\n            case 0x80:\n            case 0x81:\n            case 0x82:\n            case 0x83:\n            case 0x84:\n            case 0x85:\n            case 0x86:\n            case 0x87:\n            case 0x88:\n            case 0x89:\n            case 0x8A:\n            case 0x8B:\n            case 0x8C:\n            case 0x8D:\n            case 0x8E:\n            case 0x8F:\n            case 0x90:\n            case 0x91:\n            case 0x92:\n            case 0x93:\n            case 0x94:\n            case 0x95:\n            case 0x96:\n            case 0x97:\n                return get_cbor_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);\n\n            case 0x98: // array (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x99: // array (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9A: // array (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9B: // array (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9F: // array (indefinite length)\n                return get_cbor_array(std::size_t(-1), tag_handler);\n\n            // map (0x00..0x17 pairs of data items follow)\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n                return get_cbor_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);\n\n            case 0xB8: // map (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xB9: // map (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBA: // map (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBB: // map (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBF: // map (indefinite length)\n                return get_cbor_object(std::size_t(-1), tag_handler);\n\n            case 0xC6: // tagged item\n            case 0xC7:\n            case 0xC8:\n            case 0xC9:\n            case 0xCA:\n            case 0xCB:\n            case 0xCC:\n            case 0xCD:\n            case 0xCE:\n            case 0xCF:\n            case 0xD0:\n            case 0xD1:\n            case 0xD2:\n            case 0xD3:\n            case 0xD4:\n            case 0xD8: // tagged item (1 bytes follow)\n            case 0xD9: // tagged item (2 bytes follow)\n            case 0xDA: // tagged item (4 bytes follow)\n            case 0xDB: // tagged item (8 bytes follow)\n            {\n                switch (tag_handler)\n                {\n                    case cbor_tag_handler_t::error:\n                    {\n                        auto last_token = get_token_string();\n                        return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, \"invalid byte: 0x\" + last_token, \"value\")));\n                    }\n\n                    case cbor_tag_handler_t::ignore:\n                    {\n                        switch (current)\n                        {\n                            case 0xD8:\n                            {\n                                std::uint8_t len{};\n                                get_number(input_format_t::cbor, len);\n                                break;\n                            }\n                            case 0xD9:\n                            {\n                                std::uint16_t len{};\n                                get_number(input_format_t::cbor, len);\n                                break;\n                            }\n                            case 0xDA:\n                            {\n                                std::uint32_t len{};\n                                get_number(input_format_t::cbor, len);\n                                break;\n                            }\n                            case 0xDB:\n                            {\n                                std::uint64_t len{};\n                                get_number(input_format_t::cbor, len);\n                                break;\n                            }\n                            default:\n                                break;\n                        }\n                        return parse_cbor_internal(true, tag_handler);\n                    }\n\n                    default:            // LCOV_EXCL_LINE\n                        JSON_ASSERT(false);  // LCOV_EXCL_LINE\n                }\n            }\n\n            case 0xF4: // false\n                return sax->boolean(false);\n\n            case 0xF5: // true\n                return sax->boolean(true);\n\n            case 0xF6: // null\n                return sax->null();\n\n            case 0xF9: // Half-Precision Float (two-byte IEEE 754)\n            {\n                const auto byte1_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"number\")))\n                {\n                    return false;\n                }\n                const auto byte2_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"number\")))\n                {\n                    return false;\n                }\n\n                const auto byte1 = static_cast<unsigned char>(byte1_raw);\n                const auto byte2 = static_cast<unsigned char>(byte2_raw);\n\n                // code from RFC 7049, Appendix D, Figure 3:\n                // As half-precision floating-point numbers were only added\n                // to IEEE 754 in 2008, today's programming platforms often\n                // still only have limited support for them. It is very\n                // easy to include at least decoding support for them even\n                // without such support. An example of a small decoder for\n                // half-precision floating-point numbers in the C language\n                // is shown in Fig. 3.\n                const auto half = static_cast<unsigned int>((byte1 << 8u) + byte2);\n                const double val = [&half]\n                {\n                    const int exp = (half >> 10u) & 0x1Fu;\n                    const unsigned int mant = half & 0x3FFu;\n                    JSON_ASSERT(0 <= exp&& exp <= 32);\n                    JSON_ASSERT(mant <= 1024);\n                    switch (exp)\n                    {\n                        case 0:\n                            return std::ldexp(mant, -24);\n                        case 31:\n                            return (mant == 0)\n                            ? std::numeric_limits<double>::infinity()\n                            : std::numeric_limits<double>::quiet_NaN();\n                        default:\n                            return std::ldexp(mant + 1024, exp - 25);\n                    }\n                }();\n                return sax->number_float((half & 0x8000u) != 0\n                                         ? static_cast<number_float_t>(-val)\n                                         : static_cast<number_float_t>(val), \"\");\n            }\n\n            case 0xFA: // Single-Precision Float (four-byte IEEE 754)\n            {\n                float number{};\n                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xFB: // Double-Precision Float (eight-byte IEEE 754)\n            {\n                double number{};\n                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            default: // anything else (0xFF is handled inside the other types)\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, \"invalid byte: 0x\" + last_token, \"value\")));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a CBOR string\n\n    This function first reads starting bytes to determine the expected\n    string length and then copies this number of bytes into a string.\n    Additionally, CBOR's strings with indefinite lengths are supported.\n\n    @param[out] result  created string\n\n    @return whether string creation completed\n    */\n    bool get_cbor_string(string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"string\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // UTF-8 string (0x00..0x17 bytes follow)\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            {\n                return get_string(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7F: // UTF-8 string (indefinite length)\n            {\n                while (get() != 0xFF)\n                {\n                    string_t chunk;\n                    if (!get_cbor_string(chunk))\n                    {\n                        return false;\n                    }\n                    result.append(chunk);\n                }\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, \"expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x\" + last_token, \"string\")));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a CBOR byte array\n\n    This function first reads starting bytes to determine the expected\n    byte array length and then copies this number of bytes into the byte array.\n    Additionally, CBOR's byte arrays with indefinite lengths are supported.\n\n    @param[out] result  created byte array\n\n    @return whether byte array creation completed\n    */\n    bool get_cbor_binary(binary_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"binary\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // Binary data (0x00..0x17 bytes follow)\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            {\n                return get_binary(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0x58: // Binary data (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x59: // Binary data (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5A: // Binary data (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5B: // Binary data (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5F: // Binary data (indefinite length)\n            {\n                while (get() != 0xFF)\n                {\n                    binary_t chunk;\n                    if (!get_cbor_binary(chunk))\n                    {\n                        return false;\n                    }\n                    result.insert(result.end(), chunk.begin(), chunk.end());\n                }\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, \"expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x\" + last_token, \"binary\")));\n            }\n        }\n    }\n\n    /*!\n    @param[in] len  the length of the array or std::size_t(-1) for an\n                    array of indefinite size\n    @param[in] tag_handler how CBOR tags should be treated\n    @return whether array creation completed\n    */\n    bool get_cbor_array(const std::size_t len,\n                        const cbor_tag_handler_t tag_handler)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))\n        {\n            return false;\n        }\n\n        if (len != std::size_t(-1))\n        {\n            for (std::size_t i = 0; i < len; ++i)\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                {\n                    return false;\n                }\n            }\n        }\n        else\n        {\n            while (get() != 0xFF)\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler)))\n                {\n                    return false;\n                }\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @param[in] len  the length of the object or std::size_t(-1) for an\n                    object of indefinite size\n    @param[in] tag_handler how CBOR tags should be treated\n    @return whether object creation completed\n    */\n    bool get_cbor_object(const std::size_t len,\n                         const cbor_tag_handler_t tag_handler)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))\n        {\n            return false;\n        }\n\n        string_t key;\n        if (len != std::size_t(-1))\n        {\n            for (std::size_t i = 0; i < len; ++i)\n            {\n                get();\n                if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))\n                {\n                    return false;\n                }\n\n                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                {\n                    return false;\n                }\n                key.clear();\n            }\n        }\n        else\n        {\n            while (get() != 0xFF)\n            {\n                if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))\n                {\n                    return false;\n                }\n\n                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                {\n                    return false;\n                }\n                key.clear();\n            }\n        }\n\n        return sax->end_object();\n    }\n\n    /////////////\n    // MsgPack //\n    /////////////\n\n    /*!\n    @return whether a valid MessagePack value was passed to the SAX parser\n    */\n    bool parse_msgpack_internal()\n    {\n        switch (get())\n        {\n            // EOF\n            case std::char_traits<char_type>::eof():\n                return unexpect_eof(input_format_t::msgpack, \"value\");\n\n            // positive fixint\n            case 0x00:\n            case 0x01:\n            case 0x02:\n            case 0x03:\n            case 0x04:\n            case 0x05:\n            case 0x06:\n            case 0x07:\n            case 0x08:\n            case 0x09:\n            case 0x0A:\n            case 0x0B:\n            case 0x0C:\n            case 0x0D:\n            case 0x0E:\n            case 0x0F:\n            case 0x10:\n            case 0x11:\n            case 0x12:\n            case 0x13:\n            case 0x14:\n            case 0x15:\n            case 0x16:\n            case 0x17:\n            case 0x18:\n            case 0x19:\n            case 0x1A:\n            case 0x1B:\n            case 0x1C:\n            case 0x1D:\n            case 0x1E:\n            case 0x1F:\n            case 0x20:\n            case 0x21:\n            case 0x22:\n            case 0x23:\n            case 0x24:\n            case 0x25:\n            case 0x26:\n            case 0x27:\n            case 0x28:\n            case 0x29:\n            case 0x2A:\n            case 0x2B:\n            case 0x2C:\n            case 0x2D:\n            case 0x2E:\n            case 0x2F:\n            case 0x30:\n            case 0x31:\n            case 0x32:\n            case 0x33:\n            case 0x34:\n            case 0x35:\n            case 0x36:\n            case 0x37:\n            case 0x38:\n            case 0x39:\n            case 0x3A:\n            case 0x3B:\n            case 0x3C:\n            case 0x3D:\n            case 0x3E:\n            case 0x3F:\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            case 0x58:\n            case 0x59:\n            case 0x5A:\n            case 0x5B:\n            case 0x5C:\n            case 0x5D:\n            case 0x5E:\n            case 0x5F:\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            case 0x78:\n            case 0x79:\n            case 0x7A:\n            case 0x7B:\n            case 0x7C:\n            case 0x7D:\n            case 0x7E:\n            case 0x7F:\n                return sax->number_unsigned(static_cast<number_unsigned_t>(current));\n\n            // fixmap\n            case 0x80:\n            case 0x81:\n            case 0x82:\n            case 0x83:\n            case 0x84:\n            case 0x85:\n            case 0x86:\n            case 0x87:\n            case 0x88:\n            case 0x89:\n            case 0x8A:\n            case 0x8B:\n            case 0x8C:\n            case 0x8D:\n            case 0x8E:\n            case 0x8F:\n                return get_msgpack_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));\n\n            // fixarray\n            case 0x90:\n            case 0x91:\n            case 0x92:\n            case 0x93:\n            case 0x94:\n            case 0x95:\n            case 0x96:\n            case 0x97:\n            case 0x98:\n            case 0x99:\n            case 0x9A:\n            case 0x9B:\n            case 0x9C:\n            case 0x9D:\n            case 0x9E:\n            case 0x9F:\n                return get_msgpack_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));\n\n            // fixstr\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n            case 0xB8:\n            case 0xB9:\n            case 0xBA:\n            case 0xBB:\n            case 0xBC:\n            case 0xBD:\n            case 0xBE:\n            case 0xBF:\n            case 0xD9: // str 8\n            case 0xDA: // str 16\n            case 0xDB: // str 32\n            {\n                string_t s;\n                return get_msgpack_string(s) && sax->string(s);\n            }\n\n            case 0xC0: // nil\n                return sax->null();\n\n            case 0xC2: // false\n                return sax->boolean(false);\n\n            case 0xC3: // true\n                return sax->boolean(true);\n\n            case 0xC4: // bin 8\n            case 0xC5: // bin 16\n            case 0xC6: // bin 32\n            case 0xC7: // ext 8\n            case 0xC8: // ext 16\n            case 0xC9: // ext 32\n            case 0xD4: // fixext 1\n            case 0xD5: // fixext 2\n            case 0xD6: // fixext 4\n            case 0xD7: // fixext 8\n            case 0xD8: // fixext 16\n            {\n                binary_t b;\n                return get_msgpack_binary(b) && sax->binary(b);\n            }\n\n            case 0xCA: // float 32\n            {\n                float number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xCB: // float 64\n            {\n                double number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xCC: // uint 8\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCD: // uint 16\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCE: // uint 32\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCF: // uint 64\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xD0: // int 8\n            {\n                std::int8_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD1: // int 16\n            {\n                std::int16_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD2: // int 32\n            {\n                std::int32_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD3: // int 64\n            {\n                std::int64_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xDC: // array 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));\n            }\n\n            case 0xDD: // array 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));\n            }\n\n            case 0xDE: // map 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));\n            }\n\n            case 0xDF: // map 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));\n            }\n\n            // negative fixint\n            case 0xE0:\n            case 0xE1:\n            case 0xE2:\n            case 0xE3:\n            case 0xE4:\n            case 0xE5:\n            case 0xE6:\n            case 0xE7:\n            case 0xE8:\n            case 0xE9:\n            case 0xEA:\n            case 0xEB:\n            case 0xEC:\n            case 0xED:\n            case 0xEE:\n            case 0xEF:\n            case 0xF0:\n            case 0xF1:\n            case 0xF2:\n            case 0xF3:\n            case 0xF4:\n            case 0xF5:\n            case 0xF6:\n            case 0xF7:\n            case 0xF8:\n            case 0xF9:\n            case 0xFA:\n            case 0xFB:\n            case 0xFC:\n            case 0xFD:\n            case 0xFE:\n            case 0xFF:\n                return sax->number_integer(static_cast<std::int8_t>(current));\n\n            default: // anything else\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, \"invalid byte: 0x\" + last_token, \"value\")));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a MessagePack string\n\n    This function first reads starting bytes to determine the expected\n    string length and then copies this number of bytes into a string.\n\n    @param[out] result  created string\n\n    @return whether string creation completed\n    */\n    bool get_msgpack_string(string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, \"string\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // fixstr\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n            case 0xB8:\n            case 0xB9:\n            case 0xBA:\n            case 0xBB:\n            case 0xBC:\n            case 0xBD:\n            case 0xBE:\n            case 0xBF:\n            {\n                return get_string(input_format_t::msgpack, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0xD9: // str 8\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            case 0xDA: // str 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            case 0xDB: // str 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, \"expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x\" + last_token, \"string\")));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a MessagePack byte array\n\n    This function first reads starting bytes to determine the expected\n    byte array length and then copies this number of bytes into a byte array.\n\n    @param[out] result  created byte array\n\n    @return whether byte array creation completed\n    */\n    bool get_msgpack_binary(binary_t& result)\n    {\n        // helper function to set the subtype\n        auto assign_and_return_true = [&result](std::int8_t subtype)\n        {\n            result.set_subtype(static_cast<std::uint8_t>(subtype));\n            return true;\n        };\n\n        switch (current)\n        {\n            case 0xC4: // bin 8\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC5: // bin 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC6: // bin 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC7: // ext 8\n            {\n                std::uint8_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xC8: // ext 16\n            {\n                std::uint16_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xC9: // ext 32\n            {\n                std::uint32_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD4: // fixext 1\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 1, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD5: // fixext 2\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 2, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD6: // fixext 4\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 4, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD7: // fixext 8\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 8, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD8: // fixext 16\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 16, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            default:           // LCOV_EXCL_LINE\n                return false;  // LCOV_EXCL_LINE\n        }\n    }\n\n    /*!\n    @param[in] len  the length of the array\n    @return whether array creation completed\n    */\n    bool get_msgpack_array(const std::size_t len)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))\n        {\n            return false;\n        }\n\n        for (std::size_t i = 0; i < len; ++i)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))\n            {\n                return false;\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @param[in] len  the length of the object\n    @return whether object creation completed\n    */\n    bool get_msgpack_object(const std::size_t len)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))\n        {\n            return false;\n        }\n\n        string_t key;\n        for (std::size_t i = 0; i < len; ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key)))\n            {\n                return false;\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))\n            {\n                return false;\n            }\n            key.clear();\n        }\n\n        return sax->end_object();\n    }\n\n    ////////////\n    // UBJSON //\n    ////////////\n\n    /*!\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether a valid UBJSON value was passed to the SAX parser\n    */\n    bool parse_ubjson_internal(const bool get_char = true)\n    {\n        return get_ubjson_value(get_char ? get_ignore_noop() : current);\n    }\n\n    /*!\n    @brief reads a UBJSON string\n\n    This function is either called after reading the 'S' byte explicitly\n    indicating a string, or in case of an object key where the 'S' byte can be\n    left out.\n\n    @param[out] result   created string\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether string creation completed\n    */\n    bool get_ubjson_string(string_t& result, const bool get_char = true)\n    {\n        if (get_char)\n        {\n            get();  // TODO(niels): may we ignore N here?\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"value\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            case 'U':\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'i':\n            {\n                std::int8_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'I':\n            {\n                std::int16_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'l':\n            {\n                std::int32_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'L':\n            {\n                std::int64_t len{};\n                return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result);\n            }\n\n            default:\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"expected length type specification (U, i, I, l, L); last byte: 0x\" + last_token, \"string\")));\n        }\n    }\n\n    /*!\n    @param[out] result  determined size\n    @return whether size determination completed\n    */\n    bool get_ubjson_size_value(std::size_t& result)\n    {\n        switch (get_ignore_noop())\n        {\n            case 'U':\n            {\n                std::uint8_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'i':\n            {\n                std::int8_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'I':\n            {\n                std::int16_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'l':\n            {\n                std::int32_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'L':\n            {\n                std::int64_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"expected length type specification (U, i, I, l, L) after '#'; last byte: 0x\" + last_token, \"size\")));\n            }\n        }\n    }\n\n    /*!\n    @brief determine the type and size for a container\n\n    In the optimized UBJSON format, a type and a size can be provided to allow\n    for a more compact representation.\n\n    @param[out] result  pair of the size and the type\n\n    @return whether pair creation completed\n    */\n    bool get_ubjson_size_type(std::pair<std::size_t, char_int_type>& result)\n    {\n        result.first = string_t::npos; // size\n        result.second = 0; // type\n\n        get_ignore_noop();\n\n        if (current == '$')\n        {\n            result.second = get();  // must not ignore 'N', because 'N' maybe the type\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"type\")))\n            {\n                return false;\n            }\n\n            get_ignore_noop();\n            if (JSON_HEDLEY_UNLIKELY(current != '#'))\n            {\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"value\")))\n                {\n                    return false;\n                }\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, \"expected '#' after type information; last byte: 0x\" + last_token, \"size\")));\n            }\n\n            return get_ubjson_size_value(result.first);\n        }\n\n        if (current == '#')\n        {\n            return get_ubjson_size_value(result.first);\n        }\n\n        return true;\n    }\n\n    /*!\n    @param prefix  the previously read or set type prefix\n    @return whether value creation completed\n    */\n    bool get_ubjson_value(const char_int_type prefix)\n    {\n        switch (prefix)\n        {\n            case std::char_traits<char_type>::eof():  // EOF\n                return unexpect_eof(input_format_t::ubjson, \"value\");\n\n            case 'T':  // true\n                return sax->boolean(true);\n            case 'F':  // false\n                return sax->boolean(false);\n\n            case 'Z':  // null\n                return sax->null();\n\n            case 'U':\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number);\n            }\n\n            case 'i':\n            {\n                std::int8_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_integer(number);\n            }\n\n            case 'I':\n            {\n                std::int16_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_integer(number);\n            }\n\n            case 'l':\n            {\n                std::int32_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_integer(number);\n            }\n\n            case 'L':\n            {\n                std::int64_t number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_integer(number);\n            }\n\n            case 'd':\n            {\n                float number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 'D':\n            {\n                double number{};\n                return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 'H':\n            {\n                return get_ubjson_high_precision_number();\n            }\n\n            case 'C':  // char\n            {\n                get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"char\")))\n                {\n                    return false;\n                }\n                if (JSON_HEDLEY_UNLIKELY(current > 127))\n                {\n                    auto last_token = get_token_string();\n                    return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"byte after 'C' must be in range 0x00..0x7F; last byte: 0x\" + last_token, \"char\")));\n                }\n                string_t s(1, static_cast<typename string_t::value_type>(current));\n                return sax->string(s);\n            }\n\n            case 'S':  // string\n            {\n                string_t s;\n                return get_ubjson_string(s) && sax->string(s);\n            }\n\n            case '[':  // array\n                return get_ubjson_array();\n\n            case '{':  // object\n                return get_ubjson_object();\n\n            default: // anything else\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, \"invalid byte: 0x\" + last_token, \"value\")));\n            }\n        }\n    }\n\n    /*!\n    @return whether array creation completed\n    */\n    bool get_ubjson_array()\n    {\n        std::pair<std::size_t, char_int_type> size_and_type;\n        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))\n        {\n            return false;\n        }\n\n        if (size_and_type.first != string_t::npos)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second != 0)\n            {\n                if (size_and_type.second != 'N')\n                {\n                    for (std::size_t i = 0; i < size_and_type.first; ++i)\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))\n                        {\n                            return false;\n                        }\n                    }\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                    {\n                        return false;\n                    }\n                }\n            }\n        }\n        else\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1))))\n            {\n                return false;\n            }\n\n            while (current != ']')\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false)))\n                {\n                    return false;\n                }\n                get_ignore_noop();\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @return whether object creation completed\n    */\n    bool get_ubjson_object()\n    {\n        std::pair<std::size_t, char_int_type> size_and_type;\n        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))\n        {\n            return false;\n        }\n\n        string_t key;\n        if (size_and_type.first != string_t::npos)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second != 0)\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n        }\n        else\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))\n            {\n                return false;\n            }\n\n            while (current != '}')\n            {\n                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key)))\n                {\n                    return false;\n                }\n                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                {\n                    return false;\n                }\n                get_ignore_noop();\n                key.clear();\n            }\n        }\n\n        return sax->end_object();\n    }\n\n    // Note, no reader for UBJSON binary types is implemented because they do\n    // not exist\n\n    bool get_ubjson_high_precision_number()\n    {\n        // get size of following number string\n        std::size_t size{};\n        auto res = get_ubjson_size_value(size);\n        if (JSON_HEDLEY_UNLIKELY(!res))\n        {\n            return res;\n        }\n\n        // get number string\n        std::vector<char> number_vector;\n        for (std::size_t i = 0; i < size; ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, \"number\")))\n            {\n                return false;\n            }\n            number_vector.push_back(static_cast<char>(current));\n        }\n\n        // parse number string\n        auto number_ia = detail::input_adapter(std::forward<decltype(number_vector)>(number_vector));\n        auto number_lexer = detail::lexer<BasicJsonType, decltype(number_ia)>(std::move(number_ia), false);\n        const auto result_number = number_lexer.scan();\n        const auto number_string = number_lexer.get_token_string();\n        const auto result_remainder = number_lexer.scan();\n\n        using token_type = typename detail::lexer_base<BasicJsonType>::token_type;\n\n        if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))\n        {\n            return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, \"invalid number text: \" + number_lexer.get_token_string(), \"high-precision number\")));\n        }\n\n        switch (result_number)\n        {\n            case token_type::value_integer:\n                return sax->number_integer(number_lexer.get_number_integer());\n            case token_type::value_unsigned:\n                return sax->number_unsigned(number_lexer.get_number_unsigned());\n            case token_type::value_float:\n                return sax->number_float(number_lexer.get_number_float(), std::move(number_string));\n            default:\n                return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, \"invalid number text: \" + number_lexer.get_token_string(), \"high-precision number\")));\n        }\n    }\n\n    ///////////////////////\n    // Utility functions //\n    ///////////////////////\n\n    /*!\n    @brief get next character from the input\n\n    This function provides the interface to the used input adapter. It does\n    not throw in case the input reached EOF, but returns a -'ve valued\n    `std::char_traits<char_type>::eof()` in that case.\n\n    @return character read from the input\n    */\n    char_int_type get()\n    {\n        ++chars_read;\n        return current = ia.get_character();\n    }\n\n    /*!\n    @return character read from the input after ignoring all 'N' entries\n    */\n    char_int_type get_ignore_noop()\n    {\n        do\n        {\n            get();\n        }\n        while (current == 'N');\n\n        return current;\n    }\n\n    /*\n    @brief read a number from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format   the current format (for diagnostics)\n    @param[out] result  number of type @a NumberType\n\n    @return whether conversion completed\n\n    @note This function needs to respect the system's endianess, because\n          bytes in CBOR, MessagePack, and UBJSON are stored in network order\n          (big endian) and therefore need reordering on little endian systems.\n    */\n    template<typename NumberType, bool InputIsLittleEndian = false>\n    bool get_number(const input_format_t format, NumberType& result)\n    {\n        // step 1: read input into array with system's byte order\n        std::array<std::uint8_t, sizeof(NumberType)> vec;\n        for (std::size_t i = 0; i < sizeof(NumberType); ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, \"number\")))\n            {\n                return false;\n            }\n\n            // reverse byte order prior to conversion if necessary\n            if (is_little_endian != InputIsLittleEndian)\n            {\n                vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current);\n            }\n            else\n            {\n                vec[i] = static_cast<std::uint8_t>(current); // LCOV_EXCL_LINE\n            }\n        }\n\n        // step 2: convert array into number of type T and return\n        std::memcpy(&result, vec.data(), sizeof(NumberType));\n        return true;\n    }\n\n    /*!\n    @brief create a string by reading characters from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format the current format (for diagnostics)\n    @param[in] len number of characters to read\n    @param[out] result string created by reading @a len bytes\n\n    @return whether string creation completed\n\n    @note We can not reserve @a len bytes for the result, because @a len\n          may be too large. Usually, @ref unexpect_eof() detects the end of\n          the input before we run out of string memory.\n    */\n    template<typename NumberType>\n    bool get_string(const input_format_t format,\n                    const NumberType len,\n                    string_t& result)\n    {\n        bool success = true;\n        for (NumberType i = 0; i < len; i++)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, \"string\")))\n            {\n                success = false;\n                break;\n            }\n            result.push_back(static_cast<typename string_t::value_type>(current));\n        };\n        return success;\n    }\n\n    /*!\n    @brief create a byte array by reading bytes from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format the current format (for diagnostics)\n    @param[in] len number of bytes to read\n    @param[out] result byte array created by reading @a len bytes\n\n    @return whether byte array creation completed\n\n    @note We can not reserve @a len bytes for the result, because @a len\n          may be too large. Usually, @ref unexpect_eof() detects the end of\n          the input before we run out of memory.\n    */\n    template<typename NumberType>\n    bool get_binary(const input_format_t format,\n                    const NumberType len,\n                    binary_t& result)\n    {\n        bool success = true;\n        for (NumberType i = 0; i < len; i++)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, \"binary\")))\n            {\n                success = false;\n                break;\n            }\n            result.push_back(static_cast<std::uint8_t>(current));\n        }\n        return success;\n    }\n\n    /*!\n    @param[in] format   the current format (for diagnostics)\n    @param[in] context  further context information (for diagnostics)\n    @return whether the last read character is not EOF\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    bool unexpect_eof(const input_format_t format, const char* context) const\n    {\n        if (JSON_HEDLEY_UNLIKELY(current == std::char_traits<char_type>::eof()))\n        {\n            return sax->parse_error(chars_read, \"<end of file>\",\n                                    parse_error::create(110, chars_read, exception_message(format, \"unexpected end of input\", context)));\n        }\n        return true;\n    }\n\n    /*!\n    @return a string representation of the last read byte\n    */\n    std::string get_token_string() const\n    {\n        std::array<char, 3> cr{{}};\n        (std::snprintf)(cr.data(), cr.size(), \"%.2hhX\", static_cast<unsigned char>(current));\n        return std::string{cr.data()};\n    }\n\n    /*!\n    @param[in] format   the current format\n    @param[in] detail   a detailed error message\n    @param[in] context  further context information\n    @return a message string to use in the parse_error exceptions\n    */\n    std::string exception_message(const input_format_t format,\n                                  const std::string& detail,\n                                  const std::string& context) const\n    {\n        std::string error_msg = \"syntax error while parsing \";\n\n        switch (format)\n        {\n            case input_format_t::cbor:\n                error_msg += \"CBOR\";\n                break;\n\n            case input_format_t::msgpack:\n                error_msg += \"MessagePack\";\n                break;\n\n            case input_format_t::ubjson:\n                error_msg += \"UBJSON\";\n                break;\n\n            case input_format_t::bson:\n                error_msg += \"BSON\";\n                break;\n\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false);  // LCOV_EXCL_LINE\n        }\n\n        return error_msg + \" \" + context + \": \" + detail;\n    }\n\n  private:\n    /// input adapter\n    InputAdapterType ia;\n\n    /// the current character\n    char_int_type current = std::char_traits<char_type>::eof();\n\n    /// the number of characters read\n    std::size_t chars_read = 0;\n\n    /// whether we can assume little endianess\n    const bool is_little_endian = little_endianess();\n\n    /// the SAX parser\n    json_sax_t* sax = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/lexer.hpp>\n\n// #include <nlohmann/detail/input/parser.hpp>\n\n\n#include <cmath> // isfinite\n#include <cstdint> // uint8_t\n#include <functional> // function\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/json_sax.hpp>\n\n// #include <nlohmann/detail/input/lexer.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/is_sax.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n////////////\n// parser //\n////////////\n\nenum class parse_event_t : uint8_t\n{\n    /// the parser read `{` and started to process a JSON object\n    object_start,\n    /// the parser read `}` and finished processing a JSON object\n    object_end,\n    /// the parser read `[` and started to process a JSON array\n    array_start,\n    /// the parser read `]` and finished processing a JSON array\n    array_end,\n    /// the parser read a key of a value in an object\n    key,\n    /// the parser finished reading a JSON value\n    value\n};\n\ntemplate<typename BasicJsonType>\nusing parser_callback_t =\n    std::function<bool(int depth, parse_event_t event, BasicJsonType& parsed)>;\n\n/*!\n@brief syntax analysis\n\nThis class implements a recursive descent parser.\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType>\nclass parser\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using lexer_t = lexer<BasicJsonType, InputAdapterType>;\n    using token_type = typename lexer_t::token_type;\n\n  public:\n    /// a parser reading from an input adapter\n    explicit parser(InputAdapterType&& adapter,\n                    const parser_callback_t<BasicJsonType> cb = nullptr,\n                    const bool allow_exceptions_ = true,\n                    const bool skip_comments = false)\n        : callback(cb)\n        , m_lexer(std::move(adapter), skip_comments)\n        , allow_exceptions(allow_exceptions_)\n    {\n        // read first token\n        get_token();\n    }\n\n    /*!\n    @brief public parser interface\n\n    @param[in] strict      whether to expect the last token to be EOF\n    @param[in,out] result  parsed JSON value\n\n    @throw parse_error.101 in case of an unexpected token\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n    */\n    void parse(const bool strict, BasicJsonType& result)\n    {\n        if (callback)\n        {\n            json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);\n            sax_parse_internal(&sdp);\n            result.assert_invariant();\n\n            // in strict mode, input must be completely read\n            if (strict && (get_token() != token_type::end_of_input))\n            {\n                sdp.parse_error(m_lexer.get_position(),\n                                m_lexer.get_token_string(),\n                                parse_error::create(101, m_lexer.get_position(),\n                                                    exception_message(token_type::end_of_input, \"value\")));\n            }\n\n            // in case of an error, return discarded value\n            if (sdp.is_errored())\n            {\n                result = value_t::discarded;\n                return;\n            }\n\n            // set top-level value to null if it was discarded by the callback\n            // function\n            if (result.is_discarded())\n            {\n                result = nullptr;\n            }\n        }\n        else\n        {\n            json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);\n            sax_parse_internal(&sdp);\n            result.assert_invariant();\n\n            // in strict mode, input must be completely read\n            if (strict && (get_token() != token_type::end_of_input))\n            {\n                sdp.parse_error(m_lexer.get_position(),\n                                m_lexer.get_token_string(),\n                                parse_error::create(101, m_lexer.get_position(),\n                                                    exception_message(token_type::end_of_input, \"value\")));\n            }\n\n            // in case of an error, return discarded value\n            if (sdp.is_errored())\n            {\n                result = value_t::discarded;\n                return;\n            }\n        }\n    }\n\n    /*!\n    @brief public accept interface\n\n    @param[in] strict  whether to expect the last token to be EOF\n    @return whether the input is a proper JSON text\n    */\n    bool accept(const bool strict = true)\n    {\n        json_sax_acceptor<BasicJsonType> sax_acceptor;\n        return sax_parse(&sax_acceptor, strict);\n    }\n\n    template<typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    bool sax_parse(SAX* sax, const bool strict = true)\n    {\n        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};\n        const bool result = sax_parse_internal(sax);\n\n        // strict mode: next byte must be EOF\n        if (result && strict && (get_token() != token_type::end_of_input))\n        {\n            return sax->parse_error(m_lexer.get_position(),\n                                    m_lexer.get_token_string(),\n                                    parse_error::create(101, m_lexer.get_position(),\n                                            exception_message(token_type::end_of_input, \"value\")));\n        }\n\n        return result;\n    }\n\n  private:\n    template<typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    bool sax_parse_internal(SAX* sax)\n    {\n        // stack to remember the hierarchy of structured values we are parsing\n        // true = array; false = object\n        std::vector<bool> states;\n        // value to avoid a goto (see comment where set to true)\n        bool skip_to_state_evaluation = false;\n\n        while (true)\n        {\n            if (!skip_to_state_evaluation)\n            {\n                // invariant: get_token() was called before each iteration\n                switch (last_token)\n                {\n                    case token_type::begin_object:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1))))\n                        {\n                            return false;\n                        }\n\n                        // closing } -> we are done\n                        if (get_token() == token_type::end_object)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))\n                            {\n                                return false;\n                            }\n                            break;\n                        }\n\n                        // parse key\n                        if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    parse_error::create(101, m_lexer.get_position(),\n                                                            exception_message(token_type::value_string, \"object key\")));\n                        }\n                        if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n\n                        // parse separator (:)\n                        if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    parse_error::create(101, m_lexer.get_position(),\n                                                            exception_message(token_type::name_separator, \"object separator\")));\n                        }\n\n                        // remember we are now inside an object\n                        states.push_back(false);\n\n                        // parse values\n                        get_token();\n                        continue;\n                    }\n\n                    case token_type::begin_array:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1))))\n                        {\n                            return false;\n                        }\n\n                        // closing ] -> we are done\n                        if (get_token() == token_type::end_array)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))\n                            {\n                                return false;\n                            }\n                            break;\n                        }\n\n                        // remember we are now inside an array\n                        states.push_back(true);\n\n                        // parse values (no need to call get_token)\n                        continue;\n                    }\n\n                    case token_type::value_float:\n                    {\n                        const auto res = m_lexer.get_number_float();\n\n                        if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    out_of_range::create(406, \"number overflow parsing '\" + m_lexer.get_token_string() + \"'\"));\n                        }\n\n                        if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n\n                        break;\n                    }\n\n                    case token_type::literal_false:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false)))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::literal_null:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->null()))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::literal_true:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true)))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_integer:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_string:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_unsigned:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::parse_error:\n                    {\n                        // using \"uninitialized\" to avoid \"expected\" message\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(),\n                                                        exception_message(token_type::uninitialized, \"value\")));\n                    }\n\n                    default: // the last token was unexpected\n                    {\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(),\n                                                        exception_message(token_type::literal_or_value, \"value\")));\n                    }\n                }\n            }\n            else\n            {\n                skip_to_state_evaluation = false;\n            }\n\n            // we reached this line after we successfully parsed a value\n            if (states.empty())\n            {\n                // empty stack: we reached the end of the hierarchy: done\n                return true;\n            }\n\n            if (states.back())  // array\n            {\n                // comma -> next value\n                if (get_token() == token_type::value_separator)\n                {\n                    // parse a new value\n                    get_token();\n                    continue;\n                }\n\n                // closing ]\n                if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))\n                    {\n                        return false;\n                    }\n\n                    // We are done with this array. Before we can parse a\n                    // new value, we need to evaluate the new state first.\n                    // By setting skip_to_state_evaluation to false, we\n                    // are effectively jumping to the beginning of this if.\n                    JSON_ASSERT(!states.empty());\n                    states.pop_back();\n                    skip_to_state_evaluation = true;\n                    continue;\n                }\n\n                return sax->parse_error(m_lexer.get_position(),\n                                        m_lexer.get_token_string(),\n                                        parse_error::create(101, m_lexer.get_position(),\n                                                exception_message(token_type::end_array, \"array\")));\n            }\n            else  // object\n            {\n                // comma -> next value\n                if (get_token() == token_type::value_separator)\n                {\n                    // parse key\n                    if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))\n                    {\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(),\n                                                        exception_message(token_type::value_string, \"object key\")));\n                    }\n\n                    if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))\n                    {\n                        return false;\n                    }\n\n                    // parse separator (:)\n                    if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))\n                    {\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(),\n                                                        exception_message(token_type::name_separator, \"object separator\")));\n                    }\n\n                    // parse values\n                    get_token();\n                    continue;\n                }\n\n                // closing }\n                if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))\n                    {\n                        return false;\n                    }\n\n                    // We are done with this object. Before we can parse a\n                    // new value, we need to evaluate the new state first.\n                    // By setting skip_to_state_evaluation to false, we\n                    // are effectively jumping to the beginning of this if.\n                    JSON_ASSERT(!states.empty());\n                    states.pop_back();\n                    skip_to_state_evaluation = true;\n                    continue;\n                }\n\n                return sax->parse_error(m_lexer.get_position(),\n                                        m_lexer.get_token_string(),\n                                        parse_error::create(101, m_lexer.get_position(),\n                                                exception_message(token_type::end_object, \"object\")));\n            }\n        }\n    }\n\n    /// get next token from lexer\n    token_type get_token()\n    {\n        return last_token = m_lexer.scan();\n    }\n\n    std::string exception_message(const token_type expected, const std::string& context)\n    {\n        std::string error_msg = \"syntax error \";\n\n        if (!context.empty())\n        {\n            error_msg += \"while parsing \" + context + \" \";\n        }\n\n        error_msg += \"- \";\n\n        if (last_token == token_type::parse_error)\n        {\n            error_msg += std::string(m_lexer.get_error_message()) + \"; last read: '\" +\n                         m_lexer.get_token_string() + \"'\";\n        }\n        else\n        {\n            error_msg += \"unexpected \" + std::string(lexer_t::token_type_name(last_token));\n        }\n\n        if (expected != token_type::uninitialized)\n        {\n            error_msg += \"; expected \" + std::string(lexer_t::token_type_name(expected));\n        }\n\n        return error_msg;\n    }\n\n  private:\n    /// callback function\n    const parser_callback_t<BasicJsonType> callback = nullptr;\n    /// the type of the last read token\n    token_type last_token = token_type::uninitialized;\n    /// the lexer\n    lexer_t m_lexer;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/internal_iterator.hpp>\n\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\n\n#include <cstddef> // ptrdiff_t\n#include <limits>  // numeric_limits\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/*\n@brief an iterator for primitive JSON types\n\nThis class models an iterator for primitive JSON types (boolean, number,\nstring). It's only purpose is to allow the iterator/const_iterator classes\nto \"iterate\" over primitive values. Internally, the iterator is modeled by\na `difference_type` variable. Value begin_value (`0`) models the begin,\nend_value (`1`) models past the end.\n*/\nclass primitive_iterator_t\n{\n  private:\n    using difference_type = std::ptrdiff_t;\n    static constexpr difference_type begin_value = 0;\n    static constexpr difference_type end_value = begin_value + 1;\n\n    /// iterator as signed integer type\n    difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();\n\n  public:\n    constexpr difference_type get_value() const noexcept\n    {\n        return m_it;\n    }\n\n    /// set iterator to a defined beginning\n    void set_begin() noexcept\n    {\n        m_it = begin_value;\n    }\n\n    /// set iterator to a defined past the end\n    void set_end() noexcept\n    {\n        m_it = end_value;\n    }\n\n    /// return whether the iterator can be dereferenced\n    constexpr bool is_begin() const noexcept\n    {\n        return m_it == begin_value;\n    }\n\n    /// return whether the iterator is at end\n    constexpr bool is_end() const noexcept\n    {\n        return m_it == end_value;\n    }\n\n    friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it == rhs.m_it;\n    }\n\n    friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it < rhs.m_it;\n    }\n\n    primitive_iterator_t operator+(difference_type n) noexcept\n    {\n        auto result = *this;\n        result += n;\n        return result;\n    }\n\n    friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it - rhs.m_it;\n    }\n\n    primitive_iterator_t& operator++() noexcept\n    {\n        ++m_it;\n        return *this;\n    }\n\n    primitive_iterator_t const operator++(int) noexcept\n    {\n        auto result = *this;\n        ++m_it;\n        return result;\n    }\n\n    primitive_iterator_t& operator--() noexcept\n    {\n        --m_it;\n        return *this;\n    }\n\n    primitive_iterator_t const operator--(int) noexcept\n    {\n        auto result = *this;\n        --m_it;\n        return result;\n    }\n\n    primitive_iterator_t& operator+=(difference_type n) noexcept\n    {\n        m_it += n;\n        return *this;\n    }\n\n    primitive_iterator_t& operator-=(difference_type n) noexcept\n    {\n        m_it -= n;\n        return *this;\n    }\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/*!\n@brief an iterator value\n\n@note This structure could easily be a union, but MSVC currently does not allow\nunions members with complex constructors, see https://github.com/nlohmann/json/pull/105.\n*/\ntemplate<typename BasicJsonType> struct internal_iterator\n{\n    /// iterator for JSON objects\n    typename BasicJsonType::object_t::iterator object_iterator {};\n    /// iterator for JSON arrays\n    typename BasicJsonType::array_t::iterator array_iterator {};\n    /// generic iterator for all other types\n    primitive_iterator_t primitive_iterator {};\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/iter_impl.hpp>\n\n\n#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next\n#include <type_traits> // conditional, is_const, remove_const\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/iterators/internal_iterator.hpp>\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n// forward declare, to be able to friend it later on\ntemplate<typename IteratorType> class iteration_proxy;\ntemplate<typename IteratorType> class iteration_proxy_value;\n\n/*!\n@brief a template for a bidirectional iterator for the @ref basic_json class\nThis class implements a both iterators (iterator and const_iterator) for the\n@ref basic_json class.\n@note An iterator is called *initialized* when a pointer to a JSON value has\n      been set (e.g., by a constructor or a copy assignment). If the iterator is\n      default-constructed, it is *uninitialized* and most methods are undefined.\n      **The library uses assertions to detect calls on uninitialized iterators.**\n@requirement The class satisfies the following concept requirements:\n-\n[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):\n  The iterator that can be moved can be moved in both directions (i.e.\n  incremented and decremented).\n@since version 1.0.0, simplified in version 2.0.9, change to bidirectional\n       iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)\n*/\ntemplate<typename BasicJsonType>\nclass iter_impl\n{\n    /// allow basic_json to access private members\n    friend iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;\n    friend BasicJsonType;\n    friend iteration_proxy<iter_impl>;\n    friend iteration_proxy_value<iter_impl>;\n\n    using object_t = typename BasicJsonType::object_t;\n    using array_t = typename BasicJsonType::array_t;\n    // make sure BasicJsonType is basic_json or const basic_json\n    static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,\n                  \"iter_impl only accepts (const) basic_json\");\n\n  public:\n\n    /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.\n    /// The C++ Standard has never required user-defined iterators to derive from std::iterator.\n    /// A user-defined iterator should provide publicly accessible typedefs named\n    /// iterator_category, value_type, difference_type, pointer, and reference.\n    /// Note that value_type is required to be non-const, even for constant iterators.\n    using iterator_category = std::bidirectional_iterator_tag;\n\n    /// the type of the values when the iterator is dereferenced\n    using value_type = typename BasicJsonType::value_type;\n    /// a type to represent differences between iterators\n    using difference_type = typename BasicJsonType::difference_type;\n    /// defines a pointer to the type iterated over (value_type)\n    using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,\n          typename BasicJsonType::const_pointer,\n          typename BasicJsonType::pointer>::type;\n    /// defines a reference to the type iterated over (value_type)\n    using reference =\n        typename std::conditional<std::is_const<BasicJsonType>::value,\n        typename BasicJsonType::const_reference,\n        typename BasicJsonType::reference>::type;\n\n    /// default constructor\n    iter_impl() = default;\n\n    /*!\n    @brief constructor for a given JSON instance\n    @param[in] object  pointer to a JSON object for this iterator\n    @pre object != nullptr\n    @post The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    explicit iter_impl(pointer object) noexcept : m_object(object)\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = typename object_t::iterator();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = typename array_t::iterator();\n                break;\n            }\n\n            default:\n            {\n                m_it.primitive_iterator = primitive_iterator_t();\n                break;\n            }\n        }\n    }\n\n    /*!\n    @note The conventional copy constructor and copy assignment are implicitly\n          defined. Combined with the following converting constructor and\n          assignment, they support: (1) copy from iterator to iterator, (2)\n          copy from const iterator to const iterator, and (3) conversion from\n          iterator to const iterator. However conversion from const iterator\n          to iterator is not defined.\n    */\n\n    /*!\n    @brief const copy constructor\n    @param[in] other const iterator to copy from\n    @note This copy constructor had to be defined explicitly to circumvent a bug\n          occurring on msvc v19.0 compiler (VS 2015) debug build. For more\n          information refer to: https://github.com/nlohmann/json/issues/1608\n    */\n    iter_impl(const iter_impl<const BasicJsonType>& other) noexcept\n        : m_object(other.m_object), m_it(other.m_it)\n    {}\n\n    /*!\n    @brief converting assignment\n    @param[in] other const iterator to copy from\n    @return const/non-const iterator\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept\n    {\n        m_object = other.m_object;\n        m_it = other.m_it;\n        return *this;\n    }\n\n    /*!\n    @brief converting constructor\n    @param[in] other  non-const iterator to copy from\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept\n        : m_object(other.m_object), m_it(other.m_it)\n    {}\n\n    /*!\n    @brief converting assignment\n    @param[in] other  non-const iterator to copy from\n    @return const/non-const iterator\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept\n    {\n        m_object = other.m_object;\n        m_it = other.m_it;\n        return *this;\n    }\n\n  private:\n    /*!\n    @brief set the iterator to the first value\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    void set_begin() noexcept\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = m_object->m_value.object->begin();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = m_object->m_value.array->begin();\n                break;\n            }\n\n            case value_t::null:\n            {\n                // set to end so begin()==end() is true: null is empty\n                m_it.primitive_iterator.set_end();\n                break;\n            }\n\n            default:\n            {\n                m_it.primitive_iterator.set_begin();\n                break;\n            }\n        }\n    }\n\n    /*!\n    @brief set the iterator past the last value\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    void set_end() noexcept\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = m_object->m_value.object->end();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = m_object->m_value.array->end();\n                break;\n            }\n\n            default:\n            {\n                m_it.primitive_iterator.set_end();\n                break;\n            }\n        }\n    }\n\n  public:\n    /*!\n    @brief return a reference to the value pointed to by the iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference operator*() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());\n                return m_it.object_iterator->second;\n            }\n\n            case value_t::array:\n            {\n                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());\n                return *m_it.array_iterator;\n            }\n\n            case value_t::null:\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))\n                {\n                    return *m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n            }\n        }\n    }\n\n    /*!\n    @brief dereference the iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    pointer operator->() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());\n                return &(m_it.object_iterator->second);\n            }\n\n            case value_t::array:\n            {\n                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());\n                return &*m_it.array_iterator;\n            }\n\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))\n                {\n                    return m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n            }\n        }\n    }\n\n    /*!\n    @brief post-increment (it++)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl const operator++(int)\n    {\n        auto result = *this;\n        ++(*this);\n        return result;\n    }\n\n    /*!\n    @brief pre-increment (++it)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator++()\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                std::advance(m_it.object_iterator, 1);\n                break;\n            }\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, 1);\n                break;\n            }\n\n            default:\n            {\n                ++m_it.primitive_iterator;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief post-decrement (it--)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl const operator--(int)\n    {\n        auto result = *this;\n        --(*this);\n        return result;\n    }\n\n    /*!\n    @brief pre-decrement (--it)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator--()\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                std::advance(m_it.object_iterator, -1);\n                break;\n            }\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, -1);\n                break;\n            }\n\n            default:\n            {\n                --m_it.primitive_iterator;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief  comparison: equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator==(const iter_impl& other) const\n    {\n        // if objects are not the same, the comparison is undefined\n        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(212, \"cannot compare iterators of different containers\"));\n        }\n\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                return (m_it.object_iterator == other.m_it.object_iterator);\n\n            case value_t::array:\n                return (m_it.array_iterator == other.m_it.array_iterator);\n\n            default:\n                return (m_it.primitive_iterator == other.m_it.primitive_iterator);\n        }\n    }\n\n    /*!\n    @brief  comparison: not equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator!=(const iter_impl& other) const\n    {\n        return !operator==(other);\n    }\n\n    /*!\n    @brief  comparison: smaller\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator<(const iter_impl& other) const\n    {\n        // if objects are not the same, the comparison is undefined\n        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(212, \"cannot compare iterators of different containers\"));\n        }\n\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(213, \"cannot compare order of object iterators\"));\n\n            case value_t::array:\n                return (m_it.array_iterator < other.m_it.array_iterator);\n\n            default:\n                return (m_it.primitive_iterator < other.m_it.primitive_iterator);\n        }\n    }\n\n    /*!\n    @brief  comparison: less than or equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator<=(const iter_impl& other) const\n    {\n        return !other.operator < (*this);\n    }\n\n    /*!\n    @brief  comparison: greater than\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator>(const iter_impl& other) const\n    {\n        return !operator<=(other);\n    }\n\n    /*!\n    @brief  comparison: greater than or equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator>=(const iter_impl& other) const\n    {\n        return !operator<(other);\n    }\n\n    /*!\n    @brief  add to iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator+=(difference_type i)\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(209, \"cannot use offsets with object iterators\"));\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, i);\n                break;\n            }\n\n            default:\n            {\n                m_it.primitive_iterator += i;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief  subtract from iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator-=(difference_type i)\n    {\n        return operator+=(-i);\n    }\n\n    /*!\n    @brief  add to iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator+(difference_type i) const\n    {\n        auto result = *this;\n        result += i;\n        return result;\n    }\n\n    /*!\n    @brief  addition of distance and iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    friend iter_impl operator+(difference_type i, const iter_impl& it)\n    {\n        auto result = it;\n        result += i;\n        return result;\n    }\n\n    /*!\n    @brief  subtract from iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator-(difference_type i) const\n    {\n        auto result = *this;\n        result -= i;\n        return result;\n    }\n\n    /*!\n    @brief  return difference\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    difference_type operator-(const iter_impl& other) const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(209, \"cannot use offsets with object iterators\"));\n\n            case value_t::array:\n                return m_it.array_iterator - other.m_it.array_iterator;\n\n            default:\n                return m_it.primitive_iterator - other.m_it.primitive_iterator;\n        }\n    }\n\n    /*!\n    @brief  access to successor\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference operator[](difference_type n) const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(208, \"cannot use operator[] for object iterators\"));\n\n            case value_t::array:\n                return *std::next(m_it.array_iterator, n);\n\n            case value_t::null:\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n))\n                {\n                    return *m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n            }\n        }\n    }\n\n    /*!\n    @brief  return the key of an object iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    const typename object_t::key_type& key() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        if (JSON_HEDLEY_LIKELY(m_object->is_object()))\n        {\n            return m_it.object_iterator->first;\n        }\n\n        JSON_THROW(invalid_iterator::create(207, \"cannot use key() for non-object iterators\"));\n    }\n\n    /*!\n    @brief  return the value of an iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference value() const\n    {\n        return operator*();\n    }\n\n  private:\n    /// associated JSON instance\n    pointer m_object = nullptr;\n    /// the actual iterator of the associated instance\n    internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {};\n};\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/iteration_proxy.hpp>\n\n// #include <nlohmann/detail/iterators/json_reverse_iterator.hpp>\n\n\n#include <cstddef> // ptrdiff_t\n#include <iterator> // reverse_iterator\n#include <utility> // declval\n\nnamespace nlohmann\n{\nnamespace detail\n{\n//////////////////////\n// reverse_iterator //\n//////////////////////\n\n/*!\n@brief a template for a reverse iterator class\n\n@tparam Base the base iterator type to reverse. Valid types are @ref\niterator (to create @ref reverse_iterator) and @ref const_iterator (to\ncreate @ref const_reverse_iterator).\n\n@requirement The class satisfies the following concept requirements:\n-\n[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):\n  The iterator that can be moved can be moved in both directions (i.e.\n  incremented and decremented).\n- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):\n  It is possible to write to the pointed-to element (only if @a Base is\n  @ref iterator).\n\n@since version 1.0.0\n*/\ntemplate<typename Base>\nclass json_reverse_iterator : public std::reverse_iterator<Base>\n{\n  public:\n    using difference_type = std::ptrdiff_t;\n    /// shortcut to the reverse iterator adapter\n    using base_iterator = std::reverse_iterator<Base>;\n    /// the reference type for the pointed-to element\n    using reference = typename Base::reference;\n\n    /// create reverse iterator from iterator\n    explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept\n        : base_iterator(it) {}\n\n    /// create reverse iterator from base class\n    explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}\n\n    /// post-increment (it++)\n    json_reverse_iterator const operator++(int)\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator++(1));\n    }\n\n    /// pre-increment (++it)\n    json_reverse_iterator& operator++()\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator++());\n    }\n\n    /// post-decrement (it--)\n    json_reverse_iterator const operator--(int)\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator--(1));\n    }\n\n    /// pre-decrement (--it)\n    json_reverse_iterator& operator--()\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator--());\n    }\n\n    /// add to iterator\n    json_reverse_iterator& operator+=(difference_type i)\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));\n    }\n\n    /// add to iterator\n    json_reverse_iterator operator+(difference_type i) const\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator+(i));\n    }\n\n    /// subtract from iterator\n    json_reverse_iterator operator-(difference_type i) const\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator-(i));\n    }\n\n    /// return difference\n    difference_type operator-(const json_reverse_iterator& other) const\n    {\n        return base_iterator(*this) - base_iterator(other);\n    }\n\n    /// access to successor\n    reference operator[](difference_type n) const\n    {\n        return *(this->operator+(n));\n    }\n\n    /// return the key of an object iterator\n    auto key() const -> decltype(std::declval<Base>().key())\n    {\n        auto it = --this->base();\n        return it.key();\n    }\n\n    /// return the value of an iterator\n    reference value() const\n    {\n        auto it = --this->base();\n        return it.operator * ();\n    }\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\n// #include <nlohmann/detail/json_pointer.hpp>\n\n\n#include <algorithm> // all_of\n#include <cctype> // isdigit\n#include <limits> // max\n#include <numeric> // accumulate\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\ntemplate<typename BasicJsonType>\nclass json_pointer\n{\n    // allow basic_json to access private members\n    NLOHMANN_BASIC_JSON_TPL_DECLARATION\n    friend class basic_json;\n\n  public:\n    /*!\n    @brief create JSON pointer\n\n    Create a JSON pointer according to the syntax described in\n    [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).\n\n    @param[in] s  string representing the JSON pointer; if omitted, the empty\n                  string is assumed which references the whole JSON value\n\n    @throw parse_error.107 if the given JSON pointer @a s is nonempty and does\n                           not begin with a slash (`/`); see example below\n\n    @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is\n    not followed by `0` (representing `~`) or `1` (representing `/`); see\n    example below\n\n    @liveexample{The example shows the construction several valid JSON pointers\n    as well as the exceptional behavior.,json_pointer}\n\n    @since version 2.0.0\n    */\n    explicit json_pointer(const std::string& s = \"\")\n        : reference_tokens(split(s))\n    {}\n\n    /*!\n    @brief return a string representation of the JSON pointer\n\n    @invariant For each JSON pointer `ptr`, it holds:\n    @code {.cpp}\n    ptr == json_pointer(ptr.to_string());\n    @endcode\n\n    @return a string representation of the JSON pointer\n\n    @liveexample{The example shows the result of `to_string`.,json_pointer__to_string}\n\n    @since version 2.0.0\n    */\n    std::string to_string() const\n    {\n        return std::accumulate(reference_tokens.begin(), reference_tokens.end(),\n                               std::string{},\n                               [](const std::string & a, const std::string & b)\n        {\n            return a + \"/\" + escape(b);\n        });\n    }\n\n    /// @copydoc to_string()\n    operator std::string() const\n    {\n        return to_string();\n    }\n\n    /*!\n    @brief append another JSON pointer at the end of this JSON pointer\n\n    @param[in] ptr  JSON pointer to append\n    @return JSON pointer with @a ptr appended\n\n    @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}\n\n    @complexity Linear in the length of @a ptr.\n\n    @sa @ref operator/=(std::string) to append a reference token\n    @sa @ref operator/=(std::size_t) to append an array index\n    @sa @ref operator/(const json_pointer&, const json_pointer&) for a binary operator\n\n    @since version 3.6.0\n    */\n    json_pointer& operator/=(const json_pointer& ptr)\n    {\n        reference_tokens.insert(reference_tokens.end(),\n                                ptr.reference_tokens.begin(),\n                                ptr.reference_tokens.end());\n        return *this;\n    }\n\n    /*!\n    @brief append an unescaped reference token at the end of this JSON pointer\n\n    @param[in] token  reference token to append\n    @return JSON pointer with @a token appended without escaping @a token\n\n    @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}\n\n    @complexity Amortized constant.\n\n    @sa @ref operator/=(const json_pointer&) to append a JSON pointer\n    @sa @ref operator/=(std::size_t) to append an array index\n    @sa @ref operator/(const json_pointer&, std::size_t) for a binary operator\n\n    @since version 3.6.0\n    */\n    json_pointer& operator/=(std::string token)\n    {\n        push_back(std::move(token));\n        return *this;\n    }\n\n    /*!\n    @brief append an array index at the end of this JSON pointer\n\n    @param[in] array_idx  array index to append\n    @return JSON pointer with @a array_idx appended\n\n    @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add}\n\n    @complexity Amortized constant.\n\n    @sa @ref operator/=(const json_pointer&) to append a JSON pointer\n    @sa @ref operator/=(std::string) to append a reference token\n    @sa @ref operator/(const json_pointer&, std::string) for a binary operator\n\n    @since version 3.6.0\n    */\n    json_pointer& operator/=(std::size_t array_idx)\n    {\n        return *this /= std::to_string(array_idx);\n    }\n\n    /*!\n    @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer\n\n    @param[in] lhs  JSON pointer\n    @param[in] rhs  JSON pointer\n    @return a new JSON pointer with @a rhs appended to @a lhs\n\n    @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}\n\n    @complexity Linear in the length of @a lhs and @a rhs.\n\n    @sa @ref operator/=(const json_pointer&) to append a JSON pointer\n\n    @since version 3.6.0\n    */\n    friend json_pointer operator/(const json_pointer& lhs,\n                                  const json_pointer& rhs)\n    {\n        return json_pointer(lhs) /= rhs;\n    }\n\n    /*!\n    @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer\n\n    @param[in] ptr  JSON pointer\n    @param[in] token  reference token\n    @return a new JSON pointer with unescaped @a token appended to @a ptr\n\n    @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}\n\n    @complexity Linear in the length of @a ptr.\n\n    @sa @ref operator/=(std::string) to append a reference token\n\n    @since version 3.6.0\n    */\n    friend json_pointer operator/(const json_pointer& ptr, std::string token)\n    {\n        return json_pointer(ptr) /= std::move(token);\n    }\n\n    /*!\n    @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer\n\n    @param[in] ptr  JSON pointer\n    @param[in] array_idx  array index\n    @return a new JSON pointer with @a array_idx appended to @a ptr\n\n    @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary}\n\n    @complexity Linear in the length of @a ptr.\n\n    @sa @ref operator/=(std::size_t) to append an array index\n\n    @since version 3.6.0\n    */\n    friend json_pointer operator/(const json_pointer& ptr, std::size_t array_idx)\n    {\n        return json_pointer(ptr) /= array_idx;\n    }\n\n    /*!\n    @brief returns the parent of this JSON pointer\n\n    @return parent of this JSON pointer; in case this JSON pointer is the root,\n            the root itself is returned\n\n    @complexity Linear in the length of the JSON pointer.\n\n    @liveexample{The example shows the result of `parent_pointer` for different\n    JSON Pointers.,json_pointer__parent_pointer}\n\n    @since version 3.6.0\n    */\n    json_pointer parent_pointer() const\n    {\n        if (empty())\n        {\n            return *this;\n        }\n\n        json_pointer res = *this;\n        res.pop_back();\n        return res;\n    }\n\n    /*!\n    @brief remove last reference token\n\n    @pre not `empty()`\n\n    @liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back}\n\n    @complexity Constant.\n\n    @throw out_of_range.405 if JSON pointer has no parent\n\n    @since version 3.6.0\n    */\n    void pop_back()\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\"));\n        }\n\n        reference_tokens.pop_back();\n    }\n\n    /*!\n    @brief return last reference token\n\n    @pre not `empty()`\n    @return last reference token\n\n    @liveexample{The example shows the usage of `back`.,json_pointer__back}\n\n    @complexity Constant.\n\n    @throw out_of_range.405 if JSON pointer has no parent\n\n    @since version 3.6.0\n    */\n    const std::string& back() const\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\"));\n        }\n\n        return reference_tokens.back();\n    }\n\n    /*!\n    @brief append an unescaped token at the end of the reference pointer\n\n    @param[in] token  token to add\n\n    @complexity Amortized constant.\n\n    @liveexample{The example shows the result of `push_back` for different\n    JSON Pointers.,json_pointer__push_back}\n\n    @since version 3.6.0\n    */\n    void push_back(const std::string& token)\n    {\n        reference_tokens.push_back(token);\n    }\n\n    /// @copydoc push_back(const std::string&)\n    void push_back(std::string&& token)\n    {\n        reference_tokens.push_back(std::move(token));\n    }\n\n    /*!\n    @brief return whether pointer points to the root document\n\n    @return true iff the JSON pointer points to the root document\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example shows the result of `empty` for different JSON\n    Pointers.,json_pointer__empty}\n\n    @since version 3.6.0\n    */\n    bool empty() const noexcept\n    {\n        return reference_tokens.empty();\n    }\n\n  private:\n    /*!\n    @param[in] s  reference token to be converted into an array index\n\n    @return integer representation of @a s\n\n    @throw parse_error.106  if an array index begins with '0'\n    @throw parse_error.109  if an array index begins not with a digit\n    @throw out_of_range.404 if string @a s could not be converted to an integer\n    @throw out_of_range.410 if an array index exceeds size_type\n    */\n    static typename BasicJsonType::size_type array_index(const std::string& s)\n    {\n        using size_type = typename BasicJsonType::size_type;\n\n        // error condition (cf. RFC 6901, Sect. 4)\n        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))\n        {\n            JSON_THROW(detail::parse_error::create(106, 0,\n                                                   \"array index '\" + s +\n                                                   \"' must not begin with '0'\"));\n        }\n\n        // error condition (cf. RFC 6901, Sect. 4)\n        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))\n        {\n            JSON_THROW(detail::parse_error::create(109, 0, \"array index '\" + s + \"' is not a number\"));\n        }\n\n        std::size_t processed_chars = 0;\n        unsigned long long res = 0;\n        JSON_TRY\n        {\n            res = std::stoull(s, &processed_chars);\n        }\n        JSON_CATCH(std::out_of_range&)\n        {\n            JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + s + \"'\"));\n        }\n\n        // check if the string was completely read\n        if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size()))\n        {\n            JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + s + \"'\"));\n        }\n\n        // only triggered on special platforms (like 32bit), see also\n        // https://github.com/nlohmann/json/pull/2203\n        if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)()))\n        {\n            JSON_THROW(detail::out_of_range::create(410, \"array index \" + s + \" exceeds size_type\")); // LCOV_EXCL_LINE\n        }\n\n        return static_cast<size_type>(res);\n    }\n\n    json_pointer top() const\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\"));\n        }\n\n        json_pointer result = *this;\n        result.reference_tokens = {reference_tokens[0]};\n        return result;\n    }\n\n    /*!\n    @brief create and return a reference to the pointed to value\n\n    @complexity Linear in the number of reference tokens.\n\n    @throw parse_error.109 if array index is not a number\n    @throw type_error.313 if value cannot be unflattened\n    */\n    BasicJsonType& get_and_create(BasicJsonType& j) const\n    {\n        auto result = &j;\n\n        // in case no reference tokens exist, return a reference to the JSON value\n        // j which will be overwritten by a primitive value\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (result->type())\n            {\n                case detail::value_t::null:\n                {\n                    if (reference_token == \"0\")\n                    {\n                        // start a new array if reference token is 0\n                        result = &result->operator[](0);\n                    }\n                    else\n                    {\n                        // start a new object otherwise\n                        result = &result->operator[](reference_token);\n                    }\n                    break;\n                }\n\n                case detail::value_t::object:\n                {\n                    // create an entry in the object\n                    result = &result->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    // create an entry in the array\n                    result = &result->operator[](array_index(reference_token));\n                    break;\n                }\n\n                /*\n                The following code is only reached if there exists a reference\n                token _and_ the current value is primitive. In this case, we have\n                an error situation, because primitive values may only occur as\n                single value; that is, with an empty list of reference tokens.\n                */\n                default:\n                    JSON_THROW(detail::type_error::create(313, \"invalid value to unflatten\"));\n            }\n        }\n\n        return *result;\n    }\n\n    /*!\n    @brief return a reference to the pointed to value\n\n    @note This version does not throw if a value is not present, but tries to\n          create nested values instead. For instance, calling this function\n          with pointer `\"/this/that\"` on a null value is equivalent to calling\n          `operator[](\"this\").operator[](\"that\")` on that value, effectively\n          changing the null value to an object.\n\n    @param[in] ptr  a JSON value\n\n    @return reference to the JSON value pointed to by the JSON pointer\n\n    @complexity Linear in the length of the JSON pointer.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    BasicJsonType& get_unchecked(BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            // convert null values to arrays or objects before continuing\n            if (ptr->is_null())\n            {\n                // check if reference token is a number\n                const bool nums =\n                    std::all_of(reference_token.begin(), reference_token.end(),\n                                [](const unsigned char x)\n                {\n                    return std::isdigit(x);\n                });\n\n                // change value to array for numbers or \"-\" or to object otherwise\n                *ptr = (nums || reference_token == \"-\")\n                       ? detail::value_t::array\n                       : detail::value_t::object;\n            }\n\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // use unchecked object access\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (reference_token == \"-\")\n                    {\n                        // explicitly treat \"-\" as index beyond the end\n                        ptr = &ptr->operator[](ptr->m_value.array->size());\n                    }\n                    else\n                    {\n                        // convert array index to number; unchecked access\n                        ptr = &ptr->operator[](array_index(reference_token));\n                    }\n                    break;\n                }\n\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\"));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    BasicJsonType& get_checked(BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // note: at performs range check\n                    ptr = &ptr->at(reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        JSON_THROW(detail::out_of_range::create(402,\n                                                                \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) +\n                                                                \") is out of range\"));\n                    }\n\n                    // note: at performs range check\n                    ptr = &ptr->at(array_index(reference_token));\n                    break;\n                }\n\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\"));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @brief return a const reference to the pointed to value\n\n    @param[in] ptr  a JSON value\n\n    @return const reference to the JSON value pointed to by the JSON\n    pointer\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // use unchecked object access\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" cannot be used for const access\n                        JSON_THROW(detail::out_of_range::create(402,\n                                                                \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) +\n                                                                \") is out of range\"));\n                    }\n\n                    // use unchecked array access\n                    ptr = &ptr->operator[](array_index(reference_token));\n                    break;\n                }\n\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\"));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    const BasicJsonType& get_checked(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // note: at performs range check\n                    ptr = &ptr->at(reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        JSON_THROW(detail::out_of_range::create(402,\n                                                                \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) +\n                                                                \") is out of range\"));\n                    }\n\n                    // note: at performs range check\n                    ptr = &ptr->at(array_index(reference_token));\n                    break;\n                }\n\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\"));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    */\n    bool contains(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    if (!ptr->contains(reference_token))\n                    {\n                        // we did not find the key in the object\n                        return false;\n                    }\n\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !(\"0\" <= reference_token && reference_token <= \"9\")))\n                    {\n                        // invalid char\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1))\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9')))\n                        {\n                            // first char should be between '1' and '9'\n                            return false;\n                        }\n                        for (std::size_t i = 1; i < reference_token.size(); i++)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9')))\n                            {\n                                // other char should be between '0' and '9'\n                                return false;\n                            }\n                        }\n                    }\n\n                    const auto idx = array_index(reference_token);\n                    if (idx >= ptr->size())\n                    {\n                        // index out of range\n                        return false;\n                    }\n\n                    ptr = &ptr->operator[](idx);\n                    break;\n                }\n\n                default:\n                {\n                    // we do not expect primitive values if there is still a\n                    // reference token to process\n                    return false;\n                }\n            }\n        }\n\n        // no reference token left means we found a primitive value\n        return true;\n    }\n\n    /*!\n    @brief split the string input to reference tokens\n\n    @note This function is only called by the json_pointer constructor.\n          All exceptions below are documented there.\n\n    @throw parse_error.107  if the pointer is not empty or begins with '/'\n    @throw parse_error.108  if character '~' is not followed by '0' or '1'\n    */\n    static std::vector<std::string> split(const std::string& reference_string)\n    {\n        std::vector<std::string> result;\n\n        // special case: empty reference string -> no reference tokens\n        if (reference_string.empty())\n        {\n            return result;\n        }\n\n        // check if nonempty reference string begins with slash\n        if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))\n        {\n            JSON_THROW(detail::parse_error::create(107, 1,\n                                                   \"JSON pointer must be empty or begin with '/' - was: '\" +\n                                                   reference_string + \"'\"));\n        }\n\n        // extract the reference tokens:\n        // - slash: position of the last read slash (or end of string)\n        // - start: position after the previous slash\n        for (\n            // search for the first slash after the first character\n            std::size_t slash = reference_string.find_first_of('/', 1),\n            // set the beginning of the first reference token\n            start = 1;\n            // we can stop if start == 0 (if slash == std::string::npos)\n            start != 0;\n            // set the beginning of the next reference token\n            // (will eventually be 0 if slash == std::string::npos)\n            start = (slash == std::string::npos) ? 0 : slash + 1,\n            // find next slash\n            slash = reference_string.find_first_of('/', start))\n        {\n            // use the text between the beginning of the reference token\n            // (start) and the last slash (slash).\n            auto reference_token = reference_string.substr(start, slash - start);\n\n            // check reference tokens are properly escaped\n            for (std::size_t pos = reference_token.find_first_of('~');\n                    pos != std::string::npos;\n                    pos = reference_token.find_first_of('~', pos + 1))\n            {\n                JSON_ASSERT(reference_token[pos] == '~');\n\n                // ~ must be followed by 0 or 1\n                if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||\n                                         (reference_token[pos + 1] != '0' &&\n                                          reference_token[pos + 1] != '1')))\n                {\n                    JSON_THROW(detail::parse_error::create(108, 0, \"escape character '~' must be followed with '0' or '1'\"));\n                }\n            }\n\n            // finally, store the reference token\n            unescape(reference_token);\n            result.push_back(reference_token);\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief replace all occurrences of a substring by another string\n\n    @param[in,out] s  the string to manipulate; changed so that all\n                   occurrences of @a f are replaced with @a t\n    @param[in]     f  the substring to replace with @a t\n    @param[in]     t  the string to replace @a f\n\n    @pre The search string @a f must not be empty. **This precondition is\n    enforced with an assertion.**\n\n    @since version 2.0.0\n    */\n    static void replace_substring(std::string& s, const std::string& f,\n                                  const std::string& t)\n    {\n        JSON_ASSERT(!f.empty());\n        for (auto pos = s.find(f);                // find first occurrence of f\n                pos != std::string::npos;         // make sure f was found\n                s.replace(pos, f.size(), t),      // replace with t, and\n                pos = s.find(f, pos + t.size()))  // find next occurrence of f\n        {}\n    }\n\n    /// escape \"~\" to \"~0\" and \"/\" to \"~1\"\n    static std::string escape(std::string s)\n    {\n        replace_substring(s, \"~\", \"~0\");\n        replace_substring(s, \"/\", \"~1\");\n        return s;\n    }\n\n    /// unescape \"~1\" to tilde and \"~0\" to slash (order is important!)\n    static void unescape(std::string& s)\n    {\n        replace_substring(s, \"~1\", \"/\");\n        replace_substring(s, \"~0\", \"~\");\n    }\n\n    /*!\n    @param[in] reference_string  the reference string to the current value\n    @param[in] value             the value to consider\n    @param[in,out] result        the result object to insert values to\n\n    @note Empty objects or arrays are flattened to `null`.\n    */\n    static void flatten(const std::string& reference_string,\n                        const BasicJsonType& value,\n                        BasicJsonType& result)\n    {\n        switch (value.type())\n        {\n            case detail::value_t::array:\n            {\n                if (value.m_value.array->empty())\n                {\n                    // flatten empty array as null\n                    result[reference_string] = nullptr;\n                }\n                else\n                {\n                    // iterate array and use index as reference string\n                    for (std::size_t i = 0; i < value.m_value.array->size(); ++i)\n                    {\n                        flatten(reference_string + \"/\" + std::to_string(i),\n                                value.m_value.array->operator[](i), result);\n                    }\n                }\n                break;\n            }\n\n            case detail::value_t::object:\n            {\n                if (value.m_value.object->empty())\n                {\n                    // flatten empty object as null\n                    result[reference_string] = nullptr;\n                }\n                else\n                {\n                    // iterate object and use keys as reference string\n                    for (const auto& element : *value.m_value.object)\n                    {\n                        flatten(reference_string + \"/\" + escape(element.first), element.second, result);\n                    }\n                }\n                break;\n            }\n\n            default:\n            {\n                // add primitive value with its reference string\n                result[reference_string] = value;\n                break;\n            }\n        }\n    }\n\n    /*!\n    @param[in] value  flattened JSON\n\n    @return unflattened JSON\n\n    @throw parse_error.109 if array index is not a number\n    @throw type_error.314  if value is not an object\n    @throw type_error.315  if object values are not primitive\n    @throw type_error.313  if value cannot be unflattened\n    */\n    static BasicJsonType\n    unflatten(const BasicJsonType& value)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!value.is_object()))\n        {\n            JSON_THROW(detail::type_error::create(314, \"only objects can be unflattened\"));\n        }\n\n        BasicJsonType result;\n\n        // iterate the JSON object values\n        for (const auto& element : *value.m_value.object)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))\n            {\n                JSON_THROW(detail::type_error::create(315, \"values in object must be primitive\"));\n            }\n\n            // assign value to reference pointed to by JSON pointer; Note that if\n            // the JSON pointer is \"\" (i.e., points to the whole value), function\n            // get_and_create returns a reference to result itself. An assignment\n            // will then create a primitive value.\n            json_pointer(element.first).get_and_create(result) = element.second;\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief compares two JSON pointers for equality\n\n    @param[in] lhs  JSON pointer to compare\n    @param[in] rhs  JSON pointer to compare\n    @return whether @a lhs is equal to @a rhs\n\n    @complexity Linear in the length of the JSON pointer\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n    */\n    friend bool operator==(json_pointer const& lhs,\n                           json_pointer const& rhs) noexcept\n    {\n        return lhs.reference_tokens == rhs.reference_tokens;\n    }\n\n    /*!\n    @brief compares two JSON pointers for inequality\n\n    @param[in] lhs  JSON pointer to compare\n    @param[in] rhs  JSON pointer to compare\n    @return whether @a lhs is not equal @a rhs\n\n    @complexity Linear in the length of the JSON pointer\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n    */\n    friend bool operator!=(json_pointer const& lhs,\n                           json_pointer const& rhs) noexcept\n    {\n        return !(lhs == rhs);\n    }\n\n    /// the reference tokens\n    std::vector<std::string> reference_tokens;\n};\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/json_ref.hpp>\n\n\n#include <initializer_list>\n#include <utility>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename BasicJsonType>\nclass json_ref\n{\n  public:\n    using value_type = BasicJsonType;\n\n    json_ref(value_type&& value)\n        : owned_value(std::move(value))\n        , value_ref(&owned_value)\n        , is_rvalue(true)\n    {}\n\n    json_ref(const value_type& value)\n        : value_ref(const_cast<value_type*>(&value))\n        , is_rvalue(false)\n    {}\n\n    json_ref(std::initializer_list<json_ref> init)\n        : owned_value(init)\n        , value_ref(&owned_value)\n        , is_rvalue(true)\n    {}\n\n    template <\n        class... Args,\n        enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 >\n    json_ref(Args && ... args)\n        : owned_value(std::forward<Args>(args)...)\n        , value_ref(&owned_value)\n        , is_rvalue(true)\n    {}\n\n    // class should be movable only\n    json_ref(json_ref&&) = default;\n    json_ref(const json_ref&) = delete;\n    json_ref& operator=(const json_ref&) = delete;\n    json_ref& operator=(json_ref&&) = delete;\n    ~json_ref() = default;\n\n    value_type moved_or_copied() const\n    {\n        if (is_rvalue)\n        {\n            return std::move(*value_ref);\n        }\n        return *value_ref;\n    }\n\n    value_type const& operator*() const\n    {\n        return *static_cast<value_type const*>(value_ref);\n    }\n\n    value_type const* operator->() const\n    {\n        return static_cast<value_type const*>(value_ref);\n    }\n\n  private:\n    mutable value_type owned_value = nullptr;\n    value_type* value_ref = nullptr;\n    const bool is_rvalue = true;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/output/binary_writer.hpp>\n\n\n#include <algorithm> // reverse\n#include <array> // array\n#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t\n#include <cstring> // memcpy\n#include <limits> // numeric_limits\n#include <string> // string\n#include <cmath> // isnan, isinf\n\n// #include <nlohmann/detail/input/binary_reader.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n\n\n#include <algorithm> // copy\n#include <cstddef> // size_t\n#include <ios> // streamsize\n#include <iterator> // back_inserter\n#include <memory> // shared_ptr, make_shared\n#include <ostream> // basic_ostream\n#include <string> // basic_string\n#include <vector> // vector\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// abstract output adapter interface\ntemplate<typename CharType> struct output_adapter_protocol\n{\n    virtual void write_character(CharType c) = 0;\n    virtual void write_characters(const CharType* s, std::size_t length) = 0;\n    virtual ~output_adapter_protocol() = default;\n};\n\n/// a type to simplify interfaces\ntemplate<typename CharType>\nusing output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;\n\n/// output adapter for byte vectors\ntemplate<typename CharType>\nclass output_vector_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_vector_adapter(std::vector<CharType>& vec) noexcept\n        : v(vec)\n    {}\n\n    void write_character(CharType c) override\n    {\n        v.push_back(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        std::copy(s, s + length, std::back_inserter(v));\n    }\n\n  private:\n    std::vector<CharType>& v;\n};\n\n/// output adapter for output streams\ntemplate<typename CharType>\nclass output_stream_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept\n        : stream(s)\n    {}\n\n    void write_character(CharType c) override\n    {\n        stream.put(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        stream.write(s, static_cast<std::streamsize>(length));\n    }\n\n  private:\n    std::basic_ostream<CharType>& stream;\n};\n\n/// output adapter for basic_string\ntemplate<typename CharType, typename StringType = std::basic_string<CharType>>\nclass output_string_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_string_adapter(StringType& s) noexcept\n        : str(s)\n    {}\n\n    void write_character(CharType c) override\n    {\n        str.push_back(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        str.append(s, length);\n    }\n\n  private:\n    StringType& str;\n};\n\ntemplate<typename CharType, typename StringType = std::basic_string<CharType>>\nclass output_adapter\n{\n  public:\n    output_adapter(std::vector<CharType>& vec)\n        : oa(std::make_shared<output_vector_adapter<CharType>>(vec)) {}\n\n    output_adapter(std::basic_ostream<CharType>& s)\n        : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}\n\n    output_adapter(StringType& s)\n        : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}\n\n    operator output_adapter_t<CharType>()\n    {\n        return oa;\n    }\n\n  private:\n    output_adapter_t<CharType> oa = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////\n// binary writer //\n///////////////////\n\n/*!\n@brief serialization to CBOR and MessagePack values\n*/\ntemplate<typename BasicJsonType, typename CharType>\nclass binary_writer\n{\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n\n  public:\n    /*!\n    @brief create a binary writer\n\n    @param[in] adapter  output adapter to write to\n    */\n    explicit binary_writer(output_adapter_t<CharType> adapter) : oa(adapter)\n    {\n        JSON_ASSERT(oa);\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @pre       j.type() == value_t::object\n    */\n    void write_bson(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::object:\n            {\n                write_bson_object(*j.m_value.object);\n                break;\n            }\n\n            default:\n            {\n                JSON_THROW(type_error::create(317, \"to serialize to BSON, top-level type must be object, but is \" + std::string(j.type_name())));\n            }\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    */\n    void write_cbor(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n            {\n                oa->write_character(to_char_type(0xF6));\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                oa->write_character(j.m_value.boolean\n                                    ? to_char_type(0xF5)\n                                    : to_char_type(0xF4));\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                if (j.m_value.number_integer >= 0)\n                {\n                    // CBOR does not differentiate between positive signed\n                    // integers and unsigned integers. Therefore, we used the\n                    // code from the value_t::number_unsigned case here.\n                    if (j.m_value.number_integer <= 0x17)\n                    {\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x18));\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x19));\n                        write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x1A));\n                        write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                    }\n                    else\n                    {\n                        oa->write_character(to_char_type(0x1B));\n                        write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                    }\n                }\n                else\n                {\n                    // The conversions below encode the sign in the first\n                    // byte, and the value is converted to a positive number.\n                    const auto positive_number = -1 - j.m_value.number_integer;\n                    if (j.m_value.number_integer >= -24)\n                    {\n                        write_number(static_cast<std::uint8_t>(0x20 + positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x38));\n                        write_number(static_cast<std::uint8_t>(positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x39));\n                        write_number(static_cast<std::uint16_t>(positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x3A));\n                        write_number(static_cast<std::uint32_t>(positive_number));\n                    }\n                    else\n                    {\n                        oa->write_character(to_char_type(0x3B));\n                        write_number(static_cast<std::uint64_t>(positive_number));\n                    }\n                }\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x18));\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x19));\n                    write_number(static_cast<std::uint16_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x1A));\n                    write_number(static_cast<std::uint32_t>(j.m_value.number_unsigned));\n                }\n                else\n                {\n                    oa->write_character(to_char_type(0x1B));\n                    write_number(static_cast<std::uint64_t>(j.m_value.number_unsigned));\n                }\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                if (std::isnan(j.m_value.number_float))\n                {\n                    // NaN is 0xf97e00 in CBOR\n                    oa->write_character(to_char_type(0xF9));\n                    oa->write_character(to_char_type(0x7E));\n                    oa->write_character(to_char_type(0x00));\n                }\n                else if (std::isinf(j.m_value.number_float))\n                {\n                    // Infinity is 0xf97c00, -Infinity is 0xf9fc00\n                    oa->write_character(to_char_type(0xf9));\n                    oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC));\n                    oa->write_character(to_char_type(0x00));\n                }\n                else\n                {\n                    write_compact_float(j.m_value.number_float, detail::input_format_t::cbor);\n                }\n                break;\n            }\n\n            case value_t::string:\n            {\n                // step 1: write control byte and the string length\n                const auto N = j.m_value.string->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0x60 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x78));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x79));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x7A));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x7B));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write the string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                // step 1: write control byte and the array size\n                const auto N = j.m_value.array->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0x80 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x98));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x99));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x9A));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x9B));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_cbor(el);\n                }\n                break;\n            }\n\n            case value_t::binary:\n            {\n                if (j.m_value.binary->has_subtype())\n                {\n                    write_number(static_cast<std::uint8_t>(0xd8));\n                    write_number(j.m_value.binary->subtype());\n                }\n\n                // step 1: write control byte and the binary array size\n                const auto N = j.m_value.binary->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0x40 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x58));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x59));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x5A));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x5B));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.binary->data()),\n                    N);\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                // step 1: write control byte and the object size\n                const auto N = j.m_value.object->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0xA0 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xB8));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xB9));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xBA));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xBB));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_cbor(el.first);\n                    write_cbor(el.second);\n                }\n                break;\n            }\n\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    */\n    void write_msgpack(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::null: // nil\n            {\n                oa->write_character(to_char_type(0xC0));\n                break;\n            }\n\n            case value_t::boolean: // true and false\n            {\n                oa->write_character(j.m_value.boolean\n                                    ? to_char_type(0xC3)\n                                    : to_char_type(0xC2));\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                if (j.m_value.number_integer >= 0)\n                {\n                    // MessagePack does not differentiate between positive\n                    // signed integers and unsigned integers. Therefore, we used\n                    // the code from the value_t::number_unsigned case here.\n                    if (j.m_value.number_unsigned < 128)\n                    {\n                        // positive fixnum\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        // uint 8\n                        oa->write_character(to_char_type(0xCC));\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        // uint 16\n                        oa->write_character(to_char_type(0xCD));\n                        write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        // uint 32\n                        oa->write_character(to_char_type(0xCE));\n                        write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())\n                    {\n                        // uint 64\n                        oa->write_character(to_char_type(0xCF));\n                        write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                    }\n                }\n                else\n                {\n                    if (j.m_value.number_integer >= -32)\n                    {\n                        // negative fixnum\n                        write_number(static_cast<std::int8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())\n                    {\n                        // int 8\n                        oa->write_character(to_char_type(0xD0));\n                        write_number(static_cast<std::int8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())\n                    {\n                        // int 16\n                        oa->write_character(to_char_type(0xD1));\n                        write_number(static_cast<std::int16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())\n                    {\n                        // int 32\n                        oa->write_character(to_char_type(0xD2));\n                        write_number(static_cast<std::int32_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())\n                    {\n                        // int 64\n                        oa->write_character(to_char_type(0xD3));\n                        write_number(static_cast<std::int64_t>(j.m_value.number_integer));\n                    }\n                }\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned < 128)\n                {\n                    // positive fixnum\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    // uint 8\n                    oa->write_character(to_char_type(0xCC));\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // uint 16\n                    oa->write_character(to_char_type(0xCD));\n                    write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // uint 32\n                    oa->write_character(to_char_type(0xCE));\n                    write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    // uint 64\n                    oa->write_character(to_char_type(0xCF));\n                    write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                }\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack);\n                break;\n            }\n\n            case value_t::string:\n            {\n                // step 1: write control byte and the string length\n                const auto N = j.m_value.string->size();\n                if (N <= 31)\n                {\n                    // fixstr\n                    write_number(static_cast<std::uint8_t>(0xA0 | N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    // str 8\n                    oa->write_character(to_char_type(0xD9));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // str 16\n                    oa->write_character(to_char_type(0xDA));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // str 32\n                    oa->write_character(to_char_type(0xDB));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write the string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                // step 1: write control byte and the array size\n                const auto N = j.m_value.array->size();\n                if (N <= 15)\n                {\n                    // fixarray\n                    write_number(static_cast<std::uint8_t>(0x90 | N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // array 16\n                    oa->write_character(to_char_type(0xDC));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // array 32\n                    oa->write_character(to_char_type(0xDD));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_msgpack(el);\n                }\n                break;\n            }\n\n            case value_t::binary:\n            {\n                // step 0: determine if the binary type has a set subtype to\n                // determine whether or not to use the ext or fixext types\n                const bool use_ext = j.m_value.binary->has_subtype();\n\n                // step 1: write control byte and the byte string length\n                const auto N = j.m_value.binary->size();\n                if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    std::uint8_t output_type{};\n                    bool fixed = true;\n                    if (use_ext)\n                    {\n                        switch (N)\n                        {\n                            case 1:\n                                output_type = 0xD4; // fixext 1\n                                break;\n                            case 2:\n                                output_type = 0xD5; // fixext 2\n                                break;\n                            case 4:\n                                output_type = 0xD6; // fixext 4\n                                break;\n                            case 8:\n                                output_type = 0xD7; // fixext 8\n                                break;\n                            case 16:\n                                output_type = 0xD8; // fixext 16\n                                break;\n                            default:\n                                output_type = 0xC7; // ext 8\n                                fixed = false;\n                                break;\n                        }\n\n                    }\n                    else\n                    {\n                        output_type = 0xC4; // bin 8\n                        fixed = false;\n                    }\n\n                    oa->write_character(to_char_type(output_type));\n                    if (!fixed)\n                    {\n                        write_number(static_cast<std::uint8_t>(N));\n                    }\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    std::uint8_t output_type = use_ext\n                                               ? 0xC8 // ext 16\n                                               : 0xC5; // bin 16\n\n                    oa->write_character(to_char_type(output_type));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    std::uint8_t output_type = use_ext\n                                               ? 0xC9 // ext 32\n                                               : 0xC6; // bin 32\n\n                    oa->write_character(to_char_type(output_type));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 1.5: if this is an ext type, write the subtype\n                if (use_ext)\n                {\n                    write_number(static_cast<std::int8_t>(j.m_value.binary->subtype()));\n                }\n\n                // step 2: write the byte string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.binary->data()),\n                    N);\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                // step 1: write control byte and the object size\n                const auto N = j.m_value.object->size();\n                if (N <= 15)\n                {\n                    // fixmap\n                    write_number(static_cast<std::uint8_t>(0x80 | (N & 0xF)));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // map 16\n                    oa->write_character(to_char_type(0xDE));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // map 32\n                    oa->write_character(to_char_type(0xDF));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_msgpack(el.first);\n                    write_msgpack(el.second);\n                }\n                break;\n            }\n\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @param[in] use_count   whether to use '#' prefixes (optimized format)\n    @param[in] use_type    whether to use '$' prefixes (optimized format)\n    @param[in] add_prefix  whether prefixes need to be used for this value\n    */\n    void write_ubjson(const BasicJsonType& j, const bool use_count,\n                      const bool use_type, const bool add_prefix = true)\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('Z'));\n                }\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(j.m_value.boolean\n                                        ? to_char_type('T')\n                                        : to_char_type('F'));\n                }\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix);\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix);\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix);\n                break;\n            }\n\n            case value_t::string:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('S'));\n                }\n                write_number_with_ubjson_prefix(j.m_value.string->size(), true);\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('['));\n                }\n\n                bool prefix_required = true;\n                if (use_type && !j.m_value.array->empty())\n                {\n                    JSON_ASSERT(use_count);\n                    const CharType first_prefix = ubjson_prefix(j.front());\n                    const bool same_prefix = std::all_of(j.begin() + 1, j.end(),\n                                                         [this, first_prefix](const BasicJsonType & v)\n                    {\n                        return ubjson_prefix(v) == first_prefix;\n                    });\n\n                    if (same_prefix)\n                    {\n                        prefix_required = false;\n                        oa->write_character(to_char_type('$'));\n                        oa->write_character(first_prefix);\n                    }\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.array->size(), true);\n                }\n\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_ubjson(el, use_count, use_type, prefix_required);\n                }\n\n                if (!use_count)\n                {\n                    oa->write_character(to_char_type(']'));\n                }\n\n                break;\n            }\n\n            case value_t::binary:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('['));\n                }\n\n                if (use_type && !j.m_value.binary->empty())\n                {\n                    JSON_ASSERT(use_count);\n                    oa->write_character(to_char_type('$'));\n                    oa->write_character('U');\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.binary->size(), true);\n                }\n\n                if (use_type)\n                {\n                    oa->write_characters(\n                        reinterpret_cast<const CharType*>(j.m_value.binary->data()),\n                        j.m_value.binary->size());\n                }\n                else\n                {\n                    for (size_t i = 0; i < j.m_value.binary->size(); ++i)\n                    {\n                        oa->write_character(to_char_type('U'));\n                        oa->write_character(j.m_value.binary->data()[i]);\n                    }\n                }\n\n                if (!use_count)\n                {\n                    oa->write_character(to_char_type(']'));\n                }\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('{'));\n                }\n\n                bool prefix_required = true;\n                if (use_type && !j.m_value.object->empty())\n                {\n                    JSON_ASSERT(use_count);\n                    const CharType first_prefix = ubjson_prefix(j.front());\n                    const bool same_prefix = std::all_of(j.begin(), j.end(),\n                                                         [this, first_prefix](const BasicJsonType & v)\n                    {\n                        return ubjson_prefix(v) == first_prefix;\n                    });\n\n                    if (same_prefix)\n                    {\n                        prefix_required = false;\n                        oa->write_character(to_char_type('$'));\n                        oa->write_character(first_prefix);\n                    }\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.object->size(), true);\n                }\n\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_number_with_ubjson_prefix(el.first.size(), true);\n                    oa->write_characters(\n                        reinterpret_cast<const CharType*>(el.first.c_str()),\n                        el.first.size());\n                    write_ubjson(el.second, use_count, use_type, prefix_required);\n                }\n\n                if (!use_count)\n                {\n                    oa->write_character(to_char_type('}'));\n                }\n\n                break;\n            }\n\n            default:\n                break;\n        }\n    }\n\n  private:\n    //////////\n    // BSON //\n    //////////\n\n    /*!\n    @return The size of a BSON document entry header, including the id marker\n            and the entry name size (and its null-terminator).\n    */\n    static std::size_t calc_bson_entry_header_size(const string_t& name)\n    {\n        const auto it = name.find(static_cast<typename string_t::value_type>(0));\n        if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))\n        {\n            JSON_THROW(out_of_range::create(409,\n                                            \"BSON key cannot contain code point U+0000 (at byte \" + std::to_string(it) + \")\"));\n        }\n\n        return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;\n    }\n\n    /*!\n    @brief Writes the given @a element_type and @a name to the output adapter\n    */\n    void write_bson_entry_header(const string_t& name,\n                                 const std::uint8_t element_type)\n    {\n        oa->write_character(to_char_type(element_type)); // boolean\n        oa->write_characters(\n            reinterpret_cast<const CharType*>(name.c_str()),\n            name.size() + 1u);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and boolean value @a value\n    */\n    void write_bson_boolean(const string_t& name,\n                            const bool value)\n    {\n        write_bson_entry_header(name, 0x08);\n        oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00));\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and double value @a value\n    */\n    void write_bson_double(const string_t& name,\n                           const double value)\n    {\n        write_bson_entry_header(name, 0x01);\n        write_number<double, true>(value);\n    }\n\n    /*!\n    @return The size of the BSON-encoded string in @a value\n    */\n    static std::size_t calc_bson_string_size(const string_t& value)\n    {\n        return sizeof(std::int32_t) + value.size() + 1ul;\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and string value @a value\n    */\n    void write_bson_string(const string_t& name,\n                           const string_t& value)\n    {\n        write_bson_entry_header(name, 0x02);\n\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size() + 1ul));\n        oa->write_characters(\n            reinterpret_cast<const CharType*>(value.c_str()),\n            value.size() + 1);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and null value\n    */\n    void write_bson_null(const string_t& name)\n    {\n        write_bson_entry_header(name, 0x0A);\n    }\n\n    /*!\n    @return The size of the BSON-encoded integer @a value\n    */\n    static std::size_t calc_bson_integer_size(const std::int64_t value)\n    {\n        return (std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)()\n               ? sizeof(std::int32_t)\n               : sizeof(std::int64_t);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and integer @a value\n    */\n    void write_bson_integer(const string_t& name,\n                            const std::int64_t value)\n    {\n        if ((std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)())\n        {\n            write_bson_entry_header(name, 0x10); // int32\n            write_number<std::int32_t, true>(static_cast<std::int32_t>(value));\n        }\n        else\n        {\n            write_bson_entry_header(name, 0x12); // int64\n            write_number<std::int64_t, true>(static_cast<std::int64_t>(value));\n        }\n    }\n\n    /*!\n    @return The size of the BSON-encoded unsigned integer in @a j\n    */\n    static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept\n    {\n        return (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n               ? sizeof(std::int32_t)\n               : sizeof(std::int64_t);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and unsigned @a value\n    */\n    void write_bson_unsigned(const string_t& name,\n                             const std::uint64_t value)\n    {\n        if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n        {\n            write_bson_entry_header(name, 0x10 /* int32 */);\n            write_number<std::int32_t, true>(static_cast<std::int32_t>(value));\n        }\n        else if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n        {\n            write_bson_entry_header(name, 0x12 /* int64 */);\n            write_number<std::int64_t, true>(static_cast<std::int64_t>(value));\n        }\n        else\n        {\n            JSON_THROW(out_of_range::create(407, \"integer number \" + std::to_string(value) + \" cannot be represented by BSON as it does not fit int64\"));\n        }\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and object @a value\n    */\n    void write_bson_object_entry(const string_t& name,\n                                 const typename BasicJsonType::object_t& value)\n    {\n        write_bson_entry_header(name, 0x03); // object\n        write_bson_object(value);\n    }\n\n    /*!\n    @return The size of the BSON-encoded array @a value\n    */\n    static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value)\n    {\n        std::size_t array_index = 0ul;\n\n        const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), std::size_t(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el)\n        {\n            return result + calc_bson_element_size(std::to_string(array_index++), el);\n        });\n\n        return sizeof(std::int32_t) + embedded_document_size + 1ul;\n    }\n\n    /*!\n    @return The size of the BSON-encoded binary array @a value\n    */\n    static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value)\n    {\n        return sizeof(std::int32_t) + value.size() + 1ul;\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and array @a value\n    */\n    void write_bson_array(const string_t& name,\n                          const typename BasicJsonType::array_t& value)\n    {\n        write_bson_entry_header(name, 0x04); // array\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_array_size(value)));\n\n        std::size_t array_index = 0ul;\n\n        for (const auto& el : value)\n        {\n            write_bson_element(std::to_string(array_index++), el);\n        }\n\n        oa->write_character(to_char_type(0x00));\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and binary value @a value\n    */\n    void write_bson_binary(const string_t& name,\n                           const binary_t& value)\n    {\n        write_bson_entry_header(name, 0x05);\n\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size()));\n        write_number(value.has_subtype() ? value.subtype() : std::uint8_t(0x00));\n\n        oa->write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());\n    }\n\n    /*!\n    @brief Calculates the size necessary to serialize the JSON value @a j with its @a name\n    @return The calculated size for the BSON document entry for @a j with the given @a name.\n    */\n    static std::size_t calc_bson_element_size(const string_t& name,\n            const BasicJsonType& j)\n    {\n        const auto header_size = calc_bson_entry_header_size(name);\n        switch (j.type())\n        {\n            case value_t::object:\n                return header_size + calc_bson_object_size(*j.m_value.object);\n\n            case value_t::array:\n                return header_size + calc_bson_array_size(*j.m_value.array);\n\n            case value_t::binary:\n                return header_size + calc_bson_binary_size(*j.m_value.binary);\n\n            case value_t::boolean:\n                return header_size + 1ul;\n\n            case value_t::number_float:\n                return header_size + 8ul;\n\n            case value_t::number_integer:\n                return header_size + calc_bson_integer_size(j.m_value.number_integer);\n\n            case value_t::number_unsigned:\n                return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned);\n\n            case value_t::string:\n                return header_size + calc_bson_string_size(*j.m_value.string);\n\n            case value_t::null:\n                return header_size + 0ul;\n\n            // LCOV_EXCL_START\n            default:\n                JSON_ASSERT(false);\n                return 0ul;\n                // LCOV_EXCL_STOP\n        }\n    }\n\n    /*!\n    @brief Serializes the JSON value @a j to BSON and associates it with the\n           key @a name.\n    @param name The name to associate with the JSON entity @a j within the\n                current BSON document\n    @return The size of the BSON entry\n    */\n    void write_bson_element(const string_t& name,\n                            const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::object:\n                return write_bson_object_entry(name, *j.m_value.object);\n\n            case value_t::array:\n                return write_bson_array(name, *j.m_value.array);\n\n            case value_t::binary:\n                return write_bson_binary(name, *j.m_value.binary);\n\n            case value_t::boolean:\n                return write_bson_boolean(name, j.m_value.boolean);\n\n            case value_t::number_float:\n                return write_bson_double(name, j.m_value.number_float);\n\n            case value_t::number_integer:\n                return write_bson_integer(name, j.m_value.number_integer);\n\n            case value_t::number_unsigned:\n                return write_bson_unsigned(name, j.m_value.number_unsigned);\n\n            case value_t::string:\n                return write_bson_string(name, *j.m_value.string);\n\n            case value_t::null:\n                return write_bson_null(name);\n\n            // LCOV_EXCL_START\n            default:\n                JSON_ASSERT(false);\n                return;\n                // LCOV_EXCL_STOP\n        }\n    }\n\n    /*!\n    @brief Calculates the size of the BSON serialization of the given\n           JSON-object @a j.\n    @param[in] j  JSON value to serialize\n    @pre       j.type() == value_t::object\n    */\n    static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)\n    {\n        std::size_t document_size = std::accumulate(value.begin(), value.end(), std::size_t(0),\n                                    [](size_t result, const typename BasicJsonType::object_t::value_type & el)\n        {\n            return result += calc_bson_element_size(el.first, el.second);\n        });\n\n        return sizeof(std::int32_t) + document_size + 1ul;\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @pre       j.type() == value_t::object\n    */\n    void write_bson_object(const typename BasicJsonType::object_t& value)\n    {\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_object_size(value)));\n\n        for (const auto& el : value)\n        {\n            write_bson_element(el.first, el.second);\n        }\n\n        oa->write_character(to_char_type(0x00));\n    }\n\n    //////////\n    // CBOR //\n    //////////\n\n    static constexpr CharType get_cbor_float_prefix(float /*unused*/)\n    {\n        return to_char_type(0xFA);  // Single-Precision Float\n    }\n\n    static constexpr CharType get_cbor_float_prefix(double /*unused*/)\n    {\n        return to_char_type(0xFB);  // Double-Precision Float\n    }\n\n    /////////////\n    // MsgPack //\n    /////////////\n\n    static constexpr CharType get_msgpack_float_prefix(float /*unused*/)\n    {\n        return to_char_type(0xCA);  // float 32\n    }\n\n    static constexpr CharType get_msgpack_float_prefix(double /*unused*/)\n    {\n        return to_char_type(0xCB);  // float 64\n    }\n\n    ////////////\n    // UBJSON //\n    ////////////\n\n    // UBJSON: write number (floating point)\n    template<typename NumberType, typename std::enable_if<\n                 std::is_floating_point<NumberType>::value, int>::type = 0>\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if (add_prefix)\n        {\n            oa->write_character(get_ubjson_float_prefix(n));\n        }\n        write_number(n);\n    }\n\n    // UBJSON: write number (unsigned integer)\n    template<typename NumberType, typename std::enable_if<\n                 std::is_unsigned<NumberType>::value, int>::type = 0>\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('i'));  // int8\n            }\n            write_number(static_cast<std::uint8_t>(n));\n        }\n        else if (n <= (std::numeric_limits<std::uint8_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('U'));  // uint8\n            }\n            write_number(static_cast<std::uint8_t>(n));\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('I'));  // int16\n            }\n            write_number(static_cast<std::int16_t>(n));\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('l'));  // int32\n            }\n            write_number(static_cast<std::int32_t>(n));\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('L'));  // int64\n            }\n            write_number(static_cast<std::int64_t>(n));\n        }\n        else\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('H'));  // high-precision number\n            }\n\n            const auto number = BasicJsonType(n).dump();\n            write_number_with_ubjson_prefix(number.size(), true);\n            for (std::size_t i = 0; i < number.size(); ++i)\n            {\n                oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));\n            }\n        }\n    }\n\n    // UBJSON: write number (signed integer)\n    template < typename NumberType, typename std::enable_if <\n                   std::is_signed<NumberType>::value&&\n                   !std::is_floating_point<NumberType>::value, int >::type = 0 >\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if ((std::numeric_limits<std::int8_t>::min)() <= n && n <= (std::numeric_limits<std::int8_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('i'));  // int8\n            }\n            write_number(static_cast<std::int8_t>(n));\n        }\n        else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('U'));  // uint8\n            }\n            write_number(static_cast<std::uint8_t>(n));\n        }\n        else if ((std::numeric_limits<std::int16_t>::min)() <= n && n <= (std::numeric_limits<std::int16_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('I'));  // int16\n            }\n            write_number(static_cast<std::int16_t>(n));\n        }\n        else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('l'));  // int32\n            }\n            write_number(static_cast<std::int32_t>(n));\n        }\n        else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('L'));  // int64\n            }\n            write_number(static_cast<std::int64_t>(n));\n        }\n        // LCOV_EXCL_START\n        else\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('H'));  // high-precision number\n            }\n\n            const auto number = BasicJsonType(n).dump();\n            write_number_with_ubjson_prefix(number.size(), true);\n            for (std::size_t i = 0; i < number.size(); ++i)\n            {\n                oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));\n            }\n        }\n        // LCOV_EXCL_STOP\n    }\n\n    /*!\n    @brief determine the type prefix of container values\n    */\n    CharType ubjson_prefix(const BasicJsonType& j) const noexcept\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n                return 'Z';\n\n            case value_t::boolean:\n                return j.m_value.boolean ? 'T' : 'F';\n\n            case value_t::number_integer:\n            {\n                if ((std::numeric_limits<std::int8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())\n                {\n                    return 'i';\n                }\n                if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    return 'U';\n                }\n                if ((std::numeric_limits<std::int16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())\n                {\n                    return 'I';\n                }\n                if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())\n                {\n                    return 'l';\n                }\n                if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())\n                {\n                    return 'L';\n                }\n                // anything else is treated as high-precision number\n                return 'H'; // LCOV_EXCL_LINE\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))\n                {\n                    return 'i';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint8_t>::max)()))\n                {\n                    return 'U';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))\n                {\n                    return 'I';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n                {\n                    return 'l';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n                {\n                    return 'L';\n                }\n                // anything else is treated as high-precision number\n                return 'H'; // LCOV_EXCL_LINE\n            }\n\n            case value_t::number_float:\n                return get_ubjson_float_prefix(j.m_value.number_float);\n\n            case value_t::string:\n                return 'S';\n\n            case value_t::array: // fallthrough\n            case value_t::binary:\n                return '[';\n\n            case value_t::object:\n                return '{';\n\n            default:  // discarded values\n                return 'N';\n        }\n    }\n\n    static constexpr CharType get_ubjson_float_prefix(float /*unused*/)\n    {\n        return 'd';  // float 32\n    }\n\n    static constexpr CharType get_ubjson_float_prefix(double /*unused*/)\n    {\n        return 'D';  // float 64\n    }\n\n    ///////////////////////\n    // Utility functions //\n    ///////////////////////\n\n    /*\n    @brief write a number to output input\n    @param[in] n number of type @a NumberType\n    @tparam NumberType the type of the number\n    @tparam OutputIsLittleEndian Set to true if output data is\n                                 required to be little endian\n\n    @note This function needs to respect the system's endianess, because bytes\n          in CBOR, MessagePack, and UBJSON are stored in network order (big\n          endian) and therefore need reordering on little endian systems.\n    */\n    template<typename NumberType, bool OutputIsLittleEndian = false>\n    void write_number(const NumberType n)\n    {\n        // step 1: write number to array of length NumberType\n        std::array<CharType, sizeof(NumberType)> vec;\n        std::memcpy(vec.data(), &n, sizeof(NumberType));\n\n        // step 2: write array to output (with possible reordering)\n        if (is_little_endian != OutputIsLittleEndian)\n        {\n            // reverse byte order prior to conversion if necessary\n            std::reverse(vec.begin(), vec.end());\n        }\n\n        oa->write_characters(vec.data(), sizeof(NumberType));\n    }\n\n    void write_compact_float(const number_float_t n, detail::input_format_t format)\n    {\n        if (static_cast<double>(n) >= static_cast<double>(std::numeric_limits<float>::lowest()) &&\n                static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&\n                static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))\n        {\n            oa->write_character(format == detail::input_format_t::cbor\n                                ? get_cbor_float_prefix(static_cast<float>(n))\n                                : get_msgpack_float_prefix(static_cast<float>(n)));\n            write_number(static_cast<float>(n));\n        }\n        else\n        {\n            oa->write_character(format == detail::input_format_t::cbor\n                                ? get_cbor_float_prefix(n)\n                                : get_msgpack_float_prefix(n));\n            write_number(n);\n        }\n    }\n\n  public:\n    // The following to_char_type functions are implement the conversion\n    // between uint8_t and CharType. In case CharType is not unsigned,\n    // such a conversion is required to allow values greater than 128.\n    // See <https://github.com/nlohmann/json/issues/1286> for a discussion.\n    template < typename C = CharType,\n               enable_if_t < std::is_signed<C>::value && std::is_signed<char>::value > * = nullptr >\n    static constexpr CharType to_char_type(std::uint8_t x) noexcept\n    {\n        return *reinterpret_cast<char*>(&x);\n    }\n\n    template < typename C = CharType,\n               enable_if_t < std::is_signed<C>::value && std::is_unsigned<char>::value > * = nullptr >\n    static CharType to_char_type(std::uint8_t x) noexcept\n    {\n        static_assert(sizeof(std::uint8_t) == sizeof(CharType), \"size of CharType must be equal to std::uint8_t\");\n        static_assert(std::is_trivial<CharType>::value, \"CharType must be trivial\");\n        CharType result;\n        std::memcpy(&result, &x, sizeof(x));\n        return result;\n    }\n\n    template<typename C = CharType,\n             enable_if_t<std::is_unsigned<C>::value>* = nullptr>\n    static constexpr CharType to_char_type(std::uint8_t x) noexcept\n    {\n        return x;\n    }\n\n    template < typename InputCharType, typename C = CharType,\n               enable_if_t <\n                   std::is_signed<C>::value &&\n                   std::is_signed<char>::value &&\n                   std::is_same<char, typename std::remove_cv<InputCharType>::type>::value\n                   > * = nullptr >\n    static constexpr CharType to_char_type(InputCharType x) noexcept\n    {\n        return x;\n    }\n\n  private:\n    /// whether we can assume little endianess\n    const bool is_little_endian = little_endianess();\n\n    /// the output\n    output_adapter_t<CharType> oa = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n\n// #include <nlohmann/detail/output/serializer.hpp>\n\n\n#include <algorithm> // reverse, remove, fill, find, none_of\n#include <array> // array\n#include <clocale> // localeconv, lconv\n#include <cmath> // labs, isfinite, isnan, signbit\n#include <cstddef> // size_t, ptrdiff_t\n#include <cstdint> // uint8_t\n#include <cstdio> // snprintf\n#include <limits> // numeric_limits\n#include <string> // string, char_traits\n#include <type_traits> // is_same\n#include <utility> // move\n\n// #include <nlohmann/detail/conversions/to_chars.hpp>\n\n\n#include <array> // array\n#include <cmath>   // signbit, isfinite\n#include <cstdint> // intN_t, uintN_t\n#include <cstring> // memcpy, memmove\n#include <limits> // numeric_limits\n#include <type_traits> // conditional\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\n/*!\n@brief implements the Grisu2 algorithm for binary to decimal floating-point\nconversion.\n\nThis implementation is a slightly modified version of the reference\nimplementation which may be obtained from\nhttp://florian.loitsch.com/publications (bench.tar.gz).\n\nThe code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.\n\nFor a detailed description of the algorithm see:\n\n[1] Loitsch, \"Printing Floating-Point Numbers Quickly and Accurately with\n    Integers\", Proceedings of the ACM SIGPLAN 2010 Conference on Programming\n    Language Design and Implementation, PLDI 2010\n[2] Burger, Dybvig, \"Printing Floating-Point Numbers Quickly and Accurately\",\n    Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language\n    Design and Implementation, PLDI 1996\n*/\nnamespace dtoa_impl\n{\n\ntemplate<typename Target, typename Source>\nTarget reinterpret_bits(const Source source)\n{\n    static_assert(sizeof(Target) == sizeof(Source), \"size mismatch\");\n\n    Target target;\n    std::memcpy(&target, &source, sizeof(Source));\n    return target;\n}\n\nstruct diyfp // f * 2^e\n{\n    static constexpr int kPrecision = 64; // = q\n\n    std::uint64_t f = 0;\n    int e = 0;\n\n    constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {}\n\n    /*!\n    @brief returns x - y\n    @pre x.e == y.e and x.f >= y.f\n    */\n    static diyfp sub(const diyfp& x, const diyfp& y) noexcept\n    {\n        JSON_ASSERT(x.e == y.e);\n        JSON_ASSERT(x.f >= y.f);\n\n        return {x.f - y.f, x.e};\n    }\n\n    /*!\n    @brief returns x * y\n    @note The result is rounded. (Only the upper q bits are returned.)\n    */\n    static diyfp mul(const diyfp& x, const diyfp& y) noexcept\n    {\n        static_assert(kPrecision == 64, \"internal error\");\n\n        // Computes:\n        //  f = round((x.f * y.f) / 2^q)\n        //  e = x.e + y.e + q\n\n        // Emulate the 64-bit * 64-bit multiplication:\n        //\n        // p = u * v\n        //   = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi)\n        //   = (u_lo v_lo         ) + 2^32 ((u_lo v_hi         ) + (u_hi v_lo         )) + 2^64 (u_hi v_hi         )\n        //   = (p0                ) + 2^32 ((p1                ) + (p2                )) + 2^64 (p3                )\n        //   = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3                )\n        //   = (p0_lo             ) + 2^32 (p0_hi + p1_lo + p2_lo                      ) + 2^64 (p1_hi + p2_hi + p3)\n        //   = (p0_lo             ) + 2^32 (Q                                          ) + 2^64 (H                 )\n        //   = (p0_lo             ) + 2^32 (Q_lo + 2^32 Q_hi                           ) + 2^64 (H                 )\n        //\n        // (Since Q might be larger than 2^32 - 1)\n        //\n        //   = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H)\n        //\n        // (Q_hi + H does not overflow a 64-bit int)\n        //\n        //   = p_lo + 2^64 p_hi\n\n        const std::uint64_t u_lo = x.f & 0xFFFFFFFFu;\n        const std::uint64_t u_hi = x.f >> 32u;\n        const std::uint64_t v_lo = y.f & 0xFFFFFFFFu;\n        const std::uint64_t v_hi = y.f >> 32u;\n\n        const std::uint64_t p0 = u_lo * v_lo;\n        const std::uint64_t p1 = u_lo * v_hi;\n        const std::uint64_t p2 = u_hi * v_lo;\n        const std::uint64_t p3 = u_hi * v_hi;\n\n        const std::uint64_t p0_hi = p0 >> 32u;\n        const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu;\n        const std::uint64_t p1_hi = p1 >> 32u;\n        const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu;\n        const std::uint64_t p2_hi = p2 >> 32u;\n\n        std::uint64_t Q = p0_hi + p1_lo + p2_lo;\n\n        // The full product might now be computed as\n        //\n        // p_hi = p3 + p2_hi + p1_hi + (Q >> 32)\n        // p_lo = p0_lo + (Q << 32)\n        //\n        // But in this particular case here, the full p_lo is not required.\n        // Effectively we only need to add the highest bit in p_lo to p_hi (and\n        // Q_hi + 1 does not overflow).\n\n        Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up\n\n        const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u);\n\n        return {h, x.e + y.e + 64};\n    }\n\n    /*!\n    @brief normalize x such that the significand is >= 2^(q-1)\n    @pre x.f != 0\n    */\n    static diyfp normalize(diyfp x) noexcept\n    {\n        JSON_ASSERT(x.f != 0);\n\n        while ((x.f >> 63u) == 0)\n        {\n            x.f <<= 1u;\n            x.e--;\n        }\n\n        return x;\n    }\n\n    /*!\n    @brief normalize x such that the result has the exponent E\n    @pre e >= x.e and the upper e - x.e bits of x.f must be zero.\n    */\n    static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept\n    {\n        const int delta = x.e - target_exponent;\n\n        JSON_ASSERT(delta >= 0);\n        JSON_ASSERT(((x.f << delta) >> delta) == x.f);\n\n        return {x.f << delta, target_exponent};\n    }\n};\n\nstruct boundaries\n{\n    diyfp w;\n    diyfp minus;\n    diyfp plus;\n};\n\n/*!\nCompute the (normalized) diyfp representing the input number 'value' and its\nboundaries.\n\n@pre value must be finite and positive\n*/\ntemplate<typename FloatType>\nboundaries compute_boundaries(FloatType value)\n{\n    JSON_ASSERT(std::isfinite(value));\n    JSON_ASSERT(value > 0);\n\n    // Convert the IEEE representation into a diyfp.\n    //\n    // If v is denormal:\n    //      value = 0.F * 2^(1 - bias) = (          F) * 2^(1 - bias - (p-1))\n    // If v is normalized:\n    //      value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1))\n\n    static_assert(std::numeric_limits<FloatType>::is_iec559,\n                  \"internal error: dtoa_short requires an IEEE-754 floating-point implementation\");\n\n    constexpr int      kPrecision = std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit)\n    constexpr int      kBias      = std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1);\n    constexpr int      kMinExp    = 1 - kBias;\n    constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1)\n\n    using bits_type = typename std::conditional<kPrecision == 24, std::uint32_t, std::uint64_t >::type;\n\n    const std::uint64_t bits = reinterpret_bits<bits_type>(value);\n    const std::uint64_t E = bits >> (kPrecision - 1);\n    const std::uint64_t F = bits & (kHiddenBit - 1);\n\n    const bool is_denormal = E == 0;\n    const diyfp v = is_denormal\n                    ? diyfp(F, kMinExp)\n                    : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);\n\n    // Compute the boundaries m- and m+ of the floating-point value\n    // v = f * 2^e.\n    //\n    // Determine v- and v+, the floating-point predecessor and successor if v,\n    // respectively.\n    //\n    //      v- = v - 2^e        if f != 2^(p-1) or e == e_min                (A)\n    //         = v - 2^(e-1)    if f == 2^(p-1) and e > e_min                (B)\n    //\n    //      v+ = v + 2^e\n    //\n    // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_\n    // between m- and m+ round to v, regardless of how the input rounding\n    // algorithm breaks ties.\n    //\n    //      ---+-------------+-------------+-------------+-------------+---  (A)\n    //         v-            m-            v             m+            v+\n    //\n    //      -----------------+------+------+-------------+-------------+---  (B)\n    //                       v-     m-     v             m+            v+\n\n    const bool lower_boundary_is_closer = F == 0 && E > 1;\n    const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);\n    const diyfp m_minus = lower_boundary_is_closer\n                          ? diyfp(4 * v.f - 1, v.e - 2)  // (B)\n                          : diyfp(2 * v.f - 1, v.e - 1); // (A)\n\n    // Determine the normalized w+ = m+.\n    const diyfp w_plus = diyfp::normalize(m_plus);\n\n    // Determine w- = m- such that e_(w-) = e_(w+).\n    const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e);\n\n    return {diyfp::normalize(v), w_minus, w_plus};\n}\n\n// Given normalized diyfp w, Grisu needs to find a (normalized) cached\n// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies\n// within a certain range [alpha, gamma] (Definition 3.2 from [1])\n//\n//      alpha <= e = e_c + e_w + q <= gamma\n//\n// or\n//\n//      f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q\n//                          <= f_c * f_w * 2^gamma\n//\n// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies\n//\n//      2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma\n//\n// or\n//\n//      2^(q - 2 + alpha) <= c * w < 2^(q + gamma)\n//\n// The choice of (alpha,gamma) determines the size of the table and the form of\n// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well\n// in practice:\n//\n// The idea is to cut the number c * w = f * 2^e into two parts, which can be\n// processed independently: An integral part p1, and a fractional part p2:\n//\n//      f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e\n//              = (f div 2^-e) + (f mod 2^-e) * 2^e\n//              = p1 + p2 * 2^e\n//\n// The conversion of p1 into decimal form requires a series of divisions and\n// modulos by (a power of) 10. These operations are faster for 32-bit than for\n// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be\n// achieved by choosing\n//\n//      -e >= 32   or   e <= -32 := gamma\n//\n// In order to convert the fractional part\n//\n//      p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ...\n//\n// into decimal form, the fraction is repeatedly multiplied by 10 and the digits\n// d[-i] are extracted in order:\n//\n//      (10 * p2) div 2^-e = d[-1]\n//      (10 * p2) mod 2^-e = d[-2] / 10^1 + ...\n//\n// The multiplication by 10 must not overflow. It is sufficient to choose\n//\n//      10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64.\n//\n// Since p2 = f mod 2^-e < 2^-e,\n//\n//      -e <= 60   or   e >= -60 := alpha\n\nconstexpr int kAlpha = -60;\nconstexpr int kGamma = -32;\n\nstruct cached_power // c = f * 2^e ~= 10^k\n{\n    std::uint64_t f;\n    int e;\n    int k;\n};\n\n/*!\nFor a normalized diyfp w = f * 2^e, this function returns a (normalized) cached\npower-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c\nsatisfies (Definition 3.2 from [1])\n\n     alpha <= e_c + e + q <= gamma.\n*/\ninline cached_power get_cached_power_for_binary_exponent(int e)\n{\n    // Now\n    //\n    //      alpha <= e_c + e + q <= gamma                                    (1)\n    //      ==> f_c * 2^alpha <= c * 2^e * 2^q\n    //\n    // and since the c's are normalized, 2^(q-1) <= f_c,\n    //\n    //      ==> 2^(q - 1 + alpha) <= c * 2^(e + q)\n    //      ==> 2^(alpha - e - 1) <= c\n    //\n    // If c were an exact power of ten, i.e. c = 10^k, one may determine k as\n    //\n    //      k = ceil( log_10( 2^(alpha - e - 1) ) )\n    //        = ceil( (alpha - e - 1) * log_10(2) )\n    //\n    // From the paper:\n    // \"In theory the result of the procedure could be wrong since c is rounded,\n    //  and the computation itself is approximated [...]. In practice, however,\n    //  this simple function is sufficient.\"\n    //\n    // For IEEE double precision floating-point numbers converted into\n    // normalized diyfp's w = f * 2^e, with q = 64,\n    //\n    //      e >= -1022      (min IEEE exponent)\n    //           -52        (p - 1)\n    //           -52        (p - 1, possibly normalize denormal IEEE numbers)\n    //           -11        (normalize the diyfp)\n    //         = -1137\n    //\n    // and\n    //\n    //      e <= +1023      (max IEEE exponent)\n    //           -52        (p - 1)\n    //           -11        (normalize the diyfp)\n    //         = 960\n    //\n    // This binary exponent range [-1137,960] results in a decimal exponent\n    // range [-307,324]. One does not need to store a cached power for each\n    // k in this range. For each such k it suffices to find a cached power\n    // such that the exponent of the product lies in [alpha,gamma].\n    // This implies that the difference of the decimal exponents of adjacent\n    // table entries must be less than or equal to\n    //\n    //      floor( (gamma - alpha) * log_10(2) ) = 8.\n    //\n    // (A smaller distance gamma-alpha would require a larger table.)\n\n    // NB:\n    // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34.\n\n    constexpr int kCachedPowersMinDecExp = -300;\n    constexpr int kCachedPowersDecStep = 8;\n\n    static constexpr std::array<cached_power, 79> kCachedPowers =\n    {\n        {\n            { 0xAB70FE17C79AC6CA, -1060, -300 },\n            { 0xFF77B1FCBEBCDC4F, -1034, -292 },\n            { 0xBE5691EF416BD60C, -1007, -284 },\n            { 0x8DD01FAD907FFC3C,  -980, -276 },\n            { 0xD3515C2831559A83,  -954, -268 },\n            { 0x9D71AC8FADA6C9B5,  -927, -260 },\n            { 0xEA9C227723EE8BCB,  -901, -252 },\n            { 0xAECC49914078536D,  -874, -244 },\n            { 0x823C12795DB6CE57,  -847, -236 },\n            { 0xC21094364DFB5637,  -821, -228 },\n            { 0x9096EA6F3848984F,  -794, -220 },\n            { 0xD77485CB25823AC7,  -768, -212 },\n            { 0xA086CFCD97BF97F4,  -741, -204 },\n            { 0xEF340A98172AACE5,  -715, -196 },\n            { 0xB23867FB2A35B28E,  -688, -188 },\n            { 0x84C8D4DFD2C63F3B,  -661, -180 },\n            { 0xC5DD44271AD3CDBA,  -635, -172 },\n            { 0x936B9FCEBB25C996,  -608, -164 },\n            { 0xDBAC6C247D62A584,  -582, -156 },\n            { 0xA3AB66580D5FDAF6,  -555, -148 },\n            { 0xF3E2F893DEC3F126,  -529, -140 },\n            { 0xB5B5ADA8AAFF80B8,  -502, -132 },\n            { 0x87625F056C7C4A8B,  -475, -124 },\n            { 0xC9BCFF6034C13053,  -449, -116 },\n            { 0x964E858C91BA2655,  -422, -108 },\n            { 0xDFF9772470297EBD,  -396, -100 },\n            { 0xA6DFBD9FB8E5B88F,  -369,  -92 },\n            { 0xF8A95FCF88747D94,  -343,  -84 },\n            { 0xB94470938FA89BCF,  -316,  -76 },\n            { 0x8A08F0F8BF0F156B,  -289,  -68 },\n            { 0xCDB02555653131B6,  -263,  -60 },\n            { 0x993FE2C6D07B7FAC,  -236,  -52 },\n            { 0xE45C10C42A2B3B06,  -210,  -44 },\n            { 0xAA242499697392D3,  -183,  -36 },\n            { 0xFD87B5F28300CA0E,  -157,  -28 },\n            { 0xBCE5086492111AEB,  -130,  -20 },\n            { 0x8CBCCC096F5088CC,  -103,  -12 },\n            { 0xD1B71758E219652C,   -77,   -4 },\n            { 0x9C40000000000000,   -50,    4 },\n            { 0xE8D4A51000000000,   -24,   12 },\n            { 0xAD78EBC5AC620000,     3,   20 },\n            { 0x813F3978F8940984,    30,   28 },\n            { 0xC097CE7BC90715B3,    56,   36 },\n            { 0x8F7E32CE7BEA5C70,    83,   44 },\n            { 0xD5D238A4ABE98068,   109,   52 },\n            { 0x9F4F2726179A2245,   136,   60 },\n            { 0xED63A231D4C4FB27,   162,   68 },\n            { 0xB0DE65388CC8ADA8,   189,   76 },\n            { 0x83C7088E1AAB65DB,   216,   84 },\n            { 0xC45D1DF942711D9A,   242,   92 },\n            { 0x924D692CA61BE758,   269,  100 },\n            { 0xDA01EE641A708DEA,   295,  108 },\n            { 0xA26DA3999AEF774A,   322,  116 },\n            { 0xF209787BB47D6B85,   348,  124 },\n            { 0xB454E4A179DD1877,   375,  132 },\n            { 0x865B86925B9BC5C2,   402,  140 },\n            { 0xC83553C5C8965D3D,   428,  148 },\n            { 0x952AB45CFA97A0B3,   455,  156 },\n            { 0xDE469FBD99A05FE3,   481,  164 },\n            { 0xA59BC234DB398C25,   508,  172 },\n            { 0xF6C69A72A3989F5C,   534,  180 },\n            { 0xB7DCBF5354E9BECE,   561,  188 },\n            { 0x88FCF317F22241E2,   588,  196 },\n            { 0xCC20CE9BD35C78A5,   614,  204 },\n            { 0x98165AF37B2153DF,   641,  212 },\n            { 0xE2A0B5DC971F303A,   667,  220 },\n            { 0xA8D9D1535CE3B396,   694,  228 },\n            { 0xFB9B7CD9A4A7443C,   720,  236 },\n            { 0xBB764C4CA7A44410,   747,  244 },\n            { 0x8BAB8EEFB6409C1A,   774,  252 },\n            { 0xD01FEF10A657842C,   800,  260 },\n            { 0x9B10A4E5E9913129,   827,  268 },\n            { 0xE7109BFBA19C0C9D,   853,  276 },\n            { 0xAC2820D9623BF429,   880,  284 },\n            { 0x80444B5E7AA7CF85,   907,  292 },\n            { 0xBF21E44003ACDD2D,   933,  300 },\n            { 0x8E679C2F5E44FF8F,   960,  308 },\n            { 0xD433179D9C8CB841,   986,  316 },\n            { 0x9E19DB92B4E31BA9,  1013,  324 },\n        }\n    };\n\n    // This computation gives exactly the same results for k as\n    //      k = ceil((kAlpha - e - 1) * 0.30102999566398114)\n    // for |e| <= 1500, but doesn't require floating-point operations.\n    // NB: log_10(2) ~= 78913 / 2^18\n    JSON_ASSERT(e >= -1500);\n    JSON_ASSERT(e <=  1500);\n    const int f = kAlpha - e - 1;\n    const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0);\n\n    const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;\n    JSON_ASSERT(index >= 0);\n    JSON_ASSERT(static_cast<std::size_t>(index) < kCachedPowers.size());\n\n    const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)];\n    JSON_ASSERT(kAlpha <= cached.e + e + 64);\n    JSON_ASSERT(kGamma >= cached.e + e + 64);\n\n    return cached;\n}\n\n/*!\nFor n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.\nFor n == 0, returns 1 and sets pow10 := 1.\n*/\ninline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10)\n{\n    // LCOV_EXCL_START\n    if (n >= 1000000000)\n    {\n        pow10 = 1000000000;\n        return 10;\n    }\n    // LCOV_EXCL_STOP\n    else if (n >= 100000000)\n    {\n        pow10 = 100000000;\n        return  9;\n    }\n    else if (n >= 10000000)\n    {\n        pow10 = 10000000;\n        return  8;\n    }\n    else if (n >= 1000000)\n    {\n        pow10 = 1000000;\n        return  7;\n    }\n    else if (n >= 100000)\n    {\n        pow10 = 100000;\n        return  6;\n    }\n    else if (n >= 10000)\n    {\n        pow10 = 10000;\n        return  5;\n    }\n    else if (n >= 1000)\n    {\n        pow10 = 1000;\n        return  4;\n    }\n    else if (n >= 100)\n    {\n        pow10 = 100;\n        return  3;\n    }\n    else if (n >= 10)\n    {\n        pow10 = 10;\n        return  2;\n    }\n    else\n    {\n        pow10 = 1;\n        return 1;\n    }\n}\n\ninline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta,\n                         std::uint64_t rest, std::uint64_t ten_k)\n{\n    JSON_ASSERT(len >= 1);\n    JSON_ASSERT(dist <= delta);\n    JSON_ASSERT(rest <= delta);\n    JSON_ASSERT(ten_k > 0);\n\n    //               <--------------------------- delta ---->\n    //                                  <---- dist --------->\n    // --------------[------------------+-------------------]--------------\n    //               M-                 w                   M+\n    //\n    //                                  ten_k\n    //                                <------>\n    //                                       <---- rest ---->\n    // --------------[------------------+----+--------------]--------------\n    //                                  w    V\n    //                                       = buf * 10^k\n    //\n    // ten_k represents a unit-in-the-last-place in the decimal representation\n    // stored in buf.\n    // Decrement buf by ten_k while this takes buf closer to w.\n\n    // The tests are written in this order to avoid overflow in unsigned\n    // integer arithmetic.\n\n    while (rest < dist\n            && delta - rest >= ten_k\n            && (rest + ten_k < dist || dist - rest > rest + ten_k - dist))\n    {\n        JSON_ASSERT(buf[len - 1] != '0');\n        buf[len - 1]--;\n        rest += ten_k;\n    }\n}\n\n/*!\nGenerates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.\nM- and M+ must be normalized and share the same exponent -60 <= e <= -32.\n*/\ninline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,\n                             diyfp M_minus, diyfp w, diyfp M_plus)\n{\n    static_assert(kAlpha >= -60, \"internal error\");\n    static_assert(kGamma <= -32, \"internal error\");\n\n    // Generates the digits (and the exponent) of a decimal floating-point\n    // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's\n    // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma.\n    //\n    //               <--------------------------- delta ---->\n    //                                  <---- dist --------->\n    // --------------[------------------+-------------------]--------------\n    //               M-                 w                   M+\n    //\n    // Grisu2 generates the digits of M+ from left to right and stops as soon as\n    // V is in [M-,M+].\n\n    JSON_ASSERT(M_plus.e >= kAlpha);\n    JSON_ASSERT(M_plus.e <= kGamma);\n\n    std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)\n    std::uint64_t dist  = diyfp::sub(M_plus, w      ).f; // (significand of (M+ - w ), implicit exponent is e)\n\n    // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0):\n    //\n    //      M+ = f * 2^e\n    //         = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e\n    //         = ((p1        ) * 2^-e + (p2        )) * 2^e\n    //         = p1 + p2 * 2^e\n\n    const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e);\n\n    auto p1 = static_cast<std::uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)\n    std::uint64_t p2 = M_plus.f & (one.f - 1);                    // p2 = f mod 2^-e\n\n    // 1)\n    //\n    // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]\n\n    JSON_ASSERT(p1 > 0);\n\n    std::uint32_t pow10;\n    const int k = find_largest_pow10(p1, pow10);\n\n    //      10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)\n    //\n    //      p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1))\n    //         = (d[k-1]         ) * 10^(k-1) + (p1 mod 10^(k-1))\n    //\n    //      M+ = p1                                             + p2 * 2^e\n    //         = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1))          + p2 * 2^e\n    //         = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e\n    //         = d[k-1] * 10^(k-1) + (                         rest) * 2^e\n    //\n    // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0)\n    //\n    //      p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0]\n    //\n    // but stop as soon as\n    //\n    //      rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e\n\n    int n = k;\n    while (n > 0)\n    {\n        // Invariants:\n        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)    (buffer = 0 for n = k)\n        //      pow10 = 10^(n-1) <= p1 < 10^n\n        //\n        const std::uint32_t d = p1 / pow10;  // d = p1 div 10^(n-1)\n        const std::uint32_t r = p1 % pow10;  // r = p1 mod 10^(n-1)\n        //\n        //      M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e\n        //         = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)\n        //\n        JSON_ASSERT(d <= 9);\n        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d\n        //\n        //      M+ = buffer * 10^(n-1) + (r + p2 * 2^e)\n        //\n        p1 = r;\n        n--;\n        //\n        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)\n        //      pow10 = 10^n\n        //\n\n        // Now check if enough digits have been generated.\n        // Compute\n        //\n        //      p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e\n        //\n        // Note:\n        // Since rest and delta share the same exponent e, it suffices to\n        // compare the significands.\n        const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2;\n        if (rest <= delta)\n        {\n            // V = buffer * 10^n, with M- <= V <= M+.\n\n            decimal_exponent += n;\n\n            // We may now just stop. But instead look if the buffer could be\n            // decremented to bring V closer to w.\n            //\n            // pow10 = 10^n is now 1 ulp in the decimal representation V.\n            // The rounding procedure works with diyfp's with an implicit\n            // exponent of e.\n            //\n            //      10^n = (10^n * 2^-e) * 2^e = ulp * 2^e\n            //\n            const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e;\n            grisu2_round(buffer, length, dist, delta, rest, ten_n);\n\n            return;\n        }\n\n        pow10 /= 10;\n        //\n        //      pow10 = 10^(n-1) <= p1 < 10^n\n        // Invariants restored.\n    }\n\n    // 2)\n    //\n    // The digits of the integral part have been generated:\n    //\n    //      M+ = d[k-1]...d[1]d[0] + p2 * 2^e\n    //         = buffer            + p2 * 2^e\n    //\n    // Now generate the digits of the fractional part p2 * 2^e.\n    //\n    // Note:\n    // No decimal point is generated: the exponent is adjusted instead.\n    //\n    // p2 actually represents the fraction\n    //\n    //      p2 * 2^e\n    //          = p2 / 2^-e\n    //          = d[-1] / 10^1 + d[-2] / 10^2 + ...\n    //\n    // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...)\n    //\n    //      p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m\n    //                      + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...)\n    //\n    // using\n    //\n    //      10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e)\n    //                = (                   d) * 2^-e + (                   r)\n    //\n    // or\n    //      10^m * p2 * 2^e = d + r * 2^e\n    //\n    // i.e.\n    //\n    //      M+ = buffer + p2 * 2^e\n    //         = buffer + 10^-m * (d + r * 2^e)\n    //         = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e\n    //\n    // and stop as soon as 10^-m * r * 2^e <= delta * 2^e\n\n    JSON_ASSERT(p2 > delta);\n\n    int m = 0;\n    for (;;)\n    {\n        // Invariant:\n        //      M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e\n        //         = buffer * 10^-m + 10^-m * (p2                                 ) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * (10 * p2)                   ) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e\n        //\n        JSON_ASSERT(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);\n        p2 *= 10;\n        const std::uint64_t d = p2 >> -one.e;     // d = (10 * p2) div 2^-e\n        const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e\n        //\n        //      M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))\n        //         = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e\n        //\n        JSON_ASSERT(d <= 9);\n        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d\n        //\n        //      M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e\n        //\n        p2 = r;\n        m++;\n        //\n        //      M+ = buffer * 10^-m + 10^-m * p2 * 2^e\n        // Invariant restored.\n\n        // Check if enough digits have been generated.\n        //\n        //      10^-m * p2 * 2^e <= delta * 2^e\n        //              p2 * 2^e <= 10^m * delta * 2^e\n        //                    p2 <= 10^m * delta\n        delta *= 10;\n        dist  *= 10;\n        if (p2 <= delta)\n        {\n            break;\n        }\n    }\n\n    // V = buffer * 10^-m, with M- <= V <= M+.\n\n    decimal_exponent -= m;\n\n    // 1 ulp in the decimal representation is now 10^-m.\n    // Since delta and dist are now scaled by 10^m, we need to do the\n    // same with ulp in order to keep the units in sync.\n    //\n    //      10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e\n    //\n    const std::uint64_t ten_m = one.f;\n    grisu2_round(buffer, length, dist, delta, p2, ten_m);\n\n    // By construction this algorithm generates the shortest possible decimal\n    // number (Loitsch, Theorem 6.2) which rounds back to w.\n    // For an input number of precision p, at least\n    //\n    //      N = 1 + ceil(p * log_10(2))\n    //\n    // decimal digits are sufficient to identify all binary floating-point\n    // numbers (Matula, \"In-and-Out conversions\").\n    // This implies that the algorithm does not produce more than N decimal\n    // digits.\n    //\n    //      N = 17 for p = 53 (IEEE double precision)\n    //      N = 9  for p = 24 (IEEE single precision)\n}\n\n/*!\nv = buf * 10^decimal_exponent\nlen is the length of the buffer (number of decimal digits)\nThe buffer must be large enough, i.e. >= max_digits10.\n*/\nJSON_HEDLEY_NON_NULL(1)\ninline void grisu2(char* buf, int& len, int& decimal_exponent,\n                   diyfp m_minus, diyfp v, diyfp m_plus)\n{\n    JSON_ASSERT(m_plus.e == m_minus.e);\n    JSON_ASSERT(m_plus.e == v.e);\n\n    //  --------(-----------------------+-----------------------)--------    (A)\n    //          m-                      v                       m+\n    //\n    //  --------------------(-----------+-----------------------)--------    (B)\n    //                      m-          v                       m+\n    //\n    // First scale v (and m- and m+) such that the exponent is in the range\n    // [alpha, gamma].\n\n    const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e);\n\n    const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k\n\n    // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma]\n    const diyfp w       = diyfp::mul(v,       c_minus_k);\n    const diyfp w_minus = diyfp::mul(m_minus, c_minus_k);\n    const diyfp w_plus  = diyfp::mul(m_plus,  c_minus_k);\n\n    //  ----(---+---)---------------(---+---)---------------(---+---)----\n    //          w-                      w                       w+\n    //          = c*m-                  = c*v                   = c*m+\n    //\n    // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and\n    // w+ are now off by a small amount.\n    // In fact:\n    //\n    //      w - v * 10^k < 1 ulp\n    //\n    // To account for this inaccuracy, add resp. subtract 1 ulp.\n    //\n    //  --------+---[---------------(---+---)---------------]---+--------\n    //          w-  M-                  w                   M+  w+\n    //\n    // Now any number in [M-, M+] (bounds included) will round to w when input,\n    // regardless of how the input rounding algorithm breaks ties.\n    //\n    // And digit_gen generates the shortest possible such number in [M-, M+].\n    // Note that this does not mean that Grisu2 always generates the shortest\n    // possible number in the interval (m-, m+).\n    const diyfp M_minus(w_minus.f + 1, w_minus.e);\n    const diyfp M_plus (w_plus.f  - 1, w_plus.e );\n\n    decimal_exponent = -cached.k; // = -(-k) = k\n\n    grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);\n}\n\n/*!\nv = buf * 10^decimal_exponent\nlen is the length of the buffer (number of decimal digits)\nThe buffer must be large enough, i.e. >= max_digits10.\n*/\ntemplate<typename FloatType>\nJSON_HEDLEY_NON_NULL(1)\nvoid grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)\n{\n    static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,\n                  \"internal error: not enough precision\");\n\n    JSON_ASSERT(std::isfinite(value));\n    JSON_ASSERT(value > 0);\n\n    // If the neighbors (and boundaries) of 'value' are always computed for double-precision\n    // numbers, all float's can be recovered using strtod (and strtof). However, the resulting\n    // decimal representations are not exactly \"short\".\n    //\n    // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars)\n    // says \"value is converted to a string as if by std::sprintf in the default (\"C\") locale\"\n    // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars'\n    // does.\n    // On the other hand, the documentation for 'std::to_chars' requires that \"parsing the\n    // representation using the corresponding std::from_chars function recovers value exactly\". That\n    // indicates that single precision floating-point numbers should be recovered using\n    // 'std::strtof'.\n    //\n    // NB: If the neighbors are computed for single-precision numbers, there is a single float\n    //     (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision\n    //     value is off by 1 ulp.\n#if 0\n    const boundaries w = compute_boundaries(static_cast<double>(value));\n#else\n    const boundaries w = compute_boundaries(value);\n#endif\n\n    grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);\n}\n\n/*!\n@brief appends a decimal representation of e to buf\n@return a pointer to the element following the exponent.\n@pre -1000 < e < 1000\n*/\nJSON_HEDLEY_NON_NULL(1)\nJSON_HEDLEY_RETURNS_NON_NULL\ninline char* append_exponent(char* buf, int e)\n{\n    JSON_ASSERT(e > -1000);\n    JSON_ASSERT(e <  1000);\n\n    if (e < 0)\n    {\n        e = -e;\n        *buf++ = '-';\n    }\n    else\n    {\n        *buf++ = '+';\n    }\n\n    auto k = static_cast<std::uint32_t>(e);\n    if (k < 10)\n    {\n        // Always print at least two digits in the exponent.\n        // This is for compatibility with printf(\"%g\").\n        *buf++ = '0';\n        *buf++ = static_cast<char>('0' + k);\n    }\n    else if (k < 100)\n    {\n        *buf++ = static_cast<char>('0' + k / 10);\n        k %= 10;\n        *buf++ = static_cast<char>('0' + k);\n    }\n    else\n    {\n        *buf++ = static_cast<char>('0' + k / 100);\n        k %= 100;\n        *buf++ = static_cast<char>('0' + k / 10);\n        k %= 10;\n        *buf++ = static_cast<char>('0' + k);\n    }\n\n    return buf;\n}\n\n/*!\n@brief prettify v = buf * 10^decimal_exponent\n\nIf v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point\nnotation. Otherwise it will be printed in exponential notation.\n\n@pre min_exp < 0\n@pre max_exp > 0\n*/\nJSON_HEDLEY_NON_NULL(1)\nJSON_HEDLEY_RETURNS_NON_NULL\ninline char* format_buffer(char* buf, int len, int decimal_exponent,\n                           int min_exp, int max_exp)\n{\n    JSON_ASSERT(min_exp < 0);\n    JSON_ASSERT(max_exp > 0);\n\n    const int k = len;\n    const int n = len + decimal_exponent;\n\n    // v = buf * 10^(n-k)\n    // k is the length of the buffer (number of decimal digits)\n    // n is the position of the decimal point relative to the start of the buffer.\n\n    if (k <= n && n <= max_exp)\n    {\n        // digits[000]\n        // len <= max_exp + 2\n\n        std::memset(buf + k, '0', static_cast<size_t>(n) - static_cast<size_t>(k));\n        // Make it look like a floating-point number (#362, #378)\n        buf[n + 0] = '.';\n        buf[n + 1] = '0';\n        return buf + (static_cast<size_t>(n) + 2);\n    }\n\n    if (0 < n && n <= max_exp)\n    {\n        // dig.its\n        // len <= max_digits10 + 1\n\n        JSON_ASSERT(k > n);\n\n        std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n, static_cast<size_t>(k) - static_cast<size_t>(n));\n        buf[n] = '.';\n        return buf + (static_cast<size_t>(k) + 1U);\n    }\n\n    if (min_exp < n && n <= 0)\n    {\n        // 0.[000]digits\n        // len <= 2 + (-min_exp - 1) + max_digits10\n\n        std::memmove(buf + (2 + static_cast<size_t>(-n)), buf, static_cast<size_t>(k));\n        buf[0] = '0';\n        buf[1] = '.';\n        std::memset(buf + 2, '0', static_cast<size_t>(-n));\n        return buf + (2U + static_cast<size_t>(-n) + static_cast<size_t>(k));\n    }\n\n    if (k == 1)\n    {\n        // dE+123\n        // len <= 1 + 5\n\n        buf += 1;\n    }\n    else\n    {\n        // d.igitsE+123\n        // len <= max_digits10 + 1 + 5\n\n        std::memmove(buf + 2, buf + 1, static_cast<size_t>(k) - 1);\n        buf[1] = '.';\n        buf += 1 + static_cast<size_t>(k);\n    }\n\n    *buf++ = 'e';\n    return append_exponent(buf, n - 1);\n}\n\n} // namespace dtoa_impl\n\n/*!\n@brief generates a decimal representation of the floating-point number value in [first, last).\n\nThe format of the resulting decimal representation is similar to printf's %g\nformat. Returns an iterator pointing past-the-end of the decimal representation.\n\n@note The input number must be finite, i.e. NaN's and Inf's are not supported.\n@note The buffer must be large enough.\n@note The result is NOT null-terminated.\n*/\ntemplate<typename FloatType>\nJSON_HEDLEY_NON_NULL(1, 2)\nJSON_HEDLEY_RETURNS_NON_NULL\nchar* to_chars(char* first, const char* last, FloatType value)\n{\n    static_cast<void>(last); // maybe unused - fix warning\n    JSON_ASSERT(std::isfinite(value));\n\n    // Use signbit(value) instead of (value < 0) since signbit works for -0.\n    if (std::signbit(value))\n    {\n        value = -value;\n        *first++ = '-';\n    }\n\n    if (value == 0) // +-0\n    {\n        *first++ = '0';\n        // Make it look like a floating-point number (#362, #378)\n        *first++ = '.';\n        *first++ = '0';\n        return first;\n    }\n\n    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10);\n\n    // Compute v = buffer * 10^decimal_exponent.\n    // The decimal digits are stored in the buffer, which needs to be interpreted\n    // as an unsigned decimal integer.\n    // len is the length of the buffer, i.e. the number of decimal digits.\n    int len = 0;\n    int decimal_exponent = 0;\n    dtoa_impl::grisu2(first, len, decimal_exponent, value);\n\n    JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10);\n\n    // Format the buffer like printf(\"%.*g\", prec, value)\n    constexpr int kMinExp = -4;\n    // Use digits10 here to increase compatibility with version 2.\n    constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;\n\n    JSON_ASSERT(last - first >= kMaxExp + 2);\n    JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);\n    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);\n\n    return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);\n}\n\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/output/binary_writer.hpp>\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////\n// serialization //\n///////////////////\n\n/// how to treat decoding errors\nenum class error_handler_t\n{\n    strict,  ///< throw a type_error exception in case of invalid UTF-8\n    replace, ///< replace invalid UTF-8 sequences with U+FFFD\n    ignore   ///< ignore invalid UTF-8 sequences\n};\n\ntemplate<typename BasicJsonType>\nclass serializer\n{\n    using string_t = typename BasicJsonType::string_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using binary_char_t = typename BasicJsonType::binary_t::value_type;\n    static constexpr std::uint8_t UTF8_ACCEPT = 0;\n    static constexpr std::uint8_t UTF8_REJECT = 1;\n\n  public:\n    /*!\n    @param[in] s  output stream to serialize to\n    @param[in] ichar  indentation character to use\n    @param[in] error_handler_  how to react on decoding errors\n    */\n    serializer(output_adapter_t<char> s, const char ichar,\n               error_handler_t error_handler_ = error_handler_t::strict)\n        : o(std::move(s))\n        , loc(std::localeconv())\n        , thousands_sep(loc->thousands_sep == nullptr ? '\\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))\n        , decimal_point(loc->decimal_point == nullptr ? '\\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))\n        , indent_char(ichar)\n        , indent_string(512, indent_char)\n        , error_handler(error_handler_)\n    {}\n\n    // delete because of pointer members\n    serializer(const serializer&) = delete;\n    serializer& operator=(const serializer&) = delete;\n    serializer(serializer&&) = delete;\n    serializer& operator=(serializer&&) = delete;\n    ~serializer() = default;\n\n    /*!\n    @brief internal implementation of the serialization function\n\n    This function is called by the public member function dump and organizes\n    the serialization internally. The indentation level is propagated as\n    additional parameter. In case of arrays and objects, the function is\n    called recursively.\n\n    - strings and object keys are escaped using `escape_string()`\n    - integer numbers are converted implicitly via `operator<<`\n    - floating-point numbers are converted to a string using `\"%g\"` format\n    - binary values are serialized as objects containing the subtype and the\n      byte array\n\n    @param[in] val               value to serialize\n    @param[in] pretty_print      whether the output shall be pretty-printed\n    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters\n    in the output are escaped with `\\uXXXX` sequences, and the result consists\n    of ASCII characters only.\n    @param[in] indent_step       the indent level\n    @param[in] current_indent    the current indent level (only used internally)\n    */\n    void dump(const BasicJsonType& val,\n              const bool pretty_print,\n              const bool ensure_ascii,\n              const unsigned int indent_step,\n              const unsigned int current_indent = 0)\n    {\n        switch (val.m_type)\n        {\n            case value_t::object:\n            {\n                if (val.m_value.object->empty())\n                {\n                    o->write_characters(\"{}\", 2);\n                    return;\n                }\n\n                if (pretty_print)\n                {\n                    o->write_characters(\"{\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    // first n-1 elements\n                    auto i = val.m_value.object->cbegin();\n                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)\n                    {\n                        o->write_characters(indent_string.c_str(), new_indent);\n                        o->write_character('\\\"');\n                        dump_escaped(i->first, ensure_ascii);\n                        o->write_characters(\"\\\": \", 3);\n                        dump(i->second, true, ensure_ascii, indent_step, new_indent);\n                        o->write_characters(\",\\n\", 2);\n                    }\n\n                    // last element\n                    JSON_ASSERT(i != val.m_value.object->cend());\n                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());\n                    o->write_characters(indent_string.c_str(), new_indent);\n                    o->write_character('\\\"');\n                    dump_escaped(i->first, ensure_ascii);\n                    o->write_characters(\"\\\": \", 3);\n                    dump(i->second, true, ensure_ascii, indent_step, new_indent);\n\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character('}');\n                }\n                else\n                {\n                    o->write_character('{');\n\n                    // first n-1 elements\n                    auto i = val.m_value.object->cbegin();\n                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)\n                    {\n                        o->write_character('\\\"');\n                        dump_escaped(i->first, ensure_ascii);\n                        o->write_characters(\"\\\":\", 2);\n                        dump(i->second, false, ensure_ascii, indent_step, current_indent);\n                        o->write_character(',');\n                    }\n\n                    // last element\n                    JSON_ASSERT(i != val.m_value.object->cend());\n                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());\n                    o->write_character('\\\"');\n                    dump_escaped(i->first, ensure_ascii);\n                    o->write_characters(\"\\\":\", 2);\n                    dump(i->second, false, ensure_ascii, indent_step, current_indent);\n\n                    o->write_character('}');\n                }\n\n                return;\n            }\n\n            case value_t::array:\n            {\n                if (val.m_value.array->empty())\n                {\n                    o->write_characters(\"[]\", 2);\n                    return;\n                }\n\n                if (pretty_print)\n                {\n                    o->write_characters(\"[\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    // first n-1 elements\n                    for (auto i = val.m_value.array->cbegin();\n                            i != val.m_value.array->cend() - 1; ++i)\n                    {\n                        o->write_characters(indent_string.c_str(), new_indent);\n                        dump(*i, true, ensure_ascii, indent_step, new_indent);\n                        o->write_characters(\",\\n\", 2);\n                    }\n\n                    // last element\n                    JSON_ASSERT(!val.m_value.array->empty());\n                    o->write_characters(indent_string.c_str(), new_indent);\n                    dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);\n\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character(']');\n                }\n                else\n                {\n                    o->write_character('[');\n\n                    // first n-1 elements\n                    for (auto i = val.m_value.array->cbegin();\n                            i != val.m_value.array->cend() - 1; ++i)\n                    {\n                        dump(*i, false, ensure_ascii, indent_step, current_indent);\n                        o->write_character(',');\n                    }\n\n                    // last element\n                    JSON_ASSERT(!val.m_value.array->empty());\n                    dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);\n\n                    o->write_character(']');\n                }\n\n                return;\n            }\n\n            case value_t::string:\n            {\n                o->write_character('\\\"');\n                dump_escaped(*val.m_value.string, ensure_ascii);\n                o->write_character('\\\"');\n                return;\n            }\n\n            case value_t::binary:\n            {\n                if (pretty_print)\n                {\n                    o->write_characters(\"{\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    o->write_characters(indent_string.c_str(), new_indent);\n\n                    o->write_characters(\"\\\"bytes\\\": [\", 10);\n\n                    if (!val.m_value.binary->empty())\n                    {\n                        for (auto i = val.m_value.binary->cbegin();\n                                i != val.m_value.binary->cend() - 1; ++i)\n                        {\n                            dump_integer(*i);\n                            o->write_characters(\", \", 2);\n                        }\n                        dump_integer(val.m_value.binary->back());\n                    }\n\n                    o->write_characters(\"],\\n\", 3);\n                    o->write_characters(indent_string.c_str(), new_indent);\n\n                    o->write_characters(\"\\\"subtype\\\": \", 11);\n                    if (val.m_value.binary->has_subtype())\n                    {\n                        dump_integer(val.m_value.binary->subtype());\n                    }\n                    else\n                    {\n                        o->write_characters(\"null\", 4);\n                    }\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character('}');\n                }\n                else\n                {\n                    o->write_characters(\"{\\\"bytes\\\":[\", 10);\n\n                    if (!val.m_value.binary->empty())\n                    {\n                        for (auto i = val.m_value.binary->cbegin();\n                                i != val.m_value.binary->cend() - 1; ++i)\n                        {\n                            dump_integer(*i);\n                            o->write_character(',');\n                        }\n                        dump_integer(val.m_value.binary->back());\n                    }\n\n                    o->write_characters(\"],\\\"subtype\\\":\", 12);\n                    if (val.m_value.binary->has_subtype())\n                    {\n                        dump_integer(val.m_value.binary->subtype());\n                        o->write_character('}');\n                    }\n                    else\n                    {\n                        o->write_characters(\"null}\", 5);\n                    }\n                }\n                return;\n            }\n\n            case value_t::boolean:\n            {\n                if (val.m_value.boolean)\n                {\n                    o->write_characters(\"true\", 4);\n                }\n                else\n                {\n                    o->write_characters(\"false\", 5);\n                }\n                return;\n            }\n\n            case value_t::number_integer:\n            {\n                dump_integer(val.m_value.number_integer);\n                return;\n            }\n\n            case value_t::number_unsigned:\n            {\n                dump_integer(val.m_value.number_unsigned);\n                return;\n            }\n\n            case value_t::number_float:\n            {\n                dump_float(val.m_value.number_float);\n                return;\n            }\n\n            case value_t::discarded:\n            {\n                o->write_characters(\"<discarded>\", 11);\n                return;\n            }\n\n            case value_t::null:\n            {\n                o->write_characters(\"null\", 4);\n                return;\n            }\n\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false);  // LCOV_EXCL_LINE\n        }\n    }\n\n  private:\n    /*!\n    @brief dump escaped string\n\n    Escape a string by replacing certain special characters by a sequence of an\n    escape character (backslash) and another character and other control\n    characters by a sequence of \"\\u\" followed by a four-digit hex\n    representation. The escaped string is written to output stream @a o.\n\n    @param[in] s  the string to escape\n    @param[in] ensure_ascii  whether to escape non-ASCII characters with\n                             \\uXXXX sequences\n\n    @complexity Linear in the length of string @a s.\n    */\n    void dump_escaped(const string_t& s, const bool ensure_ascii)\n    {\n        std::uint32_t codepoint;\n        std::uint8_t state = UTF8_ACCEPT;\n        std::size_t bytes = 0;  // number of bytes written to string_buffer\n\n        // number of bytes written at the point of the last valid byte\n        std::size_t bytes_after_last_accept = 0;\n        std::size_t undumped_chars = 0;\n\n        for (std::size_t i = 0; i < s.size(); ++i)\n        {\n            const auto byte = static_cast<uint8_t>(s[i]);\n\n            switch (decode(state, codepoint, byte))\n            {\n                case UTF8_ACCEPT:  // decode found a new code point\n                {\n                    switch (codepoint)\n                    {\n                        case 0x08: // backspace\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'b';\n                            break;\n                        }\n\n                        case 0x09: // horizontal tab\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 't';\n                            break;\n                        }\n\n                        case 0x0A: // newline\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'n';\n                            break;\n                        }\n\n                        case 0x0C: // formfeed\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'f';\n                            break;\n                        }\n\n                        case 0x0D: // carriage return\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'r';\n                            break;\n                        }\n\n                        case 0x22: // quotation mark\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = '\\\"';\n                            break;\n                        }\n\n                        case 0x5C: // reverse solidus\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = '\\\\';\n                            break;\n                        }\n\n                        default:\n                        {\n                            // escape control characters (0x00..0x1F) or, if\n                            // ensure_ascii parameter is used, non-ASCII characters\n                            if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F)))\n                            {\n                                if (codepoint <= 0xFFFF)\n                                {\n                                    (std::snprintf)(string_buffer.data() + bytes, 7, \"\\\\u%04x\",\n                                                    static_cast<std::uint16_t>(codepoint));\n                                    bytes += 6;\n                                }\n                                else\n                                {\n                                    (std::snprintf)(string_buffer.data() + bytes, 13, \"\\\\u%04x\\\\u%04x\",\n                                                    static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),\n                                                    static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu)));\n                                    bytes += 12;\n                                }\n                            }\n                            else\n                            {\n                                // copy byte to buffer (all previous bytes\n                                // been copied have in default case above)\n                                string_buffer[bytes++] = s[i];\n                            }\n                            break;\n                        }\n                    }\n\n                    // write buffer and reset index; there must be 13 bytes\n                    // left, as this is the maximal number of bytes to be\n                    // written (\"\\uxxxx\\uxxxx\\0\") for one code point\n                    if (string_buffer.size() - bytes < 13)\n                    {\n                        o->write_characters(string_buffer.data(), bytes);\n                        bytes = 0;\n                    }\n\n                    // remember the byte position of this accept\n                    bytes_after_last_accept = bytes;\n                    undumped_chars = 0;\n                    break;\n                }\n\n                case UTF8_REJECT:  // decode found invalid UTF-8 byte\n                {\n                    switch (error_handler)\n                    {\n                        case error_handler_t::strict:\n                        {\n                            std::string sn(3, '\\0');\n                            (std::snprintf)(&sn[0], sn.size(), \"%.2X\", byte);\n                            JSON_THROW(type_error::create(316, \"invalid UTF-8 byte at index \" + std::to_string(i) + \": 0x\" + sn));\n                        }\n\n                        case error_handler_t::ignore:\n                        case error_handler_t::replace:\n                        {\n                            // in case we saw this character the first time, we\n                            // would like to read it again, because the byte\n                            // may be OK for itself, but just not OK for the\n                            // previous sequence\n                            if (undumped_chars > 0)\n                            {\n                                --i;\n                            }\n\n                            // reset length buffer to the last accepted index;\n                            // thus removing/ignoring the invalid characters\n                            bytes = bytes_after_last_accept;\n\n                            if (error_handler == error_handler_t::replace)\n                            {\n                                // add a replacement character\n                                if (ensure_ascii)\n                                {\n                                    string_buffer[bytes++] = '\\\\';\n                                    string_buffer[bytes++] = 'u';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'd';\n                                }\n                                else\n                                {\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xEF');\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xBF');\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xBD');\n                                }\n\n                                // write buffer and reset index; there must be 13 bytes\n                                // left, as this is the maximal number of bytes to be\n                                // written (\"\\uxxxx\\uxxxx\\0\") for one code point\n                                if (string_buffer.size() - bytes < 13)\n                                {\n                                    o->write_characters(string_buffer.data(), bytes);\n                                    bytes = 0;\n                                }\n\n                                bytes_after_last_accept = bytes;\n                            }\n\n                            undumped_chars = 0;\n\n                            // continue processing the string\n                            state = UTF8_ACCEPT;\n                            break;\n                        }\n\n                        default:            // LCOV_EXCL_LINE\n                            JSON_ASSERT(false);  // LCOV_EXCL_LINE\n                    }\n                    break;\n                }\n\n                default:  // decode found yet incomplete multi-byte code point\n                {\n                    if (!ensure_ascii)\n                    {\n                        // code point will not be escaped - copy byte to buffer\n                        string_buffer[bytes++] = s[i];\n                    }\n                    ++undumped_chars;\n                    break;\n                }\n            }\n        }\n\n        // we finished processing the string\n        if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))\n        {\n            // write buffer\n            if (bytes > 0)\n            {\n                o->write_characters(string_buffer.data(), bytes);\n            }\n        }\n        else\n        {\n            // we finish reading, but do not accept: string was incomplete\n            switch (error_handler)\n            {\n                case error_handler_t::strict:\n                {\n                    std::string sn(3, '\\0');\n                    (std::snprintf)(&sn[0], sn.size(), \"%.2X\", static_cast<std::uint8_t>(s.back()));\n                    JSON_THROW(type_error::create(316, \"incomplete UTF-8 string; last byte: 0x\" + sn));\n                }\n\n                case error_handler_t::ignore:\n                {\n                    // write all accepted bytes\n                    o->write_characters(string_buffer.data(), bytes_after_last_accept);\n                    break;\n                }\n\n                case error_handler_t::replace:\n                {\n                    // write all accepted bytes\n                    o->write_characters(string_buffer.data(), bytes_after_last_accept);\n                    // add a replacement character\n                    if (ensure_ascii)\n                    {\n                        o->write_characters(\"\\\\ufffd\", 6);\n                    }\n                    else\n                    {\n                        o->write_characters(\"\\xEF\\xBF\\xBD\", 3);\n                    }\n                    break;\n                }\n\n                default:            // LCOV_EXCL_LINE\n                    JSON_ASSERT(false);  // LCOV_EXCL_LINE\n            }\n        }\n    }\n\n    /*!\n    @brief count digits\n\n    Count the number of decimal (base 10) digits for an input unsigned integer.\n\n    @param[in] x  unsigned integer number to count its digits\n    @return    number of decimal digits\n    */\n    inline unsigned int count_digits(number_unsigned_t x) noexcept\n    {\n        unsigned int n_digits = 1;\n        for (;;)\n        {\n            if (x < 10)\n            {\n                return n_digits;\n            }\n            if (x < 100)\n            {\n                return n_digits + 1;\n            }\n            if (x < 1000)\n            {\n                return n_digits + 2;\n            }\n            if (x < 10000)\n            {\n                return n_digits + 3;\n            }\n            x = x / 10000u;\n            n_digits += 4;\n        }\n    }\n\n    /*!\n    @brief dump an integer\n\n    Dump a given integer to output stream @a o. Works internally with\n    @a number_buffer.\n\n    @param[in] x  integer number (signed or unsigned) to dump\n    @tparam NumberType either @a number_integer_t or @a number_unsigned_t\n    */\n    template < typename NumberType, detail::enable_if_t <\n                   std::is_same<NumberType, number_unsigned_t>::value ||\n                   std::is_same<NumberType, number_integer_t>::value ||\n                   std::is_same<NumberType, binary_char_t>::value,\n                   int > = 0 >\n    void dump_integer(NumberType x)\n    {\n        static constexpr std::array<std::array<char, 2>, 100> digits_to_99\n        {\n            {\n                {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}},\n                {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}},\n                {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}},\n                {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}},\n                {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}},\n                {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}},\n                {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}},\n                {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}},\n                {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}},\n                {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}},\n            }\n        };\n\n        // special case for \"0\"\n        if (x == 0)\n        {\n            o->write_character('0');\n            return;\n        }\n\n        // use a pointer to fill the buffer\n        auto buffer_ptr = number_buffer.begin();\n\n        const bool is_negative = std::is_same<NumberType, number_integer_t>::value && !(x >= 0); // see issue #755\n        number_unsigned_t abs_value;\n\n        unsigned int n_chars;\n\n        if (is_negative)\n        {\n            *buffer_ptr = '-';\n            abs_value = remove_sign(static_cast<number_integer_t>(x));\n\n            // account one more byte for the minus sign\n            n_chars = 1 + count_digits(abs_value);\n        }\n        else\n        {\n            abs_value = static_cast<number_unsigned_t>(x);\n            n_chars = count_digits(abs_value);\n        }\n\n        // spare 1 byte for '\\0'\n        JSON_ASSERT(n_chars < number_buffer.size() - 1);\n\n        // jump to the end to generate the string from backward\n        // so we later avoid reversing the result\n        buffer_ptr += n_chars;\n\n        // Fast int2ascii implementation inspired by \"Fastware\" talk by Andrei Alexandrescu\n        // See: https://www.youtube.com/watch?v=o4-CwDo2zpg\n        while (abs_value >= 100)\n        {\n            const auto digits_index = static_cast<unsigned>((abs_value % 100));\n            abs_value /= 100;\n            *(--buffer_ptr) = digits_to_99[digits_index][1];\n            *(--buffer_ptr) = digits_to_99[digits_index][0];\n        }\n\n        if (abs_value >= 10)\n        {\n            const auto digits_index = static_cast<unsigned>(abs_value);\n            *(--buffer_ptr) = digits_to_99[digits_index][1];\n            *(--buffer_ptr) = digits_to_99[digits_index][0];\n        }\n        else\n        {\n            *(--buffer_ptr) = static_cast<char>('0' + abs_value);\n        }\n\n        o->write_characters(number_buffer.data(), n_chars);\n    }\n\n    /*!\n    @brief dump a floating-point number\n\n    Dump a given floating-point number to output stream @a o. Works internally\n    with @a number_buffer.\n\n    @param[in] x  floating-point number to dump\n    */\n    void dump_float(number_float_t x)\n    {\n        // NaN / inf\n        if (!std::isfinite(x))\n        {\n            o->write_characters(\"null\", 4);\n            return;\n        }\n\n        // If number_float_t is an IEEE-754 single or double precision number,\n        // use the Grisu2 algorithm to produce short numbers which are\n        // guaranteed to round-trip, using strtof and strtod, resp.\n        //\n        // NB: The test below works if <long double> == <double>.\n        static constexpr bool is_ieee_single_or_double\n            = (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128) ||\n              (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024);\n\n        dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());\n    }\n\n    void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)\n    {\n        char* begin = number_buffer.data();\n        char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);\n\n        o->write_characters(begin, static_cast<size_t>(end - begin));\n    }\n\n    void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)\n    {\n        // get number of digits for a float -> text -> float round-trip\n        static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;\n\n        // the actual conversion\n        std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), \"%.*g\", d, x);\n\n        // negative value indicates an error\n        JSON_ASSERT(len > 0);\n        // check if buffer was large enough\n        JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());\n\n        // erase thousands separator\n        if (thousands_sep != '\\0')\n        {\n            const auto end = std::remove(number_buffer.begin(),\n                                         number_buffer.begin() + len, thousands_sep);\n            std::fill(end, number_buffer.end(), '\\0');\n            JSON_ASSERT((end - number_buffer.begin()) <= len);\n            len = (end - number_buffer.begin());\n        }\n\n        // convert decimal point to '.'\n        if (decimal_point != '\\0' && decimal_point != '.')\n        {\n            const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);\n            if (dec_pos != number_buffer.end())\n            {\n                *dec_pos = '.';\n            }\n        }\n\n        o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));\n\n        // determine if need to append \".0\"\n        const bool value_is_int_like =\n            std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,\n                         [](char c)\n        {\n            return c == '.' || c == 'e';\n        });\n\n        if (value_is_int_like)\n        {\n            o->write_characters(\".0\", 2);\n        }\n    }\n\n    /*!\n    @brief check whether a string is UTF-8 encoded\n\n    The function checks each byte of a string whether it is UTF-8 encoded. The\n    result of the check is stored in the @a state parameter. The function must\n    be called initially with state 0 (accept). State 1 means the string must\n    be rejected, because the current byte is not allowed. If the string is\n    completely processed, but the state is non-zero, the string ended\n    prematurely; that is, the last byte indicated more bytes should have\n    followed.\n\n    @param[in,out] state  the state of the decoding\n    @param[in,out] codep  codepoint (valid only if resulting state is UTF8_ACCEPT)\n    @param[in] byte       next byte to decode\n    @return               new state\n\n    @note The function has been edited: a std::array is used.\n\n    @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>\n    @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/\n    */\n    static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept\n    {\n        static const std::array<std::uint8_t, 400> utf8d =\n        {\n            {\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F\n                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F\n                7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF\n                8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF\n                0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF\n                0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF\n                0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0\n                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2\n                1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4\n                1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6\n                1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8\n            }\n        };\n\n        const std::uint8_t type = utf8d[byte];\n\n        codep = (state != UTF8_ACCEPT)\n                ? (byte & 0x3fu) | (codep << 6u)\n                : (0xFFu >> type) & (byte);\n\n        std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);\n        JSON_ASSERT(index < 400);\n        state = utf8d[index];\n        return state;\n    }\n\n    /*\n     * Overload to make the compiler happy while it is instantiating\n     * dump_integer for number_unsigned_t.\n     * Must never be called.\n     */\n    number_unsigned_t remove_sign(number_unsigned_t x)\n    {\n        JSON_ASSERT(false); // LCOV_EXCL_LINE\n        return x; // LCOV_EXCL_LINE\n    }\n\n    /*\n     * Helper function for dump_integer\n     *\n     * This function takes a negative signed integer and returns its absolute\n     * value as unsigned integer. The plus/minus shuffling is necessary as we can\n     * not directly remove the sign of an arbitrary signed integer as the\n     * absolute values of INT_MIN and INT_MAX are usually not the same. See\n     * #1708 for details.\n     */\n    inline number_unsigned_t remove_sign(number_integer_t x) noexcept\n    {\n        JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)());\n        return static_cast<number_unsigned_t>(-(x + 1)) + 1;\n    }\n\n  private:\n    /// the output of the serializer\n    output_adapter_t<char> o = nullptr;\n\n    /// a (hopefully) large enough character buffer\n    std::array<char, 64> number_buffer{{}};\n\n    /// the locale\n    const std::lconv* loc = nullptr;\n    /// the locale's thousand separator character\n    const char thousands_sep = '\\0';\n    /// the locale's decimal point character\n    const char decimal_point = '\\0';\n\n    /// string buffer\n    std::array<char, 512> string_buffer{{}};\n\n    /// the indentation character\n    const char indent_char;\n    /// the indentation string\n    string_t indent_string;\n\n    /// error_handler how to react on decoding errors\n    const error_handler_t error_handler;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/value_t.hpp>\n\n// #include <nlohmann/json_fwd.hpp>\n\n// #include <nlohmann/ordered_map.hpp>\n\n\n#include <functional> // less\n#include <memory> // allocator\n#include <utility> // pair\n#include <vector> // vector\n\nnamespace nlohmann\n{\n\n/// ordered_map: a minimal map-like container that preserves insertion order\n/// for use within nlohmann::basic_json<ordered_map>\ntemplate <class Key, class T, class IgnoredLess = std::less<Key>,\n          class Allocator = std::allocator<std::pair<const Key, T>>>\n                  struct ordered_map : std::vector<std::pair<const Key, T>, Allocator>\n{\n    using key_type = Key;\n    using mapped_type = T;\n    using Container = std::vector<std::pair<const Key, T>, Allocator>;\n    using typename Container::iterator;\n    using typename Container::const_iterator;\n    using typename Container::size_type;\n    using typename Container::value_type;\n\n    // Explicit constructors instead of `using Container::Container`\n    // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)\n    ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {}\n    template <class It>\n    ordered_map(It first, It last, const Allocator& alloc = Allocator())\n        : Container{first, last, alloc} {}\n    ordered_map(std::initializer_list<T> init, const Allocator& alloc = Allocator() )\n        : Container{init, alloc} {}\n\n    std::pair<iterator, bool> emplace(const key_type& key, T&& t)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return {it, false};\n            }\n        }\n        Container::emplace_back(key, t);\n        return {--this->end(), true};\n    }\n\n    T& operator[](const Key& key)\n    {\n        return emplace(key, T{}).first->second;\n    }\n\n    const T& operator[](const Key& key) const\n    {\n        return at(key);\n    }\n\n    T& at(const Key& key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return it->second;\n            }\n        }\n\n        throw std::out_of_range(\"key not found\");\n    }\n\n    const T& at(const Key& key) const\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return it->second;\n            }\n        }\n\n        throw std::out_of_range(\"key not found\");\n    }\n\n    size_type erase(const Key& key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                // Since we cannot move const Keys, re-construct them in place\n                for (auto next = it; ++next != this->end(); ++it)\n                {\n                    it->~value_type(); // Destroy but keep allocation\n                    new (&*it) value_type{std::move(*next)};\n                }\n                Container::pop_back();\n                return 1;\n            }\n        }\n        return 0;\n    }\n\n    iterator erase(iterator pos)\n    {\n        auto it = pos;\n\n        // Since we cannot move const Keys, re-construct them in place\n        for (auto next = it; ++next != this->end(); ++it)\n        {\n            it->~value_type(); // Destroy but keep allocation\n            new (&*it) value_type{std::move(*next)};\n        }\n        Container::pop_back();\n        return pos;\n    }\n\n    size_type count(const Key& key) const\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return 1;\n            }\n        }\n        return 0;\n    }\n\n    iterator find(const Key& key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return it;\n            }\n        }\n        return Container::end();\n    }\n\n    const_iterator find(const Key& key) const\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == key)\n            {\n                return it;\n            }\n        }\n        return Container::end();\n    }\n\n    std::pair<iterator, bool> insert( value_type&& value )\n    {\n        return emplace(value.first, std::move(value.second));\n    }\n\n    std::pair<iterator, bool> insert( const value_type& value )\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (it->first == value.first)\n            {\n                return {it, false};\n            }\n        }\n        Container::push_back(value);\n        return {--this->end(), true};\n    }\n};\n\n}  // namespace nlohmann\n\n\n/*!\n@brief namespace for Niels Lohmann\n@see https://github.com/nlohmann\n@since version 1.0.0\n*/\nnamespace nlohmann\n{\n\n/*!\n@brief a class to store JSON values\n\n@tparam ObjectType type for JSON objects (`std::map` by default; will be used\nin @ref object_t)\n@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used\nin @ref array_t)\n@tparam StringType type for JSON strings and object keys (`std::string` by\ndefault; will be used in @ref string_t)\n@tparam BooleanType type for JSON booleans (`bool` by default; will be used\nin @ref boolean_t)\n@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by\ndefault; will be used in @ref number_integer_t)\n@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c\n`uint64_t` by default; will be used in @ref number_unsigned_t)\n@tparam NumberFloatType type for JSON floating-point numbers (`double` by\ndefault; will be used in @ref number_float_t)\n@tparam BinaryType type for packed binary data for compatibility with binary\nserialization formats (`std::vector<std::uint8_t>` by default; will be used in\n@ref binary_t)\n@tparam AllocatorType type of the allocator to use (`std::allocator` by\ndefault)\n@tparam JSONSerializer the serializer to resolve internal calls to `to_json()`\nand `from_json()` (@ref adl_serializer by default)\n\n@requirement The class satisfies the following concept requirements:\n- Basic\n - [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible):\n   JSON values can be default constructed. The result will be a JSON null\n   value.\n - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible):\n   A JSON value can be constructed from an rvalue argument.\n - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible):\n   A JSON value can be copy-constructed from an lvalue expression.\n - [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable):\n   A JSON value van be assigned from an rvalue argument.\n - [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable):\n   A JSON value can be copy-assigned from an lvalue expression.\n - [Destructible](https://en.cppreference.com/w/cpp/named_req/Destructible):\n   JSON values can be destructed.\n- Layout\n - [StandardLayoutType](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType):\n   JSON values have\n   [standard layout](https://en.cppreference.com/w/cpp/language/data_members#Standard_layout):\n   All non-static data members are private and standard layout types, the\n   class has no virtual functions or (virtual) base classes.\n- Library-wide\n - [EqualityComparable](https://en.cppreference.com/w/cpp/named_req/EqualityComparable):\n   JSON values can be compared with `==`, see @ref\n   operator==(const_reference,const_reference).\n - [LessThanComparable](https://en.cppreference.com/w/cpp/named_req/LessThanComparable):\n   JSON values can be compared with `<`, see @ref\n   operator<(const_reference,const_reference).\n - [Swappable](https://en.cppreference.com/w/cpp/named_req/Swappable):\n   Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of\n   other compatible types, using unqualified function call @ref swap().\n - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer):\n   JSON values can be compared against `std::nullptr_t` objects which are used\n   to model the `null` value.\n- Container\n - [Container](https://en.cppreference.com/w/cpp/named_req/Container):\n   JSON values can be used like STL containers and provide iterator access.\n - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer);\n   JSON values can be used like STL containers and provide reverse iterator\n   access.\n\n@invariant The member variables @a m_value and @a m_type have the following\nrelationship:\n- If `m_type == value_t::object`, then `m_value.object != nullptr`.\n- If `m_type == value_t::array`, then `m_value.array != nullptr`.\n- If `m_type == value_t::string`, then `m_value.string != nullptr`.\nThe invariants are checked by member function assert_invariant().\n\n@internal\n@note ObjectType trick from https://stackoverflow.com/a/9860911\n@endinternal\n\n@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange\nFormat](http://rfc7159.net/rfc7159)\n\n@since version 1.0.0\n\n@nosubgrouping\n*/\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nclass basic_json\n{\n  private:\n    template<detail::value_t> friend struct detail::external_constructor;\n    friend ::nlohmann::json_pointer<basic_json>;\n\n    template<typename BasicJsonType, typename InputType>\n    friend class ::nlohmann::detail::parser;\n    friend ::nlohmann::detail::serializer<basic_json>;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::iter_impl;\n    template<typename BasicJsonType, typename CharType>\n    friend class ::nlohmann::detail::binary_writer;\n    template<typename BasicJsonType, typename InputType, typename SAX>\n    friend class ::nlohmann::detail::binary_reader;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::json_sax_dom_parser;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::json_sax_dom_callback_parser;\n\n    /// workaround type for MSVC\n    using basic_json_t = NLOHMANN_BASIC_JSON_TPL;\n\n    // convenience aliases for types residing in namespace detail;\n    using lexer = ::nlohmann::detail::lexer_base<basic_json>;\n\n    template<typename InputAdapterType>\n    static ::nlohmann::detail::parser<basic_json, InputAdapterType> parser(\n        InputAdapterType adapter,\n        detail::parser_callback_t<basic_json>cb = nullptr,\n        const bool allow_exceptions = true,\n        const bool ignore_comments = false\n                                 )\n    {\n        return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),\n                std::move(cb), allow_exceptions, ignore_comments);\n    }\n\n    using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;\n    template<typename BasicJsonType>\n    using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;\n    template<typename BasicJsonType>\n    using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>;\n    template<typename Iterator>\n    using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>;\n    template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>;\n\n    template<typename CharType>\n    using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>;\n\n    template<typename InputType>\n    using binary_reader = ::nlohmann::detail::binary_reader<basic_json, InputType>;\n    template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;\n\n    using serializer = ::nlohmann::detail::serializer<basic_json>;\n\n  public:\n    using value_t = detail::value_t;\n    /// JSON Pointer, see @ref nlohmann::json_pointer\n    using json_pointer = ::nlohmann::json_pointer<basic_json>;\n    template<typename T, typename SFINAE>\n    using json_serializer = JSONSerializer<T, SFINAE>;\n    /// how to treat decoding errors\n    using error_handler_t = detail::error_handler_t;\n    /// how to treat CBOR tags\n    using cbor_tag_handler_t = detail::cbor_tag_handler_t;\n    /// helper type for initializer lists of basic_json values\n    using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;\n\n    using input_format_t = detail::input_format_t;\n    /// SAX interface type, see @ref nlohmann::json_sax\n    using json_sax_t = json_sax<basic_json>;\n\n    ////////////////\n    // exceptions //\n    ////////////////\n\n    /// @name exceptions\n    /// Classes to implement user-defined exceptions.\n    /// @{\n\n    /// @copydoc detail::exception\n    using exception = detail::exception;\n    /// @copydoc detail::parse_error\n    using parse_error = detail::parse_error;\n    /// @copydoc detail::invalid_iterator\n    using invalid_iterator = detail::invalid_iterator;\n    /// @copydoc detail::type_error\n    using type_error = detail::type_error;\n    /// @copydoc detail::out_of_range\n    using out_of_range = detail::out_of_range;\n    /// @copydoc detail::other_error\n    using other_error = detail::other_error;\n\n    /// @}\n\n\n    /////////////////////\n    // container types //\n    /////////////////////\n\n    /// @name container types\n    /// The canonic container types to use @ref basic_json like any other STL\n    /// container.\n    /// @{\n\n    /// the type of elements in a basic_json container\n    using value_type = basic_json;\n\n    /// the type of an element reference\n    using reference = value_type&;\n    /// the type of an element const reference\n    using const_reference = const value_type&;\n\n    /// a type to represent differences between iterators\n    using difference_type = std::ptrdiff_t;\n    /// a type to represent container sizes\n    using size_type = std::size_t;\n\n    /// the allocator type\n    using allocator_type = AllocatorType<basic_json>;\n\n    /// the type of an element pointer\n    using pointer = typename std::allocator_traits<allocator_type>::pointer;\n    /// the type of an element const pointer\n    using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;\n\n    /// an iterator for a basic_json container\n    using iterator = iter_impl<basic_json>;\n    /// a const iterator for a basic_json container\n    using const_iterator = iter_impl<const basic_json>;\n    /// a reverse iterator for a basic_json container\n    using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>;\n    /// a const reverse iterator for a basic_json container\n    using const_reverse_iterator = json_reverse_iterator<typename basic_json::const_iterator>;\n\n    /// @}\n\n\n    /*!\n    @brief returns the allocator associated with the container\n    */\n    static allocator_type get_allocator()\n    {\n        return allocator_type();\n    }\n\n    /*!\n    @brief returns version information on the library\n\n    This function returns a JSON object with information about the library,\n    including the version number and information on the platform and compiler.\n\n    @return JSON object holding version information\n    key         | description\n    ----------- | ---------------\n    `compiler`  | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version).\n    `copyright` | The copyright line for the library as string.\n    `name`      | The name of the library as string.\n    `platform`  | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`.\n    `url`       | The URL of the project as string.\n    `version`   | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string).\n\n    @liveexample{The following code shows an example output of the `meta()`\n    function.,meta}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @complexity Constant.\n\n    @since 2.1.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json meta()\n    {\n        basic_json result;\n\n        result[\"copyright\"] = \"(C) 2013-2020 Niels Lohmann\";\n        result[\"name\"] = \"JSON for Modern C++\";\n        result[\"url\"] = \"https://github.com/nlohmann/json\";\n        result[\"version\"][\"string\"] =\n            std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + \".\" +\n            std::to_string(NLOHMANN_JSON_VERSION_MINOR) + \".\" +\n            std::to_string(NLOHMANN_JSON_VERSION_PATCH);\n        result[\"version\"][\"major\"] = NLOHMANN_JSON_VERSION_MAJOR;\n        result[\"version\"][\"minor\"] = NLOHMANN_JSON_VERSION_MINOR;\n        result[\"version\"][\"patch\"] = NLOHMANN_JSON_VERSION_PATCH;\n\n#ifdef _WIN32\n        result[\"platform\"] = \"win32\";\n#elif defined __linux__\n        result[\"platform\"] = \"linux\";\n#elif defined __APPLE__\n        result[\"platform\"] = \"apple\";\n#elif defined __unix__\n        result[\"platform\"] = \"unix\";\n#else\n        result[\"platform\"] = \"unknown\";\n#endif\n\n#if defined(__ICC) || defined(__INTEL_COMPILER)\n        result[\"compiler\"] = {{\"family\", \"icc\"}, {\"version\", __INTEL_COMPILER}};\n#elif defined(__clang__)\n        result[\"compiler\"] = {{\"family\", \"clang\"}, {\"version\", __clang_version__}};\n#elif defined(__GNUC__) || defined(__GNUG__)\n        result[\"compiler\"] = {{\"family\", \"gcc\"}, {\"version\", std::to_string(__GNUC__) + \".\" + std::to_string(__GNUC_MINOR__) + \".\" + std::to_string(__GNUC_PATCHLEVEL__)}};\n#elif defined(__HP_cc) || defined(__HP_aCC)\n        result[\"compiler\"] = \"hp\"\n#elif defined(__IBMCPP__)\n        result[\"compiler\"] = {{\"family\", \"ilecpp\"}, {\"version\", __IBMCPP__}};\n#elif defined(_MSC_VER)\n        result[\"compiler\"] = {{\"family\", \"msvc\"}, {\"version\", _MSC_VER}};\n#elif defined(__PGI)\n        result[\"compiler\"] = {{\"family\", \"pgcpp\"}, {\"version\", __PGI}};\n#elif defined(__SUNPRO_CC)\n        result[\"compiler\"] = {{\"family\", \"sunpro\"}, {\"version\", __SUNPRO_CC}};\n#else\n        result[\"compiler\"] = {{\"family\", \"unknown\"}, {\"version\", \"unknown\"}};\n#endif\n\n#ifdef __cplusplus\n        result[\"compiler\"][\"c++\"] = std::to_string(__cplusplus);\n#else\n        result[\"compiler\"][\"c++\"] = \"unknown\";\n#endif\n        return result;\n    }\n\n\n    ///////////////////////////\n    // JSON value data types //\n    ///////////////////////////\n\n    /// @name JSON value data types\n    /// The data types to store a JSON value. These types are derived from\n    /// the template arguments passed to class @ref basic_json.\n    /// @{\n\n#if defined(JSON_HAS_CPP_14)\n    // Use transparent comparator if possible, combined with perfect forwarding\n    // on find() and count() calls prevents unnecessary string construction.\n    using object_comparator_t = std::less<>;\n#else\n    using object_comparator_t = std::less<StringType>;\n#endif\n\n    /*!\n    @brief a type for an object\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows:\n    > An object is an unordered collection of zero or more name/value pairs,\n    > where a name is a string and a value is a string, number, boolean, null,\n    > object, or array.\n\n    To store objects in C++, a type is defined by the template parameters\n    described below.\n\n    @tparam ObjectType  the container to store objects (e.g., `std::map` or\n    `std::unordered_map`)\n    @tparam StringType the type of the keys or names (e.g., `std::string`).\n    The comparison function `std::less<StringType>` is used to order elements\n    inside the container.\n    @tparam AllocatorType the allocator to use for objects (e.g.,\n    `std::allocator`)\n\n    #### Default type\n\n    With the default values for @a ObjectType (`std::map`), @a StringType\n    (`std::string`), and @a AllocatorType (`std::allocator`), the default\n    value for @a object_t is:\n\n    @code {.cpp}\n    std::map<\n      std::string, // key_type\n      basic_json, // value_type\n      std::less<std::string>, // key_compare\n      std::allocator<std::pair<const std::string, basic_json>> // allocator_type\n    >\n    @endcode\n\n    #### Behavior\n\n    The choice of @a object_t influences the behavior of the JSON class. With\n    the default type, objects have the following behavior:\n\n    - When all names are unique, objects will be interoperable in the sense\n      that all software implementations receiving that object will agree on\n      the name-value mappings.\n    - When the names within an object are not unique, it is unspecified which\n      one of the values for a given key will be chosen. For instance,\n      `{\"key\": 2, \"key\": 1}` could be equal to either `{\"key\": 1}` or\n      `{\"key\": 2}`.\n    - Internally, name/value pairs are stored in lexicographical order of the\n      names. Objects will also be serialized (see @ref dump) in this order.\n      For instance, `{\"b\": 1, \"a\": 2}` and `{\"a\": 2, \"b\": 1}` will be stored\n      and serialized as `{\"a\": 2, \"b\": 1}`.\n    - When comparing objects, the order of the name/value pairs is irrelevant.\n      This makes objects interoperable in the sense that they will not be\n      affected by these differences. For instance, `{\"b\": 1, \"a\": 2}` and\n      `{\"a\": 2, \"b\": 1}` will be treated as equal.\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) specifies:\n    > An implementation may set limits on the maximum depth of nesting.\n\n    In this class, the object's limit of nesting is not explicitly constrained.\n    However, a maximum depth of nesting may be introduced by the compiler or\n    runtime environment. A theoretical limit can be queried by calling the\n    @ref max_size function of a JSON object.\n\n    #### Storage\n\n    Objects are stored as pointers in a @ref basic_json type. That is, for any\n    access to object values, a pointer of type `object_t*` must be\n    dereferenced.\n\n    @sa @ref array_t -- type for an array value\n\n    @since version 1.0.0\n\n    @note The order name/value pairs are added to the object is *not*\n    preserved by the library. Therefore, iterating an object may return\n    name/value pairs in a different order than they were originally stored. In\n    fact, keys will be traversed in alphabetical order as `std::map` with\n    `std::less` is used by default. Please note this behavior conforms to [RFC\n    7159](http://rfc7159.net/rfc7159), because any order implements the\n    specified \"unordered\" nature of JSON objects.\n    */\n    using object_t = ObjectType<StringType,\n          basic_json,\n          object_comparator_t,\n          AllocatorType<std::pair<const StringType,\n          basic_json>>>;\n\n    /*!\n    @brief a type for an array\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows:\n    > An array is an ordered sequence of zero or more values.\n\n    To store objects in C++, a type is defined by the template parameters\n    explained below.\n\n    @tparam ArrayType  container type to store arrays (e.g., `std::vector` or\n    `std::list`)\n    @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`)\n\n    #### Default type\n\n    With the default values for @a ArrayType (`std::vector`) and @a\n    AllocatorType (`std::allocator`), the default value for @a array_t is:\n\n    @code {.cpp}\n    std::vector<\n      basic_json, // value_type\n      std::allocator<basic_json> // allocator_type\n    >\n    @endcode\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) specifies:\n    > An implementation may set limits on the maximum depth of nesting.\n\n    In this class, the array's limit of nesting is not explicitly constrained.\n    However, a maximum depth of nesting may be introduced by the compiler or\n    runtime environment. A theoretical limit can be queried by calling the\n    @ref max_size function of a JSON array.\n\n    #### Storage\n\n    Arrays are stored as pointers in a @ref basic_json type. That is, for any\n    access to array values, a pointer of type `array_t*` must be dereferenced.\n\n    @sa @ref object_t -- type for an object value\n\n    @since version 1.0.0\n    */\n    using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;\n\n    /*!\n    @brief a type for a string\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows:\n    > A string is a sequence of zero or more Unicode characters.\n\n    To store objects in C++, a type is defined by the template parameter\n    described below. Unicode values are split by the JSON class into\n    byte-sized characters during deserialization.\n\n    @tparam StringType  the container to store strings (e.g., `std::string`).\n    Note this container is used for keys/names in objects, see @ref object_t.\n\n    #### Default type\n\n    With the default values for @a StringType (`std::string`), the default\n    value for @a string_t is:\n\n    @code {.cpp}\n    std::string\n    @endcode\n\n    #### Encoding\n\n    Strings are stored in UTF-8 encoding. Therefore, functions like\n    `std::string::size()` or `std::string::length()` return the number of\n    bytes in the string rather than the number of characters or glyphs.\n\n    #### String comparison\n\n    [RFC 7159](http://rfc7159.net/rfc7159) states:\n    > Software implementations are typically required to test names of object\n    > members for equality. Implementations that transform the textual\n    > representation into sequences of Unicode code units and then perform the\n    > comparison numerically, code unit by code unit, are interoperable in the\n    > sense that implementations will agree in all cases on equality or\n    > inequality of two strings. For example, implementations that compare\n    > strings with escaped characters unconverted may incorrectly find that\n    > `\"a\\\\b\"` and `\"a\\u005Cb\"` are not equal.\n\n    This implementation is interoperable as it does compare strings code unit\n    by code unit.\n\n    #### Storage\n\n    String values are stored as pointers in a @ref basic_json type. That is,\n    for any access to string values, a pointer of type `string_t*` must be\n    dereferenced.\n\n    @since version 1.0.0\n    */\n    using string_t = StringType;\n\n    /*!\n    @brief a type for a boolean\n\n    [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a\n    type which differentiates the two literals `true` and `false`.\n\n    To store objects in C++, a type is defined by the template parameter @a\n    BooleanType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a BooleanType (`bool`), the default value for\n    @a boolean_t is:\n\n    @code {.cpp}\n    bool\n    @endcode\n\n    #### Storage\n\n    Boolean values are stored directly inside a @ref basic_json type.\n\n    @since version 1.0.0\n    */\n    using boolean_t = BooleanType;\n\n    /*!\n    @brief a type for a number (integer)\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store integer numbers in C++, a type is defined by the template\n    parameter @a NumberIntegerType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberIntegerType (`int64_t`), the default\n    value for @a number_integer_t is:\n\n    @code {.cpp}\n    int64_t\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in integer literals lead to an interpretation as octal\n      number. Internally, the value will be stored as decimal number. For\n      instance, the C++ integer literal `010` will be serialized to `8`.\n      During deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) specifies:\n    > An implementation may set limits on the range and precision of numbers.\n\n    When the default type is used, the maximal integer number that can be\n    stored is `9223372036854775807` (INT64_MAX) and the minimal integer number\n    that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers\n    that are out of range will yield over/underflow when used in a\n    constructor. During deserialization, too large or small integer numbers\n    will be automatically be stored as @ref number_unsigned_t or @ref\n    number_float_t.\n\n    [RFC 7159](http://rfc7159.net/rfc7159) further states:\n    > Note that when such software is used, numbers that are integers and are\n    > in the range \\f$[-2^{53}+1, 2^{53}-1]\\f$ are interoperable in the sense\n    > that implementations will agree exactly on their numeric values.\n\n    As this range is a subrange of the exactly supported range [INT64_MIN,\n    INT64_MAX], this class's integer type is interoperable.\n\n    #### Storage\n\n    Integer number values are stored directly inside a @ref basic_json type.\n\n    @sa @ref number_float_t -- type for number values (floating-point)\n\n    @sa @ref number_unsigned_t -- type for number values (unsigned integer)\n\n    @since version 1.0.0\n    */\n    using number_integer_t = NumberIntegerType;\n\n    /*!\n    @brief a type for a number (unsigned)\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store unsigned integer numbers in C++, a type is defined by the\n    template parameter @a NumberUnsignedType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberUnsignedType (`uint64_t`), the\n    default value for @a number_unsigned_t is:\n\n    @code {.cpp}\n    uint64_t\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in integer literals lead to an interpretation as octal\n      number. Internally, the value will be stored as decimal number. For\n      instance, the C++ integer literal `010` will be serialized to `8`.\n      During deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) specifies:\n    > An implementation may set limits on the range and precision of numbers.\n\n    When the default type is used, the maximal integer number that can be\n    stored is `18446744073709551615` (UINT64_MAX) and the minimal integer\n    number that can be stored is `0`. Integer numbers that are out of range\n    will yield over/underflow when used in a constructor. During\n    deserialization, too large or small integer numbers will be automatically\n    be stored as @ref number_integer_t or @ref number_float_t.\n\n    [RFC 7159](http://rfc7159.net/rfc7159) further states:\n    > Note that when such software is used, numbers that are integers and are\n    > in the range \\f$[-2^{53}+1, 2^{53}-1]\\f$ are interoperable in the sense\n    > that implementations will agree exactly on their numeric values.\n\n    As this range is a subrange (when considered in conjunction with the\n    number_integer_t type) of the exactly supported range [0, UINT64_MAX],\n    this class's integer type is interoperable.\n\n    #### Storage\n\n    Integer number values are stored directly inside a @ref basic_json type.\n\n    @sa @ref number_float_t -- type for number values (floating-point)\n    @sa @ref number_integer_t -- type for number values (integer)\n\n    @since version 2.0.0\n    */\n    using number_unsigned_t = NumberUnsignedType;\n\n    /*!\n    @brief a type for a number (floating-point)\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store floating-point numbers in C++, a type is defined by the template\n    parameter @a NumberFloatType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberFloatType (`double`), the default\n    value for @a number_float_t is:\n\n    @code {.cpp}\n    double\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in floating-point literals will be ignored. Internally,\n      the value will be stored as decimal number. For instance, the C++\n      floating-point literal `01.2` will be serialized to `1.2`. During\n      deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) states:\n    > This specification allows implementations to set limits on the range and\n    > precision of numbers accepted. Since software that implements IEEE\n    > 754-2008 binary64 (double precision) numbers is generally available and\n    > widely used, good interoperability can be achieved by implementations\n    > that expect no more precision or range than these provide, in the sense\n    > that implementations will approximate JSON numbers within the expected\n    > precision.\n\n    This implementation does exactly follow this approach, as it uses double\n    precision floating-point numbers. Note values smaller than\n    `-1.79769313486232e+308` and values greater than `1.79769313486232e+308`\n    will be stored as NaN internally and be serialized to `null`.\n\n    #### Storage\n\n    Floating-point number values are stored directly inside a @ref basic_json\n    type.\n\n    @sa @ref number_integer_t -- type for number values (integer)\n\n    @sa @ref number_unsigned_t -- type for number values (unsigned integer)\n\n    @since version 1.0.0\n    */\n    using number_float_t = NumberFloatType;\n\n    /*!\n    @brief a type for a packed binary type\n\n    This type is a type designed to carry binary data that appears in various\n    serialized formats, such as CBOR's Major Type 2, MessagePack's bin, and\n    BSON's generic binary subtype. This type is NOT a part of standard JSON and\n    exists solely for compatibility with these binary types. As such, it is\n    simply defined as an ordered sequence of zero or more byte values.\n\n    Additionally, as an implementation detail, the subtype of the binary data is\n    carried around as a `std::uint8_t`, which is compatible with both of the\n    binary data formats that use binary subtyping, (though the specific\n    numbering is incompatible with each other, and it is up to the user to\n    translate between them).\n\n    [CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type\n    as:\n    > Major type 2: a byte string. The string's length in bytes is represented\n    > following the rules for positive integers (major type 0).\n\n    [MessagePack's documentation on the bin type\n    family](https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family)\n    describes this type as:\n    > Bin format family stores an byte array in 2, 3, or 5 bytes of extra bytes\n    > in addition to the size of the byte array.\n\n    [BSON's specifications](http://bsonspec.org/spec.html) describe several\n    binary types; however, this type is intended to represent the generic binary\n    type which has the description:\n    > Generic binary subtype - This is the most commonly used binary subtype and\n    > should be the 'default' for drivers and tools.\n\n    None of these impose any limitations on the internal representation other\n    than the basic unit of storage be some type of array whose parts are\n    decomposable into bytes.\n\n    The default representation of this binary format is a\n    `std::vector<std::uint8_t>`, which is a very common way to represent a byte\n    array in modern C++.\n\n    #### Default type\n\n    The default values for @a BinaryType is `std::vector<std::uint8_t>`\n\n    #### Storage\n\n    Binary Arrays are stored as pointers in a @ref basic_json type. That is,\n    for any access to array values, a pointer of the type `binary_t*` must be\n    dereferenced.\n\n    #### Notes on subtypes\n\n    - CBOR\n       - Binary values are represented as byte strings. No subtypes are\n         supported and will be ignored when CBOR is written.\n    - MessagePack\n       - If a subtype is given and the binary array contains exactly 1, 2, 4, 8,\n         or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8)\n         is used. For other sizes, the ext family (ext8, ext16, ext32) is used.\n         The subtype is then added as singed 8-bit integer.\n       - If no subtype is given, the bin family (bin8, bin16, bin32) is used.\n    - BSON\n       - If a subtype is given, it is used and added as unsigned 8-bit integer.\n       - If no subtype is given, the generic binary subtype 0x00 is used.\n\n    @sa @ref binary -- create a binary array\n\n    @since version 3.8.0\n    */\n    using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;\n    /// @}\n\n  private:\n\n    /// helper for exception-safe object creation\n    template<typename T, typename... Args>\n    JSON_HEDLEY_RETURNS_NON_NULL\n    static T* create(Args&& ... args)\n    {\n        AllocatorType<T> alloc;\n        using AllocatorTraits = std::allocator_traits<AllocatorType<T>>;\n\n        auto deleter = [&](T * object)\n        {\n            AllocatorTraits::deallocate(alloc, object, 1);\n        };\n        std::unique_ptr<T, decltype(deleter)> object(AllocatorTraits::allocate(alloc, 1), deleter);\n        AllocatorTraits::construct(alloc, object.get(), std::forward<Args>(args)...);\n        JSON_ASSERT(object != nullptr);\n        return object.release();\n    }\n\n    ////////////////////////\n    // JSON value storage //\n    ////////////////////////\n\n    /*!\n    @brief a JSON value\n\n    The actual storage for a JSON value of the @ref basic_json class. This\n    union combines the different storage types for the JSON value types\n    defined in @ref value_t.\n\n    JSON type | value_t type    | used type\n    --------- | --------------- | ------------------------\n    object    | object          | pointer to @ref object_t\n    array     | array           | pointer to @ref array_t\n    string    | string          | pointer to @ref string_t\n    boolean   | boolean         | @ref boolean_t\n    number    | number_integer  | @ref number_integer_t\n    number    | number_unsigned | @ref number_unsigned_t\n    number    | number_float    | @ref number_float_t\n    binary    | binary          | pointer to @ref binary_t\n    null      | null            | *no value is stored*\n\n    @note Variable-length types (objects, arrays, and strings) are stored as\n    pointers. The size of the union should not exceed 64 bits if the default\n    value types are used.\n\n    @since version 1.0.0\n    */\n    union json_value\n    {\n        /// object (stored with pointer to save storage)\n        object_t* object;\n        /// array (stored with pointer to save storage)\n        array_t* array;\n        /// string (stored with pointer to save storage)\n        string_t* string;\n        /// binary (stored with pointer to save storage)\n        binary_t* binary;\n        /// boolean\n        boolean_t boolean;\n        /// number (integer)\n        number_integer_t number_integer;\n        /// number (unsigned integer)\n        number_unsigned_t number_unsigned;\n        /// number (floating-point)\n        number_float_t number_float;\n\n        /// default constructor (for null values)\n        json_value() = default;\n        /// constructor for booleans\n        json_value(boolean_t v) noexcept : boolean(v) {}\n        /// constructor for numbers (integer)\n        json_value(number_integer_t v) noexcept : number_integer(v) {}\n        /// constructor for numbers (unsigned)\n        json_value(number_unsigned_t v) noexcept : number_unsigned(v) {}\n        /// constructor for numbers (floating-point)\n        json_value(number_float_t v) noexcept : number_float(v) {}\n        /// constructor for empty values of a given type\n        json_value(value_t t)\n        {\n            switch (t)\n            {\n                case value_t::object:\n                {\n                    object = create<object_t>();\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    array = create<array_t>();\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    string = create<string_t>(\"\");\n                    break;\n                }\n\n                case value_t::binary:\n                {\n                    binary = create<binary_t>();\n                    break;\n                }\n\n                case value_t::boolean:\n                {\n                    boolean = boolean_t(false);\n                    break;\n                }\n\n                case value_t::number_integer:\n                {\n                    number_integer = number_integer_t(0);\n                    break;\n                }\n\n                case value_t::number_unsigned:\n                {\n                    number_unsigned = number_unsigned_t(0);\n                    break;\n                }\n\n                case value_t::number_float:\n                {\n                    number_float = number_float_t(0.0);\n                    break;\n                }\n\n                case value_t::null:\n                {\n                    object = nullptr;  // silence warning, see #821\n                    break;\n                }\n\n                default:\n                {\n                    object = nullptr;  // silence warning, see #821\n                    if (JSON_HEDLEY_UNLIKELY(t == value_t::null))\n                    {\n                        JSON_THROW(other_error::create(500, \"961c151d2e87f2686a955a9be24d316f1362bf21 3.9.1\")); // LCOV_EXCL_LINE\n                    }\n                    break;\n                }\n            }\n        }\n\n        /// constructor for strings\n        json_value(const string_t& value)\n        {\n            string = create<string_t>(value);\n        }\n\n        /// constructor for rvalue strings\n        json_value(string_t&& value)\n        {\n            string = create<string_t>(std::move(value));\n        }\n\n        /// constructor for objects\n        json_value(const object_t& value)\n        {\n            object = create<object_t>(value);\n        }\n\n        /// constructor for rvalue objects\n        json_value(object_t&& value)\n        {\n            object = create<object_t>(std::move(value));\n        }\n\n        /// constructor for arrays\n        json_value(const array_t& value)\n        {\n            array = create<array_t>(value);\n        }\n\n        /// constructor for rvalue arrays\n        json_value(array_t&& value)\n        {\n            array = create<array_t>(std::move(value));\n        }\n\n        /// constructor for binary arrays\n        json_value(const typename binary_t::container_type& value)\n        {\n            binary = create<binary_t>(value);\n        }\n\n        /// constructor for rvalue binary arrays\n        json_value(typename binary_t::container_type&& value)\n        {\n            binary = create<binary_t>(std::move(value));\n        }\n\n        /// constructor for binary arrays (internal type)\n        json_value(const binary_t& value)\n        {\n            binary = create<binary_t>(value);\n        }\n\n        /// constructor for rvalue binary arrays (internal type)\n        json_value(binary_t&& value)\n        {\n            binary = create<binary_t>(std::move(value));\n        }\n\n        void destroy(value_t t) noexcept\n        {\n            // flatten the current json_value to a heap-allocated stack\n            std::vector<basic_json> stack;\n\n            // move the top-level items to stack\n            if (t == value_t::array)\n            {\n                stack.reserve(array->size());\n                std::move(array->begin(), array->end(), std::back_inserter(stack));\n            }\n            else if (t == value_t::object)\n            {\n                stack.reserve(object->size());\n                for (auto&& it : *object)\n                {\n                    stack.push_back(std::move(it.second));\n                }\n            }\n\n            while (!stack.empty())\n            {\n                // move the last item to local variable to be processed\n                basic_json current_item(std::move(stack.back()));\n                stack.pop_back();\n\n                // if current_item is array/object, move\n                // its children to the stack to be processed later\n                if (current_item.is_array())\n                {\n                    std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(),\n                              std::back_inserter(stack));\n\n                    current_item.m_value.array->clear();\n                }\n                else if (current_item.is_object())\n                {\n                    for (auto&& it : *current_item.m_value.object)\n                    {\n                        stack.push_back(std::move(it.second));\n                    }\n\n                    current_item.m_value.object->clear();\n                }\n\n                // it's now safe that current_item get destructed\n                // since it doesn't have any children\n            }\n\n            switch (t)\n            {\n                case value_t::object:\n                {\n                    AllocatorType<object_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, object);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    AllocatorType<array_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, array);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);\n                    break;\n                }\n\n                case value_t::binary:\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, binary, 1);\n                    break;\n                }\n\n                default:\n                {\n                    break;\n                }\n            }\n        }\n    };\n\n    /*!\n    @brief checks the class invariants\n\n    This function asserts the class invariants. It needs to be called at the\n    end of every constructor to make sure that created objects respect the\n    invariant. Furthermore, it has to be called each time the type of a JSON\n    value is changed, because the invariant expresses a relationship between\n    @a m_type and @a m_value.\n    */\n    void assert_invariant() const noexcept\n    {\n        JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr);\n        JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr);\n        JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr);\n        JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr);\n    }\n\n  public:\n    //////////////////////////\n    // JSON parser callback //\n    //////////////////////////\n\n    /*!\n    @brief parser event types\n\n    The parser callback distinguishes the following events:\n    - `object_start`: the parser read `{` and started to process a JSON object\n    - `key`: the parser read a key of a value in an object\n    - `object_end`: the parser read `}` and finished processing a JSON object\n    - `array_start`: the parser read `[` and started to process a JSON array\n    - `array_end`: the parser read `]` and finished processing a JSON array\n    - `value`: the parser finished reading a JSON value\n\n    @image html callback_events.png \"Example when certain parse events are triggered\"\n\n    @sa @ref parser_callback_t for more information and examples\n    */\n    using parse_event_t = detail::parse_event_t;\n\n    /*!\n    @brief per-element parser callback type\n\n    With a parser callback function, the result of parsing a JSON text can be\n    influenced. When passed to @ref parse, it is called on certain events\n    (passed as @ref parse_event_t via parameter @a event) with a set recursion\n    depth @a depth and context JSON value @a parsed. The return value of the\n    callback function is a boolean indicating whether the element that emitted\n    the callback shall be kept or not.\n\n    We distinguish six scenarios (determined by the event type) in which the\n    callback function can be called. The following table describes the values\n    of the parameters @a depth, @a event, and @a parsed.\n\n    parameter @a event | description | parameter @a depth | parameter @a parsed\n    ------------------ | ----------- | ------------------ | -------------------\n    parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded\n    parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key\n    parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object\n    parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded\n    parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array\n    parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value\n\n    @image html callback_events.png \"Example when certain parse events are triggered\"\n\n    Discarding a value (i.e., returning `false`) has different effects\n    depending on the context in which function was called:\n\n    - Discarded values in structured types are skipped. That is, the parser\n      will behave as if the discarded value was never read.\n    - In case a value outside a structured type is skipped, it is replaced\n      with `null`. This case happens if the top-level element is skipped.\n\n    @param[in] depth  the depth of the recursion during parsing\n\n    @param[in] event  an event of type parse_event_t indicating the context in\n    the callback function has been called\n\n    @param[in,out] parsed  the current intermediate parse result; note that\n    writing to this value has no effect for parse_event_t::key events\n\n    @return Whether the JSON value which called the function during parsing\n    should be kept (`true`) or not (`false`). In the latter case, it is either\n    skipped completely or replaced by an empty discarded object.\n\n    @sa @ref parse for examples\n\n    @since version 1.0.0\n    */\n    using parser_callback_t = detail::parser_callback_t<basic_json>;\n\n    //////////////////\n    // constructors //\n    //////////////////\n\n    /// @name constructors and destructors\n    /// Constructors of class @ref basic_json, copy/move constructor, copy\n    /// assignment, static functions creating objects, and the destructor.\n    /// @{\n\n    /*!\n    @brief create an empty value with a given type\n\n    Create an empty JSON value with a given type. The value will be default\n    initialized with an empty value which depends on the type:\n\n    Value type  | initial value\n    ----------- | -------------\n    null        | `null`\n    boolean     | `false`\n    string      | `\"\"`\n    number      | `0`\n    object      | `{}`\n    array       | `[]`\n    binary      | empty array\n\n    @param[in] v  the type of the value to create\n\n    @complexity Constant.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows the constructor for different @ref\n    value_t values,basic_json__value_t}\n\n    @sa @ref clear() -- restores the postcondition of this constructor\n\n    @since version 1.0.0\n    */\n    basic_json(const value_t v)\n        : m_type(v), m_value(v)\n    {\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a null object\n\n    Create a `null` JSON value. It either takes a null pointer as parameter\n    (explicitly creating `null`) or no parameter (implicitly creating `null`).\n    The passed null pointer itself is not read -- it is only used to choose\n    the right constructor.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this constructor never throws\n    exceptions.\n\n    @liveexample{The following code shows the constructor with and without a\n    null pointer parameter.,basic_json__nullptr_t}\n\n    @since version 1.0.0\n    */\n    basic_json(std::nullptr_t = nullptr) noexcept\n        : basic_json(value_t::null)\n    {\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a JSON value\n\n    This is a \"catch all\" constructor for all compatible JSON types; that is,\n    types for which a `to_json()` method exists. The constructor forwards the\n    parameter @a val to that method (to `json_serializer<U>::to_json` method\n    with `U = uncvref_t<CompatibleType>`, to be exact).\n\n    Template type @a CompatibleType includes, but is not limited to, the\n    following types:\n    - **arrays**: @ref array_t and all kinds of compatible containers such as\n      `std::vector`, `std::deque`, `std::list`, `std::forward_list`,\n      `std::array`, `std::valarray`, `std::set`, `std::unordered_set`,\n      `std::multiset`, and `std::unordered_multiset` with a `value_type` from\n      which a @ref basic_json value can be constructed.\n    - **objects**: @ref object_t and all kinds of compatible associative\n      containers such as `std::map`, `std::unordered_map`, `std::multimap`,\n      and `std::unordered_multimap` with a `key_type` compatible to\n      @ref string_t and a `value_type` from which a @ref basic_json value can\n      be constructed.\n    - **strings**: @ref string_t, string literals, and all compatible string\n      containers can be used.\n    - **numbers**: @ref number_integer_t, @ref number_unsigned_t,\n      @ref number_float_t, and all convertible number types such as `int`,\n      `size_t`, `int64_t`, `float` or `double` can be used.\n    - **boolean**: @ref boolean_t / `bool` can be used.\n    - **binary**: @ref binary_t / `std::vector<uint8_t>` may be used,\n      unfortunately because string literals cannot be distinguished from binary\n      character arrays by the C++ type system, all types compatible with `const\n      char*` will be directed to the string constructor instead.  This is both\n      for backwards compatibility, and due to the fact that a binary type is not\n      a standard JSON type.\n\n    See the examples below.\n\n    @tparam CompatibleType a type such that:\n    - @a CompatibleType is not derived from `std::istream`,\n    - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move\n         constructors),\n    - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments)\n    - @a CompatibleType is not a @ref basic_json nested type (e.g.,\n         @ref json_pointer, @ref iterator, etc ...)\n    - @ref @ref json_serializer<U> has a\n         `to_json(basic_json_t&, CompatibleType&&)` method\n\n    @tparam U = `uncvref_t<CompatibleType>`\n\n    @param[in] val the value to be forwarded to the respective constructor\n\n    @complexity Usually linear in the size of the passed @a val, also\n                depending on the implementation of the called `to_json()`\n                method.\n\n    @exceptionsafety Depends on the called constructor. For types directly\n    supported by the library (i.e., all types for which no `to_json()` function\n    was provided), strong guarantee holds: if an exception is thrown, there are\n    no changes to any JSON value.\n\n    @liveexample{The following code shows the constructor with several\n    compatible types.,basic_json__CompatibleType}\n\n    @since version 2.1.0\n    */\n    template < typename CompatibleType,\n               typename U = detail::uncvref_t<CompatibleType>,\n               detail::enable_if_t <\n                   !detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value, int > = 0 >\n    basic_json(CompatibleType && val) noexcept(noexcept(\n                JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),\n                                           std::forward<CompatibleType>(val))))\n    {\n        JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a JSON value from an existing one\n\n    This is a constructor for existing @ref basic_json types.\n    It does not hijack copy/move constructors, since the parameter has different\n    template arguments than the current ones.\n\n    The constructor tries to convert the internal @ref m_value of the parameter.\n\n    @tparam BasicJsonType a type such that:\n    - @a BasicJsonType is a @ref basic_json type.\n    - @a BasicJsonType has different template arguments than @ref basic_json_t.\n\n    @param[in] val the @ref basic_json value to be converted.\n\n    @complexity Usually linear in the size of the passed @a val, also\n                depending on the implementation of the called `to_json()`\n                method.\n\n    @exceptionsafety Depends on the called constructor. For types directly\n    supported by the library (i.e., all types for which no `to_json()` function\n    was provided), strong guarantee holds: if an exception is thrown, there are\n    no changes to any JSON value.\n\n    @since version 3.2.0\n    */\n    template < typename BasicJsonType,\n               detail::enable_if_t <\n                   detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 >\n    basic_json(const BasicJsonType& val)\n    {\n        using other_boolean_t = typename BasicJsonType::boolean_t;\n        using other_number_float_t = typename BasicJsonType::number_float_t;\n        using other_number_integer_t = typename BasicJsonType::number_integer_t;\n        using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n        using other_string_t = typename BasicJsonType::string_t;\n        using other_object_t = typename BasicJsonType::object_t;\n        using other_array_t = typename BasicJsonType::array_t;\n        using other_binary_t = typename BasicJsonType::binary_t;\n\n        switch (val.type())\n        {\n            case value_t::boolean:\n                JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());\n                break;\n            case value_t::number_float:\n                JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());\n                break;\n            case value_t::number_integer:\n                JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());\n                break;\n            case value_t::number_unsigned:\n                JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());\n                break;\n            case value_t::string:\n                JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());\n                break;\n            case value_t::object:\n                JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());\n                break;\n            case value_t::array:\n                JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());\n                break;\n            case value_t::binary:\n                JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());\n                break;\n            case value_t::null:\n                *this = nullptr;\n                break;\n            case value_t::discarded:\n                m_type = value_t::discarded;\n                break;\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false);  // LCOV_EXCL_LINE\n        }\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a container (array or object) from an initializer list\n\n    Creates a JSON value of type array or object from the passed initializer\n    list @a init. In case @a type_deduction is `true` (default), the type of\n    the JSON value to be created is deducted from the initializer list @a init\n    according to the following rules:\n\n    1. If the list is empty, an empty JSON object value `{}` is created.\n    2. If the list consists of pairs whose first element is a string, a JSON\n       object value is created where the first elements of the pairs are\n       treated as keys and the second elements are as values.\n    3. In all other cases, an array is created.\n\n    The rules aim to create the best fit between a C++ initializer list and\n    JSON values. The rationale is as follows:\n\n    1. The empty initializer list is written as `{}` which is exactly an empty\n       JSON object.\n    2. C++ has no way of describing mapped types other than to list a list of\n       pairs. As JSON requires that keys must be of type string, rule 2 is the\n       weakest constraint one can pose on initializer lists to interpret them\n       as an object.\n    3. In all other cases, the initializer list could not be interpreted as\n       JSON object type, so interpreting it as JSON array type is safe.\n\n    With the rules described above, the following JSON values cannot be\n    expressed by an initializer list:\n\n    - the empty array (`[]`): use @ref array(initializer_list_t)\n      with an empty initializer list in this case\n    - arrays whose elements satisfy rule 2: use @ref\n      array(initializer_list_t) with the same initializer list\n      in this case\n\n    @note When used without parentheses around an empty initializer list, @ref\n    basic_json() is called instead of this function, yielding the JSON null\n    value.\n\n    @param[in] init  initializer list with JSON values\n\n    @param[in] type_deduction internal parameter; when set to `true`, the type\n    of the JSON value is deducted from the initializer list @a init; when set\n    to `false`, the type provided via @a manual_type is forced. This mode is\n    used by the functions @ref array(initializer_list_t) and\n    @ref object(initializer_list_t).\n\n    @param[in] manual_type internal parameter; when @a type_deduction is set\n    to `false`, the created JSON value will use the provided type (only @ref\n    value_t::array and @ref value_t::object are valid); when @a type_deduction\n    is set to `true`, this parameter has no effect\n\n    @throw type_error.301 if @a type_deduction is `false`, @a manual_type is\n    `value_t::object`, but @a init contains an element which is not a pair\n    whose first element is a string. In this case, the constructor could not\n    create an object. If @a type_deduction would have be `true`, an array\n    would have been created. See @ref object(initializer_list_t)\n    for an example.\n\n    @complexity Linear in the size of the initializer list @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The example below shows how JSON values are created from\n    initializer lists.,basic_json__list_init_t}\n\n    @sa @ref array(initializer_list_t) -- create a JSON array\n    value from an initializer list\n    @sa @ref object(initializer_list_t) -- create a JSON object\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    basic_json(initializer_list_t init,\n               bool type_deduction = true,\n               value_t manual_type = value_t::array)\n    {\n        // check if each element is an array with two elements whose first\n        // element is a string\n        bool is_an_object = std::all_of(init.begin(), init.end(),\n                                        [](const detail::json_ref<basic_json>& element_ref)\n        {\n            return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string();\n        });\n\n        // adjust type if type deduction is not wanted\n        if (!type_deduction)\n        {\n            // if array is wanted, do not create an object though possible\n            if (manual_type == value_t::array)\n            {\n                is_an_object = false;\n            }\n\n            // if object is wanted but impossible, throw an exception\n            if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))\n            {\n                JSON_THROW(type_error::create(301, \"cannot create object from initializer list\"));\n            }\n        }\n\n        if (is_an_object)\n        {\n            // the initializer list is a list of pairs -> create object\n            m_type = value_t::object;\n            m_value = value_t::object;\n\n            std::for_each(init.begin(), init.end(), [this](const detail::json_ref<basic_json>& element_ref)\n            {\n                auto element = element_ref.moved_or_copied();\n                m_value.object->emplace(\n                    std::move(*((*element.m_value.array)[0].m_value.string)),\n                    std::move((*element.m_value.array)[1]));\n            });\n        }\n        else\n        {\n            // the initializer list describes an array -> create array\n            m_type = value_t::array;\n            m_value.array = create<array_t>(init.begin(), init.end());\n        }\n\n        assert_invariant();\n    }\n\n    /*!\n    @brief explicitly create a binary array (without subtype)\n\n    Creates a JSON binary array value from a given binary container. Binary\n    values are part of various binary formats, such as CBOR, MessagePack, and\n    BSON. This constructor is used to create a value for serialization to those\n    formats.\n\n    @note Note, this function exists because of the difficulty in correctly\n    specifying the correct template overload in the standard value ctor, as both\n    JSON arrays and JSON binary arrays are backed with some form of a\n    `std::vector`. Because JSON binary arrays are a non-standard extension it\n    was decided that it would be best to prevent automatic initialization of a\n    binary array type, for backwards compatibility and so it does not happen on\n    accident.\n\n    @param[in] init container containing bytes to use as binary type\n\n    @return JSON binary array value\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @since version 3.8.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(const typename binary_t::container_type& init)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = init;\n        return res;\n    }\n\n    /*!\n    @brief explicitly create a binary array (with subtype)\n\n    Creates a JSON binary array value from a given binary container. Binary\n    values are part of various binary formats, such as CBOR, MessagePack, and\n    BSON. This constructor is used to create a value for serialization to those\n    formats.\n\n    @note Note, this function exists because of the difficulty in correctly\n    specifying the correct template overload in the standard value ctor, as both\n    JSON arrays and JSON binary arrays are backed with some form of a\n    `std::vector`. Because JSON binary arrays are a non-standard extension it\n    was decided that it would be best to prevent automatic initialization of a\n    binary array type, for backwards compatibility and so it does not happen on\n    accident.\n\n    @param[in] init container containing bytes to use as binary type\n    @param[in] subtype subtype to use in MessagePack and BSON\n\n    @return JSON binary array value\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @since version 3.8.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(const typename binary_t::container_type& init, std::uint8_t subtype)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = binary_t(init, subtype);\n        return res;\n    }\n\n    /// @copydoc binary(const typename binary_t::container_type&)\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(typename binary_t::container_type&& init)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = std::move(init);\n        return res;\n    }\n\n    /// @copydoc binary(const typename binary_t::container_type&, std::uint8_t)\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(typename binary_t::container_type&& init, std::uint8_t subtype)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = binary_t(std::move(init), subtype);\n        return res;\n    }\n\n    /*!\n    @brief explicitly create an array from an initializer list\n\n    Creates a JSON array value from a given initializer list. That is, given a\n    list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the\n    initializer list is empty, the empty array `[]` is created.\n\n    @note This function is only needed to express two edge cases that cannot\n    be realized with the initializer list constructor (@ref\n    basic_json(initializer_list_t, bool, value_t)). These cases\n    are:\n    1. creating an array whose elements are all pairs whose first element is a\n    string -- in this case, the initializer list constructor would create an\n    object, taking the first elements as keys\n    2. creating an empty array -- passing the empty initializer list to the\n    initializer list constructor yields an empty object\n\n    @param[in] init  initializer list with JSON values to create an array from\n    (optional)\n\n    @return JSON array value\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows an example for the `array`\n    function.,array}\n\n    @sa @ref basic_json(initializer_list_t, bool, value_t) --\n    create a JSON value from an initializer list\n    @sa @ref object(initializer_list_t) -- create a JSON object\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json array(initializer_list_t init = {})\n    {\n        return basic_json(init, false, value_t::array);\n    }\n\n    /*!\n    @brief explicitly create an object from an initializer list\n\n    Creates a JSON object value from a given initializer list. The initializer\n    lists elements must be pairs, and their first elements must be strings. If\n    the initializer list is empty, the empty object `{}` is created.\n\n    @note This function is only added for symmetry reasons. In contrast to the\n    related function @ref array(initializer_list_t), there are\n    no cases which can only be expressed by this function. That is, any\n    initializer list @a init can also be passed to the initializer list\n    constructor @ref basic_json(initializer_list_t, bool, value_t).\n\n    @param[in] init  initializer list to create an object from (optional)\n\n    @return JSON object value\n\n    @throw type_error.301 if @a init is not a list of pairs whose first\n    elements are strings. In this case, no object can be created. When such a\n    value is passed to @ref basic_json(initializer_list_t, bool, value_t),\n    an array would have been created from the passed initializer list @a init.\n    See example below.\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows an example for the `object`\n    function.,object}\n\n    @sa @ref basic_json(initializer_list_t, bool, value_t) --\n    create a JSON value from an initializer list\n    @sa @ref array(initializer_list_t) -- create a JSON array\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json object(initializer_list_t init = {})\n    {\n        return basic_json(init, false, value_t::object);\n    }\n\n    /*!\n    @brief construct an array with count copies of given value\n\n    Constructs a JSON array value by creating @a cnt copies of a passed value.\n    In case @a cnt is `0`, an empty array is created.\n\n    @param[in] cnt  the number of JSON copies of @a val to create\n    @param[in] val  the JSON value to copy\n\n    @post `std::distance(begin(),end()) == cnt` holds.\n\n    @complexity Linear in @a cnt.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows examples for the @ref\n    basic_json(size_type\\, const basic_json&)\n    constructor.,basic_json__size_type_basic_json}\n\n    @since version 1.0.0\n    */\n    basic_json(size_type cnt, const basic_json& val)\n        : m_type(value_t::array)\n    {\n        m_value.array = create<array_t>(cnt, val);\n        assert_invariant();\n    }\n\n    /*!\n    @brief construct a JSON container given an iterator range\n\n    Constructs the JSON value with the contents of the range `[first, last)`.\n    The semantics depends on the different types a JSON value can have:\n    - In case of a null type, invalid_iterator.206 is thrown.\n    - In case of other primitive types (number, boolean, or string), @a first\n      must be `begin()` and @a last must be `end()`. In this case, the value is\n      copied. Otherwise, invalid_iterator.204 is thrown.\n    - In case of structured types (array, object), the constructor behaves as\n      similar versions for `std::vector` or `std::map`; that is, a JSON array\n      or object is constructed from the values in the range.\n\n    @tparam InputIT an input iterator type (@ref iterator or @ref\n    const_iterator)\n\n    @param[in] first begin of the range to copy from (included)\n    @param[in] last end of the range to copy from (excluded)\n\n    @pre Iterators @a first and @a last must be initialized. **This\n         precondition is enforced with an assertion (see warning).** If\n         assertions are switched off, a violation of this precondition yields\n         undefined behavior.\n\n    @pre Range `[first, last)` is valid. Usually, this precondition cannot be\n         checked efficiently. Only certain edge cases are detected; see the\n         description of the exceptions below. A violation of this precondition\n         yields undefined behavior.\n\n    @warning A precondition is enforced with a runtime assertion that will\n             result in calling `std::abort` if this precondition is not met.\n             Assertions can be disabled by defining `NDEBUG` at compile time.\n             See https://en.cppreference.com/w/cpp/error/assert for more\n             information.\n\n    @throw invalid_iterator.201 if iterators @a first and @a last are not\n    compatible (i.e., do not belong to the same JSON value). In this case,\n    the range `[first, last)` is undefined.\n    @throw invalid_iterator.204 if iterators @a first and @a last belong to a\n    primitive type (number, boolean, or string), but @a first does not point\n    to the first element any more. In this case, the range `[first, last)` is\n    undefined. See example code below.\n    @throw invalid_iterator.206 if iterators @a first and @a last belong to a\n    null value. In this case, the range `[first, last)` is undefined.\n\n    @complexity Linear in distance between @a first and @a last.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The example below shows several ways to create JSON values by\n    specifying a subrange with iterators.,basic_json__InputIt_InputIt}\n\n    @since version 1.0.0\n    */\n    template < class InputIT, typename std::enable_if <\n                   std::is_same<InputIT, typename basic_json_t::iterator>::value ||\n                   std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int >::type = 0 >\n    basic_json(InputIT first, InputIT last)\n    {\n        JSON_ASSERT(first.m_object != nullptr);\n        JSON_ASSERT(last.m_object != nullptr);\n\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(201, \"iterators are not compatible\"));\n        }\n\n        // copy type from first iterator\n        m_type = first.m_object->m_type;\n\n        // check if iterator range is complete for primitive values\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            {\n                if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()\n                                         || !last.m_it.primitive_iterator.is_end()))\n                {\n                    JSON_THROW(invalid_iterator::create(204, \"iterators out of range\"));\n                }\n                break;\n            }\n\n            default:\n                break;\n        }\n\n        switch (m_type)\n        {\n            case value_t::number_integer:\n            {\n                m_value.number_integer = first.m_object->m_value.number_integer;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value.number_unsigned = first.m_object->m_value.number_unsigned;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value.number_float = first.m_object->m_value.number_float;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value.boolean = first.m_object->m_value.boolean;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value = *first.m_object->m_value.string;\n                break;\n            }\n\n            case value_t::object:\n            {\n                m_value.object = create<object_t>(first.m_it.object_iterator,\n                                                  last.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value.array = create<array_t>(first.m_it.array_iterator,\n                                                last.m_it.array_iterator);\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_value = *first.m_object->m_value.binary;\n                break;\n            }\n\n            default:\n                JSON_THROW(invalid_iterator::create(206, \"cannot construct with iterators from \" +\n                                                    std::string(first.m_object->type_name())));\n        }\n\n        assert_invariant();\n    }\n\n\n    ///////////////////////////////////////\n    // other constructors and destructor //\n    ///////////////////////////////////////\n\n    template<typename JsonRef,\n             detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,\n                                 std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >\n    basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {}\n\n    /*!\n    @brief copy constructor\n\n    Creates a copy of a given JSON value.\n\n    @param[in] other  the JSON value to copy\n\n    @post `*this == other`\n\n    @complexity Linear in the size of @a other.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n    - As postcondition, it holds: `other == basic_json(other)`.\n\n    @liveexample{The following code shows an example for the copy\n    constructor.,basic_json__basic_json}\n\n    @since version 1.0.0\n    */\n    basic_json(const basic_json& other)\n        : m_type(other.m_type)\n    {\n        // check of passed value is valid\n        other.assert_invariant();\n\n        switch (m_type)\n        {\n            case value_t::object:\n            {\n                m_value = *other.m_value.object;\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value = *other.m_value.array;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value = *other.m_value.string;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value = other.m_value.boolean;\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                m_value = other.m_value.number_integer;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value = other.m_value.number_unsigned;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value = other.m_value.number_float;\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_value = *other.m_value.binary;\n                break;\n            }\n\n            default:\n                break;\n        }\n\n        assert_invariant();\n    }\n\n    /*!\n    @brief move constructor\n\n    Move constructor. Constructs a JSON value with the contents of the given\n    value @a other using move semantics. It \"steals\" the resources from @a\n    other and leaves it as JSON null value.\n\n    @param[in,out] other  value to move to this object\n\n    @post `*this` has the same value as @a other before the call.\n    @post @a other is a JSON null value.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this constructor never throws\n    exceptions.\n\n    @requirement This function helps `basic_json` satisfying the\n    [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible)\n    requirements.\n\n    @liveexample{The code below shows the move constructor explicitly called\n    via std::move.,basic_json__moveconstructor}\n\n    @since version 1.0.0\n    */\n    basic_json(basic_json&& other) noexcept\n        : m_type(std::move(other.m_type)),\n          m_value(std::move(other.m_value))\n    {\n        // check that passed value is valid\n        other.assert_invariant();\n\n        // invalidate payload\n        other.m_type = value_t::null;\n        other.m_value = {};\n\n        assert_invariant();\n    }\n\n    /*!\n    @brief copy assignment\n\n    Copy assignment operator. Copies a JSON value via the \"copy and swap\"\n    strategy: It is expressed in terms of the copy constructor, destructor,\n    and the `swap()` member function.\n\n    @param[in] other  value to copy from\n\n    @complexity Linear.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n\n    @liveexample{The code below shows and example for the copy assignment. It\n    creates a copy of value `a` which is then swapped with `b`. Finally\\, the\n    copy of `a` (which is the null value after the swap) is\n    destroyed.,basic_json__copyassignment}\n\n    @since version 1.0.0\n    */\n    basic_json& operator=(basic_json other) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&&\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        // check that passed value is valid\n        other.assert_invariant();\n\n        using std::swap;\n        swap(m_type, other.m_type);\n        swap(m_value, other.m_value);\n\n        assert_invariant();\n        return *this;\n    }\n\n    /*!\n    @brief destructor\n\n    Destroys the JSON value and frees all allocated memory.\n\n    @complexity Linear.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n    - All stored elements are destroyed and all memory is freed.\n\n    @since version 1.0.0\n    */\n    ~basic_json() noexcept\n    {\n        assert_invariant();\n        m_value.destroy(m_type);\n    }\n\n    /// @}\n\n  public:\n    ///////////////////////\n    // object inspection //\n    ///////////////////////\n\n    /// @name object inspection\n    /// Functions to inspect the type of a JSON value.\n    /// @{\n\n    /*!\n    @brief serialization\n\n    Serialization function for JSON values. The function tries to mimic\n    Python's `json.dumps()` function, and currently supports its @a indent\n    and @a ensure_ascii parameters.\n\n    @param[in] indent If indent is nonnegative, then array elements and object\n    members will be pretty-printed with that indent level. An indent level of\n    `0` will only insert newlines. `-1` (the default) selects the most compact\n    representation.\n    @param[in] indent_char The character to use for indentation if @a indent is\n    greater than `0`. The default is ` ` (space).\n    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters\n    in the output are escaped with `\\uXXXX` sequences, and the result consists\n    of ASCII characters only.\n    @param[in] error_handler  how to react on decoding errors; there are three\n    possible values: `strict` (throws and exception in case a decoding error\n    occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD),\n    and `ignore` (ignore invalid UTF-8 sequences during serialization; all\n    bytes are copied to the output unchanged).\n\n    @return string containing the serialization of the JSON value\n\n    @throw type_error.316 if a string stored inside the JSON value is not\n                          UTF-8 encoded and @a error_handler is set to strict\n\n    @note Binary values are serialized as object containing two keys:\n      - \"bytes\": an array of bytes as integers\n      - \"subtype\": the subtype as integer or \"null\" if the binary has no subtype\n\n    @complexity Linear.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @liveexample{The following example shows the effect of different @a indent\\,\n    @a indent_char\\, and @a ensure_ascii parameters to the result of the\n    serialization.,dump}\n\n    @see https://docs.python.org/2/library/json.html#json.dump\n\n    @since version 1.0.0; indentation character @a indent_char, option\n           @a ensure_ascii and exceptions added in version 3.0.0; error\n           handlers added in version 3.4.0; serialization of binary values added\n           in version 3.8.0.\n    */\n    string_t dump(const int indent = -1,\n                  const char indent_char = ' ',\n                  const bool ensure_ascii = false,\n                  const error_handler_t error_handler = error_handler_t::strict) const\n    {\n        string_t result;\n        serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler);\n\n        if (indent >= 0)\n        {\n            s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));\n        }\n        else\n        {\n            s.dump(*this, false, ensure_ascii, 0);\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief return the type of the JSON value (explicit)\n\n    Return the type of the JSON value as a value from the @ref value_t\n    enumeration.\n\n    @return the type of the JSON value\n            Value type                | return value\n            ------------------------- | -------------------------\n            null                      | value_t::null\n            boolean                   | value_t::boolean\n            string                    | value_t::string\n            number (integer)          | value_t::number_integer\n            number (unsigned integer) | value_t::number_unsigned\n            number (floating-point)   | value_t::number_float\n            object                    | value_t::object\n            array                     | value_t::array\n            binary                    | value_t::binary\n            discarded                 | value_t::discarded\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `type()` for all JSON\n    types.,type}\n\n    @sa @ref operator value_t() -- return the type of the JSON value (implicit)\n    @sa @ref type_name() -- return the type as string\n\n    @since version 1.0.0\n    */\n    constexpr value_t type() const noexcept\n    {\n        return m_type;\n    }\n\n    /*!\n    @brief return whether type is primitive\n\n    This function returns true if and only if the JSON type is primitive\n    (string, number, boolean, or null).\n\n    @return `true` if type is primitive (string, number, boolean, or null),\n    `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_primitive()` for all JSON\n    types.,is_primitive}\n\n    @sa @ref is_structured() -- returns whether JSON value is structured\n    @sa @ref is_null() -- returns whether JSON value is `null`\n    @sa @ref is_string() -- returns whether JSON value is a string\n    @sa @ref is_boolean() -- returns whether JSON value is a boolean\n    @sa @ref is_number() -- returns whether JSON value is a number\n    @sa @ref is_binary() -- returns whether JSON value is a binary array\n\n    @since version 1.0.0\n    */\n    constexpr bool is_primitive() const noexcept\n    {\n        return is_null() || is_string() || is_boolean() || is_number() || is_binary();\n    }\n\n    /*!\n    @brief return whether type is structured\n\n    This function returns true if and only if the JSON type is structured\n    (array or object).\n\n    @return `true` if type is structured (array or object), `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_structured()` for all JSON\n    types.,is_structured}\n\n    @sa @ref is_primitive() -- returns whether value is primitive\n    @sa @ref is_array() -- returns whether value is an array\n    @sa @ref is_object() -- returns whether value is an object\n\n    @since version 1.0.0\n    */\n    constexpr bool is_structured() const noexcept\n    {\n        return is_array() || is_object();\n    }\n\n    /*!\n    @brief return whether value is null\n\n    This function returns true if and only if the JSON value is null.\n\n    @return `true` if type is null, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_null()` for all JSON\n    types.,is_null}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_null() const noexcept\n    {\n        return m_type == value_t::null;\n    }\n\n    /*!\n    @brief return whether value is a boolean\n\n    This function returns true if and only if the JSON value is a boolean.\n\n    @return `true` if type is boolean, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_boolean()` for all JSON\n    types.,is_boolean}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_boolean() const noexcept\n    {\n        return m_type == value_t::boolean;\n    }\n\n    /*!\n    @brief return whether value is a number\n\n    This function returns true if and only if the JSON value is a number. This\n    includes both integer (signed and unsigned) and floating-point values.\n\n    @return `true` if type is number (regardless whether integer, unsigned\n    integer or floating-type), `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number()` for all JSON\n    types.,is_number}\n\n    @sa @ref is_number_integer() -- check if value is an integer or unsigned\n    integer number\n    @sa @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n    @sa @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number() const noexcept\n    {\n        return is_number_integer() || is_number_float();\n    }\n\n    /*!\n    @brief return whether value is an integer number\n\n    This function returns true if and only if the JSON value is a signed or\n    unsigned integer number. This excludes floating-point values.\n\n    @return `true` if type is an integer or unsigned integer number, `false`\n    otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_integer()` for all\n    JSON types.,is_number_integer}\n\n    @sa @ref is_number() -- check if value is a number\n    @sa @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n    @sa @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number_integer() const noexcept\n    {\n        return m_type == value_t::number_integer || m_type == value_t::number_unsigned;\n    }\n\n    /*!\n    @brief return whether value is an unsigned integer number\n\n    This function returns true if and only if the JSON value is an unsigned\n    integer number. This excludes floating-point and signed integer values.\n\n    @return `true` if type is an unsigned integer number, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_unsigned()` for all\n    JSON types.,is_number_unsigned}\n\n    @sa @ref is_number() -- check if value is a number\n    @sa @ref is_number_integer() -- check if value is an integer or unsigned\n    integer number\n    @sa @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 2.0.0\n    */\n    constexpr bool is_number_unsigned() const noexcept\n    {\n        return m_type == value_t::number_unsigned;\n    }\n\n    /*!\n    @brief return whether value is a floating-point number\n\n    This function returns true if and only if the JSON value is a\n    floating-point number. This excludes signed and unsigned integer values.\n\n    @return `true` if type is a floating-point number, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_float()` for all\n    JSON types.,is_number_float}\n\n    @sa @ref is_number() -- check if value is number\n    @sa @ref is_number_integer() -- check if value is an integer number\n    @sa @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number_float() const noexcept\n    {\n        return m_type == value_t::number_float;\n    }\n\n    /*!\n    @brief return whether value is an object\n\n    This function returns true if and only if the JSON value is an object.\n\n    @return `true` if type is object, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_object()` for all JSON\n    types.,is_object}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_object() const noexcept\n    {\n        return m_type == value_t::object;\n    }\n\n    /*!\n    @brief return whether value is an array\n\n    This function returns true if and only if the JSON value is an array.\n\n    @return `true` if type is array, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_array()` for all JSON\n    types.,is_array}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_array() const noexcept\n    {\n        return m_type == value_t::array;\n    }\n\n    /*!\n    @brief return whether value is a string\n\n    This function returns true if and only if the JSON value is a string.\n\n    @return `true` if type is string, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_string()` for all JSON\n    types.,is_string}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_string() const noexcept\n    {\n        return m_type == value_t::string;\n    }\n\n    /*!\n    @brief return whether value is a binary array\n\n    This function returns true if and only if the JSON value is a binary array.\n\n    @return `true` if type is binary array, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_binary()` for all JSON\n    types.,is_binary}\n\n    @since version 3.8.0\n    */\n    constexpr bool is_binary() const noexcept\n    {\n        return m_type == value_t::binary;\n    }\n\n    /*!\n    @brief return whether value is discarded\n\n    This function returns true if and only if the JSON value was discarded\n    during parsing with a callback function (see @ref parser_callback_t).\n\n    @note This function will always be `false` for JSON values after parsing.\n    That is, discarded values can only occur during parsing, but will be\n    removed when inside a structured value or replaced by null in other cases.\n\n    @return `true` if type is discarded, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_discarded()` for all JSON\n    types.,is_discarded}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_discarded() const noexcept\n    {\n        return m_type == value_t::discarded;\n    }\n\n    /*!\n    @brief return the type of the JSON value (implicit)\n\n    Implicitly return the type of the JSON value as a value from the @ref\n    value_t enumeration.\n\n    @return the type of the JSON value\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies the @ref value_t operator for\n    all JSON types.,operator__value_t}\n\n    @sa @ref type() -- return the type of the JSON value (explicit)\n    @sa @ref type_name() -- return the type as string\n\n    @since version 1.0.0\n    */\n    constexpr operator value_t() const noexcept\n    {\n        return m_type;\n    }\n\n    /// @}\n\n  private:\n    //////////////////\n    // value access //\n    //////////////////\n\n    /// get a boolean (explicit)\n    boolean_t get_impl(boolean_t* /*unused*/) const\n    {\n        if (JSON_HEDLEY_LIKELY(is_boolean()))\n        {\n            return m_value.boolean;\n        }\n\n        JSON_THROW(type_error::create(302, \"type must be boolean, but is \" + std::string(type_name())));\n    }\n\n    /// get a pointer to the value (object)\n    object_t* get_impl_ptr(object_t* /*unused*/) noexcept\n    {\n        return is_object() ? m_value.object : nullptr;\n    }\n\n    /// get a pointer to the value (object)\n    constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept\n    {\n        return is_object() ? m_value.object : nullptr;\n    }\n\n    /// get a pointer to the value (array)\n    array_t* get_impl_ptr(array_t* /*unused*/) noexcept\n    {\n        return is_array() ? m_value.array : nullptr;\n    }\n\n    /// get a pointer to the value (array)\n    constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept\n    {\n        return is_array() ? m_value.array : nullptr;\n    }\n\n    /// get a pointer to the value (string)\n    string_t* get_impl_ptr(string_t* /*unused*/) noexcept\n    {\n        return is_string() ? m_value.string : nullptr;\n    }\n\n    /// get a pointer to the value (string)\n    constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept\n    {\n        return is_string() ? m_value.string : nullptr;\n    }\n\n    /// get a pointer to the value (boolean)\n    boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept\n    {\n        return is_boolean() ? &m_value.boolean : nullptr;\n    }\n\n    /// get a pointer to the value (boolean)\n    constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept\n    {\n        return is_boolean() ? &m_value.boolean : nullptr;\n    }\n\n    /// get a pointer to the value (integer number)\n    number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept\n    {\n        return is_number_integer() ? &m_value.number_integer : nullptr;\n    }\n\n    /// get a pointer to the value (integer number)\n    constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept\n    {\n        return is_number_integer() ? &m_value.number_integer : nullptr;\n    }\n\n    /// get a pointer to the value (unsigned number)\n    number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept\n    {\n        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;\n    }\n\n    /// get a pointer to the value (unsigned number)\n    constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept\n    {\n        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;\n    }\n\n    /// get a pointer to the value (floating-point number)\n    number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept\n    {\n        return is_number_float() ? &m_value.number_float : nullptr;\n    }\n\n    /// get a pointer to the value (floating-point number)\n    constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept\n    {\n        return is_number_float() ? &m_value.number_float : nullptr;\n    }\n\n    /// get a pointer to the value (binary)\n    binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept\n    {\n        return is_binary() ? m_value.binary : nullptr;\n    }\n\n    /// get a pointer to the value (binary)\n    constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept\n    {\n        return is_binary() ? m_value.binary : nullptr;\n    }\n\n    /*!\n    @brief helper function to implement get_ref()\n\n    This function helps to implement get_ref() without code duplication for\n    const and non-const overloads\n\n    @tparam ThisType will be deduced as `basic_json` or `const basic_json`\n\n    @throw type_error.303 if ReferenceType does not match underlying value\n    type of the current JSON\n    */\n    template<typename ReferenceType, typename ThisType>\n    static ReferenceType get_ref_impl(ThisType& obj)\n    {\n        // delegate the call to get_ptr<>()\n        auto ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();\n\n        if (JSON_HEDLEY_LIKELY(ptr != nullptr))\n        {\n            return *ptr;\n        }\n\n        JSON_THROW(type_error::create(303, \"incompatible ReferenceType for get_ref, actual type is \" + std::string(obj.type_name())));\n    }\n\n  public:\n    /// @name value access\n    /// Direct access to the stored value of a JSON value.\n    /// @{\n\n    /*!\n    @brief get special-case overload\n\n    This overloads avoids a lot of template boilerplate, it can be seen as the\n    identity method\n\n    @tparam BasicJsonType == @ref basic_json\n\n    @return a copy of *this\n\n    @complexity Constant.\n\n    @since version 2.1.0\n    */\n    template<typename BasicJsonType, detail::enable_if_t<\n                 std::is_same<typename std::remove_const<BasicJsonType>::type, basic_json_t>::value,\n                 int> = 0>\n    basic_json get() const\n    {\n        return *this;\n    }\n\n    /*!\n    @brief get special-case overload\n\n    This overloads converts the current @ref basic_json in a different\n    @ref basic_json type\n\n    @tparam BasicJsonType == @ref basic_json\n\n    @return a copy of *this, converted into @tparam BasicJsonType\n\n    @complexity Depending on the implementation of the called `from_json()`\n                method.\n\n    @since version 3.2.0\n    */\n    template < typename BasicJsonType, detail::enable_if_t <\n                   !std::is_same<BasicJsonType, basic_json>::value&&\n                   detail::is_basic_json<BasicJsonType>::value, int > = 0 >\n    BasicJsonType get() const\n    {\n        return *this;\n    }\n\n    /*!\n    @brief get a value (explicit)\n\n    Explicit type conversion between the JSON value and a compatible value\n    which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)\n    and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).\n    The value is converted by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    ValueType ret;\n    JSONSerializer<ValueType>::from_json(*this, ret);\n    return ret;\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json,\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `void from_json(const basic_json&, ValueType&)`, and\n    - @ref json_serializer<ValueType> does not have a `from_json()` method of\n      the form `ValueType from_json(const basic_json&)`\n\n    @tparam ValueTypeCV the provided value type\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @a ValueType\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,get__ValueType_const}\n\n    @since version 2.1.0\n    */\n    template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,\n               detail::enable_if_t <\n                   !detail::is_basic_json<ValueType>::value &&\n                   detail::has_from_json<basic_json_t, ValueType>::value &&\n                   !detail::has_non_default_from_json<basic_json_t, ValueType>::value,\n                   int > = 0 >\n    ValueType get() const noexcept(noexcept(\n                                       JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))\n    {\n        // we cannot static_assert on ValueTypeCV being non-const, because\n        // there is support for get<const basic_json_t>(), which is why we\n        // still need the uncvref\n        static_assert(!std::is_reference<ValueTypeCV>::value,\n                      \"get() cannot be used with reference types, you might want to use get_ref()\");\n        static_assert(std::is_default_constructible<ValueType>::value,\n                      \"types must be DefaultConstructible when used with get()\");\n\n        ValueType ret;\n        JSONSerializer<ValueType>::from_json(*this, ret);\n        return ret;\n    }\n\n    /*!\n    @brief get a value (explicit); special case\n\n    Explicit type conversion between the JSON value and a compatible value\n    which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)\n    and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).\n    The value is converted by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    return JSONSerializer<ValueTypeCV>::from_json(*this);\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json and\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `ValueType from_json(const basic_json&)`\n\n    @note If @ref json_serializer<ValueType> has both overloads of\n    `from_json()`, this one is chosen.\n\n    @tparam ValueTypeCV the provided value type\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @a ValueType\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @since version 2.1.0\n    */\n    template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,\n               detail::enable_if_t < !std::is_same<basic_json_t, ValueType>::value &&\n                                     detail::has_non_default_from_json<basic_json_t, ValueType>::value,\n                                     int > = 0 >\n    ValueType get() const noexcept(noexcept(\n                                       JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>())))\n    {\n        static_assert(!std::is_reference<ValueTypeCV>::value,\n                      \"get() cannot be used with reference types, you might want to use get_ref()\");\n        return JSONSerializer<ValueType>::from_json(*this);\n    }\n\n    /*!\n    @brief get a value (explicit)\n\n    Explicit type conversion between the JSON value and a compatible value.\n    The value is filled into the input parameter by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    ValueType v;\n    JSONSerializer<ValueType>::from_json(*this, v);\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json,\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `void from_json(const basic_json&, ValueType&)`, and\n\n    @tparam ValueType the input parameter type.\n\n    @return the input parameter, allowing chaining calls.\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,get_to}\n\n    @since version 3.3.0\n    */\n    template < typename ValueType,\n               detail::enable_if_t <\n                   !detail::is_basic_json<ValueType>::value&&\n                   detail::has_from_json<basic_json_t, ValueType>::value,\n                   int > = 0 >\n    ValueType & get_to(ValueType& v) const noexcept(noexcept(\n                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))\n    {\n        JSONSerializer<ValueType>::from_json(*this, v);\n        return v;\n    }\n\n    // specialization to allow to call get_to with a basic_json value\n    // see https://github.com/nlohmann/json/issues/2175\n    template<typename ValueType,\n             detail::enable_if_t <\n                 detail::is_basic_json<ValueType>::value,\n                 int> = 0>\n    ValueType & get_to(ValueType& v) const\n    {\n        v = *this;\n        return v;\n    }\n\n    template <\n        typename T, std::size_t N,\n        typename Array = T (&)[N],\n        detail::enable_if_t <\n            detail::has_from_json<basic_json_t, Array>::value, int > = 0 >\n    Array get_to(T (&v)[N]) const\n    noexcept(noexcept(JSONSerializer<Array>::from_json(\n                          std::declval<const basic_json_t&>(), v)))\n    {\n        JSONSerializer<Array>::from_json(*this, v);\n        return v;\n    }\n\n\n    /*!\n    @brief get a pointer value (implicit)\n\n    Implicit pointer access to the internally stored JSON value. No copies are\n    made.\n\n    @warning Writing data to the pointee of the result yields an undefined\n    state.\n\n    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref\n    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,\n    @ref number_unsigned_t, or @ref number_float_t. Enforced by a static\n    assertion.\n\n    @return pointer to the internally stored JSON value if the requested\n    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how pointers to internal values of a\n    JSON value can be requested. Note that no type conversions are made and a\n    `nullptr` is returned if the value and the requested pointer type does not\n    match.,get_ptr}\n\n    @since version 1.0.0\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))\n    {\n        // delegate the call to get_impl_ptr<>()\n        return get_impl_ptr(static_cast<PointerType>(nullptr));\n    }\n\n    /*!\n    @brief get a pointer value (implicit)\n    @copydoc get_ptr()\n    */\n    template < typename PointerType, typename std::enable_if <\n                   std::is_pointer<PointerType>::value&&\n                   std::is_const<typename std::remove_pointer<PointerType>::type>::value, int >::type = 0 >\n    constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))\n    {\n        // delegate the call to get_impl_ptr<>() const\n        return get_impl_ptr(static_cast<PointerType>(nullptr));\n    }\n\n    /*!\n    @brief get a pointer value (explicit)\n\n    Explicit pointer access to the internally stored JSON value. No copies are\n    made.\n\n    @warning The pointer becomes invalid if the underlying JSON object\n    changes.\n\n    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref\n    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,\n    @ref number_unsigned_t, or @ref number_float_t.\n\n    @return pointer to the internally stored JSON value if the requested\n    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how pointers to internal values of a\n    JSON value can be requested. Note that no type conversions are made and a\n    `nullptr` is returned if the value and the requested pointer type does not\n    match.,get__PointerType}\n\n    @sa @ref get_ptr() for explicit pointer-member access\n\n    @since version 1.0.0\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    auto get() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())\n    {\n        // delegate the call to get_ptr\n        return get_ptr<PointerType>();\n    }\n\n    /*!\n    @brief get a pointer value (explicit)\n    @copydoc get()\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    constexpr auto get() const noexcept -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())\n    {\n        // delegate the call to get_ptr\n        return get_ptr<PointerType>();\n    }\n\n    /*!\n    @brief get a reference value (implicit)\n\n    Implicit reference access to the internally stored JSON value. No copies\n    are made.\n\n    @warning Writing data to the referee of the result yields an undefined\n    state.\n\n    @tparam ReferenceType reference type; must be a reference to @ref array_t,\n    @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or\n    @ref number_float_t. Enforced by static assertion.\n\n    @return reference to the internally stored JSON value if the requested\n    reference type @a ReferenceType fits to the JSON value; throws\n    type_error.303 otherwise\n\n    @throw type_error.303 in case passed type @a ReferenceType is incompatible\n    with the stored JSON value; see example below\n\n    @complexity Constant.\n\n    @liveexample{The example shows several calls to `get_ref()`.,get_ref}\n\n    @since version 1.1.0\n    */\n    template<typename ReferenceType, typename std::enable_if<\n                 std::is_reference<ReferenceType>::value, int>::type = 0>\n    ReferenceType get_ref()\n    {\n        // delegate call to get_ref_impl\n        return get_ref_impl<ReferenceType>(*this);\n    }\n\n    /*!\n    @brief get a reference value (implicit)\n    @copydoc get_ref()\n    */\n    template < typename ReferenceType, typename std::enable_if <\n                   std::is_reference<ReferenceType>::value&&\n                   std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int >::type = 0 >\n    ReferenceType get_ref() const\n    {\n        // delegate call to get_ref_impl\n        return get_ref_impl<ReferenceType>(*this);\n    }\n\n    /*!\n    @brief get a value (implicit)\n\n    Implicit type conversion between the JSON value and a compatible value.\n    The call is realized by calling @ref get() const.\n\n    @tparam ValueType non-pointer type compatible to the JSON value, for\n    instance `int` for JSON integer numbers, `bool` for JSON booleans, or\n    `std::vector` types for JSON arrays. The character type of @ref string_t\n    as well as an initializer list of this type is excluded to avoid\n    ambiguities as these types implicitly convert to `std::string`.\n\n    @return copy of the JSON value, converted to type @a ValueType\n\n    @throw type_error.302 in case passed type @a ValueType is incompatible\n    to the JSON value type (e.g., the JSON value is of type boolean, but a\n    string is requested); see example below\n\n    @complexity Linear in the size of the JSON value.\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,operator__ValueType}\n\n    @since version 1.0.0\n    */\n    template < typename ValueType, typename std::enable_if <\n                   !std::is_pointer<ValueType>::value&&\n                   !std::is_same<ValueType, detail::json_ref<basic_json>>::value&&\n                   !std::is_same<ValueType, typename string_t::value_type>::value&&\n                   !detail::is_basic_json<ValueType>::value\n                   && !std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value\n#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914))\n                   && !std::is_same<ValueType, typename std::string_view>::value\n#endif\n                   && detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value\n                   , int >::type = 0 >\n    JSON_EXPLICIT operator ValueType() const\n    {\n        // delegate the call to get<>() const\n        return get<ValueType>();\n    }\n\n    /*!\n    @return reference to the binary value\n\n    @throw type_error.302 if the value is not binary\n\n    @sa @ref is_binary() to check if the value is binary\n\n    @since version 3.8.0\n    */\n    binary_t& get_binary()\n    {\n        if (!is_binary())\n        {\n            JSON_THROW(type_error::create(302, \"type must be binary, but is \" + std::string(type_name())));\n        }\n\n        return *get_ptr<binary_t*>();\n    }\n\n    /// @copydoc get_binary()\n    const binary_t& get_binary() const\n    {\n        if (!is_binary())\n        {\n            JSON_THROW(type_error::create(302, \"type must be binary, but is \" + std::string(type_name())));\n        }\n\n        return *get_ptr<const binary_t*>();\n    }\n\n    /// @}\n\n\n    ////////////////////\n    // element access //\n    ////////////////////\n\n    /// @name element access\n    /// Access to the JSON value.\n    /// @{\n\n    /*!\n    @brief access specified array element with bounds checking\n\n    Returns a reference to the element at specified location @a idx, with\n    bounds checking.\n\n    @param[in] idx  index of the element to access\n\n    @return reference to the element at index @a idx\n\n    @throw type_error.304 if the JSON value is not an array; in this case,\n    calling `at` with an index makes no sense. See example below.\n    @throw out_of_range.401 if the index @a idx is out of range of the array;\n    that is, `idx >= size()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how array elements can be read and\n    written using `at()`. It also demonstrates the different exceptions that\n    can be thrown.,at__size_type}\n    */\n    reference at(size_type idx)\n    {\n        // at only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            JSON_TRY\n            {\n                return m_value.array->at(idx);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\"));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief access specified array element with bounds checking\n\n    Returns a const reference to the element at specified location @a idx,\n    with bounds checking.\n\n    @param[in] idx  index of the element to access\n\n    @return const reference to the element at index @a idx\n\n    @throw type_error.304 if the JSON value is not an array; in this case,\n    calling `at` with an index makes no sense. See example below.\n    @throw out_of_range.401 if the index @a idx is out of range of the array;\n    that is, `idx >= size()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how array elements can be read using\n    `at()`. It also demonstrates the different exceptions that can be thrown.,\n    at__size_type_const}\n    */\n    const_reference at(size_type idx) const\n    {\n        // at only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            JSON_TRY\n            {\n                return m_value.array->at(idx);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\"));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief access specified object element with bounds checking\n\n    Returns a reference to the element at with specified key @a key, with\n    bounds checking.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.304 if the JSON value is not an object; in this case,\n    calling `at` with a key makes no sense. See example below.\n    @throw out_of_range.403 if the key @a key is is not stored in the object;\n    that is, `find(key) == end()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Logarithmic in the size of the container.\n\n    @sa @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how object elements can be read and\n    written using `at()`. It also demonstrates the different exceptions that\n    can be thrown.,at__object_t_key_type}\n    */\n    reference at(const typename object_t::key_type& key)\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_TRY\n            {\n                return m_value.object->at(key);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(403, \"key '\" + key + \"' not found\"));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief access specified object element with bounds checking\n\n    Returns a const reference to the element at with specified key @a key,\n    with bounds checking.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @throw type_error.304 if the JSON value is not an object; in this case,\n    calling `at` with a key makes no sense. See example below.\n    @throw out_of_range.403 if the key @a key is is not stored in the object;\n    that is, `find(key) == end()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Logarithmic in the size of the container.\n\n    @sa @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how object elements can be read using\n    `at()`. It also demonstrates the different exceptions that can be thrown.,\n    at__object_t_key_type_const}\n    */\n    const_reference at(const typename object_t::key_type& key) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_TRY\n            {\n                return m_value.object->at(key);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(403, \"key '\" + key + \"' not found\"));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief access specified array element\n\n    Returns a reference to the element at specified location @a idx.\n\n    @note If @a idx is beyond the range of the array (i.e., `idx >= size()`),\n    then the array is silently filled up with `null` values to make `idx` a\n    valid reference to the last stored element.\n\n    @param[in] idx  index of the element to access\n\n    @return reference to the element at index @a idx\n\n    @throw type_error.305 if the JSON value is not an array or null; in that\n    cases, using the [] operator with an index makes no sense.\n\n    @complexity Constant if @a idx is in the range of the array. Otherwise\n    linear in `idx - size()`.\n\n    @liveexample{The example below shows how array elements can be read and\n    written using `[]` operator. Note the addition of `null`\n    values.,operatorarray__size_type}\n\n    @since version 1.0.0\n    */\n    reference operator[](size_type idx)\n    {\n        // implicitly convert null value to an empty array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value.array = create<array_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // fill up array with null values if given idx is outside range\n            if (idx >= m_value.array->size())\n            {\n                m_value.array->insert(m_value.array->end(),\n                                      idx - m_value.array->size() + 1,\n                                      basic_json());\n            }\n\n            return m_value.array->operator[](idx);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a numeric argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief access specified array element\n\n    Returns a const reference to the element at specified location @a idx.\n\n    @param[in] idx  index of the element to access\n\n    @return const reference to the element at index @a idx\n\n    @throw type_error.305 if the JSON value is not an array; in that case,\n    using the [] operator with an index makes no sense.\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how array elements can be read using\n    the `[]` operator.,operatorarray__size_type_const}\n\n    @since version 1.0.0\n    */\n    const_reference operator[](size_type idx) const\n    {\n        // const operator[] only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            return m_value.array->operator[](idx);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a numeric argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief access specified object element\n\n    Returns a reference to the element at with specified key @a key.\n\n    @note If @a key is not found in the object, then it is silently added to\n    the object and filled with a `null` value to make `key` a valid reference.\n    In case the value was `null` before, it is converted to an object.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.305 if the JSON value is not an object or null; in that\n    cases, using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read and\n    written using the `[]` operator.,operatorarray__key_type}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.0.0\n    */\n    reference operator[](const typename object_t::key_type& key)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            return m_value.object->operator[](key);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief read-only access specified object element\n\n    Returns a const reference to the element at with specified key @a key. No\n    bounds checking is performed.\n\n    @warning If the element with key @a key does not exist, the behavior is\n    undefined.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @pre The element with key @a key must exist. **This precondition is\n         enforced with an assertion.**\n\n    @throw type_error.305 if the JSON value is not an object; in that case,\n    using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read using\n    the `[]` operator.,operatorarray__key_type_const}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.0.0\n    */\n    const_reference operator[](const typename object_t::key_type& key) const\n    {\n        // const operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_ASSERT(m_value.object->find(key) != m_value.object->end());\n            return m_value.object->find(key)->second;\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief access specified object element\n\n    Returns a reference to the element at with specified key @a key.\n\n    @note If @a key is not found in the object, then it is silently added to\n    the object and filled with a `null` value to make `key` a valid reference.\n    In case the value was `null` before, it is converted to an object.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.305 if the JSON value is not an object or null; in that\n    cases, using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read and\n    written using the `[]` operator.,operatorarray__key_type}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.1.0\n    */\n    template<typename T>\n    JSON_HEDLEY_NON_NULL(2)\n    reference operator[](T* key)\n    {\n        // implicitly convert null to object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            return m_value.object->operator[](key);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief read-only access specified object element\n\n    Returns a const reference to the element at with specified key @a key. No\n    bounds checking is performed.\n\n    @warning If the element with key @a key does not exist, the behavior is\n    undefined.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @pre The element with key @a key must exist. **This precondition is\n         enforced with an assertion.**\n\n    @throw type_error.305 if the JSON value is not an object; in that case,\n    using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read using\n    the `[]` operator.,operatorarray__key_type_const}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.1.0\n    */\n    template<typename T>\n    JSON_HEDLEY_NON_NULL(2)\n    const_reference operator[](T* key) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            JSON_ASSERT(m_value.object->find(key) != m_value.object->end());\n            return m_value.object->find(key)->second;\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief access specified object element with default value\n\n    Returns either a copy of an object's element at the specified key @a key\n    or a given default value if no element with key @a key exists.\n\n    The function is basically equivalent to executing\n    @code {.cpp}\n    try {\n        return at(key);\n    } catch(out_of_range) {\n        return default_value;\n    }\n    @endcode\n\n    @note Unlike @ref at(const typename object_t::key_type&), this function\n    does not throw if the given key @a key was not found.\n\n    @note Unlike @ref operator[](const typename object_t::key_type& key), this\n    function does not implicitly add an element to the position defined by @a\n    key. This function is furthermore also applicable to const objects.\n\n    @param[in] key  key of the element to access\n    @param[in] default_value  the value to return if @a key is not found\n\n    @tparam ValueType type compatible to JSON values, for instance `int` for\n    JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for\n    JSON arrays. Note the type of the expected value at @a key and the default\n    value @a default_value must be compatible.\n\n    @return copy of the element at key @a key or @a default_value if @a key\n    is not found\n\n    @throw type_error.302 if @a default_value does not match the type of the\n    value at @a key\n    @throw type_error.306 if the JSON value is not an object; in that case,\n    using `value()` with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be queried\n    with a default value.,basic_json__value}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n\n    @since version 1.0.0\n    */\n    // using std::is_convertible in a std::enable_if will fail when using explicit conversions\n    template < class ValueType, typename std::enable_if <\n                   detail::is_getable<basic_json_t, ValueType>::value\n                   && !std::is_same<value_t, ValueType>::value, int >::type = 0 >\n    ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // if key is found, return value and given default value otherwise\n            const auto it = find(key);\n            if (it != end())\n            {\n                return it->template get<ValueType>();\n            }\n\n            return default_value;\n        }\n\n        JSON_THROW(type_error::create(306, \"cannot use value() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief overload for a default value of type const char*\n    @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const\n    */\n    string_t value(const typename object_t::key_type& key, const char* default_value) const\n    {\n        return value(key, string_t(default_value));\n    }\n\n    /*!\n    @brief access specified object element via JSON Pointer with default value\n\n    Returns either a copy of an object's element at the specified key @a key\n    or a given default value if no element with key @a key exists.\n\n    The function is basically equivalent to executing\n    @code {.cpp}\n    try {\n        return at(ptr);\n    } catch(out_of_range) {\n        return default_value;\n    }\n    @endcode\n\n    @note Unlike @ref at(const json_pointer&), this function does not throw\n    if the given key @a key was not found.\n\n    @param[in] ptr  a JSON pointer to the element to access\n    @param[in] default_value  the value to return if @a ptr found no value\n\n    @tparam ValueType type compatible to JSON values, for instance `int` for\n    JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for\n    JSON arrays. Note the type of the expected value at @a key and the default\n    value @a default_value must be compatible.\n\n    @return copy of the element at key @a key or @a default_value if @a key\n    is not found\n\n    @throw type_error.302 if @a default_value does not match the type of the\n    value at @a ptr\n    @throw type_error.306 if the JSON value is not an object; in that case,\n    using `value()` with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be queried\n    with a default value.,basic_json__value_ptr}\n\n    @sa @ref operator[](const json_pointer&) for unchecked access by reference\n\n    @since version 2.0.2\n    */\n    template<class ValueType, typename std::enable_if<\n                 detail::is_getable<basic_json_t, ValueType>::value, int>::type = 0>\n    ValueType value(const json_pointer& ptr, const ValueType& default_value) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // if pointer resolves a value, return it or use default value\n            JSON_TRY\n            {\n                return ptr.get_checked(this).template get<ValueType>();\n            }\n            JSON_INTERNAL_CATCH (out_of_range&)\n            {\n                return default_value;\n            }\n        }\n\n        JSON_THROW(type_error::create(306, \"cannot use value() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief overload for a default value of type const char*\n    @copydoc basic_json::value(const json_pointer&, ValueType) const\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    string_t value(const json_pointer& ptr, const char* default_value) const\n    {\n        return value(ptr, string_t(default_value));\n    }\n\n    /*!\n    @brief access the first element\n\n    Returns a reference to the first element in the container. For a JSON\n    container `c`, the expression `c.front()` is equivalent to `*c.begin()`.\n\n    @return In case of a structured type (array or object), a reference to the\n    first element is returned. In case of number, string, boolean, or binary\n    values, a reference to the value is returned.\n\n    @complexity Constant.\n\n    @pre The JSON value must not be `null` (would throw `std::out_of_range`)\n    or an empty array or object (undefined behavior, **guarded by\n    assertions**).\n    @post The JSON value remains unchanged.\n\n    @throw invalid_iterator.214 when called on `null` value\n\n    @liveexample{The following code shows an example for `front()`.,front}\n\n    @sa @ref back() -- access the last element\n\n    @since version 1.0.0\n    */\n    reference front()\n    {\n        return *begin();\n    }\n\n    /*!\n    @copydoc basic_json::front()\n    */\n    const_reference front() const\n    {\n        return *cbegin();\n    }\n\n    /*!\n    @brief access the last element\n\n    Returns a reference to the last element in the container. For a JSON\n    container `c`, the expression `c.back()` is equivalent to\n    @code {.cpp}\n    auto tmp = c.end();\n    --tmp;\n    return *tmp;\n    @endcode\n\n    @return In case of a structured type (array or object), a reference to the\n    last element is returned. In case of number, string, boolean, or binary\n    values, a reference to the value is returned.\n\n    @complexity Constant.\n\n    @pre The JSON value must not be `null` (would throw `std::out_of_range`)\n    or an empty array or object (undefined behavior, **guarded by\n    assertions**).\n    @post The JSON value remains unchanged.\n\n    @throw invalid_iterator.214 when called on a `null` value. See example\n    below.\n\n    @liveexample{The following code shows an example for `back()`.,back}\n\n    @sa @ref front() -- access the first element\n\n    @since version 1.0.0\n    */\n    reference back()\n    {\n        auto tmp = end();\n        --tmp;\n        return *tmp;\n    }\n\n    /*!\n    @copydoc basic_json::back()\n    */\n    const_reference back() const\n    {\n        auto tmp = cend();\n        --tmp;\n        return *tmp;\n    }\n\n    /*!\n    @brief remove element given an iterator\n\n    Removes the element specified by iterator @a pos. The iterator @a pos must\n    be valid and dereferenceable. Thus the `end()` iterator (which is valid,\n    but is not dereferenceable) cannot be used as a value for @a pos.\n\n    If called on a primitive type other than `null`, the resulting JSON value\n    will be `null`.\n\n    @param[in] pos iterator to the element to remove\n    @return Iterator following the last removed element. If the iterator @a\n    pos refers to the last element, the `end()` iterator is returned.\n\n    @tparam IteratorType an @ref iterator or @ref const_iterator\n\n    @post Invalidates iterators and references at or after the point of the\n    erase, including the `end()` iterator.\n\n    @throw type_error.307 if called on a `null` value; example: `\"cannot use\n    erase() with null\"`\n    @throw invalid_iterator.202 if called on an iterator which does not belong\n    to the current JSON value; example: `\"iterator does not fit current\n    value\"`\n    @throw invalid_iterator.205 if called on a primitive type with invalid\n    iterator (i.e., any iterator which is not `begin()`); example: `\"iterator\n    out of range\"`\n\n    @complexity The complexity depends on the type:\n    - objects: amortized constant\n    - arrays: linear in distance between @a pos and the end of the container\n    - strings and binary: linear in the length of the member\n    - other types: constant\n\n    @liveexample{The example shows the result of `erase()` for different JSON\n    types.,erase__IteratorType}\n\n    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n    @sa @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    template < class IteratorType, typename std::enable_if <\n                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||\n                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int >::type\n               = 0 >\n    IteratorType erase(IteratorType pos)\n    {\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n        }\n\n        IteratorType result = end();\n\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            case value_t::binary:\n            {\n                if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))\n                {\n                    JSON_THROW(invalid_iterator::create(205, \"iterator out of range\"));\n                }\n\n                if (is_string())\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);\n                    m_value.string = nullptr;\n                }\n                else if (is_binary())\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1);\n                    m_value.binary = nullptr;\n                }\n\n                m_type = value_t::null;\n                assert_invariant();\n                break;\n            }\n\n            case value_t::object:\n            {\n                result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);\n                break;\n            }\n\n            default:\n                JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name())));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief remove elements given an iterator range\n\n    Removes the element specified by the range `[first; last)`. The iterator\n    @a first does not need to be dereferenceable if `first == last`: erasing\n    an empty range is a no-op.\n\n    If called on a primitive type other than `null`, the resulting JSON value\n    will be `null`.\n\n    @param[in] first iterator to the beginning of the range to remove\n    @param[in] last iterator past the end of the range to remove\n    @return Iterator following the last removed element. If the iterator @a\n    second refers to the last element, the `end()` iterator is returned.\n\n    @tparam IteratorType an @ref iterator or @ref const_iterator\n\n    @post Invalidates iterators and references at or after the point of the\n    erase, including the `end()` iterator.\n\n    @throw type_error.307 if called on a `null` value; example: `\"cannot use\n    erase() with null\"`\n    @throw invalid_iterator.203 if called on iterators which does not belong\n    to the current JSON value; example: `\"iterators do not fit current value\"`\n    @throw invalid_iterator.204 if called on a primitive type with invalid\n    iterators (i.e., if `first != begin()` and `last != end()`); example:\n    `\"iterators out of range\"`\n\n    @complexity The complexity depends on the type:\n    - objects: `log(size()) + std::distance(first, last)`\n    - arrays: linear in the distance between @a first and @a last, plus linear\n      in the distance between @a last and end of the container\n    - strings and binary: linear in the length of the member\n    - other types: constant\n\n    @liveexample{The example shows the result of `erase()` for different JSON\n    types.,erase__IteratorType_IteratorType}\n\n    @sa @ref erase(IteratorType) -- removes the element at a given position\n    @sa @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n    @sa @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    template < class IteratorType, typename std::enable_if <\n                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||\n                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int >::type\n               = 0 >\n    IteratorType erase(IteratorType first, IteratorType last)\n    {\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(203, \"iterators do not fit current value\"));\n        }\n\n        IteratorType result = end();\n\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            case value_t::binary:\n            {\n                if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin()\n                                       || !last.m_it.primitive_iterator.is_end()))\n                {\n                    JSON_THROW(invalid_iterator::create(204, \"iterators out of range\"));\n                }\n\n                if (is_string())\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);\n                    m_value.string = nullptr;\n                }\n                else if (is_binary())\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1);\n                    m_value.binary = nullptr;\n                }\n\n                m_type = value_t::null;\n                assert_invariant();\n                break;\n            }\n\n            case value_t::object:\n            {\n                result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,\n                                              last.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,\n                                             last.m_it.array_iterator);\n                break;\n            }\n\n            default:\n                JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name())));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief remove element from a JSON object given a key\n\n    Removes elements from a JSON object with the key value @a key.\n\n    @param[in] key value of the elements to remove\n\n    @return Number of elements removed. If @a ObjectType is the default\n    `std::map` type, the return value will always be `0` (@a key was not\n    found) or `1` (@a key was found).\n\n    @post References and iterators to the erased elements are invalidated.\n    Other references and iterators are not affected.\n\n    @throw type_error.307 when called on a type other than JSON object;\n    example: `\"cannot use erase() with null\"`\n\n    @complexity `log(size()) + count(key)`\n\n    @liveexample{The example shows the effect of `erase()`.,erase__key_type}\n\n    @sa @ref erase(IteratorType) -- removes the element at a given position\n    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    size_type erase(const typename object_t::key_type& key)\n    {\n        // this erase only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            return m_value.object->erase(key);\n        }\n\n        JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief remove element from a JSON array given an index\n\n    Removes element from a JSON array at the index @a idx.\n\n    @param[in] idx index of the element to remove\n\n    @throw type_error.307 when called on a type other than JSON object;\n    example: `\"cannot use erase() with null\"`\n    @throw out_of_range.401 when `idx >= size()`; example: `\"array index 17\n    is out of range\"`\n\n    @complexity Linear in distance between @a idx and the end of the container.\n\n    @liveexample{The example shows the effect of `erase()`.,erase__size_type}\n\n    @sa @ref erase(IteratorType) -- removes the element at a given position\n    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n\n    @since version 1.0.0\n    */\n    void erase(const size_type idx)\n    {\n        // this erase only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            if (JSON_HEDLEY_UNLIKELY(idx >= size()))\n            {\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\"));\n            }\n\n            m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));\n        }\n        else\n        {\n            JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name())));\n        }\n    }\n\n    /// @}\n\n\n    ////////////\n    // lookup //\n    ////////////\n\n    /// @name lookup\n    /// @{\n\n    /*!\n    @brief find an element in a JSON object\n\n    Finds an element in a JSON object with key equivalent to @a key. If the\n    element is not found or the JSON value is not an object, end() is\n    returned.\n\n    @note This method always returns @ref end() when executed on a JSON type\n          that is not an object.\n\n    @param[in] key key value of the element to search for.\n\n    @return Iterator to an element with key equivalent to @a key. If no such\n    element is found or the JSON value is not an object, past-the-end (see\n    @ref end()) iterator is returned.\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The example shows how `find()` is used.,find__key_type}\n\n    @sa @ref contains(KeyT&&) const -- checks whether a key exists\n\n    @since version 1.0.0\n    */\n    template<typename KeyT>\n    iterator find(KeyT&& key)\n    {\n        auto result = end();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief find an element in a JSON object\n    @copydoc find(KeyT&&)\n    */\n    template<typename KeyT>\n    const_iterator find(KeyT&& key) const\n    {\n        auto result = cend();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief returns the number of occurrences of a key in a JSON object\n\n    Returns the number of elements with key @a key. If ObjectType is the\n    default `std::map` type, the return value will always be `0` (@a key was\n    not found) or `1` (@a key was found).\n\n    @note This method always returns `0` when executed on a JSON type that is\n          not an object.\n\n    @param[in] key key value of the element to count\n\n    @return Number of elements with key @a key. If the JSON value is not an\n    object, the return value will be `0`.\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The example shows how `count()` is used.,count}\n\n    @since version 1.0.0\n    */\n    template<typename KeyT>\n    size_type count(KeyT&& key) const\n    {\n        // return 0 for all nonobject types\n        return is_object() ? m_value.object->count(std::forward<KeyT>(key)) : 0;\n    }\n\n    /*!\n    @brief check the existence of an element in a JSON object\n\n    Check whether an element exists in a JSON object with key equivalent to\n    @a key. If the element is not found or the JSON value is not an object,\n    false is returned.\n\n    @note This method always returns false when executed on a JSON type\n          that is not an object.\n\n    @param[in] key key value to check its existence.\n\n    @return true if an element with specified @a key exists. If no such\n    element with such key is found or the JSON value is not an object,\n    false is returned.\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The following code shows an example for `contains()`.,contains}\n\n    @sa @ref find(KeyT&&) -- returns an iterator to an object element\n    @sa @ref contains(const json_pointer&) const -- checks the existence for a JSON pointer\n\n    @since version 3.6.0\n    */\n    template < typename KeyT, typename std::enable_if <\n                   !std::is_same<typename std::decay<KeyT>::type, json_pointer>::value, int >::type = 0 >\n    bool contains(KeyT && key) const\n    {\n        return is_object() && m_value.object->find(std::forward<KeyT>(key)) != m_value.object->end();\n    }\n\n    /*!\n    @brief check the existence of an element in a JSON object given a JSON pointer\n\n    Check whether the given JSON pointer @a ptr can be resolved in the current\n    JSON value.\n\n    @note This method can be executed on any JSON value type.\n\n    @param[in] ptr JSON pointer to check its existence.\n\n    @return true if the JSON pointer can be resolved to a stored value, false\n    otherwise.\n\n    @post If `j.contains(ptr)` returns true, it is safe to call `j[ptr]`.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The following code shows an example for `contains()`.,contains_json_pointer}\n\n    @sa @ref contains(KeyT &&) const -- checks the existence of a key\n\n    @since version 3.7.0\n    */\n    bool contains(const json_pointer& ptr) const\n    {\n        return ptr.contains(this);\n    }\n\n    /// @}\n\n\n    ///////////////\n    // iterators //\n    ///////////////\n\n    /// @name iterators\n    /// @{\n\n    /*!\n    @brief returns an iterator to the first element\n\n    Returns an iterator to the first element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return iterator to the first element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n\n    @liveexample{The following code shows an example for `begin()`.,begin}\n\n    @sa @ref cbegin() -- returns a const iterator to the beginning\n    @sa @ref end() -- returns an iterator to the end\n    @sa @ref cend() -- returns a const iterator to the end\n\n    @since version 1.0.0\n    */\n    iterator begin() noexcept\n    {\n        iterator result(this);\n        result.set_begin();\n        return result;\n    }\n\n    /*!\n    @copydoc basic_json::cbegin()\n    */\n    const_iterator begin() const noexcept\n    {\n        return cbegin();\n    }\n\n    /*!\n    @brief returns a const iterator to the first element\n\n    Returns a const iterator to the first element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return const iterator to the first element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).begin()`.\n\n    @liveexample{The following code shows an example for `cbegin()`.,cbegin}\n\n    @sa @ref begin() -- returns an iterator to the beginning\n    @sa @ref end() -- returns an iterator to the end\n    @sa @ref cend() -- returns a const iterator to the end\n\n    @since version 1.0.0\n    */\n    const_iterator cbegin() const noexcept\n    {\n        const_iterator result(this);\n        result.set_begin();\n        return result;\n    }\n\n    /*!\n    @brief returns an iterator to one past the last element\n\n    Returns an iterator to one past the last element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return iterator one past the last element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n\n    @liveexample{The following code shows an example for `end()`.,end}\n\n    @sa @ref cend() -- returns a const iterator to the end\n    @sa @ref begin() -- returns an iterator to the beginning\n    @sa @ref cbegin() -- returns a const iterator to the beginning\n\n    @since version 1.0.0\n    */\n    iterator end() noexcept\n    {\n        iterator result(this);\n        result.set_end();\n        return result;\n    }\n\n    /*!\n    @copydoc basic_json::cend()\n    */\n    const_iterator end() const noexcept\n    {\n        return cend();\n    }\n\n    /*!\n    @brief returns a const iterator to one past the last element\n\n    Returns a const iterator to one past the last element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return const iterator one past the last element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).end()`.\n\n    @liveexample{The following code shows an example for `cend()`.,cend}\n\n    @sa @ref end() -- returns an iterator to the end\n    @sa @ref begin() -- returns an iterator to the beginning\n    @sa @ref cbegin() -- returns a const iterator to the beginning\n\n    @since version 1.0.0\n    */\n    const_iterator cend() const noexcept\n    {\n        const_iterator result(this);\n        result.set_end();\n        return result;\n    }\n\n    /*!\n    @brief returns an iterator to the reverse-beginning\n\n    Returns an iterator to the reverse-beginning; that is, the last element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `reverse_iterator(end())`.\n\n    @liveexample{The following code shows an example for `rbegin()`.,rbegin}\n\n    @sa @ref crbegin() -- returns a const reverse iterator to the beginning\n    @sa @ref rend() -- returns a reverse iterator to the end\n    @sa @ref crend() -- returns a const reverse iterator to the end\n\n    @since version 1.0.0\n    */\n    reverse_iterator rbegin() noexcept\n    {\n        return reverse_iterator(end());\n    }\n\n    /*!\n    @copydoc basic_json::crbegin()\n    */\n    const_reverse_iterator rbegin() const noexcept\n    {\n        return crbegin();\n    }\n\n    /*!\n    @brief returns an iterator to the reverse-end\n\n    Returns an iterator to the reverse-end; that is, one before the first\n    element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `reverse_iterator(begin())`.\n\n    @liveexample{The following code shows an example for `rend()`.,rend}\n\n    @sa @ref crend() -- returns a const reverse iterator to the end\n    @sa @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa @ref crbegin() -- returns a const reverse iterator to the beginning\n\n    @since version 1.0.0\n    */\n    reverse_iterator rend() noexcept\n    {\n        return reverse_iterator(begin());\n    }\n\n    /*!\n    @copydoc basic_json::crend()\n    */\n    const_reverse_iterator rend() const noexcept\n    {\n        return crend();\n    }\n\n    /*!\n    @brief returns a const reverse iterator to the last element\n\n    Returns a const iterator to the reverse-beginning; that is, the last\n    element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).rbegin()`.\n\n    @liveexample{The following code shows an example for `crbegin()`.,crbegin}\n\n    @sa @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa @ref rend() -- returns a reverse iterator to the end\n    @sa @ref crend() -- returns a const reverse iterator to the end\n\n    @since version 1.0.0\n    */\n    const_reverse_iterator crbegin() const noexcept\n    {\n        return const_reverse_iterator(cend());\n    }\n\n    /*!\n    @brief returns a const reverse iterator to one before the first\n\n    Returns a const reverse iterator to the reverse-end; that is, one before\n    the first element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).rend()`.\n\n    @liveexample{The following code shows an example for `crend()`.,crend}\n\n    @sa @ref rend() -- returns a reverse iterator to the end\n    @sa @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa @ref crbegin() -- returns a const reverse iterator to the beginning\n\n    @since version 1.0.0\n    */\n    const_reverse_iterator crend() const noexcept\n    {\n        return const_reverse_iterator(cbegin());\n    }\n\n  public:\n    /*!\n    @brief wrapper to access iterator member functions in range-based for\n\n    This function allows to access @ref iterator::key() and @ref\n    iterator::value() during range-based for loops. In these loops, a\n    reference to the JSON values is returned, so there is no access to the\n    underlying iterator.\n\n    For loop without iterator_wrapper:\n\n    @code{cpp}\n    for (auto it = j_object.begin(); it != j_object.end(); ++it)\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    Range-based for loop without iterator proxy:\n\n    @code{cpp}\n    for (auto it : j_object)\n    {\n        // \"it\" is of type json::reference and has no key() member\n        std::cout << \"value: \" << it << '\\n';\n    }\n    @endcode\n\n    Range-based for loop with iterator proxy:\n\n    @code{cpp}\n    for (auto it : json::iterator_wrapper(j_object))\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    @note When iterating over an array, `key()` will return the index of the\n          element as string (see example).\n\n    @param[in] ref  reference to a JSON value\n    @return iteration proxy object wrapping @a ref with an interface to use in\n            range-based for loops\n\n    @liveexample{The following code shows how the wrapper is used,iterator_wrapper}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @note The name of this function is not yet final and may change in the\n    future.\n\n    @deprecated This stream operator is deprecated and will be removed in\n                future 4.0.0 of the library. Please use @ref items() instead;\n                that is, replace `json::iterator_wrapper(j)` with `j.items()`.\n    */\n    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())\n    static iteration_proxy<iterator> iterator_wrapper(reference ref) noexcept\n    {\n        return ref.items();\n    }\n\n    /*!\n    @copydoc iterator_wrapper(reference)\n    */\n    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())\n    static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref) noexcept\n    {\n        return ref.items();\n    }\n\n    /*!\n    @brief helper to access iterator member functions in range-based for\n\n    This function allows to access @ref iterator::key() and @ref\n    iterator::value() during range-based for loops. In these loops, a\n    reference to the JSON values is returned, so there is no access to the\n    underlying iterator.\n\n    For loop without `items()` function:\n\n    @code{cpp}\n    for (auto it = j_object.begin(); it != j_object.end(); ++it)\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    Range-based for loop without `items()` function:\n\n    @code{cpp}\n    for (auto it : j_object)\n    {\n        // \"it\" is of type json::reference and has no key() member\n        std::cout << \"value: \" << it << '\\n';\n    }\n    @endcode\n\n    Range-based for loop with `items()` function:\n\n    @code{cpp}\n    for (auto& el : j_object.items())\n    {\n        std::cout << \"key: \" << el.key() << \", value:\" << el.value() << '\\n';\n    }\n    @endcode\n\n    The `items()` function also allows to use\n    [structured bindings](https://en.cppreference.com/w/cpp/language/structured_binding)\n    (C++17):\n\n    @code{cpp}\n    for (auto& [key, val] : j_object.items())\n    {\n        std::cout << \"key: \" << key << \", value:\" << val << '\\n';\n    }\n    @endcode\n\n    @note When iterating over an array, `key()` will return the index of the\n          element as string (see example). For primitive types (e.g., numbers),\n          `key()` returns an empty string.\n\n    @warning Using `items()` on temporary objects is dangerous. Make sure the\n             object's lifetime exeeds the iteration. See\n             <https://github.com/nlohmann/json/issues/2040> for more\n             information.\n\n    @return iteration proxy object wrapping @a ref with an interface to use in\n            range-based for loops\n\n    @liveexample{The following code shows how the function is used.,items}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 3.1.0, structured bindings support since 3.5.0.\n    */\n    iteration_proxy<iterator> items() noexcept\n    {\n        return iteration_proxy<iterator>(*this);\n    }\n\n    /*!\n    @copydoc items()\n    */\n    iteration_proxy<const_iterator> items() const noexcept\n    {\n        return iteration_proxy<const_iterator>(*this);\n    }\n\n    /// @}\n\n\n    //////////////\n    // capacity //\n    //////////////\n\n    /// @name capacity\n    /// @{\n\n    /*!\n    @brief checks whether the container is empty.\n\n    Checks if a JSON value has no elements (i.e. whether its @ref size is `0`).\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `true`\n            boolean     | `false`\n            string      | `false`\n            number      | `false`\n            binary      | `false`\n            object      | result of function `object_t::empty()`\n            array       | result of function `array_t::empty()`\n\n    @liveexample{The following code uses `empty()` to check if a JSON\n    object contains any elements.,empty}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their `empty()` functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @note This function does not return whether a string stored as JSON value\n    is empty - it returns whether the JSON container itself is empty which is\n    false in the case of a string.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `begin() == end()`.\n\n    @sa @ref size() -- returns the number of elements\n\n    @since version 1.0.0\n    */\n    bool empty() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::null:\n            {\n                // null values are empty\n                return true;\n            }\n\n            case value_t::array:\n            {\n                // delegate call to array_t::empty()\n                return m_value.array->empty();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::empty()\n                return m_value.object->empty();\n            }\n\n            default:\n            {\n                // all other types are nonempty\n                return false;\n            }\n        }\n    }\n\n    /*!\n    @brief returns the number of elements\n\n    Returns the number of elements in a JSON value.\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `0`\n            boolean     | `1`\n            string      | `1`\n            number      | `1`\n            binary      | `1`\n            object      | result of function object_t::size()\n            array       | result of function array_t::size()\n\n    @liveexample{The following code calls `size()` on the different value\n    types.,size}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their size() functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @note This function does not return the length of a string stored as JSON\n    value - it returns the number of elements in the JSON value which is 1 in\n    the case of a string.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `std::distance(begin(), end())`.\n\n    @sa @ref empty() -- checks whether the container is empty\n    @sa @ref max_size() -- returns the maximal number of elements\n\n    @since version 1.0.0\n    */\n    size_type size() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::null:\n            {\n                // null values are empty\n                return 0;\n            }\n\n            case value_t::array:\n            {\n                // delegate call to array_t::size()\n                return m_value.array->size();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::size()\n                return m_value.object->size();\n            }\n\n            default:\n            {\n                // all other types have size 1\n                return 1;\n            }\n        }\n    }\n\n    /*!\n    @brief returns the maximum possible number of elements\n\n    Returns the maximum number of elements a JSON value is able to hold due to\n    system or library implementation limitations, i.e. `std::distance(begin(),\n    end())` for the JSON value.\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `0` (same as `size()`)\n            boolean     | `1` (same as `size()`)\n            string      | `1` (same as `size()`)\n            number      | `1` (same as `size()`)\n            binary      | `1` (same as `size()`)\n            object      | result of function `object_t::max_size()`\n            array       | result of function `array_t::max_size()`\n\n    @liveexample{The following code calls `max_size()` on the different value\n    types. Note the output is implementation specific.,max_size}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their `max_size()` functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of returning `b.size()` where `b` is the largest\n      possible JSON value.\n\n    @sa @ref size() -- returns the number of elements\n\n    @since version 1.0.0\n    */\n    size_type max_size() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::array:\n            {\n                // delegate call to array_t::max_size()\n                return m_value.array->max_size();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::max_size()\n                return m_value.object->max_size();\n            }\n\n            default:\n            {\n                // all other types have max_size() == size()\n                return size();\n            }\n        }\n    }\n\n    /// @}\n\n\n    ///////////////\n    // modifiers //\n    ///////////////\n\n    /// @name modifiers\n    /// @{\n\n    /*!\n    @brief clears the contents\n\n    Clears the content of a JSON value and resets it to the default value as\n    if @ref basic_json(value_t) would have been called with the current value\n    type from @ref type():\n\n    Value type  | initial value\n    ----------- | -------------\n    null        | `null`\n    boolean     | `false`\n    string      | `\"\"`\n    number      | `0`\n    binary      | An empty byte vector\n    object      | `{}`\n    array       | `[]`\n\n    @post Has the same effect as calling\n    @code {.cpp}\n    *this = basic_json(type());\n    @endcode\n\n    @liveexample{The example below shows the effect of `clear()` to different\n    JSON types.,clear}\n\n    @complexity Linear in the size of the JSON value.\n\n    @iterators All iterators, pointers and references related to this container\n               are invalidated.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @sa @ref basic_json(value_t) -- constructor that creates an object with the\n        same value than calling `clear()`\n\n    @since version 1.0.0\n    */\n    void clear() noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::number_integer:\n            {\n                m_value.number_integer = 0;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value.number_unsigned = 0;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value.number_float = 0.0;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value.boolean = false;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value.string->clear();\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_value.binary->clear();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value.array->clear();\n                break;\n            }\n\n            case value_t::object:\n            {\n                m_value.object->clear();\n                break;\n            }\n\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @brief add an object to an array\n\n    Appends the given element @a val to the end of the JSON value. If the\n    function is called on a JSON null value, an empty array is created before\n    appending @a val.\n\n    @param[in] val the value to add to the JSON array\n\n    @throw type_error.308 when called on a type other than JSON array or\n    null; example: `\"cannot use push_back() with number\"`\n\n    @complexity Amortized constant.\n\n    @liveexample{The example shows how `push_back()` and `+=` can be used to\n    add elements to a JSON array. Note how the `null` value was silently\n    converted to a JSON array.,push_back}\n\n    @since version 1.0.0\n    */\n    void push_back(basic_json&& val)\n    {\n        // push_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array (move semantics)\n        m_value.array->push_back(std::move(val));\n        // if val is moved from, basic_json move constructor marks it null so we do not call the destructor\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    reference operator+=(basic_json&& val)\n    {\n        push_back(std::move(val));\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    void push_back(const basic_json& val)\n    {\n        // push_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array\n        m_value.array->push_back(val);\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    reference operator+=(const basic_json& val)\n    {\n        push_back(val);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an object\n\n    Inserts the given element @a val to the JSON object. If the function is\n    called on a JSON null value, an empty object is created before inserting\n    @a val.\n\n    @param[in] val the value to add to the JSON object\n\n    @throw type_error.308 when called on a type other than JSON object or\n    null; example: `\"cannot use push_back() with number\"`\n\n    @complexity Logarithmic in the size of the container, O(log(`size()`)).\n\n    @liveexample{The example shows how `push_back()` and `+=` can be used to\n    add elements to a JSON object. Note how the `null` value was silently\n    converted to a JSON object.,push_back__object_t__value}\n\n    @since version 1.0.0\n    */\n    void push_back(const typename object_t::value_type& val)\n    {\n        // push_back only works for null objects or objects\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // add element to array\n        m_value.object->insert(val);\n    }\n\n    /*!\n    @brief add an object to an object\n    @copydoc push_back(const typename object_t::value_type&)\n    */\n    reference operator+=(const typename object_t::value_type& val)\n    {\n        push_back(val);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an object\n\n    This function allows to use `push_back` with an initializer list. In case\n\n    1. the current value is an object,\n    2. the initializer list @a init contains only two elements, and\n    3. the first element of @a init is a string,\n\n    @a init is converted into an object element and added using\n    @ref push_back(const typename object_t::value_type&). Otherwise, @a init\n    is converted to a JSON value and added using @ref push_back(basic_json&&).\n\n    @param[in] init  an initializer list\n\n    @complexity Linear in the size of the initializer list @a init.\n\n    @note This function is required to resolve an ambiguous overload error,\n          because pairs like `{\"key\", \"value\"}` can be both interpreted as\n          `object_t::value_type` or `std::initializer_list<basic_json>`, see\n          https://github.com/nlohmann/json/issues/235 for more information.\n\n    @liveexample{The example shows how initializer lists are treated as\n    objects when possible.,push_back__initializer_list}\n    */\n    void push_back(initializer_list_t init)\n    {\n        if (is_object() && init.size() == 2 && (*init.begin())->is_string())\n        {\n            basic_json&& key = init.begin()->moved_or_copied();\n            push_back(typename object_t::value_type(\n                          std::move(key.get_ref<string_t&>()), (init.begin() + 1)->moved_or_copied()));\n        }\n        else\n        {\n            push_back(basic_json(init));\n        }\n    }\n\n    /*!\n    @brief add an object to an object\n    @copydoc push_back(initializer_list_t)\n    */\n    reference operator+=(initializer_list_t init)\n    {\n        push_back(init);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an array\n\n    Creates a JSON value from the passed parameters @a args to the end of the\n    JSON value. If the function is called on a JSON null value, an empty array\n    is created before appending the value created from @a args.\n\n    @param[in] args arguments to forward to a constructor of @ref basic_json\n    @tparam Args compatible types to create a @ref basic_json object\n\n    @return reference to the inserted element\n\n    @throw type_error.311 when called on a type other than JSON array or\n    null; example: `\"cannot use emplace_back() with number\"`\n\n    @complexity Amortized constant.\n\n    @liveexample{The example shows how `push_back()` can be used to add\n    elements to a JSON array. Note how the `null` value was silently converted\n    to a JSON array.,emplace_back}\n\n    @since version 2.0.8, returns reference since 3.7.0\n    */\n    template<class... Args>\n    reference emplace_back(Args&& ... args)\n    {\n        // emplace_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(311, \"cannot use emplace_back() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array (perfect forwarding)\n#ifdef JSON_HAS_CPP_17\n        return m_value.array->emplace_back(std::forward<Args>(args)...);\n#else\n        m_value.array->emplace_back(std::forward<Args>(args)...);\n        return m_value.array->back();\n#endif\n    }\n\n    /*!\n    @brief add an object to an object if key does not exist\n\n    Inserts a new element into a JSON object constructed in-place with the\n    given @a args if there is no element with the key in the container. If the\n    function is called on a JSON null value, an empty object is created before\n    appending the value created from @a args.\n\n    @param[in] args arguments to forward to a constructor of @ref basic_json\n    @tparam Args compatible types to create a @ref basic_json object\n\n    @return a pair consisting of an iterator to the inserted element, or the\n            already-existing element if no insertion happened, and a bool\n            denoting whether the insertion took place.\n\n    @throw type_error.311 when called on a type other than JSON object or\n    null; example: `\"cannot use emplace() with number\"`\n\n    @complexity Logarithmic in the size of the container, O(log(`size()`)).\n\n    @liveexample{The example shows how `emplace()` can be used to add elements\n    to a JSON object. Note how the `null` value was silently converted to a\n    JSON object. Further note how no value is added if there was already one\n    value stored with the same key.,emplace}\n\n    @since version 2.0.8\n    */\n    template<class... Args>\n    std::pair<iterator, bool> emplace(Args&& ... args)\n    {\n        // emplace only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))\n        {\n            JSON_THROW(type_error::create(311, \"cannot use emplace() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // add element to array (perfect forwarding)\n        auto res = m_value.object->emplace(std::forward<Args>(args)...);\n        // create result iterator and set iterator to the result of emplace\n        auto it = begin();\n        it.m_it.object_iterator = res.first;\n\n        // return pair of iterator and boolean\n        return {it, res.second};\n    }\n\n    /// Helper for insertion of an iterator\n    /// @note: This uses std::distance to support GCC 4.8,\n    ///        see https://github.com/nlohmann/json/pull/1257\n    template<typename... Args>\n    iterator insert_iterator(const_iterator pos, Args&& ... args)\n    {\n        iterator result(this);\n        JSON_ASSERT(m_value.array != nullptr);\n\n        auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator);\n        m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...);\n        result.m_it.array_iterator = m_value.array->begin() + insert_pos;\n\n        // This could have been written as:\n        // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);\n        // but the return value of insert is missing in GCC 4.8, so it is written this way instead.\n\n        return result;\n    }\n\n    /*!\n    @brief inserts element\n\n    Inserts element @a val before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] val element to insert\n    @return iterator pointing to the inserted @a val.\n\n    @throw type_error.309 if called on JSON values other than arrays;\n    example: `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @complexity Constant plus linear in the distance between @a pos and end of\n    the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, const basic_json& val)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // check if iterator pos fits to this JSON value\n            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n            {\n                JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n            }\n\n            // insert to array and return iterator\n            return insert_iterator(pos, val);\n        }\n\n        JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief inserts element\n    @copydoc insert(const_iterator, const basic_json&)\n    */\n    iterator insert(const_iterator pos, basic_json&& val)\n    {\n        return insert(pos, val);\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts @a cnt copies of @a val before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] cnt number of copies of @a val to insert\n    @param[in] val element to insert\n    @return iterator pointing to the first element inserted, or @a pos if\n    `cnt==0`\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @complexity Linear in @a cnt plus linear in the distance between @a pos\n    and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__count}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, size_type cnt, const basic_json& val)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // check if iterator pos fits to this JSON value\n            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n            {\n                JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n            }\n\n            // insert to array and return iterator\n            return insert_iterator(pos, cnt, val);\n        }\n\n        JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from range `[first, last)` before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n    @throw invalid_iterator.211 if @a first or @a last are iterators into\n    container for which insert is called; example: `\"passed iterators may not\n    belong to container\"`\n\n    @return iterator pointing to the first element inserted, or @a pos if\n    `first==last`\n\n    @complexity Linear in `std::distance(first, last)` plus linear in the\n    distance between @a pos and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__range}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, const_iterator first, const_iterator last)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_UNLIKELY(!is_array()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n        }\n\n        // check if iterator pos fits to this JSON value\n        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\"));\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(first.m_object == this))\n        {\n            JSON_THROW(invalid_iterator::create(211, \"passed iterators may not belong to container\"));\n        }\n\n        // insert to array and return iterator\n        return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from initializer list @a ilist before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] ilist initializer list to insert the values from\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @return iterator pointing to the first element inserted, or @a pos if\n    `ilist` is empty\n\n    @complexity Linear in `ilist.size()` plus linear in the distance between\n    @a pos and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__ilist}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, initializer_list_t ilist)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_UNLIKELY(!is_array()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n        }\n\n        // check if iterator pos fits to this JSON value\n        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n        }\n\n        // insert to array and return iterator\n        return insert_iterator(pos, ilist.begin(), ilist.end());\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from range `[first, last)`.\n\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n\n    @throw type_error.309 if called on JSON values other than objects; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if iterator @a first or @a last does does not\n    point to an object; example: `\"iterators first and last must point to\n    objects\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n\n    @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number\n    of elements to insert.\n\n    @liveexample{The example shows how `insert()` is used.,insert__range_object}\n\n    @since version 3.0.0\n    */\n    void insert(const_iterator first, const_iterator last)\n    {\n        // insert only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\"));\n        }\n\n        // passed iterators must belong to objects\n        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterators first and last must point to objects\"));\n        }\n\n        m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);\n    }\n\n    /*!\n    @brief updates a JSON object from another object, overwriting existing keys\n\n    Inserts all values from JSON object @a j and overwrites existing keys.\n\n    @param[in] j  JSON object to read values from\n\n    @throw type_error.312 if called on JSON values other than objects; example:\n    `\"cannot use update() with string\"`\n\n    @complexity O(N*log(size() + N)), where N is the number of elements to\n                insert.\n\n    @liveexample{The example shows how `update()` is used.,update}\n\n    @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update\n\n    @since version 3.0.0\n    */\n    void update(const_reference j)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(312, \"cannot use update() with \" + std::string(type_name())));\n        }\n        if (JSON_HEDLEY_UNLIKELY(!j.is_object()))\n        {\n            JSON_THROW(type_error::create(312, \"cannot use update() with \" + std::string(j.type_name())));\n        }\n\n        for (auto it = j.cbegin(); it != j.cend(); ++it)\n        {\n            m_value.object->operator[](it.key()) = it.value();\n        }\n    }\n\n    /*!\n    @brief updates a JSON object from another object, overwriting existing keys\n\n    Inserts all values from from range `[first, last)` and overwrites existing\n    keys.\n\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n\n    @throw type_error.312 if called on JSON values other than objects; example:\n    `\"cannot use update() with string\"`\n    @throw invalid_iterator.202 if iterator @a first or @a last does does not\n    point to an object; example: `\"iterators first and last must point to\n    objects\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n\n    @complexity O(N*log(size() + N)), where N is the number of elements to\n                insert.\n\n    @liveexample{The example shows how `update()` is used__range.,update}\n\n    @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update\n\n    @since version 3.0.0\n    */\n    void update(const_iterator first, const_iterator last)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(312, \"cannot use update() with \" + std::string(type_name())));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\"));\n        }\n\n        // passed iterators must belong to objects\n        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()\n                                 || !last.m_object->is_object()))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterators first and last must point to objects\"));\n        }\n\n        for (auto it = first; it != last; ++it)\n        {\n            m_value.object->operator[](it.key()) = it.value();\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of the JSON value with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other JSON value to exchange the contents with\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how JSON values can be swapped with\n    `swap()`.,swap__reference}\n\n    @since version 1.0.0\n    */\n    void swap(reference other) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&&\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        std::swap(m_type, other.m_type);\n        std::swap(m_value, other.m_value);\n        assert_invariant();\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of the JSON value from @a left with those of @a right. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated. implemented as a friend function callable via ADL.\n\n    @param[in,out] left JSON value to exchange the contents with\n    @param[in,out] right JSON value to exchange the contents with\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how JSON values can be swapped with\n    `swap()`.,swap__reference}\n\n    @since version 1.0.0\n    */\n    friend void swap(reference left, reference right) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&&\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        left.swap(right);\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON array with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other array to exchange the contents with\n\n    @throw type_error.310 when JSON value is not an array; example: `\"cannot\n    use swap() with string\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how arrays can be swapped with\n    `swap()`.,swap__array_t}\n\n    @since version 1.0.0\n    */\n    void swap(array_t& other)\n    {\n        // swap only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            std::swap(*(m_value.array), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON object with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other object to exchange the contents with\n\n    @throw type_error.310 when JSON value is not an object; example:\n    `\"cannot use swap() with string\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how objects can be swapped with\n    `swap()`.,swap__object_t}\n\n    @since version 1.0.0\n    */\n    void swap(object_t& other)\n    {\n        // swap only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            std::swap(*(m_value.object), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON string with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other string to exchange the contents with\n\n    @throw type_error.310 when JSON value is not a string; example: `\"cannot\n    use swap() with boolean\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how strings can be swapped with\n    `swap()`.,swap__string_t}\n\n    @since version 1.0.0\n    */\n    void swap(string_t& other)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_string()))\n        {\n            std::swap(*(m_value.string), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON string with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other binary to exchange the contents with\n\n    @throw type_error.310 when JSON value is not a string; example: `\"cannot\n    use swap() with boolean\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how strings can be swapped with\n    `swap()`.,swap__binary_t}\n\n    @since version 3.8.0\n    */\n    void swap(binary_t& other)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_binary()))\n        {\n            std::swap(*(m_value.binary), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name())));\n        }\n    }\n\n    /// @copydoc swap(binary_t)\n    void swap(typename binary_t::container_type& other)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_binary()))\n        {\n            std::swap(*(m_value.binary), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name())));\n        }\n    }\n\n    /// @}\n\n  public:\n    //////////////////////////////////////////\n    // lexicographical comparison operators //\n    //////////////////////////////////////////\n\n    /// @name lexicographical comparison operators\n    /// @{\n\n    /*!\n    @brief comparison: equal\n\n    Compares two JSON values for equality according to the following rules:\n    - Two JSON values are equal if (1) they are from the same type and (2)\n      their stored values are the same according to their respective\n      `operator==`.\n    - Integer and floating-point numbers are automatically converted before\n      comparison. Note that two NaN values are always treated as unequal.\n    - Two JSON null values are equal.\n\n    @note Floating-point inside JSON values numbers are compared with\n    `json::number_float_t::operator==` which is `double::operator==` by\n    default. To compare floating-point while respecting an epsilon, an alternative\n    [comparison function](https://github.com/mariokonrad/marnav/blob/master/include/marnav/math/floatingpoint.hpp#L34-#L39)\n    could be used, for instance\n    @code {.cpp}\n    template<typename T, typename = typename std::enable_if<std::is_floating_point<T>::value, T>::type>\n    inline bool is_same(T a, T b, T epsilon = std::numeric_limits<T>::epsilon()) noexcept\n    {\n        return std::abs(a - b) <= epsilon;\n    }\n    @endcode\n    Or you can self-defined operator equal function like this:\n    @code {.cpp}\n    bool my_equal(const_reference lhs, const_reference rhs) {\n    const auto lhs_type lhs.type();\n    const auto rhs_type rhs.type();\n    if (lhs_type == rhs_type) {\n        switch(lhs_type)\n            // self_defined case\n            case value_t::number_float:\n                return std::abs(lhs - rhs) <= std::numeric_limits<float>::epsilon();\n            // other cases remain the same with the original\n            ...\n    }\n    ...\n    }\n    @endcode\n\n    @note NaN values never compare equal to themselves or to other NaN values.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether the values @a lhs and @a rhs are equal\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @complexity Linear.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__equal}\n\n    @since version 1.0.0\n    */\n    friend bool operator==(const_reference lhs, const_reference rhs) noexcept\n    {\n        const auto lhs_type = lhs.type();\n        const auto rhs_type = rhs.type();\n\n        if (lhs_type == rhs_type)\n        {\n            switch (lhs_type)\n            {\n                case value_t::array:\n                    return *lhs.m_value.array == *rhs.m_value.array;\n\n                case value_t::object:\n                    return *lhs.m_value.object == *rhs.m_value.object;\n\n                case value_t::null:\n                    return true;\n\n                case value_t::string:\n                    return *lhs.m_value.string == *rhs.m_value.string;\n\n                case value_t::boolean:\n                    return lhs.m_value.boolean == rhs.m_value.boolean;\n\n                case value_t::number_integer:\n                    return lhs.m_value.number_integer == rhs.m_value.number_integer;\n\n                case value_t::number_unsigned:\n                    return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned;\n\n                case value_t::number_float:\n                    return lhs.m_value.number_float == rhs.m_value.number_float;\n\n                case value_t::binary:\n                    return *lhs.m_value.binary == *rhs.m_value.binary;\n\n                default:\n                    return false;\n            }\n        }\n        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)\n        {\n            return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer);\n        }\n        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned);\n        }\n        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)\n        {\n            return static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer;\n        }\n        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned);\n        }\n\n        return false;\n    }\n\n    /*!\n    @brief comparison: equal\n    @copydoc operator==(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs == basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: equal\n    @copydoc operator==(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) == rhs;\n    }\n\n    /*!\n    @brief comparison: not equal\n\n    Compares two JSON values for inequality by calculating `not (lhs == rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether the values @a lhs and @a rhs are not equal\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__notequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator!=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return !(lhs == rhs);\n    }\n\n    /*!\n    @brief comparison: not equal\n    @copydoc operator!=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs != basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: not equal\n    @copydoc operator!=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) != rhs;\n    }\n\n    /*!\n    @brief comparison: less than\n\n    Compares whether one JSON value @a lhs is less than another JSON value @a\n    rhs according to the following rules:\n    - If @a lhs and @a rhs have the same type, the values are compared using\n      the default `<` operator.\n    - Integer and floating-point numbers are automatically converted before\n      comparison\n    - In case @a lhs and @a rhs have different types, the values are ignored\n      and the order of the types is considered, see\n      @ref operator<(const value_t, const value_t).\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is less than @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__less}\n\n    @since version 1.0.0\n    */\n    friend bool operator<(const_reference lhs, const_reference rhs) noexcept\n    {\n        const auto lhs_type = lhs.type();\n        const auto rhs_type = rhs.type();\n\n        if (lhs_type == rhs_type)\n        {\n            switch (lhs_type)\n            {\n                case value_t::array:\n                    // note parentheses are necessary, see\n                    // https://github.com/nlohmann/json/issues/1530\n                    return (*lhs.m_value.array) < (*rhs.m_value.array);\n\n                case value_t::object:\n                    return (*lhs.m_value.object) < (*rhs.m_value.object);\n\n                case value_t::null:\n                    return false;\n\n                case value_t::string:\n                    return (*lhs.m_value.string) < (*rhs.m_value.string);\n\n                case value_t::boolean:\n                    return (lhs.m_value.boolean) < (rhs.m_value.boolean);\n\n                case value_t::number_integer:\n                    return (lhs.m_value.number_integer) < (rhs.m_value.number_integer);\n\n                case value_t::number_unsigned:\n                    return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned);\n\n                case value_t::number_float:\n                    return (lhs.m_value.number_float) < (rhs.m_value.number_float);\n\n                case value_t::binary:\n                    return (*lhs.m_value.binary) < (*rhs.m_value.binary);\n\n                default:\n                    return false;\n            }\n        }\n        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_integer) < rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)\n        {\n            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_integer);\n        }\n        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_unsigned);\n        }\n        else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_integer < static_cast<number_integer_t>(rhs.m_value.number_unsigned);\n        }\n        else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)\n        {\n            return static_cast<number_integer_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer;\n        }\n\n        // We only reach this line if we cannot compare values. In that case,\n        // we compare types. Note we have to call the operator explicitly,\n        // because MSVC has problems otherwise.\n        return operator<(lhs_type, rhs_type);\n    }\n\n    /*!\n    @brief comparison: less than\n    @copydoc operator<(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs < basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: less than\n    @copydoc operator<(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) < rhs;\n    }\n\n    /*!\n    @brief comparison: less than or equal\n\n    Compares whether one JSON value @a lhs is less than or equal to another\n    JSON value by calculating `not (rhs < lhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is less than or equal to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__greater}\n\n    @since version 1.0.0\n    */\n    friend bool operator<=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return !(rhs < lhs);\n    }\n\n    /*!\n    @brief comparison: less than or equal\n    @copydoc operator<=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs <= basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: less than or equal\n    @copydoc operator<=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) <= rhs;\n    }\n\n    /*!\n    @brief comparison: greater than\n\n    Compares whether one JSON value @a lhs is greater than another\n    JSON value by calculating `not (lhs <= rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is greater than to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__lessequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator>(const_reference lhs, const_reference rhs) noexcept\n    {\n        return !(lhs <= rhs);\n    }\n\n    /*!\n    @brief comparison: greater than\n    @copydoc operator>(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs > basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: greater than\n    @copydoc operator>(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) > rhs;\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n\n    Compares whether one JSON value @a lhs is greater than or equal to another\n    JSON value by calculating `not (lhs < rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is greater than or equal to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__greaterequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator>=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return !(lhs < rhs);\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n    @copydoc operator>=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return lhs >= basic_json(rhs);\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n    @copydoc operator>=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) >= rhs;\n    }\n\n    /// @}\n\n    ///////////////////\n    // serialization //\n    ///////////////////\n\n    /// @name serialization\n    /// @{\n\n    /*!\n    @brief serialize to stream\n\n    Serialize the given JSON value @a j to the output stream @a o. The JSON\n    value will be serialized using the @ref dump member function.\n\n    - The indentation of the output can be controlled with the member variable\n      `width` of the output stream @a o. For instance, using the manipulator\n      `std::setw(4)` on @a o sets the indentation level to `4` and the\n      serialization result is the same as calling `dump(4)`.\n\n    - The indentation character can be controlled with the member variable\n      `fill` of the output stream @a o. For instance, the manipulator\n      `std::setfill('\\\\t')` sets indentation to use a tab character rather than\n      the default space character.\n\n    @param[in,out] o  stream to serialize to\n    @param[in] j  JSON value to serialize\n\n    @return the stream @a o\n\n    @throw type_error.316 if a string stored inside the JSON value is not\n                          UTF-8 encoded\n\n    @complexity Linear.\n\n    @liveexample{The example below shows the serialization with different\n    parameters to `width` to adjust the indentation level.,operator_serialize}\n\n    @since version 1.0.0; indentation character added in version 3.0.0\n    */\n    friend std::ostream& operator<<(std::ostream& o, const basic_json& j)\n    {\n        // read width member and use it as indentation parameter if nonzero\n        const bool pretty_print = o.width() > 0;\n        const auto indentation = pretty_print ? o.width() : 0;\n\n        // reset width to 0 for subsequent calls to this stream\n        o.width(0);\n\n        // do the actual serialization\n        serializer s(detail::output_adapter<char>(o), o.fill());\n        s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation));\n        return o;\n    }\n\n    /*!\n    @brief serialize to stream\n    @deprecated This stream operator is deprecated and will be removed in\n                future 4.0.0 of the library. Please use\n                @ref operator<<(std::ostream&, const basic_json&)\n                instead; that is, replace calls like `j >> o;` with `o << j;`.\n    @since version 1.0.0; deprecated since version 3.0.0\n    */\n    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&))\n    friend std::ostream& operator>>(const basic_json& j, std::ostream& o)\n    {\n        return o << j;\n    }\n\n    /// @}\n\n\n    /////////////////////\n    // deserialization //\n    /////////////////////\n\n    /// @name deserialization\n    /// @{\n\n    /*!\n    @brief deserialize from a compatible input\n\n    @tparam InputType A compatible input, for instance\n    - an std::istream object\n    - a FILE pointer\n    - a C-style array of characters\n    - a pointer to a null-terminated string of single byte characters\n    - an object obj for which begin(obj) and end(obj) produces a valid pair of\n      iterators.\n\n    @param[in] i  input to read from\n    @param[in] cb  a parser callback function of type @ref parser_callback_t\n    which is used to control the deserialization by filtering unwanted values\n    (optional)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n    @param[in] ignore_comments  whether comments should be ignored and treated\n    like whitespace (true) or yield a parse error (true); (optional, false by\n    default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.101 if a parse error occurs; example: `\"\"unexpected end\n    of input; expected string literal\"\"`\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser. The complexity can be higher if the parser callback function\n    @a cb or reading from the input @a i has a super-linear complexity.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `parse()` function reading\n    from an array.,parse__array__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function with\n    and without callback function.,parse__string__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function with\n    and without callback function.,parse__istream__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function reading\n    from a contiguous container.,parse__contiguouscontainer__parser_callback_t}\n\n    @since version 2.0.3 (contiguous containers); version 3.9.0 allowed to\n    ignore comments.\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json parse(InputType&& i,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false)\n    {\n        basic_json result;\n        parser(detail::input_adapter(std::forward<InputType>(i)), cb, allow_exceptions, ignore_comments).parse(true, result);\n        return result;\n    }\n\n    /*!\n    @brief deserialize from a pair of character iterators\n\n    The value_type of the iterator must be a integral type with size of 1, 2 or\n    4 bytes, which will be interpreted respectively as UTF-8, UTF-16 and UTF-32.\n\n    @param[in] first iterator to start of character range\n    @param[in] last  iterator to end of character range\n    @param[in] cb  a parser callback function of type @ref parser_callback_t\n    which is used to control the deserialization by filtering unwanted values\n    (optional)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n    @param[in] ignore_comments  whether comments should be ignored and treated\n    like whitespace (true) or yield a parse error (true); (optional, false by\n    default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.101 if a parse error occurs; example: `\"\"unexpected end\n    of input; expected string literal\"\"`\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json parse(IteratorType first,\n                            IteratorType last,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false)\n    {\n        basic_json result;\n        parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result);\n        return result;\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))\n    static basic_json parse(detail::span_input_adapter&& i,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false)\n    {\n        basic_json result;\n        parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result);\n        return result;\n    }\n\n    /*!\n    @brief check if the input is valid JSON\n\n    Unlike the @ref parse(InputType&&, const parser_callback_t,const bool)\n    function, this function neither throws an exception in case of invalid JSON\n    input (i.e., a parse error) nor creates diagnostic information.\n\n    @tparam InputType A compatible input, for instance\n    - an std::istream object\n    - a FILE pointer\n    - a C-style array of characters\n    - a pointer to a null-terminated string of single byte characters\n    - an object obj for which begin(obj) and end(obj) produces a valid pair of\n      iterators.\n\n    @param[in] i input to read from\n    @param[in] ignore_comments  whether comments should be ignored and treated\n    like whitespace (true) or yield a parse error (true); (optional, false by\n    default)\n\n    @return Whether the input read from @a i is valid JSON.\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `accept()` function reading\n    from a string.,accept__string}\n    */\n    template<typename InputType>\n    static bool accept(InputType&& i,\n                       const bool ignore_comments = false)\n    {\n        return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments).accept(true);\n    }\n\n    template<typename IteratorType>\n    static bool accept(IteratorType first, IteratorType last,\n                       const bool ignore_comments = false)\n    {\n        return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))\n    static bool accept(detail::span_input_adapter&& i,\n                       const bool ignore_comments = false)\n    {\n        return parser(i.get(), nullptr, false, ignore_comments).accept(true);\n    }\n\n    /*!\n    @brief generate SAX events\n\n    The SAX event lister must follow the interface of @ref json_sax.\n\n    This function reads from a compatible input. Examples are:\n    - an std::istream object\n    - a FILE pointer\n    - a C-style array of characters\n    - a pointer to a null-terminated string of single byte characters\n    - an object obj for which begin(obj) and end(obj) produces a valid pair of\n      iterators.\n\n    @param[in] i  input to read from\n    @param[in,out] sax  SAX event listener\n    @param[in] format  the format to parse (JSON, CBOR, MessagePack, or UBJSON)\n    @param[in] strict  whether the input has to be consumed completely\n    @param[in] ignore_comments  whether comments should be ignored and treated\n    like whitespace (true) or yield a parse error (true); (optional, false by\n    default); only applies to the JSON file format.\n\n    @return return value of the last processed SAX event\n\n    @throw parse_error.101 if a parse error occurs; example: `\"\"unexpected end\n    of input; expected string literal\"\"`\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser. The complexity can be higher if the SAX consumer @a sax has\n    a super-linear complexity.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `sax_parse()` function\n    reading from string and processing the events with a user-defined SAX\n    event consumer.,sax_parse}\n\n    @since version 3.2.0\n    */\n    template <typename InputType, typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    static bool sax_parse(InputType&& i, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false)\n    {\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        return format == input_format_t::json\n               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);\n    }\n\n    template<class IteratorType, class SAX>\n    JSON_HEDLEY_NON_NULL(3)\n    static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false)\n    {\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        return format == input_format_t::json\n               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);\n    }\n\n    template <typename SAX>\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...))\n    JSON_HEDLEY_NON_NULL(2)\n    static bool sax_parse(detail::span_input_adapter&& i, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false)\n    {\n        auto ia = i.get();\n        return format == input_format_t::json\n               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia)).sax_parse(format, sax, strict);\n    }\n\n    /*!\n    @brief deserialize from stream\n    @deprecated This stream operator is deprecated and will be removed in\n                version 4.0.0 of the library. Please use\n                @ref operator>>(std::istream&, basic_json&)\n                instead; that is, replace calls like `j << i;` with `i >> j;`.\n    @since version 1.0.0; deprecated since version 3.0.0\n    */\n    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&))\n    friend std::istream& operator<<(basic_json& j, std::istream& i)\n    {\n        return operator>>(i, j);\n    }\n\n    /*!\n    @brief deserialize from stream\n\n    Deserializes an input stream to a JSON value.\n\n    @param[in,out] i  input stream to read a serialized JSON value from\n    @param[in,out] j  JSON value to write the deserialized input to\n\n    @throw parse_error.101 in case of an unexpected token\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below shows how a JSON value is constructed by\n    reading a serialization from a stream.,operator_deserialize}\n\n    @sa parse(std::istream&, const parser_callback_t) for a variant with a\n    parser callback function to filter values while parsing\n\n    @since version 1.0.0\n    */\n    friend std::istream& operator>>(std::istream& i, basic_json& j)\n    {\n        parser(detail::input_adapter(i)).parse(false, j);\n        return i;\n    }\n\n    /// @}\n\n    ///////////////////////////\n    // convenience functions //\n    ///////////////////////////\n\n    /*!\n    @brief return the type as string\n\n    Returns the type name as string to be used in error messages - usually to\n    indicate that a function was called on a wrong JSON type.\n\n    @return a string representation of a the @a m_type member:\n            Value type  | return value\n            ----------- | -------------\n            null        | `\"null\"`\n            boolean     | `\"boolean\"`\n            string      | `\"string\"`\n            number      | `\"number\"` (for all number types)\n            object      | `\"object\"`\n            array       | `\"array\"`\n            binary      | `\"binary\"`\n            discarded   | `\"discarded\"`\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @complexity Constant.\n\n    @liveexample{The following code exemplifies `type_name()` for all JSON\n    types.,type_name}\n\n    @sa @ref type() -- return the type of the JSON value\n    @sa @ref operator value_t() -- return the type of the JSON value (implicit)\n\n    @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept`\n    since 3.0.0\n    */\n    JSON_HEDLEY_RETURNS_NON_NULL\n    const char* type_name() const noexcept\n    {\n        {\n            switch (m_type)\n            {\n                case value_t::null:\n                    return \"null\";\n                case value_t::object:\n                    return \"object\";\n                case value_t::array:\n                    return \"array\";\n                case value_t::string:\n                    return \"string\";\n                case value_t::boolean:\n                    return \"boolean\";\n                case value_t::binary:\n                    return \"binary\";\n                case value_t::discarded:\n                    return \"discarded\";\n                default:\n                    return \"number\";\n            }\n        }\n    }\n\n\n  private:\n    //////////////////////\n    // member variables //\n    //////////////////////\n\n    /// the type of the current element\n    value_t m_type = value_t::null;\n\n    /// the value of the current element\n    json_value m_value = {};\n\n    //////////////////////////////////////////\n    // binary serialization/deserialization //\n    //////////////////////////////////////////\n\n    /// @name binary serialization/deserialization support\n    /// @{\n\n  public:\n    /*!\n    @brief create a CBOR serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the CBOR (Concise\n    Binary Object Representation) serialization format. CBOR is a binary\n    serialization format which aims to be more compact than JSON itself, yet\n    more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    CBOR types according to the CBOR specification (RFC 7049):\n\n    JSON value type | value/range                                | CBOR type                          | first byte\n    --------------- | ------------------------------------------ | ---------------------------------- | ---------------\n    null            | `null`                                     | Null                               | 0xF6\n    boolean         | `true`                                     | True                               | 0xF5\n    boolean         | `false`                                    | False                              | 0xF4\n    number_integer  | -9223372036854775808..-2147483649          | Negative integer (8 bytes follow)  | 0x3B\n    number_integer  | -2147483648..-32769                        | Negative integer (4 bytes follow)  | 0x3A\n    number_integer  | -32768..-129                               | Negative integer (2 bytes follow)  | 0x39\n    number_integer  | -128..-25                                  | Negative integer (1 byte follow)   | 0x38\n    number_integer  | -24..-1                                    | Negative integer                   | 0x20..0x37\n    number_integer  | 0..23                                      | Integer                            | 0x00..0x17\n    number_integer  | 24..255                                    | Unsigned integer (1 byte follow)   | 0x18\n    number_integer  | 256..65535                                 | Unsigned integer (2 bytes follow)  | 0x19\n    number_integer  | 65536..4294967295                          | Unsigned integer (4 bytes follow)  | 0x1A\n    number_integer  | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow)  | 0x1B\n    number_unsigned | 0..23                                      | Integer                            | 0x00..0x17\n    number_unsigned | 24..255                                    | Unsigned integer (1 byte follow)   | 0x18\n    number_unsigned | 256..65535                                 | Unsigned integer (2 bytes follow)  | 0x19\n    number_unsigned | 65536..4294967295                          | Unsigned integer (4 bytes follow)  | 0x1A\n    number_unsigned | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow)  | 0x1B\n    number_float    | *any value representable by a float*       | Single-Precision Float             | 0xFA\n    number_float    | *any value NOT representable by a float*   | Double-Precision Float             | 0xFB\n    string          | *length*: 0..23                            | UTF-8 string                       | 0x60..0x77\n    string          | *length*: 23..255                          | UTF-8 string (1 byte follow)       | 0x78\n    string          | *length*: 256..65535                       | UTF-8 string (2 bytes follow)      | 0x79\n    string          | *length*: 65536..4294967295                | UTF-8 string (4 bytes follow)      | 0x7A\n    string          | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow)      | 0x7B\n    array           | *size*: 0..23                              | array                              | 0x80..0x97\n    array           | *size*: 23..255                            | array (1 byte follow)              | 0x98\n    array           | *size*: 256..65535                         | array (2 bytes follow)             | 0x99\n    array           | *size*: 65536..4294967295                  | array (4 bytes follow)             | 0x9A\n    array           | *size*: 4294967296..18446744073709551615   | array (8 bytes follow)             | 0x9B\n    object          | *size*: 0..23                              | map                                | 0xA0..0xB7\n    object          | *size*: 23..255                            | map (1 byte follow)                | 0xB8\n    object          | *size*: 256..65535                         | map (2 bytes follow)               | 0xB9\n    object          | *size*: 65536..4294967295                  | map (4 bytes follow)               | 0xBA\n    object          | *size*: 4294967296..18446744073709551615   | map (8 bytes follow)               | 0xBB\n    binary          | *size*: 0..23                              | byte string                        | 0x40..0x57\n    binary          | *size*: 23..255                            | byte string (1 byte follow)        | 0x58\n    binary          | *size*: 256..65535                         | byte string (2 bytes follow)       | 0x59\n    binary          | *size*: 65536..4294967295                  | byte string (4 bytes follow)       | 0x5A\n    binary          | *size*: 4294967296..18446744073709551615   | byte string (8 bytes follow)       | 0x5B\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a CBOR value.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @note The following CBOR types are not used in the conversion:\n          - UTF-8 strings terminated by \"break\" (0x7F)\n          - arrays terminated by \"break\" (0x9F)\n          - maps terminated by \"break\" (0xBF)\n          - byte strings terminated by \"break\" (0x5F)\n          - date/time (0xC0..0xC1)\n          - bignum (0xC2..0xC3)\n          - decimal fraction (0xC4)\n          - bigfloat (0xC5)\n          - expected conversions (0xD5..0xD7)\n          - simple values (0xE0..0xF3, 0xF8)\n          - undefined (0xF7)\n          - half-precision floats (0xF9)\n          - break (0xFF)\n\n    @param[in] j  JSON value to serialize\n    @return CBOR serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in CBOR format.,to_cbor}\n\n    @sa http://cbor.io\n    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the\n        analogous deserialization\n    @sa @ref to_msgpack(const basic_json&) for the related MessagePack format\n    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n\n    @since version 2.0.9; compact representation of floating-point numbers\n           since version 3.8.0\n    */\n    static std::vector<uint8_t> to_cbor(const basic_json& j)\n    {\n        std::vector<uint8_t> result;\n        to_cbor(j, result);\n        return result;\n    }\n\n    static void to_cbor(const basic_json& j, detail::output_adapter<uint8_t> o)\n    {\n        binary_writer<uint8_t>(o).write_cbor(j);\n    }\n\n    static void to_cbor(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_cbor(j);\n    }\n\n    /*!\n    @brief create a MessagePack serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the MessagePack\n    serialization format. MessagePack is a binary serialization format which\n    aims to be more compact than JSON itself, yet more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    MessagePack types according to the MessagePack specification:\n\n    JSON value type | value/range                       | MessagePack type | first byte\n    --------------- | --------------------------------- | ---------------- | ----------\n    null            | `null`                            | nil              | 0xC0\n    boolean         | `true`                            | true             | 0xC3\n    boolean         | `false`                           | false            | 0xC2\n    number_integer  | -9223372036854775808..-2147483649 | int64            | 0xD3\n    number_integer  | -2147483648..-32769               | int32            | 0xD2\n    number_integer  | -32768..-129                      | int16            | 0xD1\n    number_integer  | -128..-33                         | int8             | 0xD0\n    number_integer  | -32..-1                           | negative fixint  | 0xE0..0xFF\n    number_integer  | 0..127                            | positive fixint  | 0x00..0x7F\n    number_integer  | 128..255                          | uint 8           | 0xCC\n    number_integer  | 256..65535                        | uint 16          | 0xCD\n    number_integer  | 65536..4294967295                 | uint 32          | 0xCE\n    number_integer  | 4294967296..18446744073709551615  | uint 64          | 0xCF\n    number_unsigned | 0..127                            | positive fixint  | 0x00..0x7F\n    number_unsigned | 128..255                          | uint 8           | 0xCC\n    number_unsigned | 256..65535                        | uint 16          | 0xCD\n    number_unsigned | 65536..4294967295                 | uint 32          | 0xCE\n    number_unsigned | 4294967296..18446744073709551615  | uint 64          | 0xCF\n    number_float    | *any value representable by a float*     | float 32 | 0xCA\n    number_float    | *any value NOT representable by a float* | float 64 | 0xCB\n    string          | *length*: 0..31                   | fixstr           | 0xA0..0xBF\n    string          | *length*: 32..255                 | str 8            | 0xD9\n    string          | *length*: 256..65535              | str 16           | 0xDA\n    string          | *length*: 65536..4294967295       | str 32           | 0xDB\n    array           | *size*: 0..15                     | fixarray         | 0x90..0x9F\n    array           | *size*: 16..65535                 | array 16         | 0xDC\n    array           | *size*: 65536..4294967295         | array 32         | 0xDD\n    object          | *size*: 0..15                     | fix map          | 0x80..0x8F\n    object          | *size*: 16..65535                 | map 16           | 0xDE\n    object          | *size*: 65536..4294967295         | map 32           | 0xDF\n    binary          | *size*: 0..255                    | bin 8            | 0xC4\n    binary          | *size*: 256..65535                | bin 16           | 0xC5\n    binary          | *size*: 65536..4294967295         | bin 32           | 0xC6\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a MessagePack value.\n\n    @note The following values can **not** be converted to a MessagePack value:\n          - strings with more than 4294967295 bytes\n          - byte strings with more than 4294967295 bytes\n          - arrays with more than 4294967295 elements\n          - objects with more than 4294967295 elements\n\n    @note Any MessagePack output created @ref to_msgpack can be successfully\n          parsed by @ref from_msgpack.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @param[in] j  JSON value to serialize\n    @return MessagePack serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in MessagePack format.,to_msgpack}\n\n    @sa http://msgpack.org\n    @sa @ref from_msgpack for the analogous deserialization\n    @sa @ref to_cbor(const basic_json& for the related CBOR format\n    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n\n    @since version 2.0.9\n    */\n    static std::vector<uint8_t> to_msgpack(const basic_json& j)\n    {\n        std::vector<uint8_t> result;\n        to_msgpack(j, result);\n        return result;\n    }\n\n    static void to_msgpack(const basic_json& j, detail::output_adapter<uint8_t> o)\n    {\n        binary_writer<uint8_t>(o).write_msgpack(j);\n    }\n\n    static void to_msgpack(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_msgpack(j);\n    }\n\n    /*!\n    @brief create a UBJSON serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the UBJSON\n    (Universal Binary JSON) serialization format. UBJSON aims to be more compact\n    than JSON itself, yet more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    UBJSON types according to the UBJSON specification:\n\n    JSON value type | value/range                       | UBJSON type | marker\n    --------------- | --------------------------------- | ----------- | ------\n    null            | `null`                            | null        | `Z`\n    boolean         | `true`                            | true        | `T`\n    boolean         | `false`                           | false       | `F`\n    number_integer  | -9223372036854775808..-2147483649 | int64       | `L`\n    number_integer  | -2147483648..-32769               | int32       | `l`\n    number_integer  | -32768..-129                      | int16       | `I`\n    number_integer  | -128..127                         | int8        | `i`\n    number_integer  | 128..255                          | uint8       | `U`\n    number_integer  | 256..32767                        | int16       | `I`\n    number_integer  | 32768..2147483647                 | int32       | `l`\n    number_integer  | 2147483648..9223372036854775807   | int64       | `L`\n    number_unsigned | 0..127                            | int8        | `i`\n    number_unsigned | 128..255                          | uint8       | `U`\n    number_unsigned | 256..32767                        | int16       | `I`\n    number_unsigned | 32768..2147483647                 | int32       | `l`\n    number_unsigned | 2147483648..9223372036854775807   | int64       | `L`\n    number_unsigned | 2147483649..18446744073709551615  | high-precision | `H`\n    number_float    | *any value*                       | float64     | `D`\n    string          | *with shortest length indicator*  | string      | `S`\n    array           | *see notes on optimized format*   | array       | `[`\n    object          | *see notes on optimized format*   | map         | `{`\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a UBJSON value.\n\n    @note The following values can **not** be converted to a UBJSON value:\n          - strings with more than 9223372036854775807 bytes (theoretical)\n\n    @note The following markers are not used in the conversion:\n          - `Z`: no-op values are not created.\n          - `C`: single-byte strings are serialized with `S` markers.\n\n    @note Any UBJSON output created @ref to_ubjson can be successfully parsed\n          by @ref from_ubjson.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @note The optimized formats for containers are supported: Parameter\n          @a use_size adds size information to the beginning of a container and\n          removes the closing marker. Parameter @a use_type further checks\n          whether all elements of a container have the same type and adds the\n          type marker to the beginning of the container. The @a use_type\n          parameter must only be used together with @a use_size = true. Note\n          that @a use_size = true alone may result in larger representations -\n          the benefit of this parameter is that the receiving side is\n          immediately informed on the number of elements of the container.\n\n    @note If the JSON data contains the binary type, the value stored is a list\n          of integers, as suggested by the UBJSON documentation.  In particular,\n          this means that serialization and the deserialization of a JSON\n          containing binary values into UBJSON and back will result in a\n          different JSON object.\n\n    @param[in] j  JSON value to serialize\n    @param[in] use_size  whether to add size annotations to container types\n    @param[in] use_type  whether to add type annotations to container types\n                         (must be combined with @a use_size = true)\n    @return UBJSON serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in UBJSON format.,to_ubjson}\n\n    @sa http://ubjson.org\n    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the\n        analogous deserialization\n    @sa @ref to_cbor(const basic_json& for the related CBOR format\n    @sa @ref to_msgpack(const basic_json&) for the related MessagePack format\n\n    @since version 3.1.0\n    */\n    static std::vector<uint8_t> to_ubjson(const basic_json& j,\n                                          const bool use_size = false,\n                                          const bool use_type = false)\n    {\n        std::vector<uint8_t> result;\n        to_ubjson(j, result, use_size, use_type);\n        return result;\n    }\n\n    static void to_ubjson(const basic_json& j, detail::output_adapter<uint8_t> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<uint8_t>(o).write_ubjson(j, use_size, use_type);\n    }\n\n    static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<char>(o).write_ubjson(j, use_size, use_type);\n    }\n\n\n    /*!\n    @brief Serializes the given JSON object `j` to BSON and returns a vector\n           containing the corresponding BSON-representation.\n\n    BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are\n    stored as a single entity (a so-called document).\n\n    The library uses the following mapping from JSON values types to BSON types:\n\n    JSON value type | value/range                       | BSON type   | marker\n    --------------- | --------------------------------- | ----------- | ------\n    null            | `null`                            | null        | 0x0A\n    boolean         | `true`, `false`                   | boolean     | 0x08\n    number_integer  | -9223372036854775808..-2147483649 | int64       | 0x12\n    number_integer  | -2147483648..2147483647           | int32       | 0x10\n    number_integer  | 2147483648..9223372036854775807   | int64       | 0x12\n    number_unsigned | 0..2147483647                     | int32       | 0x10\n    number_unsigned | 2147483648..9223372036854775807   | int64       | 0x12\n    number_unsigned | 9223372036854775808..18446744073709551615| --   | --\n    number_float    | *any value*                       | double      | 0x01\n    string          | *any value*                       | string      | 0x02\n    array           | *any value*                       | document    | 0x04\n    object          | *any value*                       | document    | 0x03\n    binary          | *any value*                       | binary      | 0x05\n\n    @warning The mapping is **incomplete**, since only JSON-objects (and things\n    contained therein) can be serialized to BSON.\n    Also, integers larger than 9223372036854775807 cannot be serialized to BSON,\n    and the keys may not contain U+0000, since they are serialized a\n    zero-terminated c-strings.\n\n    @throw out_of_range.407  if `j.is_number_unsigned() && j.get<std::uint64_t>() > 9223372036854775807`\n    @throw out_of_range.409  if a key in `j` contains a NULL (U+0000)\n    @throw type_error.317    if `!j.is_object()`\n\n    @pre The input `j` is required to be an object: `j.is_object() == true`.\n\n    @note Any BSON output created via @ref to_bson can be successfully parsed\n          by @ref from_bson.\n\n    @param[in] j  JSON value to serialize\n    @return BSON serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in BSON format.,to_bson}\n\n    @sa http://bsonspec.org/spec.html\n    @sa @ref from_bson(detail::input_adapter&&, const bool strict) for the\n        analogous deserialization\n    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n    @sa @ref to_cbor(const basic_json&) for the related CBOR format\n    @sa @ref to_msgpack(const basic_json&) for the related MessagePack format\n    */\n    static std::vector<uint8_t> to_bson(const basic_json& j)\n    {\n        std::vector<uint8_t> result;\n        to_bson(j, result);\n        return result;\n    }\n\n    /*!\n    @brief Serializes the given JSON object `j` to BSON and forwards the\n           corresponding BSON-representation to the given output_adapter `o`.\n    @param j The JSON object to convert to BSON.\n    @param o The output adapter that receives the binary BSON representation.\n    @pre The input `j` shall be an object: `j.is_object() == true`\n    @sa @ref to_bson(const basic_json&)\n    */\n    static void to_bson(const basic_json& j, detail::output_adapter<uint8_t> o)\n    {\n        binary_writer<uint8_t>(o).write_bson(j);\n    }\n\n    /*!\n    @copydoc to_bson(const basic_json&, detail::output_adapter<uint8_t>)\n    */\n    static void to_bson(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_bson(j);\n    }\n\n\n    /*!\n    @brief create a JSON value from an input in CBOR format\n\n    Deserializes a given input @a i to a JSON value using the CBOR (Concise\n    Binary Object Representation) serialization format.\n\n    The library maps CBOR types to JSON value types as follows:\n\n    CBOR type              | JSON value type | first byte\n    ---------------------- | --------------- | ----------\n    Integer                | number_unsigned | 0x00..0x17\n    Unsigned integer       | number_unsigned | 0x18\n    Unsigned integer       | number_unsigned | 0x19\n    Unsigned integer       | number_unsigned | 0x1A\n    Unsigned integer       | number_unsigned | 0x1B\n    Negative integer       | number_integer  | 0x20..0x37\n    Negative integer       | number_integer  | 0x38\n    Negative integer       | number_integer  | 0x39\n    Negative integer       | number_integer  | 0x3A\n    Negative integer       | number_integer  | 0x3B\n    Byte string            | binary          | 0x40..0x57\n    Byte string            | binary          | 0x58\n    Byte string            | binary          | 0x59\n    Byte string            | binary          | 0x5A\n    Byte string            | binary          | 0x5B\n    UTF-8 string           | string          | 0x60..0x77\n    UTF-8 string           | string          | 0x78\n    UTF-8 string           | string          | 0x79\n    UTF-8 string           | string          | 0x7A\n    UTF-8 string           | string          | 0x7B\n    UTF-8 string           | string          | 0x7F\n    array                  | array           | 0x80..0x97\n    array                  | array           | 0x98\n    array                  | array           | 0x99\n    array                  | array           | 0x9A\n    array                  | array           | 0x9B\n    array                  | array           | 0x9F\n    map                    | object          | 0xA0..0xB7\n    map                    | object          | 0xB8\n    map                    | object          | 0xB9\n    map                    | object          | 0xBA\n    map                    | object          | 0xBB\n    map                    | object          | 0xBF\n    False                  | `false`         | 0xF4\n    True                   | `true`          | 0xF5\n    Null                   | `null`          | 0xF6\n    Half-Precision Float   | number_float    | 0xF9\n    Single-Precision Float | number_float    | 0xFA\n    Double-Precision Float | number_float    | 0xFB\n\n    @warning The mapping is **incomplete** in the sense that not all CBOR\n             types can be converted to a JSON value. The following CBOR types\n             are not supported and will yield parse errors (parse_error.112):\n             - date/time (0xC0..0xC1)\n             - bignum (0xC2..0xC3)\n             - decimal fraction (0xC4)\n             - bigfloat (0xC5)\n             - expected conversions (0xD5..0xD7)\n             - simple values (0xE0..0xF3, 0xF8)\n             - undefined (0xF7)\n\n    @warning CBOR allows map keys of any type, whereas JSON only allows\n             strings as keys in object values. Therefore, CBOR maps with keys\n             other than UTF-8 strings are rejected (parse_error.113).\n\n    @note Any CBOR output created @ref to_cbor can be successfully parsed by\n          @ref from_cbor.\n\n    @param[in] i  an input in CBOR format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n    @param[in] tag_handler how to treat CBOR tags (optional, error by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if unsupported features from CBOR were\n    used in the given input @a v or if the input is not valid CBOR\n    @throw parse_error.113 if a string was expected as map key, but not found\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in CBOR\n    format to a JSON value.,from_cbor}\n\n    @sa http://cbor.io\n    @sa @ref to_cbor(const basic_json&) for the analogous serialization\n    @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for the\n        related MessagePack format\n    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the\n        related UBJSON format\n\n    @since version 2.0.9; parameter @a start_index since 2.1.1; changed to\n           consume input adapters, removed start_index parameter, and added\n           @a strict parameter since 3.0.0; added @a allow_exceptions parameter\n           since 3.2.0; added @a tag_handler parameter since 3.9.0.\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_cbor(InputType&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t)\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_cbor(IteratorType first, IteratorType last,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))\n    static basic_json from_cbor(const T* ptr, std::size_t len,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler);\n    }\n\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))\n    static basic_json from_cbor(detail::span_input_adapter&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @brief create a JSON value from an input in MessagePack format\n\n    Deserializes a given input @a i to a JSON value using the MessagePack\n    serialization format.\n\n    The library maps MessagePack types to JSON value types as follows:\n\n    MessagePack type | JSON value type | first byte\n    ---------------- | --------------- | ----------\n    positive fixint  | number_unsigned | 0x00..0x7F\n    fixmap           | object          | 0x80..0x8F\n    fixarray         | array           | 0x90..0x9F\n    fixstr           | string          | 0xA0..0xBF\n    nil              | `null`          | 0xC0\n    false            | `false`         | 0xC2\n    true             | `true`          | 0xC3\n    float 32         | number_float    | 0xCA\n    float 64         | number_float    | 0xCB\n    uint 8           | number_unsigned | 0xCC\n    uint 16          | number_unsigned | 0xCD\n    uint 32          | number_unsigned | 0xCE\n    uint 64          | number_unsigned | 0xCF\n    int 8            | number_integer  | 0xD0\n    int 16           | number_integer  | 0xD1\n    int 32           | number_integer  | 0xD2\n    int 64           | number_integer  | 0xD3\n    str 8            | string          | 0xD9\n    str 16           | string          | 0xDA\n    str 32           | string          | 0xDB\n    array 16         | array           | 0xDC\n    array 32         | array           | 0xDD\n    map 16           | object          | 0xDE\n    map 32           | object          | 0xDF\n    bin 8            | binary          | 0xC4\n    bin 16           | binary          | 0xC5\n    bin 32           | binary          | 0xC6\n    ext 8            | binary          | 0xC7\n    ext 16           | binary          | 0xC8\n    ext 32           | binary          | 0xC9\n    fixext 1         | binary          | 0xD4\n    fixext 2         | binary          | 0xD5\n    fixext 4         | binary          | 0xD6\n    fixext 8         | binary          | 0xD7\n    fixext 16        | binary          | 0xD8\n    negative fixint  | number_integer  | 0xE0-0xFF\n\n    @note Any MessagePack output created @ref to_msgpack can be successfully\n          parsed by @ref from_msgpack.\n\n    @param[in] i  an input in MessagePack format convertible to an input\n                  adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if unsupported features from MessagePack were\n    used in the given input @a i or if the input is not valid MessagePack\n    @throw parse_error.113 if a string was expected as map key, but not found\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    MessagePack format to a JSON value.,from_msgpack}\n\n    @sa http://msgpack.org\n    @sa @ref to_msgpack(const basic_json&) for the analogous serialization\n    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the\n        related CBOR format\n    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for\n        the related UBJSON format\n    @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for\n        the related BSON format\n\n    @since version 2.0.9; parameter @a start_index since 2.1.1; changed to\n           consume input adapters, removed start_index parameter, and added\n           @a strict parameter since 3.0.0; added @a allow_exceptions parameter\n           since 3.2.0\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_msgpack(InputType&& i,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_msgpack(detail::input_adapter&&, const bool, const bool)\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_msgpack(IteratorType first, IteratorType last,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))\n    static basic_json from_msgpack(const T* ptr, std::size_t len,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        return from_msgpack(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))\n    static basic_json from_msgpack(detail::span_input_adapter&& i,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n\n    /*!\n    @brief create a JSON value from an input in UBJSON format\n\n    Deserializes a given input @a i to a JSON value using the UBJSON (Universal\n    Binary JSON) serialization format.\n\n    The library maps UBJSON types to JSON value types as follows:\n\n    UBJSON type | JSON value type                         | marker\n    ----------- | --------------------------------------- | ------\n    no-op       | *no value, next value is read*          | `N`\n    null        | `null`                                  | `Z`\n    false       | `false`                                 | `F`\n    true        | `true`                                  | `T`\n    float32     | number_float                            | `d`\n    float64     | number_float                            | `D`\n    uint8       | number_unsigned                         | `U`\n    int8        | number_integer                          | `i`\n    int16       | number_integer                          | `I`\n    int32       | number_integer                          | `l`\n    int64       | number_integer                          | `L`\n    high-precision number | number_integer, number_unsigned, or number_float - depends on number string | 'H'\n    string      | string                                  | `S`\n    char        | string                                  | `C`\n    array       | array (optimized values are supported)  | `[`\n    object      | object (optimized values are supported) | `{`\n\n    @note The mapping is **complete** in the sense that any UBJSON value can\n          be converted to a JSON value.\n\n    @param[in] i  an input in UBJSON format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if a parse error occurs\n    @throw parse_error.113 if a string could not be parsed successfully\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    UBJSON format to a JSON value.,from_ubjson}\n\n    @sa http://ubjson.org\n    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             analogous serialization\n    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the\n        related CBOR format\n    @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for\n        the related MessagePack format\n    @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for\n        the related BSON format\n\n    @since version 3.1.0; added @a allow_exceptions parameter since 3.2.0\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_ubjson(InputType&& i,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_ubjson(detail::input_adapter&&, const bool, const bool)\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_ubjson(IteratorType first, IteratorType last,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))\n    static basic_json from_ubjson(const T* ptr, std::size_t len,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        return from_ubjson(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))\n    static basic_json from_ubjson(detail::span_input_adapter&& i,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n\n    /*!\n    @brief Create a JSON value from an input in BSON format\n\n    Deserializes a given input @a i to a JSON value using the BSON (Binary JSON)\n    serialization format.\n\n    The library maps BSON record types to JSON value types as follows:\n\n    BSON type       | BSON marker byte | JSON value type\n    --------------- | ---------------- | ---------------------------\n    double          | 0x01             | number_float\n    string          | 0x02             | string\n    document        | 0x03             | object\n    array           | 0x04             | array\n    binary          | 0x05             | still unsupported\n    undefined       | 0x06             | still unsupported\n    ObjectId        | 0x07             | still unsupported\n    boolean         | 0x08             | boolean\n    UTC Date-Time   | 0x09             | still unsupported\n    null            | 0x0A             | null\n    Regular Expr.   | 0x0B             | still unsupported\n    DB Pointer      | 0x0C             | still unsupported\n    JavaScript Code | 0x0D             | still unsupported\n    Symbol          | 0x0E             | still unsupported\n    JavaScript Code | 0x0F             | still unsupported\n    int32           | 0x10             | number_integer\n    Timestamp       | 0x11             | still unsupported\n    128-bit decimal float | 0x13       | still unsupported\n    Max Key         | 0x7F             | still unsupported\n    Min Key         | 0xFF             | still unsupported\n\n    @warning The mapping is **incomplete**. The unsupported mappings\n             are indicated in the table above.\n\n    @param[in] i  an input in BSON format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value; in case of a parse error and\n            @a allow_exceptions set to `false`, the return value will be\n            value_t::discarded.\n\n    @throw parse_error.114 if an unsupported BSON record type is encountered\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    BSON format to a JSON value.,from_bson}\n\n    @sa http://bsonspec.org/spec.html\n    @sa @ref to_bson(const basic_json&) for the analogous serialization\n    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool, const cbor_tag_handler_t) for the\n        related CBOR format\n    @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for\n        the related MessagePack format\n    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the\n        related UBJSON format\n    */\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bson(InputType&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_bson(detail::input_adapter&&, const bool, const bool)\n    */\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bson(IteratorType first, IteratorType last,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))\n    static basic_json from_bson(const T* ptr, std::size_t len,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        return from_bson(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))\n    static basic_json from_bson(detail::span_input_adapter&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        const bool res = binary_reader<decltype(ia)>(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n    /// @}\n\n    //////////////////////////\n    // JSON Pointer support //\n    //////////////////////////\n\n    /// @name JSON Pointer functions\n    /// @{\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Uses a JSON pointer to retrieve a reference to the respective JSON value.\n    No bound checking is performed. Similar to @ref operator[](const typename\n    object_t::key_type&), `null` values are created in arrays and objects if\n    necessary.\n\n    In particular:\n    - If the JSON pointer points to an object key that does not exist, it\n      is created an filled with a `null` value before a reference to it\n      is returned.\n    - If the JSON pointer points to an array index that does not exist, it\n      is created an filled with a `null` value before a reference to it\n      is returned. All indices between the current maximum and the given\n      index are also filled with `null`.\n    - The special value `-` is treated as a synonym for the index past the\n      end.\n\n    @param[in] ptr  a JSON pointer\n\n    @return reference to the element pointed to by @a ptr\n\n    @complexity Constant.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n\n    @liveexample{The behavior is shown in the example.,operatorjson_pointer}\n\n    @since version 2.0.0\n    */\n    reference operator[](const json_pointer& ptr)\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Uses a JSON pointer to retrieve a reference to the respective JSON value.\n    No bound checking is performed. The function does not change the JSON\n    value; no `null` values are created. In particular, the special value\n    `-` yields an exception.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return const reference to the element pointed to by @a ptr\n\n    @complexity Constant.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n\n    @liveexample{The behavior is shown in the example.,operatorjson_pointer_const}\n\n    @since version 2.0.0\n    */\n    const_reference operator[](const json_pointer& ptr) const\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Returns a reference to the element at with specified JSON pointer @a ptr,\n    with bounds checking.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return reference to the element pointed to by @a ptr\n\n    @throw parse_error.106 if an array index in the passed JSON pointer @a ptr\n    begins with '0'. See example below.\n\n    @throw parse_error.109 if an array index in the passed JSON pointer @a ptr\n    is not a number. See example below.\n\n    @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr\n    is out of range. See example below.\n\n    @throw out_of_range.402 if the array index '-' is used in the passed JSON\n    pointer @a ptr. As `at` provides checked access (and no elements are\n    implicitly inserted), the index '-' is always invalid. See example below.\n\n    @throw out_of_range.403 if the JSON pointer describes a key of an object\n    which cannot be found. See example below.\n\n    @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.\n    See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 2.0.0\n\n    @liveexample{The behavior is shown in the example.,at_json_pointer}\n    */\n    reference at(const json_pointer& ptr)\n    {\n        return ptr.get_checked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Returns a const reference to the element at with specified JSON pointer @a\n    ptr, with bounds checking.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return reference to the element pointed to by @a ptr\n\n    @throw parse_error.106 if an array index in the passed JSON pointer @a ptr\n    begins with '0'. See example below.\n\n    @throw parse_error.109 if an array index in the passed JSON pointer @a ptr\n    is not a number. See example below.\n\n    @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr\n    is out of range. See example below.\n\n    @throw out_of_range.402 if the array index '-' is used in the passed JSON\n    pointer @a ptr. As `at` provides checked access (and no elements are\n    implicitly inserted), the index '-' is always invalid. See example below.\n\n    @throw out_of_range.403 if the JSON pointer describes a key of an object\n    which cannot be found. See example below.\n\n    @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.\n    See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 2.0.0\n\n    @liveexample{The behavior is shown in the example.,at_json_pointer_const}\n    */\n    const_reference at(const json_pointer& ptr) const\n    {\n        return ptr.get_checked(this);\n    }\n\n    /*!\n    @brief return flattened JSON value\n\n    The function creates a JSON object whose keys are JSON pointers (see [RFC\n    6901](https://tools.ietf.org/html/rfc6901)) and whose values are all\n    primitive. The original JSON value can be restored using the @ref\n    unflatten() function.\n\n    @return an object that maps JSON pointers to primitive values\n\n    @note Empty objects and arrays are flattened to `null` and will not be\n          reconstructed correctly by the @ref unflatten() function.\n\n    @complexity Linear in the size the JSON value.\n\n    @liveexample{The following code shows how a JSON object is flattened to an\n    object whose keys consist of JSON pointers.,flatten}\n\n    @sa @ref unflatten() for the reverse function\n\n    @since version 2.0.0\n    */\n    basic_json flatten() const\n    {\n        basic_json result(value_t::object);\n        json_pointer::flatten(\"\", *this, result);\n        return result;\n    }\n\n    /*!\n    @brief unflatten a previously flattened JSON value\n\n    The function restores the arbitrary nesting of a JSON value that has been\n    flattened before using the @ref flatten() function. The JSON value must\n    meet certain constraints:\n    1. The value must be an object.\n    2. The keys must be JSON pointers (see\n       [RFC 6901](https://tools.ietf.org/html/rfc6901))\n    3. The mapped values must be primitive JSON types.\n\n    @return the original JSON from a flattened version\n\n    @note Empty objects and arrays are flattened by @ref flatten() to `null`\n          values and can not unflattened to their original type. Apart from\n          this example, for a JSON value `j`, the following is always true:\n          `j == j.flatten().unflatten()`.\n\n    @complexity Linear in the size the JSON value.\n\n    @throw type_error.314  if value is not an object\n    @throw type_error.315  if object values are not primitive\n\n    @liveexample{The following code shows how a flattened JSON object is\n    unflattened into the original nested JSON object.,unflatten}\n\n    @sa @ref flatten() for the reverse function\n\n    @since version 2.0.0\n    */\n    basic_json unflatten() const\n    {\n        return json_pointer::unflatten(*this);\n    }\n\n    /// @}\n\n    //////////////////////////\n    // JSON Patch functions //\n    //////////////////////////\n\n    /// @name JSON Patch functions\n    /// @{\n\n    /*!\n    @brief applies a JSON patch\n\n    [JSON Patch](http://jsonpatch.com) defines a JSON document structure for\n    expressing a sequence of operations to apply to a JSON) document. With\n    this function, a JSON Patch is applied to the current JSON value by\n    executing all operations from the patch.\n\n    @param[in] json_patch  JSON patch document\n    @return patched document\n\n    @note The application of a patch is atomic: Either all operations succeed\n          and the patched document is returned or an exception is thrown. In\n          any case, the original value is not changed: the patch is applied\n          to a copy of the value.\n\n    @throw parse_error.104 if the JSON patch does not consist of an array of\n    objects\n\n    @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory\n    attributes are missing); example: `\"operation add must have member path\"`\n\n    @throw out_of_range.401 if an array index is out of range.\n\n    @throw out_of_range.403 if a JSON pointer inside the patch could not be\n    resolved successfully in the current JSON value; example: `\"key baz not\n    found\"`\n\n    @throw out_of_range.405 if JSON pointer has no parent (\"add\", \"remove\",\n    \"move\")\n\n    @throw other_error.501 if \"test\" operation was unsuccessful\n\n    @complexity Linear in the size of the JSON value and the length of the\n    JSON patch. As usually only a fraction of the JSON value is affected by\n    the patch, the complexity can usually be neglected.\n\n    @liveexample{The following code shows how a JSON patch is applied to a\n    value.,patch}\n\n    @sa @ref diff -- create a JSON patch by comparing two JSON values\n\n    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)\n    @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901)\n\n    @since version 2.0.0\n    */\n    basic_json patch(const basic_json& json_patch) const\n    {\n        // make a working copy to apply the patch to\n        basic_json result = *this;\n\n        // the valid JSON Patch operations\n        enum class patch_operations {add, remove, replace, move, copy, test, invalid};\n\n        const auto get_op = [](const std::string & op)\n        {\n            if (op == \"add\")\n            {\n                return patch_operations::add;\n            }\n            if (op == \"remove\")\n            {\n                return patch_operations::remove;\n            }\n            if (op == \"replace\")\n            {\n                return patch_operations::replace;\n            }\n            if (op == \"move\")\n            {\n                return patch_operations::move;\n            }\n            if (op == \"copy\")\n            {\n                return patch_operations::copy;\n            }\n            if (op == \"test\")\n            {\n                return patch_operations::test;\n            }\n\n            return patch_operations::invalid;\n        };\n\n        // wrapper for \"add\" operation; add value at ptr\n        const auto operation_add = [&result](json_pointer & ptr, basic_json val)\n        {\n            // adding to the root of the target document means replacing it\n            if (ptr.empty())\n            {\n                result = val;\n                return;\n            }\n\n            // make sure the top element of the pointer exists\n            json_pointer top_pointer = ptr.top();\n            if (top_pointer != ptr)\n            {\n                result.at(top_pointer);\n            }\n\n            // get reference to parent of JSON pointer ptr\n            const auto last_path = ptr.back();\n            ptr.pop_back();\n            basic_json& parent = result[ptr];\n\n            switch (parent.m_type)\n            {\n                case value_t::null:\n                case value_t::object:\n                {\n                    // use operator[] to add value\n                    parent[last_path] = val;\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    if (last_path == \"-\")\n                    {\n                        // special case: append to back\n                        parent.push_back(val);\n                    }\n                    else\n                    {\n                        const auto idx = json_pointer::array_index(last_path);\n                        if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))\n                        {\n                            // avoid undefined behavior\n                            JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\"));\n                        }\n\n                        // default case: insert add offset\n                        parent.insert(parent.begin() + static_cast<difference_type>(idx), val);\n                    }\n                    break;\n                }\n\n                // if there exists a parent it cannot be primitive\n                default:            // LCOV_EXCL_LINE\n                    JSON_ASSERT(false);  // LCOV_EXCL_LINE\n            }\n        };\n\n        // wrapper for \"remove\" operation; remove value at ptr\n        const auto operation_remove = [&result](json_pointer & ptr)\n        {\n            // get reference to parent of JSON pointer ptr\n            const auto last_path = ptr.back();\n            ptr.pop_back();\n            basic_json& parent = result.at(ptr);\n\n            // remove child\n            if (parent.is_object())\n            {\n                // perform range check\n                auto it = parent.find(last_path);\n                if (JSON_HEDLEY_LIKELY(it != parent.end()))\n                {\n                    parent.erase(it);\n                }\n                else\n                {\n                    JSON_THROW(out_of_range::create(403, \"key '\" + last_path + \"' not found\"));\n                }\n            }\n            else if (parent.is_array())\n            {\n                // note erase performs range check\n                parent.erase(json_pointer::array_index(last_path));\n            }\n        };\n\n        // type check: top level value must be an array\n        if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))\n        {\n            JSON_THROW(parse_error::create(104, 0, \"JSON patch must be an array of objects\"));\n        }\n\n        // iterate and apply the operations\n        for (const auto& val : json_patch)\n        {\n            // wrapper to get a value for an operation\n            const auto get_value = [&val](const std::string & op,\n                                          const std::string & member,\n                                          bool string_type) -> basic_json &\n            {\n                // find value\n                auto it = val.m_value.object->find(member);\n\n                // context-sensitive error message\n                const auto error_msg = (op == \"op\") ? \"operation\" : \"operation '\" + op + \"'\";\n\n                // check if desired value is present\n                if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end()))\n                {\n                    JSON_THROW(parse_error::create(105, 0, error_msg + \" must have member '\" + member + \"'\"));\n                }\n\n                // check if result is of type string\n                if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))\n                {\n                    JSON_THROW(parse_error::create(105, 0, error_msg + \" must have string member '\" + member + \"'\"));\n                }\n\n                // no error: return value\n                return it->second;\n            };\n\n            // type check: every element of the array must be an object\n            if (JSON_HEDLEY_UNLIKELY(!val.is_object()))\n            {\n                JSON_THROW(parse_error::create(104, 0, \"JSON patch must be an array of objects\"));\n            }\n\n            // collect mandatory members\n            const auto op = get_value(\"op\", \"op\", true).template get<std::string>();\n            const auto path = get_value(op, \"path\", true).template get<std::string>();\n            json_pointer ptr(path);\n\n            switch (get_op(op))\n            {\n                case patch_operations::add:\n                {\n                    operation_add(ptr, get_value(\"add\", \"value\", false));\n                    break;\n                }\n\n                case patch_operations::remove:\n                {\n                    operation_remove(ptr);\n                    break;\n                }\n\n                case patch_operations::replace:\n                {\n                    // the \"path\" location must exist - use at()\n                    result.at(ptr) = get_value(\"replace\", \"value\", false);\n                    break;\n                }\n\n                case patch_operations::move:\n                {\n                    const auto from_path = get_value(\"move\", \"from\", true).template get<std::string>();\n                    json_pointer from_ptr(from_path);\n\n                    // the \"from\" location must exist - use at()\n                    basic_json v = result.at(from_ptr);\n\n                    // The move operation is functionally identical to a\n                    // \"remove\" operation on the \"from\" location, followed\n                    // immediately by an \"add\" operation at the target\n                    // location with the value that was just removed.\n                    operation_remove(from_ptr);\n                    operation_add(ptr, v);\n                    break;\n                }\n\n                case patch_operations::copy:\n                {\n                    const auto from_path = get_value(\"copy\", \"from\", true).template get<std::string>();\n                    const json_pointer from_ptr(from_path);\n\n                    // the \"from\" location must exist - use at()\n                    basic_json v = result.at(from_ptr);\n\n                    // The copy is functionally identical to an \"add\"\n                    // operation at the target location using the value\n                    // specified in the \"from\" member.\n                    operation_add(ptr, v);\n                    break;\n                }\n\n                case patch_operations::test:\n                {\n                    bool success = false;\n                    JSON_TRY\n                    {\n                        // check if \"value\" matches the one at \"path\"\n                        // the \"path\" location must exist - use at()\n                        success = (result.at(ptr) == get_value(\"test\", \"value\", false));\n                    }\n                    JSON_INTERNAL_CATCH (out_of_range&)\n                    {\n                        // ignore out of range errors: success remains false\n                    }\n\n                    // throw an exception if test fails\n                    if (JSON_HEDLEY_UNLIKELY(!success))\n                    {\n                        JSON_THROW(other_error::create(501, \"unsuccessful: \" + val.dump()));\n                    }\n\n                    break;\n                }\n\n                default:\n                {\n                    // op must be \"add\", \"remove\", \"replace\", \"move\", \"copy\", or\n                    // \"test\"\n                    JSON_THROW(parse_error::create(105, 0, \"operation value '\" + op + \"' is invalid\"));\n                }\n            }\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief creates a diff as a JSON patch\n\n    Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can\n    be changed into the value @a target by calling @ref patch function.\n\n    @invariant For two JSON values @a source and @a target, the following code\n    yields always `true`:\n    @code {.cpp}\n    source.patch(diff(source, target)) == target;\n    @endcode\n\n    @note Currently, only `remove`, `add`, and `replace` operations are\n          generated.\n\n    @param[in] source  JSON value to compare from\n    @param[in] target  JSON value to compare against\n    @param[in] path    helper value to create JSON pointers\n\n    @return a JSON patch to convert the @a source to @a target\n\n    @complexity Linear in the lengths of @a source and @a target.\n\n    @liveexample{The following code shows how a JSON patch is created as a\n    diff for two JSON values.,diff}\n\n    @sa @ref patch -- apply a JSON patch\n    @sa @ref merge_patch -- apply a JSON Merge Patch\n\n    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)\n\n    @since version 2.0.0\n    */\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json diff(const basic_json& source, const basic_json& target,\n                           const std::string& path = \"\")\n    {\n        // the patch\n        basic_json result(value_t::array);\n\n        // if the values are the same, return empty patch\n        if (source == target)\n        {\n            return result;\n        }\n\n        if (source.type() != target.type())\n        {\n            // different types: replace value\n            result.push_back(\n            {\n                {\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}\n            });\n            return result;\n        }\n\n        switch (source.type())\n        {\n            case value_t::array:\n            {\n                // first pass: traverse common elements\n                std::size_t i = 0;\n                while (i < source.size() && i < target.size())\n                {\n                    // recursive call to compare array values at index i\n                    auto temp_diff = diff(source[i], target[i], path + \"/\" + std::to_string(i));\n                    result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                    ++i;\n                }\n\n                // i now reached the end of at least one array\n                // in a second pass, traverse the remaining elements\n\n                // remove my remaining elements\n                const auto end_index = static_cast<difference_type>(result.size());\n                while (i < source.size())\n                {\n                    // add operations in reverse order to avoid invalid\n                    // indices\n                    result.insert(result.begin() + end_index, object(\n                    {\n                        {\"op\", \"remove\"},\n                        {\"path\", path + \"/\" + std::to_string(i)}\n                    }));\n                    ++i;\n                }\n\n                // add other remaining elements\n                while (i < target.size())\n                {\n                    result.push_back(\n                    {\n                        {\"op\", \"add\"},\n                        {\"path\", path + \"/-\"},\n                        {\"value\", target[i]}\n                    });\n                    ++i;\n                }\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                // first pass: traverse this object's elements\n                for (auto it = source.cbegin(); it != source.cend(); ++it)\n                {\n                    // escape the key name to be used in a JSON patch\n                    const auto key = json_pointer::escape(it.key());\n\n                    if (target.find(it.key()) != target.end())\n                    {\n                        // recursive call to compare object values at key it\n                        auto temp_diff = diff(it.value(), target[it.key()], path + \"/\" + key);\n                        result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                    }\n                    else\n                    {\n                        // found a key that is not in o -> remove it\n                        result.push_back(object(\n                        {\n                            {\"op\", \"remove\"}, {\"path\", path + \"/\" + key}\n                        }));\n                    }\n                }\n\n                // second pass: traverse other object's elements\n                for (auto it = target.cbegin(); it != target.cend(); ++it)\n                {\n                    if (source.find(it.key()) == source.end())\n                    {\n                        // found a key that is not in this -> add it\n                        const auto key = json_pointer::escape(it.key());\n                        result.push_back(\n                        {\n                            {\"op\", \"add\"}, {\"path\", path + \"/\" + key},\n                            {\"value\", it.value()}\n                        });\n                    }\n                }\n\n                break;\n            }\n\n            default:\n            {\n                // both primitive type: replace value\n                result.push_back(\n                {\n                    {\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}\n                });\n                break;\n            }\n        }\n\n        return result;\n    }\n\n    /// @}\n\n    ////////////////////////////////\n    // JSON Merge Patch functions //\n    ////////////////////////////////\n\n    /// @name JSON Merge Patch functions\n    /// @{\n\n    /*!\n    @brief applies a JSON Merge Patch\n\n    The merge patch format is primarily intended for use with the HTTP PATCH\n    method as a means of describing a set of modifications to a target\n    resource's content. This function applies a merge patch to the current\n    JSON value.\n\n    The function implements the following algorithm from Section 2 of\n    [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396):\n\n    ```\n    define MergePatch(Target, Patch):\n      if Patch is an Object:\n        if Target is not an Object:\n          Target = {} // Ignore the contents and set it to an empty Object\n        for each Name/Value pair in Patch:\n          if Value is null:\n            if Name exists in Target:\n              remove the Name/Value pair from Target\n          else:\n            Target[Name] = MergePatch(Target[Name], Value)\n        return Target\n      else:\n        return Patch\n    ```\n\n    Thereby, `Target` is the current object; that is, the patch is applied to\n    the current value.\n\n    @param[in] apply_patch  the patch to apply\n\n    @complexity Linear in the lengths of @a patch.\n\n    @liveexample{The following code shows how a JSON Merge Patch is applied to\n    a JSON document.,merge_patch}\n\n    @sa @ref patch -- apply a JSON patch\n    @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396)\n\n    @since version 3.0.0\n    */\n    void merge_patch(const basic_json& apply_patch)\n    {\n        if (apply_patch.is_object())\n        {\n            if (!is_object())\n            {\n                *this = object();\n            }\n            for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it)\n            {\n                if (it.value().is_null())\n                {\n                    erase(it.key());\n                }\n                else\n                {\n                    operator[](it.key()).merge_patch(it.value());\n                }\n            }\n        }\n        else\n        {\n            *this = apply_patch;\n        }\n    }\n\n    /// @}\n};\n\n/*!\n@brief user-defined to_string function for JSON values\n\nThis function implements a user-defined to_string  for JSON objects.\n\n@param[in] j  a JSON object\n@return a std::string object\n*/\n\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nstd::string to_string(const NLOHMANN_BASIC_JSON_TPL& j)\n{\n    return j.dump();\n}\n} // namespace nlohmann\n\n///////////////////////\n// nonmember support //\n///////////////////////\n\n// specialization of std::swap, and std::hash\nnamespace std\n{\n\n/// hash value for JSON objects\ntemplate<>\nstruct hash<nlohmann::json>\n{\n    /*!\n    @brief return a hash value for a JSON object\n\n    @since version 1.0.0\n    */\n    std::size_t operator()(const nlohmann::json& j) const\n    {\n        return nlohmann::detail::hash(j);\n    }\n};\n\n/// specialization for std::less<value_t>\n/// @note: do not remove the space after '<',\n///        see https://github.com/nlohmann/json/pull/679\ntemplate<>\nstruct less<::nlohmann::detail::value_t>\n{\n    /*!\n    @brief compare two value_t enum values\n    @since version 3.0.0\n    */\n    bool operator()(nlohmann::detail::value_t lhs,\n                    nlohmann::detail::value_t rhs) const noexcept\n    {\n        return nlohmann::detail::operator<(lhs, rhs);\n    }\n};\n\n// C++20 prohibit function specialization in the std namespace.\n#ifndef JSON_HAS_CPP_20\n\n/*!\n@brief exchanges the values of two JSON objects\n\n@since version 1.0.0\n*/\ntemplate<>\ninline void swap<nlohmann::json>(nlohmann::json& j1, nlohmann::json& j2) noexcept(\n    is_nothrow_move_constructible<nlohmann::json>::value&&\n    is_nothrow_move_assignable<nlohmann::json>::value\n                              )\n{\n    j1.swap(j2);\n}\n\n#endif\n\n} // namespace std\n\n/*!\n@brief user-defined string literal for JSON values\n\nThis operator implements a user-defined string literal for JSON objects. It\ncan be used by adding `\"_json\"` to a string literal and returns a JSON object\nif no parse error occurred.\n\n@param[in] s  a string representation of a JSON object\n@param[in] n  the length of string @a s\n@return a JSON object\n\n@since version 1.0.0\n*/\nJSON_HEDLEY_NON_NULL(1)\ninline nlohmann::json operator \"\" _json(const char* s, std::size_t n)\n{\n    return nlohmann::json::parse(s, s + n);\n}\n\n/*!\n@brief user-defined string literal for JSON pointer\n\nThis operator implements a user-defined string literal for JSON Pointers. It\ncan be used by adding `\"_json_pointer\"` to a string literal and returns a JSON pointer\nobject if no parse error occurred.\n\n@param[in] s  a string representation of a JSON Pointer\n@param[in] n  the length of string @a s\n@return a JSON pointer object\n\n@since version 2.0.0\n*/\nJSON_HEDLEY_NON_NULL(1)\ninline nlohmann::json::json_pointer operator \"\" _json_pointer(const char* s, std::size_t n)\n{\n    return nlohmann::json::json_pointer(std::string(s, n));\n}\n\n// #include <nlohmann/detail/macro_unscope.hpp>\n\n\n// restore GCC/clang diagnostic settings\n#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)\n    #pragma GCC diagnostic pop\n#endif\n#if defined(__clang__)\n    #pragma GCC diagnostic pop\n#endif\n\n// clean up\n#undef JSON_ASSERT\n#undef JSON_INTERNAL_CATCH\n#undef JSON_CATCH\n#undef JSON_THROW\n#undef JSON_TRY\n#undef JSON_HAS_CPP_14\n#undef JSON_HAS_CPP_17\n#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION\n#undef NLOHMANN_BASIC_JSON_TPL\n#undef JSON_EXPLICIT\n\n// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>\n#undef JSON_HEDLEY_ALWAYS_INLINE\n#undef JSON_HEDLEY_ARM_VERSION\n#undef JSON_HEDLEY_ARM_VERSION_CHECK\n#undef JSON_HEDLEY_ARRAY_PARAM\n#undef JSON_HEDLEY_ASSUME\n#undef JSON_HEDLEY_BEGIN_C_DECLS\n#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_BUILTIN\n#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_EXTENSION\n#undef JSON_HEDLEY_CLANG_HAS_FEATURE\n#undef JSON_HEDLEY_CLANG_HAS_WARNING\n#undef JSON_HEDLEY_COMPCERT_VERSION\n#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK\n#undef JSON_HEDLEY_CONCAT\n#undef JSON_HEDLEY_CONCAT3\n#undef JSON_HEDLEY_CONCAT3_EX\n#undef JSON_HEDLEY_CONCAT_EX\n#undef JSON_HEDLEY_CONST\n#undef JSON_HEDLEY_CONSTEXPR\n#undef JSON_HEDLEY_CONST_CAST\n#undef JSON_HEDLEY_CPP_CAST\n#undef JSON_HEDLEY_CRAY_VERSION\n#undef JSON_HEDLEY_CRAY_VERSION_CHECK\n#undef JSON_HEDLEY_C_DECL\n#undef JSON_HEDLEY_DEPRECATED\n#undef JSON_HEDLEY_DEPRECATED_FOR\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#undef JSON_HEDLEY_DIAGNOSTIC_POP\n#undef JSON_HEDLEY_DIAGNOSTIC_PUSH\n#undef JSON_HEDLEY_DMC_VERSION\n#undef JSON_HEDLEY_DMC_VERSION_CHECK\n#undef JSON_HEDLEY_EMPTY_BASES\n#undef JSON_HEDLEY_EMSCRIPTEN_VERSION\n#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK\n#undef JSON_HEDLEY_END_C_DECLS\n#undef JSON_HEDLEY_FLAGS\n#undef JSON_HEDLEY_FLAGS_CAST\n#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_BUILTIN\n#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_EXTENSION\n#undef JSON_HEDLEY_GCC_HAS_FEATURE\n#undef JSON_HEDLEY_GCC_HAS_WARNING\n#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK\n#undef JSON_HEDLEY_GCC_VERSION\n#undef JSON_HEDLEY_GCC_VERSION_CHECK\n#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_BUILTIN\n#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_EXTENSION\n#undef JSON_HEDLEY_GNUC_HAS_FEATURE\n#undef JSON_HEDLEY_GNUC_HAS_WARNING\n#undef JSON_HEDLEY_GNUC_VERSION\n#undef JSON_HEDLEY_GNUC_VERSION_CHECK\n#undef JSON_HEDLEY_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_BUILTIN\n#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS\n#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_EXTENSION\n#undef JSON_HEDLEY_HAS_FEATURE\n#undef JSON_HEDLEY_HAS_WARNING\n#undef JSON_HEDLEY_IAR_VERSION\n#undef JSON_HEDLEY_IAR_VERSION_CHECK\n#undef JSON_HEDLEY_IBM_VERSION\n#undef JSON_HEDLEY_IBM_VERSION_CHECK\n#undef JSON_HEDLEY_IMPORT\n#undef JSON_HEDLEY_INLINE\n#undef JSON_HEDLEY_INTEL_VERSION\n#undef JSON_HEDLEY_INTEL_VERSION_CHECK\n#undef JSON_HEDLEY_IS_CONSTANT\n#undef JSON_HEDLEY_IS_CONSTEXPR_\n#undef JSON_HEDLEY_LIKELY\n#undef JSON_HEDLEY_MALLOC\n#undef JSON_HEDLEY_MESSAGE\n#undef JSON_HEDLEY_MSVC_VERSION\n#undef JSON_HEDLEY_MSVC_VERSION_CHECK\n#undef JSON_HEDLEY_NEVER_INLINE\n#undef JSON_HEDLEY_NON_NULL\n#undef JSON_HEDLEY_NO_ESCAPE\n#undef JSON_HEDLEY_NO_RETURN\n#undef JSON_HEDLEY_NO_THROW\n#undef JSON_HEDLEY_NULL\n#undef JSON_HEDLEY_PELLES_VERSION\n#undef JSON_HEDLEY_PELLES_VERSION_CHECK\n#undef JSON_HEDLEY_PGI_VERSION\n#undef JSON_HEDLEY_PGI_VERSION_CHECK\n#undef JSON_HEDLEY_PREDICT\n#undef JSON_HEDLEY_PRINTF_FORMAT\n#undef JSON_HEDLEY_PRIVATE\n#undef JSON_HEDLEY_PUBLIC\n#undef JSON_HEDLEY_PURE\n#undef JSON_HEDLEY_REINTERPRET_CAST\n#undef JSON_HEDLEY_REQUIRE\n#undef JSON_HEDLEY_REQUIRE_CONSTEXPR\n#undef JSON_HEDLEY_REQUIRE_MSG\n#undef JSON_HEDLEY_RESTRICT\n#undef JSON_HEDLEY_RETURNS_NON_NULL\n#undef JSON_HEDLEY_SENTINEL\n#undef JSON_HEDLEY_STATIC_ASSERT\n#undef JSON_HEDLEY_STATIC_CAST\n#undef JSON_HEDLEY_STRINGIFY\n#undef JSON_HEDLEY_STRINGIFY_EX\n#undef JSON_HEDLEY_SUNPRO_VERSION\n#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK\n#undef JSON_HEDLEY_TINYC_VERSION\n#undef JSON_HEDLEY_TINYC_VERSION_CHECK\n#undef JSON_HEDLEY_TI_ARMCL_VERSION\n#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL2000_VERSION\n#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL430_VERSION\n#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL6X_VERSION\n#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL7X_VERSION\n#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CLPRU_VERSION\n#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK\n#undef JSON_HEDLEY_TI_VERSION\n#undef JSON_HEDLEY_TI_VERSION_CHECK\n#undef JSON_HEDLEY_UNAVAILABLE\n#undef JSON_HEDLEY_UNLIKELY\n#undef JSON_HEDLEY_UNPREDICTABLE\n#undef JSON_HEDLEY_UNREACHABLE\n#undef JSON_HEDLEY_UNREACHABLE_RETURN\n#undef JSON_HEDLEY_VERSION\n#undef JSON_HEDLEY_VERSION_DECODE_MAJOR\n#undef JSON_HEDLEY_VERSION_DECODE_MINOR\n#undef JSON_HEDLEY_VERSION_DECODE_REVISION\n#undef JSON_HEDLEY_VERSION_ENCODE\n#undef JSON_HEDLEY_WARNING\n#undef JSON_HEDLEY_WARN_UNUSED_RESULT\n#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG\n#undef JSON_HEDLEY_FALL_THROUGH\n\n\n\n#endif  // INCLUDE_NLOHMANN_JSON_HPP_\n"
  },
  {
    "path": "ext/qcustomplot.cpp",
    "content": "/***************************************************************************\n**                                                                        **\n**  QCustomPlot, an easy to use, modern plotting widget for Qt            **\n**  Copyright (C) 2011-2021 Emanuel Eichhammer                            **\n**                                                                        **\n**  This program is free software: you can redistribute it and/or modify  **\n**  it under the terms of the GNU General Public License as published by  **\n**  the Free Software Foundation, either version 3 of the License, or     **\n**  (at your option) any later version.                                   **\n**                                                                        **\n**  This program is distributed in the hope that it will be useful,       **\n**  but WITHOUT ANY WARRANTY; without even the implied warranty of        **\n**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         **\n**  GNU General Public License for more details.                          **\n**                                                                        **\n**  You should have received a copy of the GNU General Public License     **\n**  along with this program.  If not, see http://www.gnu.org/licenses/.   **\n**                                                                        **\n****************************************************************************\n**           Author: Emanuel Eichhammer                                   **\n**  Website/Contact: http://www.qcustomplot.com/                          **\n**             Date: 29.03.21                                             **\n**          Version: 2.1.0                                                **\n****************************************************************************/\n\n#include \"qcustomplot.h\"\n\n\n/* including file 'src/vector2d.cpp'       */\n/* modified 2021-03-29T02:30:44, size 7973 */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPVector2D\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPVector2D\n  \\brief Represents two doubles as a mathematical 2D vector\n  \n  This class acts as a replacement for QVector2D with the advantage of double precision instead of\n  single, and some convenience methods tailored for the QCustomPlot library.\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn void QCPVector2D::setX(double x)\n  \n  Sets the x coordinate of this vector to \\a x.\n  \n  \\see setY\n*/\n\n/*! \\fn void QCPVector2D::setY(double y)\n  \n  Sets the y coordinate of this vector to \\a y.\n  \n  \\see setX\n*/\n\n/*! \\fn double QCPVector2D::length() const\n  \n  Returns the length of this vector.\n  \n  \\see lengthSquared\n*/\n\n/*! \\fn double QCPVector2D::lengthSquared() const\n  \n  Returns the squared length of this vector. In some situations, e.g. when just trying to find the\n  shortest vector of a group, this is faster than calculating \\ref length, because it avoids\n  calculation of a square root.\n  \n  \\see length\n*/\n\n/*! \\fn double QCPVector2D::angle() const\n  \n  Returns the angle of the vector in radians. The angle is measured between the positive x line and\n  the vector, counter-clockwise in a mathematical coordinate system (y axis upwards positive). In\n  screen/widget coordinates where the y axis is inverted, the angle appears clockwise.\n*/\n\n/*! \\fn QPoint QCPVector2D::toPoint() const\n  \n  Returns a QPoint which has the x and y coordinates of this vector, truncating any floating point\n  information.\n  \n  \\see toPointF\n*/\n\n/*! \\fn QPointF QCPVector2D::toPointF() const\n  \n  Returns a QPointF which has the x and y coordinates of this vector.\n  \n  \\see toPoint\n*/\n\n/*! \\fn bool QCPVector2D::isNull() const\n  \n  Returns whether this vector is null. A vector is null if \\c qIsNull returns true for both x and y\n  coordinates, i.e. if both are binary equal to 0.\n*/\n\n/*! \\fn QCPVector2D QCPVector2D::perpendicular() const\n  \n  Returns a vector perpendicular to this vector, with the same length.\n*/\n\n/*! \\fn double QCPVector2D::dot() const\n  \n  Returns the dot/scalar product of this vector with the specified vector \\a vec.\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Creates a QCPVector2D object and initializes the x and y coordinates to 0.\n*/\nQCPVector2D::QCPVector2D() :\n  mX(0),\n  mY(0)\n{\n}\n\n/*!\n  Creates a QCPVector2D object and initializes the \\a x and \\a y coordinates with the specified\n  values.\n*/\nQCPVector2D::QCPVector2D(double x, double y) :\n  mX(x),\n  mY(y)\n{\n}\n\n/*!\n  Creates a QCPVector2D object and initializes the x and y coordinates respective coordinates of\n  the specified \\a point.\n*/\nQCPVector2D::QCPVector2D(const QPoint &point) :\n  mX(point.x()),\n  mY(point.y())\n{\n}\n\n/*!\n  Creates a QCPVector2D object and initializes the x and y coordinates respective coordinates of\n  the specified \\a point.\n*/\nQCPVector2D::QCPVector2D(const QPointF &point) :\n  mX(point.x()),\n  mY(point.y())\n{\n}\n\n/*!\n  Normalizes this vector. After this operation, the length of the vector is equal to 1.\n  \n  If the vector has both entries set to zero, this method does nothing.\n  \n  \\see normalized, length, lengthSquared\n*/\nvoid QCPVector2D::normalize()\n{\n  if (mX == 0.0 && mY == 0.0) return;\n  const double lenInv = 1.0/length();\n  mX *= lenInv;\n  mY *= lenInv;\n}\n\n/*!\n  Returns a normalized version of this vector. The length of the returned vector is equal to 1.\n  \n  If the vector has both entries set to zero, this method returns the vector unmodified.\n  \n  \\see normalize, length, lengthSquared\n*/\nQCPVector2D QCPVector2D::normalized() const\n{\n  if (mX == 0.0 && mY == 0.0) return *this;\n  const double lenInv = 1.0/length();\n  return QCPVector2D(mX*lenInv, mY*lenInv);\n}\n\n/*! \\overload\n  \n  Returns the squared shortest distance of this vector (interpreted as a point) to the finite line\n  segment given by \\a start and \\a end.\n  \n  \\see distanceToStraightLine\n*/\ndouble QCPVector2D::distanceSquaredToLine(const QCPVector2D &start, const QCPVector2D &end) const\n{\n  const QCPVector2D v(end-start);\n  const double vLengthSqr = v.lengthSquared();\n  if (!qFuzzyIsNull(vLengthSqr))\n  {\n    const double mu = v.dot(*this-start)/vLengthSqr;\n    if (mu < 0)\n      return (*this-start).lengthSquared();\n    else if (mu > 1)\n      return (*this-end).lengthSquared();\n    else\n      return ((start + mu*v)-*this).lengthSquared();\n  } else\n    return (*this-start).lengthSquared();\n}\n\n/*! \\overload\n  \n  Returns the squared shortest distance of this vector (interpreted as a point) to the finite line\n  segment given by \\a line.\n  \n  \\see distanceToStraightLine\n*/\ndouble QCPVector2D::distanceSquaredToLine(const QLineF &line) const\n{\n  return distanceSquaredToLine(QCPVector2D(line.p1()), QCPVector2D(line.p2()));\n}\n\n/*!\n  Returns the shortest distance of this vector (interpreted as a point) to the infinite straight\n  line given by a \\a base point and a \\a direction vector.\n  \n  \\see distanceSquaredToLine\n*/\ndouble QCPVector2D::distanceToStraightLine(const QCPVector2D &base, const QCPVector2D &direction) const\n{\n  return qAbs((*this-base).dot(direction.perpendicular()))/direction.length();\n}\n\n/*!\n  Scales this vector by the given \\a factor, i.e. the x and y components are multiplied by \\a\n  factor.\n*/\nQCPVector2D &QCPVector2D::operator*=(double factor)\n{\n  mX *= factor;\n  mY *= factor;\n  return *this;\n}\n\n/*!\n  Scales this vector by the given \\a divisor, i.e. the x and y components are divided by \\a\n  divisor.\n*/\nQCPVector2D &QCPVector2D::operator/=(double divisor)\n{\n  mX /= divisor;\n  mY /= divisor;\n  return *this;\n}\n\n/*!\n  Adds the given \\a vector to this vector component-wise.\n*/\nQCPVector2D &QCPVector2D::operator+=(const QCPVector2D &vector)\n{\n  mX += vector.mX;\n  mY += vector.mY;\n  return *this;\n}\n\n/*!\n  subtracts the given \\a vector from this vector component-wise.\n*/\nQCPVector2D &QCPVector2D::operator-=(const QCPVector2D &vector)\n{\n  mX -= vector.mX;\n  mY -= vector.mY;\n  return *this;\n}\n/* end of 'src/vector2d.cpp' */\n\n\n/* including file 'src/painter.cpp'        */\n/* modified 2021-03-29T02:30:44, size 8656 */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPPainter\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPPainter\n  \\brief QPainter subclass used internally\n  \n  This QPainter subclass is used to provide some extended functionality e.g. for tweaking position\n  consistency between antialiased and non-antialiased painting. Further it provides workarounds\n  for QPainter quirks.\n  \n  \\warning This class intentionally hides non-virtual functions of QPainter, e.g. setPen, save and\n  restore. So while it is possible to pass a QCPPainter instance to a function that expects a\n  QPainter pointer, some of the workarounds and tweaks will be unavailable to the function (because\n  it will call the base class implementations of the functions actually hidden by QCPPainter).\n*/\n\n/*!\n  Creates a new QCPPainter instance and sets default values\n*/\nQCPPainter::QCPPainter() :\n  mModes(pmDefault),\n  mIsAntialiasing(false)\n{\n  // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter isn't active yet and\n  // a call to begin() will follow\n}\n\n/*!\n  Creates a new QCPPainter instance on the specified paint \\a device and sets default values. Just\n  like the analogous QPainter constructor, begins painting on \\a device immediately.\n  \n  Like \\ref begin, this method sets QPainter::NonCosmeticDefaultPen in Qt versions before Qt5.\n*/\nQCPPainter::QCPPainter(QPaintDevice *device) :\n  QPainter(device),\n  mModes(pmDefault),\n  mIsAntialiasing(false)\n{\n#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.\n  if (isActive())\n    setRenderHint(QPainter::NonCosmeticDefaultPen);\n#endif\n}\n\n/*!\n  Sets the pen of the painter and applies certain fixes to it, depending on the mode of this\n  QCPPainter.\n  \n  \\note this function hides the non-virtual base class implementation.\n*/\nvoid QCPPainter::setPen(const QPen &pen)\n{\n  QPainter::setPen(pen);\n  if (mModes.testFlag(pmNonCosmetic))\n    makeNonCosmetic();\n}\n\n/*! \\overload\n  \n  Sets the pen (by color) of the painter and applies certain fixes to it, depending on the mode of\n  this QCPPainter.\n  \n  \\note this function hides the non-virtual base class implementation.\n*/\nvoid QCPPainter::setPen(const QColor &color)\n{\n  QPainter::setPen(color);\n  if (mModes.testFlag(pmNonCosmetic))\n    makeNonCosmetic();\n}\n\n/*! \\overload\n  \n  Sets the pen (by style) of the painter and applies certain fixes to it, depending on the mode of\n  this QCPPainter.\n  \n  \\note this function hides the non-virtual base class implementation.\n*/\nvoid QCPPainter::setPen(Qt::PenStyle penStyle)\n{\n  QPainter::setPen(penStyle);\n  if (mModes.testFlag(pmNonCosmetic))\n    makeNonCosmetic();\n}\n\n/*! \\overload\n  \n  Works around a Qt bug introduced with Qt 4.8 which makes drawing QLineF unpredictable when\n  antialiasing is disabled. Thus when antialiasing is disabled, it rounds the \\a line to\n  integer coordinates and then passes it to the original drawLine.\n  \n  \\note this function hides the non-virtual base class implementation.\n*/\nvoid QCPPainter::drawLine(const QLineF &line)\n{\n  if (mIsAntialiasing || mModes.testFlag(pmVectorized))\n    QPainter::drawLine(line);\n  else\n    QPainter::drawLine(line.toLine());\n}\n\n/*!\n  Sets whether painting uses antialiasing or not. Use this method instead of using setRenderHint\n  with QPainter::Antialiasing directly, as it allows QCPPainter to regain pixel exactness between\n  antialiased and non-antialiased painting (Since Qt < 5.0 uses slightly different coordinate systems for\n  AA/Non-AA painting).\n*/\nvoid QCPPainter::setAntialiasing(bool enabled)\n{\n  setRenderHint(QPainter::Antialiasing, enabled);\n  if (mIsAntialiasing != enabled)\n  {\n    mIsAntialiasing = enabled;\n    if (!mModes.testFlag(pmVectorized)) // antialiasing half-pixel shift only needed for rasterized outputs\n    {\n      if (mIsAntialiasing)\n        translate(0.5, 0.5);\n      else\n        translate(-0.5, -0.5);\n    }\n  }\n}\n\n/*!\n  Sets the mode of the painter. This controls whether the painter shall adjust its\n  fixes/workarounds optimized for certain output devices.\n*/\nvoid QCPPainter::setModes(QCPPainter::PainterModes modes)\n{\n  mModes = modes;\n}\n\n/*!\n  Sets the QPainter::NonCosmeticDefaultPen in Qt versions before Qt5 after beginning painting on \\a\n  device. This is necessary to get cosmetic pen consistency across Qt versions, because since Qt5,\n  all pens are non-cosmetic by default, and in Qt4 this render hint must be set to get that\n  behaviour.\n  \n  The Constructor \\ref QCPPainter(QPaintDevice *device) which directly starts painting also sets\n  the render hint as appropriate.\n  \n  \\note this function hides the non-virtual base class implementation.\n*/\nbool QCPPainter::begin(QPaintDevice *device)\n{\n  bool result = QPainter::begin(device);\n#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.\n  if (result)\n    setRenderHint(QPainter::NonCosmeticDefaultPen);\n#endif\n  return result;\n}\n\n/*! \\overload\n  \n  Sets the mode of the painter. This controls whether the painter shall adjust its\n  fixes/workarounds optimized for certain output devices.\n*/\nvoid QCPPainter::setMode(QCPPainter::PainterMode mode, bool enabled)\n{\n  if (!enabled && mModes.testFlag(mode))\n    mModes &= ~mode;\n  else if (enabled && !mModes.testFlag(mode))\n    mModes |= mode;\n}\n\n/*!\n  Saves the painter (see QPainter::save). Since QCPPainter adds some new internal state to\n  QPainter, the save/restore functions are reimplemented to also save/restore those members.\n  \n  \\note this function hides the non-virtual base class implementation.\n  \n  \\see restore\n*/\nvoid QCPPainter::save()\n{\n  mAntialiasingStack.push(mIsAntialiasing);\n  QPainter::save();\n}\n\n/*!\n  Restores the painter (see QPainter::restore). Since QCPPainter adds some new internal state to\n  QPainter, the save/restore functions are reimplemented to also save/restore those members.\n  \n  \\note this function hides the non-virtual base class implementation.\n  \n  \\see save\n*/\nvoid QCPPainter::restore()\n{\n  if (!mAntialiasingStack.isEmpty())\n    mIsAntialiasing = mAntialiasingStack.pop();\n  else\n    qDebug() << Q_FUNC_INFO << \"Unbalanced save/restore\";\n  QPainter::restore();\n}\n\n/*!\n  Changes the pen width to 1 if it currently is 0. This function is called in the \\ref setPen\n  overrides when the \\ref pmNonCosmetic mode is set.\n*/\nvoid QCPPainter::makeNonCosmetic()\n{\n  if (qFuzzyIsNull(pen().widthF()))\n  {\n    QPen p = pen();\n    p.setWidth(1);\n    QPainter::setPen(p);\n  }\n}\n/* end of 'src/painter.cpp' */\n\n\n/* including file 'src/paintbuffer.cpp'     */\n/* modified 2021-03-29T02:30:44, size 18915 */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPAbstractPaintBuffer\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPAbstractPaintBuffer\n  \\brief The abstract base class for paint buffers, which define the rendering backend\n\n  This abstract base class defines the basic interface that a paint buffer needs to provide in\n  order to be usable by QCustomPlot.\n\n  A paint buffer manages both a surface to draw onto, and the matching paint device. The size of\n  the surface can be changed via \\ref setSize. External classes (\\ref QCustomPlot and \\ref\n  QCPLayer) request a painter via \\ref startPainting and then perform the draw calls. Once the\n  painting is complete, \\ref donePainting is called, so the paint buffer implementation can do\n  clean up if necessary. Before rendering a frame, each paint buffer is usually filled with a color\n  using \\ref clear (usually the color is \\c Qt::transparent), to remove the contents of the\n  previous frame.\n\n  The simplest paint buffer implementation is \\ref QCPPaintBufferPixmap which allows regular\n  software rendering via the raster engine. Hardware accelerated rendering via pixel buffers and\n  frame buffer objects is provided by \\ref QCPPaintBufferGlPbuffer and \\ref QCPPaintBufferGlFbo.\n  They are used automatically if \\ref QCustomPlot::setOpenGl is enabled.\n*/\n\n/* start documentation of pure virtual functions */\n\n/*! \\fn virtual QCPPainter *QCPAbstractPaintBuffer::startPainting() = 0\n\n  Returns a \\ref QCPPainter which is ready to draw to this buffer. The ownership and thus the\n  responsibility to delete the painter after the painting operations are complete is given to the\n  caller of this method.\n\n  Once you are done using the painter, delete the painter and call \\ref donePainting.\n\n  While a painter generated with this method is active, you must not call \\ref setSize, \\ref\n  setDevicePixelRatio or \\ref clear.\n\n  This method may return 0, if a painter couldn't be activated on the buffer. This usually\n  indicates a problem with the respective painting backend.\n*/\n\n/*! \\fn virtual void QCPAbstractPaintBuffer::draw(QCPPainter *painter) const = 0\n\n  Draws the contents of this buffer with the provided \\a painter. This is the method that is used\n  to finally join all paint buffers and draw them onto the screen.\n*/\n\n/*! \\fn virtual void QCPAbstractPaintBuffer::clear(const QColor &color) = 0\n\n  Fills the entire buffer with the provided \\a color. To have an empty transparent buffer, use the\n  named color \\c Qt::transparent.\n\n  This method must not be called if there is currently a painter (acquired with \\ref startPainting)\n  active.\n*/\n\n/*! \\fn virtual void QCPAbstractPaintBuffer::reallocateBuffer() = 0\n\n  Reallocates the internal buffer with the currently configured size (\\ref setSize) and device\n  pixel ratio, if applicable (\\ref setDevicePixelRatio). It is called as soon as any of those\n  properties are changed on this paint buffer.\n\n  \\note Subclasses of \\ref QCPAbstractPaintBuffer must call their reimplementation of this method\n  in their constructor, to perform the first allocation (this can not be done by the base class\n  because calling pure virtual methods in base class constructors is not possible).\n*/\n\n/* end documentation of pure virtual functions */\n/* start documentation of inline functions */\n\n/*! \\fn virtual void QCPAbstractPaintBuffer::donePainting()\n\n  If you have acquired a \\ref QCPPainter to paint onto this paint buffer via \\ref startPainting,\n  call this method as soon as you are done with the painting operations and have deleted the\n  painter.\n\n  paint buffer subclasses may use this method to perform any type of cleanup that is necessary. The\n  default implementation does nothing.\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Creates a paint buffer and initializes it with the provided \\a size and \\a devicePixelRatio.\n\n  Subclasses must call their \\ref reallocateBuffer implementation in their respective constructors.\n*/\nQCPAbstractPaintBuffer::QCPAbstractPaintBuffer(const QSize &size, double devicePixelRatio) :\n  mSize(size),\n  mDevicePixelRatio(devicePixelRatio),\n  mInvalidated(true)\n{\n}\n\nQCPAbstractPaintBuffer::~QCPAbstractPaintBuffer()\n{\n}\n\n/*!\n  Sets the paint buffer size.\n\n  The buffer is reallocated (by calling \\ref reallocateBuffer), so any painters that were obtained\n  by \\ref startPainting are invalidated and must not be used after calling this method.\n\n  If \\a size is already the current buffer size, this method does nothing.\n*/\nvoid QCPAbstractPaintBuffer::setSize(const QSize &size)\n{\n  if (mSize != size)\n  {\n    mSize = size;\n    reallocateBuffer();\n  }\n}\n\n/*!\n  Sets the invalidated flag to \\a invalidated.\n\n  This mechanism is used internally in conjunction with isolated replotting of \\ref QCPLayer\n  instances (in \\ref QCPLayer::lmBuffered mode). If \\ref QCPLayer::replot is called on a buffered\n  layer, i.e. an isolated repaint of only that layer (and its dedicated paint buffer) is requested,\n  QCustomPlot will decide depending on the invalidated flags of other paint buffers whether it also\n  replots them, instead of only the layer on which the replot was called.\n\n  The invalidated flag is set to true when \\ref QCPLayer association has changed, i.e. if layers\n  were added or removed from this buffer, or if they were reordered. It is set to false as soon as\n  all associated \\ref QCPLayer instances are drawn onto the buffer.\n\n  Under normal circumstances, it is not necessary to manually call this method.\n*/\nvoid QCPAbstractPaintBuffer::setInvalidated(bool invalidated)\n{\n  mInvalidated = invalidated;\n}\n\n/*!\n  Sets the device pixel ratio to \\a ratio. This is useful to render on high-DPI output devices.\n  The ratio is automatically set to the device pixel ratio used by the parent QCustomPlot instance.\n\n  The buffer is reallocated (by calling \\ref reallocateBuffer), so any painters that were obtained\n  by \\ref startPainting are invalidated and must not be used after calling this method.\n\n  \\note This method is only available for Qt versions 5.4 and higher.\n*/\nvoid QCPAbstractPaintBuffer::setDevicePixelRatio(double ratio)\n{\n  if (!qFuzzyCompare(ratio, mDevicePixelRatio))\n  {\n#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED\n    mDevicePixelRatio = ratio;\n    reallocateBuffer();\n#else\n    qDebug() << Q_FUNC_INFO << \"Device pixel ratios not supported for Qt versions before 5.4\";\n    mDevicePixelRatio = 1.0;\n#endif\n  }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPPaintBufferPixmap\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPPaintBufferPixmap\n  \\brief A paint buffer based on QPixmap, using software raster rendering\n\n  This paint buffer is the default and fall-back paint buffer which uses software rendering and\n  QPixmap as internal buffer. It is used if \\ref QCustomPlot::setOpenGl is false.\n*/\n\n/*!\n  Creates a pixmap paint buffer instancen with the specified \\a size and \\a devicePixelRatio, if\n  applicable.\n*/\nQCPPaintBufferPixmap::QCPPaintBufferPixmap(const QSize &size, double devicePixelRatio) :\n  QCPAbstractPaintBuffer(size, devicePixelRatio)\n{\n  QCPPaintBufferPixmap::reallocateBuffer();\n}\n\nQCPPaintBufferPixmap::~QCPPaintBufferPixmap()\n{\n}\n\n/* inherits documentation from base class */\nQCPPainter *QCPPaintBufferPixmap::startPainting()\n{\n  QCPPainter *result = new QCPPainter(&mBuffer);\n#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)\n  result->setRenderHint(QPainter::HighQualityAntialiasing);\n#endif\n  return result;\n}\n\n/* inherits documentation from base class */\nvoid QCPPaintBufferPixmap::draw(QCPPainter *painter) const\n{\n  if (painter && painter->isActive())\n    painter->drawPixmap(0, 0, mBuffer);\n  else\n    qDebug() << Q_FUNC_INFO << \"invalid or inactive painter passed\";\n}\n\n/* inherits documentation from base class */\nvoid QCPPaintBufferPixmap::clear(const QColor &color)\n{\n  mBuffer.fill(color);\n}\n\n/* inherits documentation from base class */\nvoid QCPPaintBufferPixmap::reallocateBuffer()\n{\n  setInvalidated();\n  if (!qFuzzyCompare(1.0, mDevicePixelRatio))\n  {\n#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED\n    mBuffer = QPixmap(mSize*mDevicePixelRatio);\n    mBuffer.setDevicePixelRatio(mDevicePixelRatio);\n#else\n    qDebug() << Q_FUNC_INFO << \"Device pixel ratios not supported for Qt versions before 5.4\";\n    mDevicePixelRatio = 1.0;\n    mBuffer = QPixmap(mSize);\n#endif\n  } else\n  {\n    mBuffer = QPixmap(mSize);\n  }\n}\n\n\n#ifdef QCP_OPENGL_PBUFFER\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPPaintBufferGlPbuffer\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPPaintBufferGlPbuffer\n  \\brief A paint buffer based on OpenGL pixel buffers, using hardware accelerated rendering\n\n  This paint buffer is one of the OpenGL paint buffers which facilitate hardware accelerated plot\n  rendering. It is based on OpenGL pixel buffers (pbuffer) and is used in Qt versions before 5.0.\n  (See \\ref QCPPaintBufferGlFbo used in newer Qt versions.)\n\n  The OpenGL paint buffers are used if \\ref QCustomPlot::setOpenGl is set to true, and if they are\n  supported by the system.\n*/\n\n/*!\n  Creates a \\ref QCPPaintBufferGlPbuffer instance with the specified \\a size and \\a\n  devicePixelRatio, if applicable.\n\n  The parameter \\a multisamples defines how many samples are used per pixel. Higher values thus\n  result in higher quality antialiasing. If the specified \\a multisamples value exceeds the\n  capability of the graphics hardware, the highest supported multisampling is used.\n*/\nQCPPaintBufferGlPbuffer::QCPPaintBufferGlPbuffer(const QSize &size, double devicePixelRatio, int multisamples) :\n  QCPAbstractPaintBuffer(size, devicePixelRatio),\n  mGlPBuffer(0),\n  mMultisamples(qMax(0, multisamples))\n{\n  QCPPaintBufferGlPbuffer::reallocateBuffer();\n}\n\nQCPPaintBufferGlPbuffer::~QCPPaintBufferGlPbuffer()\n{\n  if (mGlPBuffer)\n    delete mGlPBuffer;\n}\n\n/* inherits documentation from base class */\nQCPPainter *QCPPaintBufferGlPbuffer::startPainting()\n{\n  if (!mGlPBuffer->isValid())\n  {\n    qDebug() << Q_FUNC_INFO << \"OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?\";\n    return 0;\n  }\n  \n  QCPPainter *result = new QCPPainter(mGlPBuffer);\n  result->setRenderHint(QPainter::HighQualityAntialiasing);\n  return result;\n}\n\n/* inherits documentation from base class */\nvoid QCPPaintBufferGlPbuffer::draw(QCPPainter *painter) const\n{\n  if (!painter || !painter->isActive())\n  {\n    qDebug() << Q_FUNC_INFO << \"invalid or inactive painter passed\";\n    return;\n  }\n  if (!mGlPBuffer->isValid())\n  {\n    qDebug() << Q_FUNC_INFO << \"OpenGL pbuffer isn't valid, reallocateBuffer was not called?\";\n    return;\n  }\n  painter->drawImage(0, 0, mGlPBuffer->toImage());\n}\n\n/* inherits documentation from base class */\nvoid QCPPaintBufferGlPbuffer::clear(const QColor &color)\n{\n  if (mGlPBuffer->isValid())\n  {\n    mGlPBuffer->makeCurrent();\n    glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());\n    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n    mGlPBuffer->doneCurrent();\n  } else\n    qDebug() << Q_FUNC_INFO << \"OpenGL pbuffer invalid or context not current\";\n}\n\n/* inherits documentation from base class */\nvoid QCPPaintBufferGlPbuffer::reallocateBuffer()\n{\n  if (mGlPBuffer)\n    delete mGlPBuffer;\n  \n  QGLFormat format;\n  format.setAlpha(true);\n  format.setSamples(mMultisamples);\n  mGlPBuffer = new QGLPixelBuffer(mSize, format);\n}\n#endif // QCP_OPENGL_PBUFFER\n\n\n#ifdef QCP_OPENGL_FBO\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPPaintBufferGlFbo\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPPaintBufferGlFbo\n  \\brief A paint buffer based on OpenGL frame buffers objects, using hardware accelerated rendering\n\n  This paint buffer is one of the OpenGL paint buffers which facilitate hardware accelerated plot\n  rendering. It is based on OpenGL frame buffer objects (fbo) and is used in Qt versions 5.0 and\n  higher. (See \\ref QCPPaintBufferGlPbuffer used in older Qt versions.)\n\n  The OpenGL paint buffers are used if \\ref QCustomPlot::setOpenGl is set to true, and if they are\n  supported by the system.\n*/\n\n/*!\n  Creates a \\ref QCPPaintBufferGlFbo instance with the specified \\a size and \\a devicePixelRatio,\n  if applicable.\n\n  All frame buffer objects shall share one OpenGL context and paint device, which need to be set up\n  externally and passed via \\a glContext and \\a glPaintDevice. The set-up is done in \\ref\n  QCustomPlot::setupOpenGl and the context and paint device are managed by the parent QCustomPlot\n  instance.\n*/\nQCPPaintBufferGlFbo::QCPPaintBufferGlFbo(const QSize &size, double devicePixelRatio, QWeakPointer<QOpenGLContext> glContext, QWeakPointer<QOpenGLPaintDevice> glPaintDevice) :\n  QCPAbstractPaintBuffer(size, devicePixelRatio),\n  mGlContext(glContext),\n  mGlPaintDevice(glPaintDevice),\n  mGlFrameBuffer(0)\n{\n  QCPPaintBufferGlFbo::reallocateBuffer();\n}\n\nQCPPaintBufferGlFbo::~QCPPaintBufferGlFbo()\n{\n  if (mGlFrameBuffer)\n    delete mGlFrameBuffer;\n}\n\n/* inherits documentation from base class */\nQCPPainter *QCPPaintBufferGlFbo::startPainting()\n{\n  QSharedPointer<QOpenGLPaintDevice> paintDevice = mGlPaintDevice.toStrongRef();\n  QSharedPointer<QOpenGLContext> context = mGlContext.toStrongRef();\n  if (!paintDevice)\n  {\n    qDebug() << Q_FUNC_INFO << \"OpenGL paint device doesn't exist\";\n    return 0;\n  }\n  if (!context)\n  {\n    qDebug() << Q_FUNC_INFO << \"OpenGL context doesn't exist\";\n    return 0;\n  }\n  if (!mGlFrameBuffer)\n  {\n    qDebug() << Q_FUNC_INFO << \"OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?\";\n    return 0;\n  }\n  \n  if (QOpenGLContext::currentContext() != context.data())\n    context->makeCurrent(context->surface());\n  mGlFrameBuffer->bind();\n  QCPPainter *result = new QCPPainter(paintDevice.data());\n#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)\n  result->setRenderHint(QPainter::HighQualityAntialiasing);\n#endif\n  return result;\n}\n\n/* inherits documentation from base class */\nvoid QCPPaintBufferGlFbo::donePainting()\n{\n  if (mGlFrameBuffer && mGlFrameBuffer->isBound())\n    mGlFrameBuffer->release();\n  else\n    qDebug() << Q_FUNC_INFO << \"Either OpenGL frame buffer not valid or was not bound\";\n}\n\n/* inherits documentation from base class */\nvoid QCPPaintBufferGlFbo::draw(QCPPainter *painter) const\n{\n  if (!painter || !painter->isActive())\n  {\n    qDebug() << Q_FUNC_INFO << \"invalid or inactive painter passed\";\n    return;\n  }\n  if (!mGlFrameBuffer)\n  {\n    qDebug() << Q_FUNC_INFO << \"OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?\";\n    return;\n  }\n  painter->drawImage(0, 0, mGlFrameBuffer->toImage());\n}\n\n/* inherits documentation from base class */\nvoid QCPPaintBufferGlFbo::clear(const QColor &color)\n{\n  QSharedPointer<QOpenGLContext> context = mGlContext.toStrongRef();\n  if (!context)\n  {\n    qDebug() << Q_FUNC_INFO << \"OpenGL context doesn't exist\";\n    return;\n  }\n  if (!mGlFrameBuffer)\n  {\n    qDebug() << Q_FUNC_INFO << \"OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?\";\n    return;\n  }\n  \n  if (QOpenGLContext::currentContext() != context.data())\n    context->makeCurrent(context->surface());\n  mGlFrameBuffer->bind();\n  glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());\n  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n  mGlFrameBuffer->release();\n}\n\n/* inherits documentation from base class */\nvoid QCPPaintBufferGlFbo::reallocateBuffer()\n{\n  // release and delete possibly existing framebuffer:\n  if (mGlFrameBuffer)\n  {\n    if (mGlFrameBuffer->isBound())\n      mGlFrameBuffer->release();\n    delete mGlFrameBuffer;\n    mGlFrameBuffer = 0;\n  }\n  \n  QSharedPointer<QOpenGLPaintDevice> paintDevice = mGlPaintDevice.toStrongRef();\n  QSharedPointer<QOpenGLContext> context = mGlContext.toStrongRef();\n  if (!paintDevice)\n  {\n    qDebug() << Q_FUNC_INFO << \"OpenGL paint device doesn't exist\";\n    return;\n  }\n  if (!context)\n  {\n    qDebug() << Q_FUNC_INFO << \"OpenGL context doesn't exist\";\n    return;\n  }\n  \n  // create new fbo with appropriate size:\n  context->makeCurrent(context->surface());\n  QOpenGLFramebufferObjectFormat frameBufferFormat;\n  frameBufferFormat.setSamples(context->format().samples());\n  frameBufferFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);\n  mGlFrameBuffer = new QOpenGLFramebufferObject(mSize*mDevicePixelRatio, frameBufferFormat);\n  if (paintDevice->size() != mSize*mDevicePixelRatio)\n    paintDevice->setSize(mSize*mDevicePixelRatio);\n#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED\n  paintDevice->setDevicePixelRatio(mDevicePixelRatio);\n#endif\n}\n#endif // QCP_OPENGL_FBO\n/* end of 'src/paintbuffer.cpp' */\n\n\n/* including file 'src/layer.cpp'           */\n/* modified 2021-03-29T02:30:44, size 37615 */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPLayer\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPLayer\n  \\brief A layer that may contain objects, to control the rendering order\n\n  The Layering system of QCustomPlot is the mechanism to control the rendering order of the\n  elements inside the plot.\n\n  It is based on the two classes QCPLayer and QCPLayerable. QCustomPlot holds an ordered list of\n  one or more instances of QCPLayer (see QCustomPlot::addLayer, QCustomPlot::layer,\n  QCustomPlot::moveLayer, etc.). When replotting, QCustomPlot goes through the list of layers\n  bottom to top and successively draws the layerables of the layers into the paint buffer(s).\n\n  A QCPLayer contains an ordered list of QCPLayerable instances. QCPLayerable is an abstract base\n  class from which almost all visible objects derive, like axes, grids, graphs, items, etc.\n\n  \\section qcplayer-defaultlayers Default layers\n\n  Initially, QCustomPlot has six layers: \"background\", \"grid\", \"main\", \"axes\", \"legend\" and\n  \"overlay\" (in that order). On top is the \"overlay\" layer, which only contains the QCustomPlot's\n  selection rect (\\ref QCustomPlot::selectionRect). The next two layers \"axes\" and \"legend\" contain\n  the default axes and legend, so they will be drawn above plottables. In the middle, there is the\n  \"main\" layer. It is initially empty and set as the current layer (see\n  QCustomPlot::setCurrentLayer). This means, all new plottables, items etc. are created on this\n  layer by default. Then comes the \"grid\" layer which contains the QCPGrid instances (which belong\n  tightly to QCPAxis, see \\ref QCPAxis::grid). The Axis rect background shall be drawn behind\n  everything else, thus the default QCPAxisRect instance is placed on the \"background\" layer. Of\n  course, the layer affiliation of the individual objects can be changed as required (\\ref\n  QCPLayerable::setLayer).\n\n  \\section qcplayer-ordering Controlling the rendering order via layers\n\n  Controlling the ordering of layerables in the plot is easy: Create a new layer in the position\n  you want the layerable to be in, e.g. above \"main\", with \\ref QCustomPlot::addLayer. Then set the\n  current layer with \\ref QCustomPlot::setCurrentLayer to that new layer and finally create the\n  objects normally. They will be placed on the new layer automatically, due to the current layer\n  setting. Alternatively you could have also ignored the current layer setting and just moved the\n  objects with \\ref QCPLayerable::setLayer to the desired layer after creating them.\n\n  It is also possible to move whole layers. For example, If you want the grid to be shown in front\n  of all plottables/items on the \"main\" layer, just move it above \"main\" with\n  QCustomPlot::moveLayer.\n\n  The rendering order within one layer is simply by order of creation or insertion. The item\n  created last (or added last to the layer), is drawn on top of all other objects on that layer.\n\n  When a layer is deleted, the objects on it are not deleted with it, but fall on the layer below\n  the deleted layer, see QCustomPlot::removeLayer.\n\n  \\section qcplayer-buffering Replotting only a specific layer\n\n  If the layer mode (\\ref setMode) is set to \\ref lmBuffered, you can replot only this specific\n  layer by calling \\ref replot. In certain situations this can provide better replot performance,\n  compared with a full replot of all layers. Upon creation of a new layer, the layer mode is\n  initialized to \\ref lmLogical. The only layer that is set to \\ref lmBuffered in a new \\ref\n  QCustomPlot instance is the \"overlay\" layer, containing the selection rect.\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn QList<QCPLayerable*> QCPLayer::children() const\n  \n  Returns a list of all layerables on this layer. The order corresponds to the rendering order:\n  layerables with higher indices are drawn above layerables with lower indices.\n*/\n\n/*! \\fn int QCPLayer::index() const\n  \n  Returns the index this layer has in the QCustomPlot. The index is the integer number by which this layer can be\n  accessed via \\ref QCustomPlot::layer.\n  \n  Layers with higher indices will be drawn above layers with lower indices.\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Creates a new QCPLayer instance.\n  \n  Normally you shouldn't directly instantiate layers, use \\ref QCustomPlot::addLayer instead.\n  \n  \\warning It is not checked that \\a layerName is actually a unique layer name in \\a parentPlot.\n  This check is only performed by \\ref QCustomPlot::addLayer.\n*/\nQCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) :\n  QObject(parentPlot),\n  mParentPlot(parentPlot),\n  mName(layerName),\n  mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function\n  mVisible(true),\n  mMode(lmLogical)\n{\n  // Note: no need to make sure layerName is unique, because layer\n  // management is done with QCustomPlot functions.\n}\n\nQCPLayer::~QCPLayer()\n{\n  // If child layerables are still on this layer, detach them, so they don't try to reach back to this\n  // then invalid layer once they get deleted/moved themselves. This only happens when layers are deleted\n  // directly, like in the QCustomPlot destructor. (The regular layer removal procedure for the user is to\n  // call QCustomPlot::removeLayer, which moves all layerables off this layer before deleting it.)\n  \n  while (!mChildren.isEmpty())\n    mChildren.last()->setLayer(nullptr); // removes itself from mChildren via removeChild()\n  \n  if (mParentPlot->currentLayer() == this)\n    qDebug() << Q_FUNC_INFO << \"The parent plot's mCurrentLayer will be a dangling pointer. Should have been set to a valid layer or nullptr beforehand.\";\n}\n\n/*!\n  Sets whether this layer is visible or not. If \\a visible is set to false, all layerables on this\n  layer will be invisible.\n\n  This function doesn't change the visibility property of the layerables (\\ref\n  QCPLayerable::setVisible), but the \\ref QCPLayerable::realVisibility of each layerable takes the\n  visibility of the parent layer into account.\n*/\nvoid QCPLayer::setVisible(bool visible)\n{\n  mVisible = visible;\n}\n\n/*!\n  Sets the rendering mode of this layer.\n\n  If \\a mode is set to \\ref lmBuffered for a layer, it will be given a dedicated paint buffer by\n  the parent QCustomPlot instance. This means it may be replotted individually by calling \\ref\n  QCPLayer::replot, without needing to replot all other layers.\n\n  Layers which are set to \\ref lmLogical (the default) are used only to define the rendering order\n  and can't be replotted individually.\n\n  Note that each layer which is set to \\ref lmBuffered requires additional paint buffers for the\n  layers below, above and for the layer itself. This increases the memory consumption and\n  (slightly) decreases the repainting speed because multiple paint buffers need to be joined. So\n  you should carefully choose which layers benefit from having their own paint buffer. A typical\n  example would be a layer which contains certain layerables (e.g. items) that need to be changed\n  and thus replotted regularly, while all other layerables on other layers stay static. By default,\n  only the topmost layer called \"overlay\" is in mode \\ref lmBuffered, and contains the selection\n  rect.\n\n  \\see replot\n*/\nvoid QCPLayer::setMode(QCPLayer::LayerMode mode)\n{\n  if (mMode != mode)\n  {\n    mMode = mode;\n    if (QSharedPointer<QCPAbstractPaintBuffer> pb = mPaintBuffer.toStrongRef())\n      pb->setInvalidated();\n  }\n}\n\n/*! \\internal\n\n  Draws the contents of this layer with the provided \\a painter.\n\n  \\see replot, drawToPaintBuffer\n*/\nvoid QCPLayer::draw(QCPPainter *painter)\n{\n  foreach (QCPLayerable *child, mChildren)\n  {\n    if (child->realVisibility())\n    {\n      painter->save();\n      painter->setClipRect(child->clipRect().translated(0, -1));\n      child->applyDefaultAntialiasingHint(painter);\n      child->draw(painter);\n      painter->restore();\n    }\n  }\n}\n\n/*! \\internal\n\n  Draws the contents of this layer into the paint buffer which is associated with this layer. The\n  association is established by the parent QCustomPlot, which manages all paint buffers (see \\ref\n  QCustomPlot::setupPaintBuffers).\n\n  \\see draw\n*/\nvoid QCPLayer::drawToPaintBuffer()\n{\n  if (QSharedPointer<QCPAbstractPaintBuffer> pb = mPaintBuffer.toStrongRef())\n  {\n    if (QCPPainter *painter = pb->startPainting())\n    {\n      if (painter->isActive())\n        draw(painter);\n      else\n        qDebug() << Q_FUNC_INFO << \"paint buffer returned inactive painter\";\n      delete painter;\n      pb->donePainting();\n    } else\n      qDebug() << Q_FUNC_INFO << \"paint buffer returned nullptr painter\";\n  } else\n    qDebug() << Q_FUNC_INFO << \"no valid paint buffer associated with this layer\";\n}\n\n/*!\n  If the layer mode (\\ref setMode) is set to \\ref lmBuffered, this method allows replotting only\n  the layerables on this specific layer, without the need to replot all other layers (as a call to\n  \\ref QCustomPlot::replot would do).\n\n  QCustomPlot also makes sure to replot all layers instead of only this one, if the layer ordering\n  or any layerable-layer-association has changed since the last full replot and any other paint\n  buffers were thus invalidated.\n\n  If the layer mode is \\ref lmLogical however, this method simply calls \\ref QCustomPlot::replot on\n  the parent QCustomPlot instance.\n\n  \\see draw\n*/\nvoid QCPLayer::replot()\n{\n  if (mMode == lmBuffered && !mParentPlot->hasInvalidatedPaintBuffers())\n  {\n    if (QSharedPointer<QCPAbstractPaintBuffer> pb = mPaintBuffer.toStrongRef())\n    {\n      pb->clear(Qt::transparent);\n      drawToPaintBuffer();\n      pb->setInvalidated(false); // since layer is lmBuffered, we know only this layer is on buffer and we can reset invalidated flag\n      mParentPlot->update();\n    } else\n      qDebug() << Q_FUNC_INFO << \"no valid paint buffer associated with this layer\";\n  } else\n    mParentPlot->replot();\n}\n\n/*! \\internal\n  \n  Adds the \\a layerable to the list of this layer. If \\a prepend is set to true, the layerable will\n  be prepended to the list, i.e. be drawn beneath the other layerables already in the list.\n  \n  This function does not change the \\a mLayer member of \\a layerable to this layer. (Use\n  QCPLayerable::setLayer to change the layer of an object, not this function.)\n  \n  \\see removeChild\n*/\nvoid QCPLayer::addChild(QCPLayerable *layerable, bool prepend)\n{\n  if (!mChildren.contains(layerable))\n  {\n    if (prepend)\n      mChildren.prepend(layerable);\n    else\n      mChildren.append(layerable);\n    if (QSharedPointer<QCPAbstractPaintBuffer> pb = mPaintBuffer.toStrongRef())\n      pb->setInvalidated();\n  } else\n    qDebug() << Q_FUNC_INFO << \"layerable is already child of this layer\" << reinterpret_cast<quintptr>(layerable);\n}\n\n/*! \\internal\n  \n  Removes the \\a layerable from the list of this layer.\n  \n  This function does not change the \\a mLayer member of \\a layerable. (Use QCPLayerable::setLayer\n  to change the layer of an object, not this function.)\n  \n  \\see addChild\n*/\nvoid QCPLayer::removeChild(QCPLayerable *layerable)\n{\n  if (mChildren.removeOne(layerable))\n  {\n    if (QSharedPointer<QCPAbstractPaintBuffer> pb = mPaintBuffer.toStrongRef())\n      pb->setInvalidated();\n  } else\n    qDebug() << Q_FUNC_INFO << \"layerable is not child of this layer\" << reinterpret_cast<quintptr>(layerable);\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPLayerable\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPLayerable\n  \\brief Base class for all drawable objects\n  \n  This is the abstract base class most visible objects derive from, e.g. plottables, axes, grid\n  etc.\n\n  Every layerable is on a layer (QCPLayer) which allows controlling the rendering order by stacking\n  the layers accordingly.\n  \n  For details about the layering mechanism, see the QCPLayer documentation.\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn QCPLayerable *QCPLayerable::parentLayerable() const\n \n  Returns the parent layerable of this layerable. The parent layerable is used to provide\n  visibility hierarchies in conjunction with the method \\ref realVisibility. This way, layerables\n  only get drawn if their parent layerables are visible, too.\n  \n  Note that a parent layerable is not necessarily also the QObject parent for memory management.\n  Further, a layerable doesn't always have a parent layerable, so this function may return \\c\n  nullptr.\n  \n  A parent layerable is set implicitly when placed inside layout elements and doesn't need to be\n  set manually by the user.\n*/\n\n/* end documentation of inline functions */\n/* start documentation of pure virtual functions */\n\n/*! \\fn virtual void QCPLayerable::applyDefaultAntialiasingHint(QCPPainter *painter) const = 0\n  \\internal\n  \n  This function applies the default antialiasing setting to the specified \\a painter, using the\n  function \\ref applyAntialiasingHint. It is the antialiasing state the painter is put in, when\n  \\ref draw is called on the layerable. If the layerable has multiple entities whose antialiasing\n  setting may be specified individually, this function should set the antialiasing state of the\n  most prominent entity. In this case however, the \\ref draw function usually calls the specialized\n  versions of this function before drawing each entity, effectively overriding the setting of the\n  default antialiasing hint.\n  \n  <b>First example:</b> QCPGraph has multiple entities that have an antialiasing setting: The graph\n  line, fills and scatters. Those can be configured via QCPGraph::setAntialiased,\n  QCPGraph::setAntialiasedFill and QCPGraph::setAntialiasedScatters. Consequently, there isn't only\n  the QCPGraph::applyDefaultAntialiasingHint function (which corresponds to the graph line's\n  antialiasing), but specialized ones like QCPGraph::applyFillAntialiasingHint and\n  QCPGraph::applyScattersAntialiasingHint. So before drawing one of those entities, QCPGraph::draw\n  calls the respective specialized applyAntialiasingHint function.\n  \n  <b>Second example:</b> QCPItemLine consists only of a line so there is only one antialiasing\n  setting which can be controlled with QCPItemLine::setAntialiased. (This function is inherited by\n  all layerables. The specialized functions, as seen on QCPGraph, must be added explicitly to the\n  respective layerable subclass.) Consequently it only has the normal\n  QCPItemLine::applyDefaultAntialiasingHint. The \\ref QCPItemLine::draw function doesn't need to\n  care about setting any antialiasing states, because the default antialiasing hint is already set\n  on the painter when the \\ref draw function is called, and that's the state it wants to draw the\n  line with.\n*/\n\n/*! \\fn virtual void QCPLayerable::draw(QCPPainter *painter) const = 0\n  \\internal\n  \n  This function draws the layerable with the specified \\a painter. It is only called by\n  QCustomPlot, if the layerable is visible (\\ref setVisible).\n  \n  Before this function is called, the painter's antialiasing state is set via \\ref\n  applyDefaultAntialiasingHint, see the documentation there. Further, the clipping rectangle was\n  set to \\ref clipRect.\n*/\n\n/* end documentation of pure virtual functions */\n/* start documentation of signals */\n\n/*! \\fn void QCPLayerable::layerChanged(QCPLayer *newLayer);\n  \n  This signal is emitted when the layer of this layerable changes, i.e. this layerable is moved to\n  a different layer.\n  \n  \\see setLayer\n*/\n\n/* end documentation of signals */\n\n/*!\n  Creates a new QCPLayerable instance.\n  \n  Since QCPLayerable is an abstract base class, it can't be instantiated directly. Use one of the\n  derived classes.\n  \n  If \\a plot is provided, it automatically places itself on the layer named \\a targetLayer. If \\a\n  targetLayer is an empty string, it places itself on the current layer of the plot (see \\ref\n  QCustomPlot::setCurrentLayer).\n  \n  It is possible to provide \\c nullptr as \\a plot. In that case, you should assign a parent plot at\n  a later time with \\ref initializeParentPlot.\n  \n  The layerable's parent layerable is set to \\a parentLayerable, if provided. Direct layerable\n  parents are mainly used to control visibility in a hierarchy of layerables. This means a\n  layerable is only drawn, if all its ancestor layerables are also visible. Note that \\a\n  parentLayerable does not become the QObject-parent (for memory management) of this layerable, \\a\n  plot does. It is not uncommon to set the QObject-parent to something else in the constructors of\n  QCPLayerable subclasses, to guarantee a working destruction hierarchy.\n*/\nQCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable *parentLayerable) :\n  QObject(plot),\n  mVisible(true),\n  mParentPlot(plot),\n  mParentLayerable(parentLayerable),\n  mLayer(nullptr),\n  mAntialiased(true)\n{\n  if (mParentPlot)\n  {\n    if (targetLayer.isEmpty())\n      setLayer(mParentPlot->currentLayer());\n    else if (!setLayer(targetLayer))\n      qDebug() << Q_FUNC_INFO << \"setting QCPlayerable initial layer to\" << targetLayer << \"failed.\";\n  }\n}\n\nQCPLayerable::~QCPLayerable()\n{\n  if (mLayer)\n  {\n    mLayer->removeChild(this);\n    mLayer = nullptr;\n  }\n}\n\n/*!\n  Sets the visibility of this layerable object. If an object is not visible, it will not be drawn\n  on the QCustomPlot surface, and user interaction with it (e.g. click and selection) is not\n  possible.\n*/\nvoid QCPLayerable::setVisible(bool on)\n{\n  mVisible = on;\n}\n\n/*!\n  Sets the \\a layer of this layerable object. The object will be placed on top of the other objects\n  already on \\a layer.\n  \n  If \\a layer is 0, this layerable will not be on any layer and thus not appear in the plot (or\n  interact/receive events).\n  \n  Returns true if the layer of this layerable was successfully changed to \\a layer.\n*/\nbool QCPLayerable::setLayer(QCPLayer *layer)\n{\n  return moveToLayer(layer, false);\n}\n\n/*! \\overload\n  Sets the layer of this layerable object by name\n  \n  Returns true on success, i.e. if \\a layerName is a valid layer name.\n*/\nbool QCPLayerable::setLayer(const QString &layerName)\n{\n  if (!mParentPlot)\n  {\n    qDebug() << Q_FUNC_INFO << \"no parent QCustomPlot set\";\n    return false;\n  }\n  if (QCPLayer *layer = mParentPlot->layer(layerName))\n  {\n    return setLayer(layer);\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"there is no layer with name\" << layerName;\n    return false;\n  }\n}\n\n/*!\n  Sets whether this object will be drawn antialiased or not.\n  \n  Note that antialiasing settings may be overridden by QCustomPlot::setAntialiasedElements and\n  QCustomPlot::setNotAntialiasedElements.\n*/\nvoid QCPLayerable::setAntialiased(bool enabled)\n{\n  mAntialiased = enabled;\n}\n\n/*!\n  Returns whether this layerable is visible, taking the visibility of the layerable parent and the\n  visibility of this layerable's layer into account. This is the method that is consulted to decide\n  whether a layerable shall be drawn or not.\n  \n  If this layerable has a direct layerable parent (usually set via hierarchies implemented in\n  subclasses, like in the case of \\ref QCPLayoutElement), this function returns true only if this\n  layerable has its visibility set to true and the parent layerable's \\ref realVisibility returns\n  true.\n*/\nbool QCPLayerable::realVisibility() const\n{\n  return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility());\n}\n\n/*!\n  This function is used to decide whether a click hits a layerable object or not.\n\n  \\a pos is a point in pixel coordinates on the QCustomPlot surface. This function returns the\n  shortest pixel distance of this point to the object. If the object is either invisible or the\n  distance couldn't be determined, -1.0 is returned. Further, if \\a onlySelectable is true and the\n  object is not selectable, -1.0 is returned, too.\n\n  If the object is represented not by single lines but by an area like a \\ref QCPItemText or the\n  bars of a \\ref QCPBars plottable, a click inside the area should also be considered a hit. In\n  these cases this function thus returns a constant value greater zero but still below the parent\n  plot's selection tolerance. (typically the selectionTolerance multiplied by 0.99).\n  \n  Providing a constant value for area objects allows selecting line objects even when they are\n  obscured by such area objects, by clicking close to the lines (i.e. closer than\n  0.99*selectionTolerance).\n  \n  The actual setting of the selection state is not done by this function. This is handled by the\n  parent QCustomPlot when the mouseReleaseEvent occurs, and the finally selected object is notified\n  via the \\ref selectEvent/\\ref deselectEvent methods.\n  \n  \\a details is an optional output parameter. Every layerable subclass may place any information\n  in \\a details. This information will be passed to \\ref selectEvent when the parent QCustomPlot\n  decides on the basis of this selectTest call, that the object was successfully selected. The\n  subsequent call to \\ref selectEvent will carry the \\a details. This is useful for multi-part\n  objects (like QCPAxis). This way, a possibly complex calculation to decide which part was clicked\n  is only done once in \\ref selectTest. The result (i.e. the actually clicked part) can then be\n  placed in \\a details. So in the subsequent \\ref selectEvent, the decision which part was\n  selected doesn't have to be done a second time for a single selection operation.\n  \n  In the case of 1D Plottables (\\ref QCPAbstractPlottable1D, like \\ref QCPGraph or \\ref QCPBars) \\a\n  details will be set to a \\ref QCPDataSelection, describing the closest data point to \\a pos.\n  \n  You may pass \\c nullptr as \\a details to indicate that you are not interested in those selection\n  details.\n  \n  \\see selectEvent, deselectEvent, mousePressEvent, wheelEvent, QCustomPlot::setInteractions,\n  QCPAbstractPlottable1D::selectTestRect\n*/\ndouble QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(pos)\n  Q_UNUSED(onlySelectable)\n  Q_UNUSED(details)\n  return -1.0;\n}\n\n/*! \\internal\n  \n  Sets the parent plot of this layerable. Use this function once to set the parent plot if you have\n  passed \\c nullptr in the constructor. It can not be used to move a layerable from one QCustomPlot\n  to another one.\n  \n  Note that, unlike when passing a non \\c nullptr parent plot in the constructor, this function\n  does not make \\a parentPlot the QObject-parent of this layerable. If you want this, call\n  QObject::setParent(\\a parentPlot) in addition to this function.\n  \n  Further, you will probably want to set a layer (\\ref setLayer) after calling this function, to\n  make the layerable appear on the QCustomPlot.\n  \n  The parent plot change will be propagated to subclasses via a call to \\ref parentPlotInitialized\n  so they can react accordingly (e.g. also initialize the parent plot of child layerables, like\n  QCPLayout does).\n*/\nvoid QCPLayerable::initializeParentPlot(QCustomPlot *parentPlot)\n{\n  if (mParentPlot)\n  {\n    qDebug() << Q_FUNC_INFO << \"called with mParentPlot already initialized\";\n    return;\n  }\n  \n  if (!parentPlot)\n    qDebug() << Q_FUNC_INFO << \"called with parentPlot zero\";\n  \n  mParentPlot = parentPlot;\n  parentPlotInitialized(mParentPlot);\n}\n\n/*! \\internal\n  \n  Sets the parent layerable of this layerable to \\a parentLayerable. Note that \\a parentLayerable does not\n  become the QObject-parent (for memory management) of this layerable.\n  \n  The parent layerable has influence on the return value of the \\ref realVisibility method. Only\n  layerables with a fully visible parent tree will return true for \\ref realVisibility, and thus be\n  drawn.\n  \n  \\see realVisibility\n*/\nvoid QCPLayerable::setParentLayerable(QCPLayerable *parentLayerable)\n{\n  mParentLayerable = parentLayerable;\n}\n\n/*! \\internal\n  \n  Moves this layerable object to \\a layer. If \\a prepend is true, this object will be prepended to\n  the new layer's list, i.e. it will be drawn below the objects already on the layer. If it is\n  false, the object will be appended.\n  \n  Returns true on success, i.e. if \\a layer is a valid layer.\n*/\nbool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend)\n{\n  if (layer && !mParentPlot)\n  {\n    qDebug() << Q_FUNC_INFO << \"no parent QCustomPlot set\";\n    return false;\n  }\n  if (layer && layer->parentPlot() != mParentPlot)\n  {\n    qDebug() << Q_FUNC_INFO << \"layer\" << layer->name() << \"is not in same QCustomPlot as this layerable\";\n    return false;\n  }\n  \n  QCPLayer *oldLayer = mLayer;\n  if (mLayer)\n    mLayer->removeChild(this);\n  mLayer = layer;\n  if (mLayer)\n    mLayer->addChild(this, prepend);\n  if (mLayer != oldLayer)\n    emit layerChanged(mLayer);\n  return true;\n}\n\n/*! \\internal\n\n  Sets the QCPainter::setAntialiasing state on the provided \\a painter, depending on the \\a\n  localAntialiased value as well as the overrides \\ref QCustomPlot::setAntialiasedElements and \\ref\n  QCustomPlot::setNotAntialiasedElements. Which override enum this function takes into account is\n  controlled via \\a overrideElement.\n*/\nvoid QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const\n{\n  if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))\n    painter->setAntialiasing(false);\n  else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))\n    painter->setAntialiasing(true);\n  else\n    painter->setAntialiasing(localAntialiased);\n}\n\n/*! \\internal\n\n  This function is called by \\ref initializeParentPlot, to allow subclasses to react on the setting\n  of a parent plot. This is the case when \\c nullptr was passed as parent plot in the constructor,\n  and the parent plot is set at a later time.\n  \n  For example, QCPLayoutElement/QCPLayout hierarchies may be created independently of any\n  QCustomPlot at first. When they are then added to a layout inside the QCustomPlot, the top level\n  element of the hierarchy gets its parent plot initialized with \\ref initializeParentPlot. To\n  propagate the parent plot to all the children of the hierarchy, the top level element then uses\n  this function to pass the parent plot on to its child elements.\n  \n  The default implementation does nothing.\n  \n  \\see initializeParentPlot\n*/\nvoid QCPLayerable::parentPlotInitialized(QCustomPlot *parentPlot)\n{\n   Q_UNUSED(parentPlot)\n}\n\n/*! \\internal\n\n  Returns the selection category this layerable shall belong to. The selection category is used in\n  conjunction with \\ref QCustomPlot::setInteractions to control which objects are selectable and\n  which aren't.\n  \n  Subclasses that don't fit any of the normal \\ref QCP::Interaction values can use \\ref\n  QCP::iSelectOther. This is what the default implementation returns.\n  \n  \\see QCustomPlot::setInteractions\n*/\nQCP::Interaction QCPLayerable::selectionCategory() const\n{\n  return QCP::iSelectOther;\n}\n\n/*! \\internal\n  \n  Returns the clipping rectangle of this layerable object. By default, this is the viewport of the\n  parent QCustomPlot. Specific subclasses may reimplement this function to provide different\n  clipping rects.\n  \n  The returned clipping rect is set on the painter before the draw function of the respective\n  object is called.\n*/\nQRect QCPLayerable::clipRect() const\n{\n  if (mParentPlot)\n    return mParentPlot->viewport();\n  else\n    return {};\n}\n\n/*! \\internal\n  \n  This event is called when the layerable shall be selected, as a consequence of a click by the\n  user. Subclasses should react to it by setting their selection state appropriately. The default\n  implementation does nothing.\n  \n  \\a event is the mouse event that caused the selection. \\a additive indicates, whether the user\n  was holding the multi-select-modifier while performing the selection (see \\ref\n  QCustomPlot::setMultiSelectModifier). if \\a additive is true, the selection state must be toggled\n  (i.e. become selected when unselected and unselected when selected).\n  \n  Every selectEvent is preceded by a call to \\ref selectTest, which has returned positively (i.e.\n  returned a value greater than 0 and less than the selection tolerance of the parent QCustomPlot).\n  The \\a details data you output from \\ref selectTest is fed back via \\a details here. You may\n  use it to transport any kind of information from the selectTest to the possibly subsequent\n  selectEvent. Usually \\a details is used to transfer which part was clicked, if it is a layerable\n  that has multiple individually selectable parts (like QCPAxis). This way selectEvent doesn't need\n  to do the calculation again to find out which part was actually clicked.\n  \n  \\a selectionStateChanged is an output parameter. If the pointer is non-null, this function must\n  set the value either to true or false, depending on whether the selection state of this layerable\n  was actually changed. For layerables that only are selectable as a whole and not in parts, this\n  is simple: if \\a additive is true, \\a selectionStateChanged must also be set to true, because the\n  selection toggles. If \\a additive is false, \\a selectionStateChanged is only set to true, if the\n  layerable was previously unselected and now is switched to the selected state.\n  \n  \\see selectTest, deselectEvent\n*/\nvoid QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)\n{\n  Q_UNUSED(event)\n  Q_UNUSED(additive)\n  Q_UNUSED(details)\n  Q_UNUSED(selectionStateChanged)\n}\n\n/*! \\internal\n  \n  This event is called when the layerable shall be deselected, either as consequence of a user\n  interaction or a call to \\ref QCustomPlot::deselectAll. Subclasses should react to it by\n  unsetting their selection appropriately.\n  \n  just as in \\ref selectEvent, the output parameter \\a selectionStateChanged (if non-null), must\n  return true or false when the selection state of this layerable has changed or not changed,\n  respectively.\n  \n  \\see selectTest, selectEvent\n*/\nvoid QCPLayerable::deselectEvent(bool *selectionStateChanged)\n{\n  Q_UNUSED(selectionStateChanged)\n}\n\n/*!\n  This event gets called when the user presses a mouse button while the cursor is over the\n  layerable. Whether a cursor is over the layerable is decided by a preceding call to \\ref\n  selectTest.\n\n  The current pixel position of the cursor on the QCustomPlot widget is accessible via \\c\n  event->pos(). The parameter \\a details contains layerable-specific details about the hit, which\n  were generated in the previous call to \\ref selectTest. For example, One-dimensional plottables\n  like \\ref QCPGraph or \\ref QCPBars convey the clicked data point in the \\a details parameter, as\n  \\ref QCPDataSelection packed as QVariant. Multi-part objects convey the specific \\c\n  SelectablePart that was hit (e.g. \\ref QCPAxis::SelectablePart in the case of axes).\n\n  QCustomPlot uses an event propagation system that works the same as Qt's system. If your\n  layerable doesn't reimplement the \\ref mousePressEvent or explicitly calls \\c event->ignore() in\n  its reimplementation, the event will be propagated to the next layerable in the stacking order.\n\n  Once a layerable has accepted the \\ref mousePressEvent, it is considered the mouse grabber and\n  will receive all following calls to \\ref mouseMoveEvent or \\ref mouseReleaseEvent for this mouse\n  interaction (a \"mouse interaction\" in this context ends with the release).\n\n  The default implementation does nothing except explicitly ignoring the event with \\c\n  event->ignore().\n\n  \\see mouseMoveEvent, mouseReleaseEvent, mouseDoubleClickEvent, wheelEvent\n*/\nvoid QCPLayerable::mousePressEvent(QMouseEvent *event, const QVariant &details)\n{\n  Q_UNUSED(details)\n  event->ignore();\n}\n\n/*!\n  This event gets called when the user moves the mouse while holding a mouse button, after this\n  layerable has become the mouse grabber by accepting the preceding \\ref mousePressEvent.\n\n  The current pixel position of the cursor on the QCustomPlot widget is accessible via \\c\n  event->pos(). The parameter \\a startPos indicates the position where the initial \\ref\n  mousePressEvent occurred, that started the mouse interaction.\n\n  The default implementation does nothing.\n\n  \\see mousePressEvent, mouseReleaseEvent, mouseDoubleClickEvent, wheelEvent\n*/\nvoid QCPLayerable::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)\n{\n  Q_UNUSED(startPos)\n  event->ignore();\n}\n\n/*!\n  This event gets called when the user releases the mouse button, after this layerable has become\n  the mouse grabber by accepting the preceding \\ref mousePressEvent.\n\n  The current pixel position of the cursor on the QCustomPlot widget is accessible via \\c\n  event->pos(). The parameter \\a startPos indicates the position where the initial \\ref\n  mousePressEvent occurred, that started the mouse interaction.\n\n  The default implementation does nothing.\n\n  \\see mousePressEvent, mouseMoveEvent, mouseDoubleClickEvent, wheelEvent\n*/\nvoid QCPLayerable::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)\n{\n  Q_UNUSED(startPos)\n  event->ignore();\n}\n\n/*!\n  This event gets called when the user presses the mouse button a second time in a double-click,\n  while the cursor is over the layerable. Whether a cursor is over the layerable is decided by a\n  preceding call to \\ref selectTest.\n\n  The \\ref mouseDoubleClickEvent is called instead of the second \\ref mousePressEvent. So in the\n  case of a double-click, the event succession is\n  <i>pressEvent &ndash; releaseEvent &ndash; doubleClickEvent &ndash; releaseEvent</i>.\n\n  The current pixel position of the cursor on the QCustomPlot widget is accessible via \\c\n  event->pos(). The parameter \\a details contains layerable-specific details about the hit, which\n  were generated in the previous call to \\ref selectTest. For example, One-dimensional plottables\n  like \\ref QCPGraph or \\ref QCPBars convey the clicked data point in the \\a details parameter, as\n  \\ref QCPDataSelection packed as QVariant. Multi-part objects convey the specific \\c\n  SelectablePart that was hit (e.g. \\ref QCPAxis::SelectablePart in the case of axes).\n\n  Similarly to \\ref mousePressEvent, once a layerable has accepted the \\ref mouseDoubleClickEvent,\n  it is considered the mouse grabber and will receive all following calls to \\ref mouseMoveEvent\n  and \\ref mouseReleaseEvent for this mouse interaction (a \"mouse interaction\" in this context ends\n  with the release).\n\n  The default implementation does nothing except explicitly ignoring the event with \\c\n  event->ignore().\n\n  \\see mousePressEvent, mouseMoveEvent, mouseReleaseEvent, wheelEvent\n*/\nvoid QCPLayerable::mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details)\n{\n  Q_UNUSED(details)\n  event->ignore();\n}\n\n/*!\n  This event gets called when the user turns the mouse scroll wheel while the cursor is over the\n  layerable. Whether a cursor is over the layerable is decided by a preceding call to \\ref\n  selectTest.\n\n  The current pixel position of the cursor on the QCustomPlot widget is accessible via \\c\n  event->pos().\n\n  The \\c event->angleDelta() indicates how far the mouse wheel was turned, which is usually +/- 120\n  for single rotation steps. However, if the mouse wheel is turned rapidly, multiple steps may\n  accumulate to one event, making the delta larger. On the other hand, if the wheel has very smooth\n  steps or none at all, the delta may be smaller.\n\n  The default implementation does nothing.\n\n  \\see mousePressEvent, mouseMoveEvent, mouseReleaseEvent, mouseDoubleClickEvent\n*/\nvoid QCPLayerable::wheelEvent(QWheelEvent *event)\n{\n  event->ignore();\n}\n/* end of 'src/layer.cpp' */\n\n\n/* including file 'src/axis/range.cpp'      */\n/* modified 2021-03-29T02:30:44, size 12221 */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPRange\n////////////////////////////////////////////////////////////////////////////////////////////////////\n/*! \\class QCPRange\n  \\brief Represents the range an axis is encompassing.\n  \n  contains a \\a lower and \\a upper double value and provides convenience input, output and\n  modification functions.\n  \n  \\see QCPAxis::setRange\n*/\n\n/* start of documentation of inline functions */\n\n/*! \\fn double QCPRange::size() const\n\n  Returns the size of the range, i.e. \\a upper-\\a lower\n*/\n\n/*! \\fn double QCPRange::center() const\n\n  Returns the center of the range, i.e. (\\a upper+\\a lower)*0.5\n*/\n\n/*! \\fn void QCPRange::normalize()\n\n  Makes sure \\a lower is numerically smaller than \\a upper. If this is not the case, the values are\n  swapped.\n*/\n\n/*! \\fn bool QCPRange::contains(double value) const\n\n  Returns true when \\a value lies within or exactly on the borders of the range.\n*/\n\n/*! \\fn QCPRange &QCPRange::operator+=(const double& value)\n\n  Adds \\a value to both boundaries of the range.\n*/\n\n/*! \\fn QCPRange &QCPRange::operator-=(const double& value)\n\n  Subtracts \\a value from both boundaries of the range.\n*/\n\n/*! \\fn QCPRange &QCPRange::operator*=(const double& value)\n\n  Multiplies both boundaries of the range by \\a value.\n*/\n\n/*! \\fn QCPRange &QCPRange::operator/=(const double& value)\n\n  Divides both boundaries of the range by \\a value.\n*/\n\n/* end of documentation of inline functions */\n\n/*!\n  Minimum range size (\\a upper - \\a lower) the range changing functions will accept. Smaller\n  intervals would cause errors due to the 11-bit exponent of double precision numbers,\n  corresponding to a minimum magnitude of roughly 1e-308.\n\n  \\warning Do not use this constant to indicate \"arbitrarily small\" values in plotting logic (as\n  values that will appear in the plot)! It is intended only as a bound to compare against, e.g. to\n  prevent axis ranges from obtaining underflowing ranges.\n\n  \\see validRange, maxRange\n*/\nconst double QCPRange::minRange = 1e-280;\n\n/*!\n  Maximum values (negative and positive) the range will accept in range-changing functions.\n  Larger absolute values would cause errors due to the 11-bit exponent of double precision numbers,\n  corresponding to a maximum magnitude of roughly 1e308.\n\n  \\warning Do not use this constant to indicate \"arbitrarily large\" values in plotting logic (as\n  values that will appear in the plot)! It is intended only as a bound to compare against, e.g. to\n  prevent axis ranges from obtaining overflowing ranges.\n\n  \\see validRange, minRange\n*/\nconst double QCPRange::maxRange = 1e250;\n\n/*!\n  Constructs a range with \\a lower and \\a upper set to zero.\n*/\nQCPRange::QCPRange() :\n  lower(0),\n  upper(0)\n{\n}\n\n/*! \\overload\n\n  Constructs a range with the specified \\a lower and \\a upper values.\n\n  The resulting range will be normalized (see \\ref normalize), so if \\a lower is not numerically\n  smaller than \\a upper, they will be swapped.\n*/\nQCPRange::QCPRange(double lower, double upper) :\n  lower(lower),\n  upper(upper)\n{\n  normalize();\n}\n\n/*! \\overload\n\n  Expands this range such that \\a otherRange is contained in the new range. It is assumed that both\n  this range and \\a otherRange are normalized (see \\ref normalize).\n\n  If this range contains NaN as lower or upper bound, it will be replaced by the respective bound\n  of \\a otherRange.\n\n  If \\a otherRange is already inside the current range, this function does nothing.\n\n  \\see expanded\n*/\nvoid QCPRange::expand(const QCPRange &otherRange)\n{\n  if (lower > otherRange.lower || qIsNaN(lower))\n    lower = otherRange.lower;\n  if (upper < otherRange.upper || qIsNaN(upper))\n    upper = otherRange.upper;\n}\n\n/*! \\overload\n\n  Expands this range such that \\a includeCoord is contained in the new range. It is assumed that\n  this range is normalized (see \\ref normalize).\n\n  If this range contains NaN as lower or upper bound, the respective bound will be set to \\a\n  includeCoord.\n\n  If \\a includeCoord is already inside the current range, this function does nothing.\n\n  \\see expand\n*/\nvoid QCPRange::expand(double includeCoord)\n{\n  if (lower > includeCoord || qIsNaN(lower))\n    lower = includeCoord;\n  if (upper < includeCoord || qIsNaN(upper))\n    upper = includeCoord;\n}\n\n\n/*! \\overload\n\n  Returns an expanded range that contains this and \\a otherRange. It is assumed that both this\n  range and \\a otherRange are normalized (see \\ref normalize).\n\n  If this range contains NaN as lower or upper bound, the returned range's bound will be taken from\n  \\a otherRange.\n\n  \\see expand\n*/\nQCPRange QCPRange::expanded(const QCPRange &otherRange) const\n{\n  QCPRange result = *this;\n  result.expand(otherRange);\n  return result;\n}\n\n/*! \\overload\n\n  Returns an expanded range that includes the specified \\a includeCoord. It is assumed that this\n  range is normalized (see \\ref normalize).\n\n  If this range contains NaN as lower or upper bound, the returned range's bound will be set to \\a\n  includeCoord.\n\n  \\see expand\n*/\nQCPRange QCPRange::expanded(double includeCoord) const\n{\n  QCPRange result = *this;\n  result.expand(includeCoord);\n  return result;\n}\n\n/*!\n  Returns this range, possibly modified to not exceed the bounds provided as \\a lowerBound and \\a\n  upperBound. If possible, the size of the current range is preserved in the process.\n  \n  If the range shall only be bounded at the lower side, you can set \\a upperBound to \\ref\n  QCPRange::maxRange. If it shall only be bounded at the upper side, set \\a lowerBound to -\\ref\n  QCPRange::maxRange.\n*/\nQCPRange QCPRange::bounded(double lowerBound, double upperBound) const\n{\n  if (lowerBound > upperBound)\n    qSwap(lowerBound, upperBound);\n  \n  QCPRange result(lower, upper);\n  if (result.lower < lowerBound)\n  {\n    result.lower = lowerBound;\n    result.upper = lowerBound + size();\n    if (result.upper > upperBound || qFuzzyCompare(size(), upperBound-lowerBound))\n      result.upper = upperBound;\n  } else if (result.upper > upperBound)\n  {\n    result.upper = upperBound;\n    result.lower = upperBound - size();\n    if (result.lower < lowerBound || qFuzzyCompare(size(), upperBound-lowerBound))\n      result.lower = lowerBound;\n  }\n  \n  return result;\n}\n\n/*!\n  Returns a sanitized version of the range. Sanitized means for logarithmic scales, that\n  the range won't span the positive and negative sign domain, i.e. contain zero. Further\n  \\a lower will always be numerically smaller (or equal) to \\a upper.\n  \n  If the original range does span positive and negative sign domains or contains zero,\n  the returned range will try to approximate the original range as good as possible.\n  If the positive interval of the original range is wider than the negative interval, the\n  returned range will only contain the positive interval, with lower bound set to \\a rangeFac or\n  \\a rangeFac *\\a upper, whichever is closer to zero. Same procedure is used if the negative interval\n  is wider than the positive interval, this time by changing the \\a upper bound.\n*/\nQCPRange QCPRange::sanitizedForLogScale() const\n{\n  double rangeFac = 1e-3;\n  QCPRange sanitizedRange(lower, upper);\n  sanitizedRange.normalize();\n  // can't have range spanning negative and positive values in log plot, so change range to fix it\n  //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1))\n  if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)\n  {\n    // case lower is 0\n    if (rangeFac < sanitizedRange.upper*rangeFac)\n      sanitizedRange.lower = rangeFac;\n    else\n      sanitizedRange.lower = sanitizedRange.upper*rangeFac;\n  } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))\n  else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)\n  {\n    // case upper is 0\n    if (-rangeFac > sanitizedRange.lower*rangeFac)\n      sanitizedRange.upper = -rangeFac;\n    else\n      sanitizedRange.upper = sanitizedRange.lower*rangeFac;\n  } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)\n  {\n    // find out whether negative or positive interval is wider to decide which sign domain will be chosen\n    if (-sanitizedRange.lower > sanitizedRange.upper)\n    {\n      // negative is wider, do same as in case upper is 0\n      if (-rangeFac > sanitizedRange.lower*rangeFac)\n        sanitizedRange.upper = -rangeFac;\n      else\n        sanitizedRange.upper = sanitizedRange.lower*rangeFac;\n    } else\n    {\n      // positive is wider, do same as in case lower is 0\n      if (rangeFac < sanitizedRange.upper*rangeFac)\n        sanitizedRange.lower = rangeFac;\n      else\n        sanitizedRange.lower = sanitizedRange.upper*rangeFac;\n    }\n  }\n  // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper<lower\n  return sanitizedRange;\n}\n\n/*!\n  Returns a sanitized version of the range. Sanitized means for linear scales, that\n  \\a lower will always be numerically smaller (or equal) to \\a upper.\n*/\nQCPRange QCPRange::sanitizedForLinScale() const\n{\n  QCPRange sanitizedRange(lower, upper);\n  sanitizedRange.normalize();\n  return sanitizedRange;\n}\n\n/*!\n  Checks, whether the specified range is within valid bounds, which are defined\n  as QCPRange::maxRange and QCPRange::minRange.\n  A valid range means:\n  \\li range bounds within -maxRange and maxRange\n  \\li range size above minRange\n  \\li range size below maxRange\n*/\nbool QCPRange::validRange(double lower, double upper)\n{\n  return (lower > -maxRange &&\n          upper < maxRange &&\n          qAbs(lower-upper) > minRange &&\n          qAbs(lower-upper) < maxRange &&\n          !(lower > 0 && qIsInf(upper/lower)) &&\n          !(upper < 0 && qIsInf(lower/upper)));\n}\n\n/*!\n  \\overload\n  Checks, whether the specified range is within valid bounds, which are defined\n  as QCPRange::maxRange and QCPRange::minRange.\n  A valid range means:\n  \\li range bounds within -maxRange and maxRange\n  \\li range size above minRange\n  \\li range size below maxRange\n*/\nbool QCPRange::validRange(const QCPRange &range)\n{\n  return (range.lower > -maxRange &&\n          range.upper < maxRange &&\n          qAbs(range.lower-range.upper) > minRange &&\n          qAbs(range.lower-range.upper) < maxRange &&\n          !(range.lower > 0 && qIsInf(range.upper/range.lower)) &&\n          !(range.upper < 0 && qIsInf(range.lower/range.upper)));\n}\n/* end of 'src/axis/range.cpp' */\n\n\n/* including file 'src/selection.cpp'       */\n/* modified 2021-03-29T02:30:44, size 21837 */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPDataRange\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPDataRange\n  \\brief Describes a data range given by begin and end index\n  \n  QCPDataRange holds two integers describing the begin (\\ref setBegin) and end (\\ref setEnd) index\n  of a contiguous set of data points. The \\a end index corresponds to the data point just after the\n  last data point of the data range, like in standard iterators.\n\n  Data Ranges are not bound to a certain plottable, thus they can be freely exchanged, created and\n  modified. If a non-contiguous data set shall be described, the class \\ref QCPDataSelection is\n  used, which holds and manages multiple instances of \\ref QCPDataRange. In most situations, \\ref\n  QCPDataSelection is thus used.\n  \n  Both \\ref QCPDataRange and \\ref QCPDataSelection offer convenience methods to work with them,\n  e.g. \\ref bounded, \\ref expanded, \\ref intersects, \\ref intersection, \\ref adjusted, \\ref\n  contains. Further, addition and subtraction operators (defined in \\ref QCPDataSelection) can be\n  used to join/subtract data ranges and data selections (or mixtures), to retrieve a corresponding\n  \\ref QCPDataSelection.\n  \n  %QCustomPlot's \\ref dataselection \"data selection mechanism\" is based on \\ref QCPDataSelection and\n  QCPDataRange.\n  \n  \\note Do not confuse \\ref QCPDataRange with \\ref QCPRange. A \\ref QCPRange describes an interval\n  in floating point plot coordinates, e.g. the current axis range.\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn int QCPDataRange::size() const\n  \n  Returns the number of data points described by this data range. This is equal to the end index\n  minus the begin index.\n  \n  \\see length\n*/\n\n/*! \\fn int QCPDataRange::length() const\n  \n  Returns the number of data points described by this data range. Equivalent to \\ref size.\n*/\n\n/*! \\fn void QCPDataRange::setBegin(int begin)\n  \n  Sets the begin of this data range. The \\a begin index points to the first data point that is part\n  of the data range.\n  \n  No checks or corrections are made to ensure the resulting range is valid (\\ref isValid).\n  \n  \\see setEnd\n*/\n\n/*! \\fn void QCPDataRange::setEnd(int end)\n  \n  Sets the end of this data range. The \\a end index points to the data point just after the last\n  data point that is part of the data range.\n  \n  No checks or corrections are made to ensure the resulting range is valid (\\ref isValid).\n  \n  \\see setBegin\n*/\n\n/*! \\fn bool QCPDataRange::isValid() const\n  \n  Returns whether this range is valid. A valid range has a begin index greater or equal to 0, and\n  an end index greater or equal to the begin index.\n  \n  \\note Invalid ranges should be avoided and are never the result of any of QCustomPlot's methods\n  (unless they are themselves fed with invalid ranges). Do not pass invalid ranges to QCustomPlot's\n  methods. The invalid range is not inherently prevented in QCPDataRange, to allow temporary\n  invalid begin/end values while manipulating the range. An invalid range is not necessarily empty\n  (\\ref isEmpty), since its \\ref length can be negative and thus non-zero.\n*/\n\n/*! \\fn bool QCPDataRange::isEmpty() const\n  \n  Returns whether this range is empty, i.e. whether its begin index equals its end index.\n  \n  \\see size, length\n*/\n\n/*! \\fn QCPDataRange QCPDataRange::adjusted(int changeBegin, int changeEnd) const\n  \n  Returns a data range where \\a changeBegin and \\a changeEnd were added to the begin and end\n  indices, respectively.\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Creates an empty QCPDataRange, with begin and end set to 0.\n*/\nQCPDataRange::QCPDataRange() :\n  mBegin(0),\n  mEnd(0)\n{\n}\n\n/*!\n  Creates a QCPDataRange, initialized with the specified \\a begin and \\a end.\n  \n  No checks or corrections are made to ensure the resulting range is valid (\\ref isValid).\n*/\nQCPDataRange::QCPDataRange(int begin, int end) :\n  mBegin(begin),\n  mEnd(end)\n{\n}\n\n/*!\n  Returns a data range that matches this data range, except that parts exceeding \\a other are\n  excluded.\n  \n  This method is very similar to \\ref intersection, with one distinction: If this range and the \\a\n  other range share no intersection, the returned data range will be empty with begin and end set\n  to the respective boundary side of \\a other, at which this range is residing. (\\ref intersection\n  would just return a range with begin and end set to 0.)\n*/\nQCPDataRange QCPDataRange::bounded(const QCPDataRange &other) const\n{\n  QCPDataRange result(intersection(other));\n  if (result.isEmpty()) // no intersection, preserve respective bounding side of otherRange as both begin and end of return value\n  {\n    if (mEnd <= other.mBegin)\n      result = QCPDataRange(other.mBegin, other.mBegin);\n    else\n      result = QCPDataRange(other.mEnd, other.mEnd);\n  }\n  return result;\n}\n\n/*!\n  Returns a data range that contains both this data range as well as \\a other.\n*/\nQCPDataRange QCPDataRange::expanded(const QCPDataRange &other) const\n{\n  return {qMin(mBegin, other.mBegin), qMax(mEnd, other.mEnd)};\n}\n\n/*!\n  Returns the data range which is contained in both this data range and \\a other.\n  \n  This method is very similar to \\ref bounded, with one distinction: If this range and the \\a other\n  range share no intersection, the returned data range will be empty with begin and end set to 0.\n  (\\ref bounded would return a range with begin and end set to one of the boundaries of \\a other,\n  depending on which side this range is on.)\n  \n  \\see QCPDataSelection::intersection\n*/\nQCPDataRange QCPDataRange::intersection(const QCPDataRange &other) const\n{\n  QCPDataRange result(qMax(mBegin, other.mBegin), qMin(mEnd, other.mEnd));\n  if (result.isValid())\n    return result;\n  else\n    return {};\n}\n\n/*!\n  Returns whether this data range and \\a other share common data points.\n  \n  \\see intersection, contains\n*/\nbool QCPDataRange::intersects(const QCPDataRange &other) const\n{\n   return !( (mBegin > other.mBegin && mBegin >= other.mEnd) ||\n             (mEnd <= other.mBegin && mEnd < other.mEnd) );\n}\n\n/*!\n  Returns whether all data points of \\a other are also contained inside this data range.\n  \n  \\see intersects\n*/\nbool QCPDataRange::contains(const QCPDataRange &other) const\n{\n  return mBegin <= other.mBegin && mEnd >= other.mEnd;\n}\n\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPDataSelection\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPDataSelection\n  \\brief Describes a data set by holding multiple QCPDataRange instances\n  \n  QCPDataSelection manages multiple instances of QCPDataRange in order to represent any (possibly\n  disjoint) set of data selection.\n  \n  The data selection can be modified with addition and subtraction operators which take\n  QCPDataSelection and QCPDataRange instances, as well as methods such as \\ref addDataRange and\n  \\ref clear. Read access is provided by \\ref dataRange, \\ref dataRanges, \\ref dataRangeCount, etc.\n  \n  The method \\ref simplify is used to join directly adjacent or even overlapping QCPDataRange\n  instances. QCPDataSelection automatically simplifies when using the addition/subtraction\n  operators. The only case when \\ref simplify is left to the user, is when calling \\ref\n  addDataRange, with the parameter \\a simplify explicitly set to false. This is useful if many data\n  ranges will be added to the selection successively and the overhead for simplifying after each\n  iteration shall be avoided. In this case, you should make sure to call \\ref simplify after\n  completing the operation.\n  \n  Use \\ref enforceType to bring the data selection into a state complying with the constraints for\n  selections defined in \\ref QCP::SelectionType.\n  \n  %QCustomPlot's \\ref dataselection \"data selection mechanism\" is based on QCPDataSelection and\n  QCPDataRange.\n  \n  \\section qcpdataselection-iterating Iterating over a data selection\n  \n  As an example, the following code snippet calculates the average value of a graph's data\n  \\ref QCPAbstractPlottable::selection \"selection\":\n  \n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpdataselection-iterating-1\n  \n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn int QCPDataSelection::dataRangeCount() const\n  \n  Returns the number of ranges that make up the data selection. The ranges can be accessed by \\ref\n  dataRange via their index.\n  \n  \\see dataRange, dataPointCount\n*/\n\n/*! \\fn QList<QCPDataRange> QCPDataSelection::dataRanges() const\n  \n  Returns all data ranges that make up the data selection. If the data selection is simplified (the\n  usual state of the selection, see \\ref simplify), the ranges are sorted by ascending data point\n  index.\n  \n  \\see dataRange\n*/\n\n/*! \\fn bool QCPDataSelection::isEmpty() const\n  \n  Returns true if there are no data ranges, and thus no data points, in this QCPDataSelection\n  instance.\n  \n  \\see dataRangeCount\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Creates an empty QCPDataSelection.\n*/\nQCPDataSelection::QCPDataSelection()\n{\n}\n\n/*!\n  Creates a QCPDataSelection containing the provided \\a range.\n*/\nQCPDataSelection::QCPDataSelection(const QCPDataRange &range)\n{\n  mDataRanges.append(range);\n}\n\n/*!\n  Returns true if this selection is identical (contains the same data ranges with the same begin\n  and end indices) to \\a other.\n\n  Note that both data selections must be in simplified state (the usual state of the selection, see\n  \\ref simplify) for this operator to return correct results.\n*/\nbool QCPDataSelection::operator==(const QCPDataSelection &other) const\n{\n  if (mDataRanges.size() != other.mDataRanges.size())\n    return false;\n  for (int i=0; i<mDataRanges.size(); ++i)\n  {\n    if (mDataRanges.at(i) != other.mDataRanges.at(i))\n      return false;\n  }\n  return true;\n}\n\n/*!\n  Adds the data selection of \\a other to this data selection, and then simplifies this data\n  selection (see \\ref simplify).\n*/\nQCPDataSelection &QCPDataSelection::operator+=(const QCPDataSelection &other)\n{\n  mDataRanges << other.mDataRanges;\n  simplify();\n  return *this;\n}\n\n/*!\n  Adds the data range \\a other to this data selection, and then simplifies this data selection (see\n  \\ref simplify).\n*/\nQCPDataSelection &QCPDataSelection::operator+=(const QCPDataRange &other)\n{\n  addDataRange(other);\n  return *this;\n}\n\n/*!\n  Removes all data point indices that are described by \\a other from this data selection.\n*/\nQCPDataSelection &QCPDataSelection::operator-=(const QCPDataSelection &other)\n{\n  for (int i=0; i<other.dataRangeCount(); ++i)\n    *this -= other.dataRange(i);\n  \n  return *this;\n}\n\n/*!\n  Removes all data point indices that are described by \\a other from this data selection.\n*/\nQCPDataSelection &QCPDataSelection::operator-=(const QCPDataRange &other)\n{\n  if (other.isEmpty() || isEmpty())\n    return *this;\n  \n  simplify();\n  int i=0;\n  while (i < mDataRanges.size())\n  {\n    const int thisBegin = mDataRanges.at(i).begin();\n    const int thisEnd = mDataRanges.at(i).end();\n    if (thisBegin >= other.end())\n      break; // since data ranges are sorted after the simplify() call, no ranges which contain other will come after this\n    \n    if (thisEnd > other.begin()) // ranges which don't fulfill this are entirely before other and can be ignored\n    {\n      if (thisBegin >= other.begin()) // range leading segment is encompassed\n      {\n        if (thisEnd <= other.end()) // range fully encompassed, remove completely\n        {\n          mDataRanges.removeAt(i);\n          continue;\n        } else // only leading segment is encompassed, trim accordingly\n          mDataRanges[i].setBegin(other.end());\n      } else // leading segment is not encompassed\n      {\n        if (thisEnd <= other.end()) // only trailing segment is encompassed, trim accordingly\n        {\n          mDataRanges[i].setEnd(other.begin());\n        } else // other lies inside this range, so split range\n        {\n          mDataRanges[i].setEnd(other.begin());\n          mDataRanges.insert(i+1, QCPDataRange(other.end(), thisEnd));\n          break; // since data ranges are sorted (and don't overlap) after simplify() call, we're done here\n        }\n      }\n    }\n    ++i;\n  }\n  \n  return *this;\n}\n\n/*!\n  Returns the total number of data points contained in all data ranges that make up this data\n  selection.\n*/\nint QCPDataSelection::dataPointCount() const\n{\n  int result = 0;\n  foreach (QCPDataRange dataRange, mDataRanges)\n    result += dataRange.length();\n  return result;\n}\n\n/*!\n  Returns the data range with the specified \\a index.\n  \n  If the data selection is simplified (the usual state of the selection, see \\ref simplify), the\n  ranges are sorted by ascending data point index.\n  \n  \\see dataRangeCount\n*/\nQCPDataRange QCPDataSelection::dataRange(int index) const\n{\n  if (index >= 0 && index < mDataRanges.size())\n  {\n    return mDataRanges.at(index);\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"index out of range:\" << index;\n    return {};\n  }\n}\n\n/*!\n  Returns a \\ref QCPDataRange which spans the entire data selection, including possible\n  intermediate segments which are not part of the original data selection.\n*/\nQCPDataRange QCPDataSelection::span() const\n{\n  if (isEmpty())\n    return {};\n  else\n    return {mDataRanges.first().begin(), mDataRanges.last().end()};\n}\n\n/*!\n  Adds the given \\a dataRange to this data selection. This is equivalent to the += operator but\n  allows disabling immediate simplification by setting \\a simplify to false. This can improve\n  performance if adding a very large amount of data ranges successively. In this case, make sure to\n  call \\ref simplify manually, after the operation.\n*/\nvoid QCPDataSelection::addDataRange(const QCPDataRange &dataRange, bool simplify)\n{\n  mDataRanges.append(dataRange);\n  if (simplify)\n    this->simplify();\n}\n\n/*!\n  Removes all data ranges. The data selection then contains no data points.\n  \n  \\ref isEmpty\n*/\nvoid QCPDataSelection::clear()\n{\n  mDataRanges.clear();\n}\n\n/*!\n  Sorts all data ranges by range begin index in ascending order, and then joins directly adjacent\n  or overlapping ranges. This can reduce the number of individual data ranges in the selection, and\n  prevents possible double-counting when iterating over the data points held by the data ranges.\n\n  This method is automatically called when using the addition/subtraction operators. The only case\n  when \\ref simplify is left to the user, is when calling \\ref addDataRange, with the parameter \\a\n  simplify explicitly set to false.\n*/\nvoid QCPDataSelection::simplify()\n{\n  // remove any empty ranges:\n  for (int i=mDataRanges.size()-1; i>=0; --i)\n  {\n    if (mDataRanges.at(i).isEmpty())\n      mDataRanges.removeAt(i);\n  }\n  if (mDataRanges.isEmpty())\n    return;\n  \n  // sort ranges by starting value, ascending:\n  std::sort(mDataRanges.begin(), mDataRanges.end(), lessThanDataRangeBegin);\n  \n  // join overlapping/contiguous ranges:\n  int i = 1;\n  while (i < mDataRanges.size())\n  {\n    if (mDataRanges.at(i-1).end() >= mDataRanges.at(i).begin()) // range i overlaps/joins with i-1, so expand range i-1 appropriately and remove range i from list\n    {\n      mDataRanges[i-1].setEnd(qMax(mDataRanges.at(i-1).end(), mDataRanges.at(i).end()));\n      mDataRanges.removeAt(i);\n    } else\n      ++i;\n  }\n}\n\n/*!\n  Makes sure this data selection conforms to the specified \\a type selection type. Before the type\n  is enforced, \\ref simplify is called.\n  \n  Depending on \\a type, enforcing means adding new data points that were previously not part of the\n  selection, or removing data points from the selection. If the current selection already conforms\n  to \\a type, the data selection is not changed.\n  \n  \\see QCP::SelectionType\n*/\nvoid QCPDataSelection::enforceType(QCP::SelectionType type)\n{\n  simplify();\n  switch (type)\n  {\n    case QCP::stNone:\n    {\n      mDataRanges.clear();\n      break;\n    }\n    case QCP::stWhole:\n    {\n      // whole selection isn't defined by data range, so don't change anything (is handled in plottable methods)\n      break;\n    }\n    case QCP::stSingleData:\n    {\n      // reduce all data ranges to the single first data point:\n      if (!mDataRanges.isEmpty())\n      {\n        if (mDataRanges.size() > 1)\n          mDataRanges = QList<QCPDataRange>() << mDataRanges.first();\n        if (mDataRanges.first().length() > 1)\n          mDataRanges.first().setEnd(mDataRanges.first().begin()+1);\n      }\n      break;\n    }\n    case QCP::stDataRange:\n    {\n      if (!isEmpty())\n        mDataRanges = QList<QCPDataRange>() << span();\n      break;\n    }\n    case QCP::stMultipleDataRanges:\n    {\n      // this is the selection type that allows all concievable combinations of ranges, so do nothing\n      break;\n    }\n  }\n}\n\n/*!\n  Returns true if the data selection \\a other is contained entirely in this data selection, i.e.\n  all data point indices that are in \\a other are also in this data selection.\n  \n  \\see QCPDataRange::contains\n*/\nbool QCPDataSelection::contains(const QCPDataSelection &other) const\n{\n  if (other.isEmpty()) return false;\n  \n  int otherIndex = 0;\n  int thisIndex = 0;\n  while (thisIndex < mDataRanges.size() && otherIndex < other.mDataRanges.size())\n  {\n    if (mDataRanges.at(thisIndex).contains(other.mDataRanges.at(otherIndex)))\n      ++otherIndex;\n    else\n      ++thisIndex;\n  }\n  return thisIndex < mDataRanges.size(); // if thisIndex ran all the way to the end to find a containing range for the current otherIndex, other is not contained in this\n}\n\n/*!\n  Returns a data selection containing the points which are both in this data selection and in the\n  data range \\a other.\n\n  A common use case is to limit an unknown data selection to the valid range of a data container,\n  using \\ref QCPDataContainer::dataRange as \\a other. One can then safely iterate over the returned\n  data selection without exceeding the data container's bounds.\n*/\nQCPDataSelection QCPDataSelection::intersection(const QCPDataRange &other) const\n{\n  QCPDataSelection result;\n  foreach (QCPDataRange dataRange, mDataRanges)\n    result.addDataRange(dataRange.intersection(other), false);\n  result.simplify();\n  return result;\n}\n\n/*!\n  Returns a data selection containing the points which are both in this data selection and in the\n  data selection \\a other.\n*/\nQCPDataSelection QCPDataSelection::intersection(const QCPDataSelection &other) const\n{\n  QCPDataSelection result;\n  for (int i=0; i<other.dataRangeCount(); ++i)\n    result += intersection(other.dataRange(i));\n  result.simplify();\n  return result;\n}\n\n/*!\n  Returns a data selection which is the exact inverse of this data selection, with \\a outerRange\n  defining the base range on which to invert. If \\a outerRange is smaller than the \\ref span of\n  this data selection, it is expanded accordingly.\n\n  For example, this method can be used to retrieve all unselected segments by setting \\a outerRange\n  to the full data range of the plottable, and calling this method on a data selection holding the\n  selected segments.\n*/\nQCPDataSelection QCPDataSelection::inverse(const QCPDataRange &outerRange) const\n{\n  if (isEmpty())\n    return QCPDataSelection(outerRange);\n  QCPDataRange fullRange = outerRange.expanded(span());\n  \n  QCPDataSelection result;\n  // first unselected segment:\n  if (mDataRanges.first().begin() != fullRange.begin())\n    result.addDataRange(QCPDataRange(fullRange.begin(), mDataRanges.first().begin()), false);\n  // intermediate unselected segments:\n  for (int i=1; i<mDataRanges.size(); ++i)\n    result.addDataRange(QCPDataRange(mDataRanges.at(i-1).end(), mDataRanges.at(i).begin()), false);\n  // last unselected segment:\n  if (mDataRanges.last().end() != fullRange.end())\n    result.addDataRange(QCPDataRange(mDataRanges.last().end(), fullRange.end()), false);\n  result.simplify();\n  return result;\n}\n/* end of 'src/selection.cpp' */\n\n\n/* including file 'src/selectionrect.cpp'  */\n/* modified 2021-03-29T02:30:44, size 9215 */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPSelectionRect\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPSelectionRect\n  \\brief Provides rect/rubber-band data selection and range zoom interaction\n  \n  QCPSelectionRect is used by QCustomPlot when the \\ref QCustomPlot::setSelectionRectMode is not\n  \\ref QCP::srmNone. When the user drags the mouse across the plot, the current selection rect\n  instance (\\ref QCustomPlot::setSelectionRect) is forwarded these events and makes sure an\n  according rect shape is drawn. At the begin, during, and after completion of the interaction, it\n  emits the corresponding signals \\ref started, \\ref changed, \\ref canceled, and \\ref accepted.\n  \n  The QCustomPlot instance connects own slots to the current selection rect instance, in order to\n  react to an accepted selection rect interaction accordingly.\n  \n  \\ref isActive can be used to check whether the selection rect is currently active. An ongoing\n  selection interaction can be cancelled programmatically via calling \\ref cancel at any time.\n  \n  The appearance of the selection rect can be controlled via \\ref setPen and \\ref setBrush.\n\n  If you wish to provide custom behaviour, e.g. a different visual representation of the selection\n  rect (\\ref QCPSelectionRect::draw), you can subclass QCPSelectionRect and pass an instance of\n  your subclass to \\ref QCustomPlot::setSelectionRect.\n*/\n\n/* start of documentation of inline functions */\n\n/*! \\fn bool QCPSelectionRect::isActive() const\n   \n  Returns true if there is currently a selection going on, i.e. the user has started dragging a\n  selection rect, but hasn't released the mouse button yet.\n    \n  \\see cancel\n*/\n\n/* end of documentation of inline functions */\n/* start documentation of signals */\n\n/*! \\fn void QCPSelectionRect::started(QMouseEvent *event);\n   \n  This signal is emitted when a selection rect interaction was initiated, i.e. the user just\n  started dragging the selection rect with the mouse.\n*/\n\n/*! \\fn void QCPSelectionRect::changed(const QRect &rect, QMouseEvent *event);\n  \n  This signal is emitted while the selection rect interaction is ongoing and the \\a rect has\n  changed its size due to the user moving the mouse.\n  \n  Note that \\a rect may have a negative width or height, if the selection is being dragged to the\n  upper or left side of the selection rect origin.\n*/\n\n/*! \\fn void QCPSelectionRect::canceled(const QRect &rect, QInputEvent *event);\n  \n  This signal is emitted when the selection interaction was cancelled. Note that \\a event is \\c\n  nullptr if the selection interaction was cancelled programmatically, by a call to \\ref cancel.\n  \n  The user may cancel the selection interaction by pressing the escape key. In this case, \\a event\n  holds the respective input event.\n  \n  Note that \\a rect may have a negative width or height, if the selection is being dragged to the\n  upper or left side of the selection rect origin.\n*/\n\n/*! \\fn void QCPSelectionRect::accepted(const QRect &rect, QMouseEvent *event);\n  \n  This signal is emitted when the selection interaction was completed by the user releasing the\n  mouse button.\n    \n  Note that \\a rect may have a negative width or height, if the selection is being dragged to the\n  upper or left side of the selection rect origin.\n*/\n\n/* end documentation of signals */\n\n/*!\n  Creates a new QCPSelectionRect instance. To make QCustomPlot use the selection rect instance,\n  pass it to \\ref QCustomPlot::setSelectionRect. \\a parentPlot should be set to the same\n  QCustomPlot widget.\n*/\nQCPSelectionRect::QCPSelectionRect(QCustomPlot *parentPlot) :\n  QCPLayerable(parentPlot),\n  mPen(QBrush(Qt::gray), 0, Qt::DashLine),\n  mBrush(Qt::NoBrush),\n  mActive(false)\n{\n}\n\nQCPSelectionRect::~QCPSelectionRect()\n{\n  cancel();\n}\n\n/*!\n  A convenience function which returns the coordinate range of the provided \\a axis, that this\n  selection rect currently encompasses.\n*/\nQCPRange QCPSelectionRect::range(const QCPAxis *axis) const\n{\n  if (axis)\n  {\n    if (axis->orientation() == Qt::Horizontal)\n      return {axis->pixelToCoord(mRect.left()), axis->pixelToCoord(mRect.left()+mRect.width())};\n    else\n      return {axis->pixelToCoord(mRect.top()+mRect.height()), axis->pixelToCoord(mRect.top())};\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"called with axis zero\";\n    return {};\n  }\n}\n\n/*!\n  Sets the pen that will be used to draw the selection rect outline.\n  \n  \\see setBrush\n*/\nvoid QCPSelectionRect::setPen(const QPen &pen)\n{\n  mPen = pen;\n}\n\n/*!\n  Sets the brush that will be used to fill the selection rect. By default the selection rect is not\n  filled, i.e. \\a brush is <tt>Qt::NoBrush</tt>.\n  \n  \\see setPen\n*/\nvoid QCPSelectionRect::setBrush(const QBrush &brush)\n{\n  mBrush = brush;\n}\n\n/*!\n  If there is currently a selection interaction going on (\\ref isActive), the interaction is\n  canceled. The selection rect will emit the \\ref canceled signal.\n*/\nvoid QCPSelectionRect::cancel()\n{\n  if (mActive)\n  {\n    mActive = false;\n    emit canceled(mRect, nullptr);\n  }\n}\n\n/*! \\internal\n  \n  This method is called by QCustomPlot to indicate that a selection rect interaction was initiated.\n  The default implementation sets the selection rect to active, initializes the selection rect\n  geometry and emits the \\ref started signal.\n*/\nvoid QCPSelectionRect::startSelection(QMouseEvent *event)\n{\n  mActive = true;\n  mRect = QRect(event->pos(), event->pos());\n  emit started(event);\n}\n\n/*! \\internal\n  \n  This method is called by QCustomPlot to indicate that an ongoing selection rect interaction needs\n  to update its geometry. The default implementation updates the rect and emits the \\ref changed\n  signal.\n*/\nvoid QCPSelectionRect::moveSelection(QMouseEvent *event)\n{\n  mRect.setBottomRight(event->pos());\n  emit changed(mRect, event);\n  layer()->replot();\n}\n\n/*! \\internal\n  \n  This method is called by QCustomPlot to indicate that an ongoing selection rect interaction has\n  finished by the user releasing the mouse button. The default implementation deactivates the\n  selection rect and emits the \\ref accepted signal.\n*/\nvoid QCPSelectionRect::endSelection(QMouseEvent *event)\n{\n  mRect.setBottomRight(event->pos());\n  mActive = false;\n  emit accepted(mRect, event);\n}\n\n/*! \\internal\n  \n  This method is called by QCustomPlot when a key has been pressed by the user while the selection\n  rect interaction is active. The default implementation allows to \\ref cancel the interaction by\n  hitting the escape key.\n*/\nvoid QCPSelectionRect::keyPressEvent(QKeyEvent *event)\n{\n  if (event->key() == Qt::Key_Escape && mActive)\n  {\n    mActive = false;\n    emit canceled(mRect, event);\n  }\n}\n\n/* inherits documentation from base class */\nvoid QCPSelectionRect::applyDefaultAntialiasingHint(QCPPainter *painter) const\n{\n  applyAntialiasingHint(painter, mAntialiased, QCP::aeOther);\n}\n\n/*! \\internal\n  \n  If the selection rect is active (\\ref isActive), draws the selection rect defined by \\a mRect.\n  \n  \\seebaseclassmethod\n*/\nvoid QCPSelectionRect::draw(QCPPainter *painter)\n{\n  if (mActive)\n  {\n    painter->setPen(mPen);\n    painter->setBrush(mBrush);\n    painter->drawRect(mRect);\n  }\n}\n/* end of 'src/selectionrect.cpp' */\n\n\n/* including file 'src/layout.cpp'          */\n/* modified 2021-03-29T02:30:44, size 78863 */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPMarginGroup\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPMarginGroup\n  \\brief A margin group allows synchronization of margin sides if working with multiple layout elements.\n  \n  QCPMarginGroup allows you to tie a margin side of two or more layout elements together, such that\n  they will all have the same size, based on the largest required margin in the group.\n  \n  \\n\n  \\image html QCPMarginGroup.png \"Demonstration of QCPMarginGroup\"\n  \\n\n  \n  In certain situations it is desirable that margins at specific sides are synchronized across\n  layout elements. For example, if one QCPAxisRect is below another one in a grid layout, it will\n  provide a cleaner look to the user if the left and right margins of the two axis rects are of the\n  same size. The left axis of the top axis rect will then be at the same horizontal position as the\n  left axis of the lower axis rect, making them appear aligned. The same applies for the right\n  axes. This is what QCPMarginGroup makes possible.\n  \n  To add/remove a specific side of a layout element to/from a margin group, use the \\ref\n  QCPLayoutElement::setMarginGroup method. To completely break apart the margin group, either call\n  \\ref clear, or just delete the margin group.\n  \n  \\section QCPMarginGroup-example Example\n  \n  First create a margin group:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-1\n  Then set this group on the layout element sides:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-2\n  Here, we've used the first two axis rects of the plot and synchronized their left margins with\n  each other and their right margins with each other.\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn QList<QCPLayoutElement*> QCPMarginGroup::elements(QCP::MarginSide side) const\n  \n  Returns a list of all layout elements that have their margin \\a side associated with this margin\n  group.\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Creates a new QCPMarginGroup instance in \\a parentPlot.\n*/\nQCPMarginGroup::QCPMarginGroup(QCustomPlot *parentPlot) :\n  QObject(parentPlot),\n  mParentPlot(parentPlot)\n{\n  mChildren.insert(QCP::msLeft, QList<QCPLayoutElement*>());\n  mChildren.insert(QCP::msRight, QList<QCPLayoutElement*>());\n  mChildren.insert(QCP::msTop, QList<QCPLayoutElement*>());\n  mChildren.insert(QCP::msBottom, QList<QCPLayoutElement*>());\n}\n\nQCPMarginGroup::~QCPMarginGroup()\n{\n  clear();\n}\n\n/*!\n  Returns whether this margin group is empty. If this function returns true, no layout elements use\n  this margin group to synchronize margin sides.\n*/\nbool QCPMarginGroup::isEmpty() const\n{\n  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);\n  while (it.hasNext())\n  {\n    it.next();\n    if (!it.value().isEmpty())\n      return false;\n  }\n  return true;\n}\n\n/*!\n  Clears this margin group. The synchronization of the margin sides that use this margin group is\n  lifted and they will use their individual margin sizes again.\n*/\nvoid QCPMarginGroup::clear()\n{\n  // make all children remove themselves from this margin group:\n  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);\n  while (it.hasNext())\n  {\n    it.next();\n    const QList<QCPLayoutElement*> elements = it.value();\n    for (int i=elements.size()-1; i>=0; --i)\n      elements.at(i)->setMarginGroup(it.key(), nullptr); // removes itself from mChildren via removeChild\n  }\n}\n\n/*! \\internal\n  \n  Returns the synchronized common margin for \\a side. This is the margin value that will be used by\n  the layout element on the respective side, if it is part of this margin group.\n  \n  The common margin is calculated by requesting the automatic margin (\\ref\n  QCPLayoutElement::calculateAutoMargin) of each element associated with \\a side in this margin\n  group, and choosing the largest returned value. (QCPLayoutElement::minimumMargins is taken into\n  account, too.)\n*/\nint QCPMarginGroup::commonMargin(QCP::MarginSide side) const\n{\n  // query all automatic margins of the layout elements in this margin group side and find maximum:\n  int result = 0;\n  foreach (QCPLayoutElement *el, mChildren.value(side))\n  {\n    if (!el->autoMargins().testFlag(side))\n      continue;\n    int m = qMax(el->calculateAutoMargin(side), QCP::getMarginValue(el->minimumMargins(), side));\n    if (m > result)\n      result = m;\n  }\n  return result;\n}\n\n/*! \\internal\n  \n  Adds \\a element to the internal list of child elements, for the margin \\a side.\n  \n  This function does not modify the margin group property of \\a element.\n*/\nvoid QCPMarginGroup::addChild(QCP::MarginSide side, QCPLayoutElement *element)\n{\n  if (!mChildren[side].contains(element))\n    mChildren[side].append(element);\n  else\n    qDebug() << Q_FUNC_INFO << \"element is already child of this margin group side\" << reinterpret_cast<quintptr>(element);\n}\n\n/*! \\internal\n  \n  Removes \\a element from the internal list of child elements, for the margin \\a side.\n  \n  This function does not modify the margin group property of \\a element.\n*/\nvoid QCPMarginGroup::removeChild(QCP::MarginSide side, QCPLayoutElement *element)\n{\n  if (!mChildren[side].removeOne(element))\n    qDebug() << Q_FUNC_INFO << \"element is not child of this margin group side\" << reinterpret_cast<quintptr>(element);\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPLayoutElement\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPLayoutElement\n  \\brief The abstract base class for all objects that form \\ref thelayoutsystem \"the layout system\".\n  \n  This is an abstract base class. As such, it can't be instantiated directly, rather use one of its subclasses.\n  \n  A Layout element is a rectangular object which can be placed in layouts. It has an outer rect\n  (QCPLayoutElement::outerRect) and an inner rect (\\ref QCPLayoutElement::rect). The difference\n  between outer and inner rect is called its margin. The margin can either be set to automatic or\n  manual (\\ref setAutoMargins) on a per-side basis. If a side is set to manual, that margin can be\n  set explicitly with \\ref setMargins and will stay fixed at that value. If it's set to automatic,\n  the layout element subclass will control the value itself (via \\ref calculateAutoMargin).\n  \n  Layout elements can be placed in layouts (base class QCPLayout) like QCPLayoutGrid. The top level\n  layout is reachable via \\ref QCustomPlot::plotLayout, and is a \\ref QCPLayoutGrid. Since \\ref\n  QCPLayout itself derives from \\ref QCPLayoutElement, layouts can be nested.\n  \n  Thus in QCustomPlot one can divide layout elements into two categories: The ones that are\n  invisible by themselves, because they don't draw anything. Their only purpose is to manage the\n  position and size of other layout elements. This category of layout elements usually use\n  QCPLayout as base class. Then there is the category of layout elements which actually draw\n  something. For example, QCPAxisRect, QCPLegend and QCPTextElement are of this category. This does\n  not necessarily mean that the latter category can't have child layout elements. QCPLegend for\n  instance, actually derives from QCPLayoutGrid and the individual legend items are child layout\n  elements in the grid layout.\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn QCPLayout *QCPLayoutElement::layout() const\n  \n  Returns the parent layout of this layout element.\n*/\n\n/*! \\fn QRect QCPLayoutElement::rect() const\n  \n  Returns the inner rect of this layout element. The inner rect is the outer rect (\\ref outerRect, \\ref\n  setOuterRect) shrinked by the margins (\\ref setMargins, \\ref setAutoMargins).\n  \n  In some cases, the area between outer and inner rect is left blank. In other cases the margin\n  area is used to display peripheral graphics while the main content is in the inner rect. This is\n  where automatic margin calculation becomes interesting because it allows the layout element to\n  adapt the margins to the peripheral graphics it wants to draw. For example, \\ref QCPAxisRect\n  draws the axis labels and tick labels in the margin area, thus needs to adjust the margins (if\n  \\ref setAutoMargins is enabled) according to the space required by the labels of the axes.\n  \n  \\see outerRect\n*/\n\n/*! \\fn QRect QCPLayoutElement::outerRect() const\n  \n  Returns the outer rect of this layout element. The outer rect is the inner rect expanded by the\n  margins (\\ref setMargins, \\ref setAutoMargins). The outer rect is used (and set via \\ref\n  setOuterRect) by the parent \\ref QCPLayout to control the size of this layout element.\n  \n  \\see rect\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Creates an instance of QCPLayoutElement and sets default values.\n*/\nQCPLayoutElement::QCPLayoutElement(QCustomPlot *parentPlot) :\n  QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout)\n  mParentLayout(nullptr),\n  mMinimumSize(),\n  mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),\n  mSizeConstraintRect(scrInnerRect),\n  mRect(0, 0, 0, 0),\n  mOuterRect(0, 0, 0, 0),\n  mMargins(0, 0, 0, 0),\n  mMinimumMargins(0, 0, 0, 0),\n  mAutoMargins(QCP::msAll)\n{\n}\n\nQCPLayoutElement::~QCPLayoutElement()\n{\n  setMarginGroup(QCP::msAll, nullptr); // unregister at margin groups, if there are any\n  // unregister at layout:\n  if (qobject_cast<QCPLayout*>(mParentLayout)) // the qobject_cast is just a safeguard in case the layout forgets to call clear() in its dtor and this dtor is called by QObject dtor\n    mParentLayout->take(this);\n}\n\n/*!\n  Sets the outer rect of this layout element. If the layout element is inside a layout, the layout\n  sets the position and size of this layout element using this function.\n  \n  Calling this function externally has no effect, since the layout will overwrite any changes to\n  the outer rect upon the next replot.\n  \n  The layout element will adapt its inner \\ref rect by applying the margins inward to the outer rect.\n  \n  \\see rect\n*/\nvoid QCPLayoutElement::setOuterRect(const QRect &rect)\n{\n  if (mOuterRect != rect)\n  {\n    mOuterRect = rect;\n    mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());\n  }\n}\n\n/*!\n  Sets the margins of this layout element. If \\ref setAutoMargins is disabled for some or all\n  sides, this function is used to manually set the margin on those sides. Sides that are still set\n  to be handled automatically are ignored and may have any value in \\a margins.\n  \n  The margin is the distance between the outer rect (controlled by the parent layout via \\ref\n  setOuterRect) and the inner \\ref rect (which usually contains the main content of this layout\n  element).\n  \n  \\see setAutoMargins\n*/\nvoid QCPLayoutElement::setMargins(const QMargins &margins)\n{\n  if (mMargins != margins)\n  {\n    mMargins = margins;\n    mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());\n  }\n}\n\n/*!\n  If \\ref setAutoMargins is enabled on some or all margins, this function is used to provide\n  minimum values for those margins.\n  \n  The minimum values are not enforced on margin sides that were set to be under manual control via\n  \\ref setAutoMargins.\n  \n  \\see setAutoMargins\n*/\nvoid QCPLayoutElement::setMinimumMargins(const QMargins &margins)\n{\n  if (mMinimumMargins != margins)\n  {\n    mMinimumMargins = margins;\n  }\n}\n\n/*!\n  Sets on which sides the margin shall be calculated automatically. If a side is calculated\n  automatically, a minimum margin value may be provided with \\ref setMinimumMargins. If a side is\n  set to be controlled manually, the value may be specified with \\ref setMargins.\n  \n  Margin sides that are under automatic control may participate in a \\ref QCPMarginGroup (see \\ref\n  setMarginGroup), to synchronize (align) it with other layout elements in the plot.\n  \n  \\see setMinimumMargins, setMargins, QCP::MarginSide\n*/\nvoid QCPLayoutElement::setAutoMargins(QCP::MarginSides sides)\n{\n  mAutoMargins = sides;\n}\n\n/*!\n  Sets the minimum size of this layout element. A parent layout tries to respect the \\a size here\n  by changing row/column sizes in the layout accordingly.\n  \n  If the parent layout size is not sufficient to satisfy all minimum size constraints of its child\n  layout elements, the layout may set a size that is actually smaller than \\a size. QCustomPlot\n  propagates the layout's size constraints to the outside by setting its own minimum QWidget size\n  accordingly, so violations of \\a size should be exceptions.\n  \n  Whether this constraint applies to the inner or the outer rect can be specified with \\ref\n  setSizeConstraintRect (see \\ref rect and \\ref outerRect).\n*/\nvoid QCPLayoutElement::setMinimumSize(const QSize &size)\n{\n  if (mMinimumSize != size)\n  {\n    mMinimumSize = size;\n    if (mParentLayout)\n      mParentLayout->sizeConstraintsChanged();\n  }\n}\n\n/*! \\overload\n  \n  Sets the minimum size of this layout element.\n  \n  Whether this constraint applies to the inner or the outer rect can be specified with \\ref\n  setSizeConstraintRect (see \\ref rect and \\ref outerRect).\n*/\nvoid QCPLayoutElement::setMinimumSize(int width, int height)\n{\n  setMinimumSize(QSize(width, height));\n}\n\n/*!\n  Sets the maximum size of this layout element. A parent layout tries to respect the \\a size here\n  by changing row/column sizes in the layout accordingly.\n  \n  Whether this constraint applies to the inner or the outer rect can be specified with \\ref\n  setSizeConstraintRect (see \\ref rect and \\ref outerRect).\n*/\nvoid QCPLayoutElement::setMaximumSize(const QSize &size)\n{\n  if (mMaximumSize != size)\n  {\n    mMaximumSize = size;\n    if (mParentLayout)\n      mParentLayout->sizeConstraintsChanged();\n  }\n}\n\n/*! \\overload\n  \n  Sets the maximum size of this layout element.\n  \n  Whether this constraint applies to the inner or the outer rect can be specified with \\ref\n  setSizeConstraintRect (see \\ref rect and \\ref outerRect).\n*/\nvoid QCPLayoutElement::setMaximumSize(int width, int height)\n{\n  setMaximumSize(QSize(width, height));\n}\n\n/*!\n  Sets to which rect of a layout element the size constraints apply. Size constraints can be set\n  via \\ref setMinimumSize and \\ref setMaximumSize.\n  \n  The outer rect (\\ref outerRect) includes the margins (e.g. in the case of a QCPAxisRect the axis\n  labels), whereas the inner rect (\\ref rect) does not.\n  \n  \\see setMinimumSize, setMaximumSize\n*/\nvoid QCPLayoutElement::setSizeConstraintRect(SizeConstraintRect constraintRect)\n{\n  if (mSizeConstraintRect != constraintRect)\n  {\n    mSizeConstraintRect = constraintRect;\n    if (mParentLayout)\n      mParentLayout->sizeConstraintsChanged();\n  }\n}\n\n/*!\n  Sets the margin \\a group of the specified margin \\a sides.\n  \n  Margin groups allow synchronizing specified margins across layout elements, see the documentation\n  of \\ref QCPMarginGroup.\n  \n  To unset the margin group of \\a sides, set \\a group to \\c nullptr.\n  \n  Note that margin groups only work for margin sides that are set to automatic (\\ref\n  setAutoMargins).\n  \n  \\see QCP::MarginSide\n*/\nvoid QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)\n{\n  QVector<QCP::MarginSide> sideVector;\n  if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);\n  if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);\n  if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);\n  if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);\n  \n  foreach (QCP::MarginSide side, sideVector)\n  {\n    if (marginGroup(side) != group)\n    {\n      QCPMarginGroup *oldGroup = marginGroup(side);\n      if (oldGroup) // unregister at old group\n        oldGroup->removeChild(side, this);\n      \n      if (!group) // if setting to 0, remove hash entry. Else set hash entry to new group and register there\n      {\n        mMarginGroups.remove(side);\n      } else // setting to a new group\n      {\n        mMarginGroups[side] = group;\n        group->addChild(side, this);\n      }\n    }\n  }\n}\n\n/*!\n  Updates the layout element and sub-elements. This function is automatically called before every\n  replot by the parent layout element. It is called multiple times, once for every \\ref\n  UpdatePhase. The phases are run through in the order of the enum values. For details about what\n  happens at the different phases, see the documentation of \\ref UpdatePhase.\n  \n  Layout elements that have child elements should call the \\ref update method of their child\n  elements, and pass the current \\a phase unchanged.\n  \n  The default implementation executes the automatic margin mechanism in the \\ref upMargins phase.\n  Subclasses should make sure to call the base class implementation.\n*/\nvoid QCPLayoutElement::update(UpdatePhase phase)\n{\n  if (phase == upMargins)\n  {\n    if (mAutoMargins != QCP::msNone)\n    {\n      // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:\n      QMargins newMargins = mMargins;\n      const QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;\n      foreach (QCP::MarginSide side, allMarginSides)\n      {\n        if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically\n        {\n          if (mMarginGroups.contains(side))\n            QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group\n          else\n            QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly\n          // apply minimum margin restrictions:\n          if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))\n            QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));\n        }\n      }\n      setMargins(newMargins);\n    }\n  }\n}\n\n/*!\n  Returns the suggested minimum size this layout element (the \\ref outerRect) may be compressed to,\n  if no manual minimum size is set.\n  \n  if a minimum size (\\ref setMinimumSize) was not set manually, parent layouts use the returned size\n  (usually indirectly through \\ref QCPLayout::getFinalMinimumOuterSize) to determine the minimum\n  allowed size of this layout element.\n\n  A manual minimum size is considered set if it is non-zero.\n  \n  The default implementation simply returns the sum of the horizontal margins for the width and the\n  sum of the vertical margins for the height. Reimplementations may use their detailed knowledge\n  about the layout element's content to provide size hints.\n*/\nQSize QCPLayoutElement::minimumOuterSizeHint() const\n{\n  return {mMargins.left()+mMargins.right(), mMargins.top()+mMargins.bottom()};\n}\n\n/*!\n  Returns the suggested maximum size this layout element (the \\ref outerRect) may be expanded to,\n  if no manual maximum size is set.\n  \n  if a maximum size (\\ref setMaximumSize) was not set manually, parent layouts use the returned\n  size (usually indirectly through \\ref QCPLayout::getFinalMaximumOuterSize) to determine the\n  maximum allowed size of this layout element.\n\n  A manual maximum size is considered set if it is smaller than Qt's \\c QWIDGETSIZE_MAX.\n  \n  The default implementation simply returns \\c QWIDGETSIZE_MAX for both width and height, implying\n  no suggested maximum size. Reimplementations may use their detailed knowledge about the layout\n  element's content to provide size hints.\n*/\nQSize QCPLayoutElement::maximumOuterSizeHint() const\n{\n  return {QWIDGETSIZE_MAX, QWIDGETSIZE_MAX};\n}\n\n/*!\n  Returns a list of all child elements in this layout element. If \\a recursive is true, all\n  sub-child elements are included in the list, too.\n  \n  \\warning There may be \\c nullptr entries in the returned list. For example, QCPLayoutGrid may\n  have empty cells which yield \\c nullptr at the respective index.\n*/\nQList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const\n{\n  Q_UNUSED(recursive)\n  return QList<QCPLayoutElement*>();\n}\n\n/*!\n  Layout elements are sensitive to events inside their outer rect. If \\a pos is within the outer\n  rect, this method returns a value corresponding to 0.99 times the parent plot's selection\n  tolerance. However, layout elements are not selectable by default. So if \\a onlySelectable is\n  true, -1.0 is returned.\n  \n  See \\ref QCPLayerable::selectTest for a general explanation of this virtual method.\n  \n  QCPLayoutElement subclasses may reimplement this method to provide more specific selection test\n  behaviour.\n*/\ndouble QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(details)\n  \n  if (onlySelectable)\n    return -1;\n  \n  if (QRectF(mOuterRect).contains(pos))\n  {\n    if (mParentPlot)\n      return mParentPlot->selectionTolerance()*0.99;\n    else\n    {\n      qDebug() << Q_FUNC_INFO << \"parent plot not defined\";\n      return -1;\n    }\n  } else\n    return -1;\n}\n\n/*! \\internal\n  \n  propagates the parent plot initialization to all child elements, by calling \\ref\n  QCPLayerable::initializeParentPlot on them.\n*/\nvoid QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot)\n{\n  foreach (QCPLayoutElement *el, elements(false))\n  {\n    if (!el->parentPlot())\n      el->initializeParentPlot(parentPlot);\n  }\n}\n\n/*! \\internal\n  \n  Returns the margin size for this \\a side. It is used if automatic margins is enabled for this \\a\n  side (see \\ref setAutoMargins). If a minimum margin was set with \\ref setMinimumMargins, the\n  returned value will not be smaller than the specified minimum margin.\n  \n  The default implementation just returns the respective manual margin (\\ref setMargins) or the\n  minimum margin, whichever is larger.\n*/\nint QCPLayoutElement::calculateAutoMargin(QCP::MarginSide side)\n{\n  return qMax(QCP::getMarginValue(mMargins, side), QCP::getMarginValue(mMinimumMargins, side));\n}\n\n/*! \\internal\n  \n  This virtual method is called when this layout element was moved to a different QCPLayout, or\n  when this layout element has changed its logical position (e.g. row and/or column) within the\n  same QCPLayout. Subclasses may use this to react accordingly.\n  \n  Since this method is called after the completion of the move, you can access the new parent\n  layout via \\ref layout().\n  \n  The default implementation does nothing.\n*/\nvoid QCPLayoutElement::layoutChanged()\n{\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPLayout\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPLayout\n  \\brief The abstract base class for layouts\n  \n  This is an abstract base class for layout elements whose main purpose is to define the position\n  and size of other child layout elements. In most cases, layouts don't draw anything themselves\n  (but there are exceptions to this, e.g. QCPLegend).\n  \n  QCPLayout derives from QCPLayoutElement, and thus can itself be nested in other layouts.\n  \n  QCPLayout introduces a common interface for accessing and manipulating the child elements. Those\n  functions are most notably \\ref elementCount, \\ref elementAt, \\ref takeAt, \\ref take, \\ref\n  simplify, \\ref removeAt, \\ref remove and \\ref clear. Individual subclasses may add more functions\n  to this interface which are more specialized to the form of the layout. For example, \\ref\n  QCPLayoutGrid adds functions that take row and column indices to access cells of the layout grid\n  more conveniently.\n  \n  Since this is an abstract base class, you can't instantiate it directly. Rather use one of its\n  subclasses like QCPLayoutGrid or QCPLayoutInset.\n  \n  For a general introduction to the layout system, see the dedicated documentation page \\ref\n  thelayoutsystem \"The Layout System\".\n*/\n\n/* start documentation of pure virtual functions */\n\n/*! \\fn virtual int QCPLayout::elementCount() const = 0\n  \n  Returns the number of elements/cells in the layout.\n  \n  \\see elements, elementAt\n*/\n\n/*! \\fn virtual QCPLayoutElement* QCPLayout::elementAt(int index) const = 0\n  \n  Returns the element in the cell with the given \\a index. If \\a index is invalid, returns \\c\n  nullptr.\n  \n  Note that even if \\a index is valid, the respective cell may be empty in some layouts (e.g.\n  QCPLayoutGrid), so this function may return \\c nullptr in those cases. You may use this function\n  to check whether a cell is empty or not.\n  \n  \\see elements, elementCount, takeAt\n*/\n\n/*! \\fn virtual QCPLayoutElement* QCPLayout::takeAt(int index) = 0\n  \n  Removes the element with the given \\a index from the layout and returns it.\n  \n  If the \\a index is invalid or the cell with that index is empty, returns \\c nullptr.\n  \n  Note that some layouts don't remove the respective cell right away but leave an empty cell after\n  successful removal of the layout element. To collapse empty cells, use \\ref simplify.\n  \n  \\see elementAt, take\n*/\n\n/*! \\fn virtual bool QCPLayout::take(QCPLayoutElement* element) = 0\n  \n  Removes the specified \\a element from the layout and returns true on success.\n  \n  If the \\a element isn't in this layout, returns false.\n  \n  Note that some layouts don't remove the respective cell right away but leave an empty cell after\n  successful removal of the layout element. To collapse empty cells, use \\ref simplify.\n  \n  \\see takeAt\n*/\n\n/* end documentation of pure virtual functions */\n\n/*!\n  Creates an instance of QCPLayout and sets default values. Note that since QCPLayout\n  is an abstract base class, it can't be instantiated directly.\n*/\nQCPLayout::QCPLayout()\n{\n}\n\n/*!\n  If \\a phase is \\ref upLayout, calls \\ref updateLayout, which subclasses may reimplement to\n  reposition and resize their cells.\n  \n  Finally, the call is propagated down to all child \\ref QCPLayoutElement \"QCPLayoutElements\".\n  \n  For details about this method and the update phases, see the documentation of \\ref\n  QCPLayoutElement::update.\n*/\nvoid QCPLayout::update(UpdatePhase phase)\n{\n  QCPLayoutElement::update(phase);\n  \n  // set child element rects according to layout:\n  if (phase == upLayout)\n    updateLayout();\n  \n  // propagate update call to child elements:\n  const int elCount = elementCount();\n  for (int i=0; i<elCount; ++i)\n  {\n    if (QCPLayoutElement *el = elementAt(i))\n      el->update(phase);\n  }\n}\n\n/* inherits documentation from base class */\nQList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const\n{\n  const int c = elementCount();\n  QList<QCPLayoutElement*> result;\n#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)\n  result.reserve(c);\n#endif\n  for (int i=0; i<c; ++i)\n    result.append(elementAt(i));\n  if (recursive)\n  {\n    for (int i=0; i<c; ++i)\n    {\n      if (result.at(i))\n        result << result.at(i)->elements(recursive);\n    }\n  }\n  return result;\n}\n\n/*!\n  Simplifies the layout by collapsing empty cells. The exact behavior depends on subclasses, the\n  default implementation does nothing.\n  \n  Not all layouts need simplification. For example, QCPLayoutInset doesn't use explicit\n  simplification while QCPLayoutGrid does.\n*/\nvoid QCPLayout::simplify()\n{\n}\n\n/*!\n  Removes and deletes the element at the provided \\a index. Returns true on success. If \\a index is\n  invalid or points to an empty cell, returns false.\n  \n  This function internally uses \\ref takeAt to remove the element from the layout and then deletes\n  the returned element. Note that some layouts don't remove the respective cell right away but leave an\n  empty cell after successful removal of the layout element. To collapse empty cells, use \\ref\n  simplify.\n  \n  \\see remove, takeAt\n*/\nbool QCPLayout::removeAt(int index)\n{\n  if (QCPLayoutElement *el = takeAt(index))\n  {\n    delete el;\n    return true;\n  } else\n    return false;\n}\n\n/*!\n  Removes and deletes the provided \\a element. Returns true on success. If \\a element is not in the\n  layout, returns false.\n  \n  This function internally uses \\ref takeAt to remove the element from the layout and then deletes\n  the element. Note that some layouts don't remove the respective cell right away but leave an\n  empty cell after successful removal of the layout element. To collapse empty cells, use \\ref\n  simplify.\n  \n  \\see removeAt, take\n*/\nbool QCPLayout::remove(QCPLayoutElement *element)\n{\n  if (take(element))\n  {\n    delete element;\n    return true;\n  } else\n    return false;\n}\n\n/*!\n  Removes and deletes all layout elements in this layout. Finally calls \\ref simplify to make sure\n  all empty cells are collapsed.\n  \n  \\see remove, removeAt\n*/\nvoid QCPLayout::clear()\n{\n  for (int i=elementCount()-1; i>=0; --i)\n  {\n    if (elementAt(i))\n      removeAt(i);\n  }\n  simplify();\n}\n\n/*!\n  Subclasses call this method to report changed (minimum/maximum) size constraints.\n  \n  If the parent of this layout is again a QCPLayout, forwards the call to the parent's \\ref\n  sizeConstraintsChanged. If the parent is a QWidget (i.e. is the \\ref QCustomPlot::plotLayout of\n  QCustomPlot), calls QWidget::updateGeometry, so if the QCustomPlot widget is inside a Qt QLayout,\n  it may update itself and resize cells accordingly.\n*/\nvoid QCPLayout::sizeConstraintsChanged() const\n{\n  if (QWidget *w = qobject_cast<QWidget*>(parent()))\n    w->updateGeometry();\n  else if (QCPLayout *l = qobject_cast<QCPLayout*>(parent()))\n    l->sizeConstraintsChanged();\n}\n\n/*! \\internal\n  \n  Subclasses reimplement this method to update the position and sizes of the child elements/cells\n  via calling their \\ref QCPLayoutElement::setOuterRect. The default implementation does nothing.\n  \n  The geometry used as a reference is the inner \\ref rect of this layout. Child elements should stay\n  within that rect.\n  \n  \\ref getSectionSizes may help with the reimplementation of this function.\n  \n  \\see update\n*/\nvoid QCPLayout::updateLayout()\n{\n}\n\n\n/*! \\internal\n  \n  Associates \\a el with this layout. This is done by setting the \\ref QCPLayoutElement::layout, the\n  \\ref QCPLayerable::parentLayerable and the QObject parent to this layout.\n  \n  Further, if \\a el didn't previously have a parent plot, calls \\ref\n  QCPLayerable::initializeParentPlot on \\a el to set the paret plot.\n  \n  This method is used by subclass specific methods that add elements to the layout. Note that this\n  method only changes properties in \\a el. The removal from the old layout and the insertion into\n  the new layout must be done additionally.\n*/\nvoid QCPLayout::adoptElement(QCPLayoutElement *el)\n{\n  if (el)\n  {\n    el->mParentLayout = this;\n    el->setParentLayerable(this);\n    el->setParent(this);\n    if (!el->parentPlot())\n      el->initializeParentPlot(mParentPlot);\n    el->layoutChanged();\n  } else\n    qDebug() << Q_FUNC_INFO << \"Null element passed\";\n}\n\n/*! \\internal\n  \n  Disassociates \\a el from this layout. This is done by setting the \\ref QCPLayoutElement::layout\n  and the \\ref QCPLayerable::parentLayerable to zero. The QObject parent is set to the parent\n  QCustomPlot.\n  \n  This method is used by subclass specific methods that remove elements from the layout (e.g. \\ref\n  take or \\ref takeAt). Note that this method only changes properties in \\a el. The removal from\n  the old layout must be done additionally.\n*/\nvoid QCPLayout::releaseElement(QCPLayoutElement *el)\n{\n  if (el)\n  {\n    el->mParentLayout = nullptr;\n    el->setParentLayerable(nullptr);\n    el->setParent(mParentPlot);\n    // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot\n  } else\n    qDebug() << Q_FUNC_INFO << \"Null element passed\";\n}\n\n/*! \\internal\n  \n  This is a helper function for the implementation of \\ref updateLayout in subclasses.\n  \n  It calculates the sizes of one-dimensional sections with provided constraints on maximum section\n  sizes, minimum section sizes, relative stretch factors and the final total size of all sections.\n  \n  The QVector entries refer to the sections. Thus all QVectors must have the same size.\n  \n  \\a maxSizes gives the maximum allowed size of each section. If there shall be no maximum size\n  imposed, set all vector values to Qt's QWIDGETSIZE_MAX.\n  \n  \\a minSizes gives the minimum allowed size of each section. If there shall be no minimum size\n  imposed, set all vector values to zero. If the \\a minSizes entries add up to a value greater than\n  \\a totalSize, sections will be scaled smaller than the proposed minimum sizes. (In other words,\n  not exceeding the allowed total size is taken to be more important than not going below minimum\n  section sizes.)\n  \n  \\a stretchFactors give the relative proportions of the sections to each other. If all sections\n  shall be scaled equally, set all values equal. If the first section shall be double the size of\n  each individual other section, set the first number of \\a stretchFactors to double the value of\n  the other individual values (e.g. {2, 1, 1, 1}).\n  \n  \\a totalSize is the value that the final section sizes will add up to. Due to rounding, the\n  actual sum may differ slightly. If you want the section sizes to sum up to exactly that value,\n  you could distribute the remaining difference on the sections.\n  \n  The return value is a QVector containing the section sizes.\n*/\nQVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const\n{\n  if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size())\n  {\n    qDebug() << Q_FUNC_INFO << \"Passed vector sizes aren't equal:\" << maxSizes << minSizes << stretchFactors;\n    return QVector<int>();\n  }\n  if (stretchFactors.isEmpty())\n    return QVector<int>();\n  int sectionCount = stretchFactors.size();\n  QVector<double> sectionSizes(sectionCount);\n  // if provided total size is forced smaller than total minimum size, ignore minimum sizes (squeeze sections):\n  int minSizeSum = 0;\n  for (int i=0; i<sectionCount; ++i)\n    minSizeSum += minSizes.at(i);\n  if (totalSize < minSizeSum)\n  {\n    // new stretch factors are minimum sizes and minimum sizes are set to zero:\n    for (int i=0; i<sectionCount; ++i)\n    {\n      stretchFactors[i] = minSizes.at(i);\n      minSizes[i] = 0;\n    }\n  }\n  \n  QList<int> minimumLockedSections;\n  QList<int> unfinishedSections;\n  for (int i=0; i<sectionCount; ++i)\n    unfinishedSections.append(i);\n  double freeSize = totalSize;\n  \n  int outerIterations = 0;\n  while (!unfinishedSections.isEmpty() && outerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens\n  {\n    ++outerIterations;\n    int innerIterations = 0;\n    while (!unfinishedSections.isEmpty() && innerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens\n    {\n      ++innerIterations;\n      // find section that hits its maximum next:\n      int nextId = -1;\n      double nextMax = 1e12;\n      foreach (int secId, unfinishedSections)\n      {\n        double hitsMaxAt = (maxSizes.at(secId)-sectionSizes.at(secId))/stretchFactors.at(secId);\n        if (hitsMaxAt < nextMax)\n        {\n          nextMax = hitsMaxAt;\n          nextId = secId;\n        }\n      }\n      // check if that maximum is actually within the bounds of the total size (i.e. can we stretch all remaining sections so far that the found section\n      // actually hits its maximum, without exceeding the total size when we add up all sections)\n      double stretchFactorSum = 0;\n      foreach (int secId, unfinishedSections)\n        stretchFactorSum += stretchFactors.at(secId);\n      double nextMaxLimit = freeSize/stretchFactorSum;\n      if (nextMax < nextMaxLimit) // next maximum is actually hit, move forward to that point and fix the size of that section\n      {\n        foreach (int secId, unfinishedSections)\n        {\n          sectionSizes[secId] += nextMax*stretchFactors.at(secId); // increment all sections\n          freeSize -= nextMax*stretchFactors.at(secId);\n        }\n        unfinishedSections.removeOne(nextId); // exclude the section that is now at maximum from further changes\n      } else // next maximum isn't hit, just distribute rest of free space on remaining sections\n      {\n        foreach (int secId, unfinishedSections)\n          sectionSizes[secId] += nextMaxLimit*stretchFactors.at(secId); // increment all sections\n        unfinishedSections.clear();\n      }\n    }\n    if (innerIterations == sectionCount*2)\n      qDebug() << Q_FUNC_INFO << \"Exceeded maximum expected inner iteration count, layouting aborted. Input was:\" << maxSizes << minSizes << stretchFactors << totalSize;\n    \n    // now check whether the resulting section sizes violate minimum restrictions:\n    bool foundMinimumViolation = false;\n    for (int i=0; i<sectionSizes.size(); ++i)\n    {\n      if (minimumLockedSections.contains(i))\n        continue;\n      if (sectionSizes.at(i) < minSizes.at(i)) // section violates minimum\n      {\n        sectionSizes[i] = minSizes.at(i); // set it to minimum\n        foundMinimumViolation = true; // make sure we repeat the whole optimization process\n        minimumLockedSections.append(i);\n      }\n    }\n    if (foundMinimumViolation)\n    {\n      freeSize = totalSize;\n      for (int i=0; i<sectionCount; ++i)\n      {\n        if (!minimumLockedSections.contains(i)) // only put sections that haven't hit their minimum back into the pool\n          unfinishedSections.append(i);\n        else\n          freeSize -= sectionSizes.at(i); // remove size of minimum locked sections from available space in next round\n      }\n      // reset all section sizes to zero that are in unfinished sections (all others have been set to their minimum):\n      foreach (int secId, unfinishedSections)\n        sectionSizes[secId] = 0;\n    }\n  }\n  if (outerIterations == sectionCount*2)\n    qDebug() << Q_FUNC_INFO << \"Exceeded maximum expected outer iteration count, layouting aborted. Input was:\" << maxSizes << minSizes << stretchFactors << totalSize;\n  \n  QVector<int> result(sectionCount);\n  for (int i=0; i<sectionCount; ++i)\n    result[i] = qRound(sectionSizes.at(i));\n  return result;\n}\n\n/*! \\internal\n  \n  This is a helper function for the implementation of subclasses.\n  \n  It returns the minimum size that should finally be used for the outer rect of the passed layout\n  element \\a el.\n  \n  It takes into account whether a manual minimum size is set (\\ref\n  QCPLayoutElement::setMinimumSize), which size constraint is set (\\ref\n  QCPLayoutElement::setSizeConstraintRect), as well as the minimum size hint, if no manual minimum\n  size was set (\\ref QCPLayoutElement::minimumOuterSizeHint).\n*/\nQSize QCPLayout::getFinalMinimumOuterSize(const QCPLayoutElement *el)\n{\n  QSize minOuterHint = el->minimumOuterSizeHint();\n  QSize minOuter = el->minimumSize(); // depending on sizeConstraitRect this might be with respect to inner rect, so possibly add margins in next four lines (preserving unset minimum of 0)\n  if (minOuter.width() > 0 && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)\n    minOuter.rwidth() += el->margins().left() + el->margins().right();\n  if (minOuter.height() > 0 && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)\n    minOuter.rheight() += el->margins().top() + el->margins().bottom();\n  \n  return {minOuter.width() > 0 ? minOuter.width() : minOuterHint.width(),\n               minOuter.height() > 0 ? minOuter.height() : minOuterHint.height()};\n}\n\n/*! \\internal\n  \n  This is a helper function for the implementation of subclasses.\n  \n  It returns the maximum size that should finally be used for the outer rect of the passed layout\n  element \\a el.\n  \n  It takes into account whether a manual maximum size is set (\\ref\n  QCPLayoutElement::setMaximumSize), which size constraint is set (\\ref\n  QCPLayoutElement::setSizeConstraintRect), as well as the maximum size hint, if no manual maximum\n  size was set (\\ref QCPLayoutElement::maximumOuterSizeHint).\n*/\nQSize QCPLayout::getFinalMaximumOuterSize(const QCPLayoutElement *el)\n{\n  QSize maxOuterHint = el->maximumOuterSizeHint();\n  QSize maxOuter = el->maximumSize(); // depending on sizeConstraitRect this might be with respect to inner rect, so possibly add margins in next four lines (preserving unset maximum of QWIDGETSIZE_MAX)\n  if (maxOuter.width() < QWIDGETSIZE_MAX && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)\n    maxOuter.rwidth() += el->margins().left() + el->margins().right();\n  if (maxOuter.height() < QWIDGETSIZE_MAX && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)\n    maxOuter.rheight() += el->margins().top() + el->margins().bottom();\n  \n  return {maxOuter.width() < QWIDGETSIZE_MAX ? maxOuter.width() : maxOuterHint.width(),\n               maxOuter.height() < QWIDGETSIZE_MAX ? maxOuter.height() : maxOuterHint.height()};\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPLayoutGrid\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPLayoutGrid\n  \\brief A layout that arranges child elements in a grid\n\n  Elements are laid out in a grid with configurable stretch factors (\\ref setColumnStretchFactor,\n  \\ref setRowStretchFactor) and spacing (\\ref setColumnSpacing, \\ref setRowSpacing).\n\n  Elements can be added to cells via \\ref addElement. The grid is expanded if the specified row or\n  column doesn't exist yet. Whether a cell contains a valid layout element can be checked with \\ref\n  hasElement, that element can be retrieved with \\ref element. If rows and columns that only have\n  empty cells shall be removed, call \\ref simplify. Removal of elements is either done by just\n  adding the element to a different layout or by using the QCPLayout interface \\ref take or \\ref\n  remove.\n\n  If you use \\ref addElement(QCPLayoutElement*) without explicit parameters for \\a row and \\a\n  column, the grid layout will choose the position according to the current \\ref setFillOrder and\n  the wrapping (\\ref setWrap).\n\n  Row and column insertion can be performed with \\ref insertRow and \\ref insertColumn.\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn int QCPLayoutGrid::rowCount() const\n\n  Returns the number of rows in the layout.\n\n  \\see columnCount\n*/\n\n/*! \\fn int QCPLayoutGrid::columnCount() const\n\n  Returns the number of columns in the layout.\n\n  \\see rowCount\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Creates an instance of QCPLayoutGrid and sets default values.\n*/\nQCPLayoutGrid::QCPLayoutGrid() :\n  mColumnSpacing(5),\n  mRowSpacing(5),\n  mWrap(0),\n  mFillOrder(foColumnsFirst)\n{\n}\n\nQCPLayoutGrid::~QCPLayoutGrid()\n{\n  // clear all child layout elements. This is important because only the specific layouts know how\n  // to handle removing elements (clear calls virtual removeAt method to do that).\n  clear();\n}\n\n/*!\n  Returns the element in the cell in \\a row and \\a column.\n  \n  Returns \\c nullptr if either the row/column is invalid or if the cell is empty. In those cases, a\n  qDebug message is printed. To check whether a cell exists and isn't empty, use \\ref hasElement.\n  \n  \\see addElement, hasElement\n*/\nQCPLayoutElement *QCPLayoutGrid::element(int row, int column) const\n{\n  if (row >= 0 && row < mElements.size())\n  {\n    if (column >= 0 && column < mElements.first().size())\n    {\n      if (QCPLayoutElement *result = mElements.at(row).at(column))\n        return result;\n      else\n        qDebug() << Q_FUNC_INFO << \"Requested cell is empty. Row:\" << row << \"Column:\" << column;\n    } else\n      qDebug() << Q_FUNC_INFO << \"Invalid column. Row:\" << row << \"Column:\" << column;\n  } else\n    qDebug() << Q_FUNC_INFO << \"Invalid row. Row:\" << row << \"Column:\" << column;\n  return nullptr;\n}\n\n\n/*! \\overload\n\n  Adds the \\a element to cell with \\a row and \\a column. If \\a element is already in a layout, it\n  is first removed from there. If \\a row or \\a column don't exist yet, the layout is expanded\n  accordingly.\n\n  Returns true if the element was added successfully, i.e. if the cell at \\a row and \\a column\n  didn't already have an element.\n\n  Use the overload of this method without explicit row/column index to place the element according\n  to the configured fill order and wrapping settings.\n\n  \\see element, hasElement, take, remove\n*/\nbool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element)\n{\n  if (!hasElement(row, column))\n  {\n    if (element && element->layout()) // remove from old layout first\n      element->layout()->take(element);\n    expandTo(row+1, column+1);\n    mElements[row][column] = element;\n    if (element)\n      adoptElement(element);\n    return true;\n  } else\n    qDebug() << Q_FUNC_INFO << \"There is already an element in the specified row/column:\" << row << column;\n  return false;\n}\n\n/*! \\overload\n\n  Adds the \\a element to the next empty cell according to the current fill order (\\ref\n  setFillOrder) and wrapping (\\ref setWrap). If \\a element is already in a layout, it is first\n  removed from there. If necessary, the layout is expanded to hold the new element.\n\n  Returns true if the element was added successfully.\n\n  \\see setFillOrder, setWrap, element, hasElement, take, remove\n*/\nbool QCPLayoutGrid::addElement(QCPLayoutElement *element)\n{\n  int rowIndex = 0;\n  int colIndex = 0;\n  if (mFillOrder == foColumnsFirst)\n  {\n    while (hasElement(rowIndex, colIndex))\n    {\n      ++colIndex;\n      if (colIndex >= mWrap && mWrap > 0)\n      {\n        colIndex = 0;\n        ++rowIndex;\n      }\n    }\n  } else\n  {\n    while (hasElement(rowIndex, colIndex))\n    {\n      ++rowIndex;\n      if (rowIndex >= mWrap && mWrap > 0)\n      {\n        rowIndex = 0;\n        ++colIndex;\n      }\n    }\n  }\n  return addElement(rowIndex, colIndex, element);\n}\n\n/*!\n  Returns whether the cell at \\a row and \\a column exists and contains a valid element, i.e. isn't\n  empty.\n  \n  \\see element\n*/\nbool QCPLayoutGrid::hasElement(int row, int column)\n{\n  if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())\n    return mElements.at(row).at(column);\n  else\n    return false;\n}\n\n/*!\n  Sets the stretch \\a factor of \\a column.\n  \n  Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond\n  their minimum and maximum widths/heights, regardless of the stretch factor. (see \\ref\n  QCPLayoutElement::setMinimumSize, \\ref QCPLayoutElement::setMaximumSize, \\ref\n  QCPLayoutElement::setSizeConstraintRect.)\n  \n  The default stretch factor of newly created rows/columns is 1.\n  \n  \\see setColumnStretchFactors, setRowStretchFactor\n*/\nvoid QCPLayoutGrid::setColumnStretchFactor(int column, double factor)\n{\n  if (column >= 0 && column < columnCount())\n  {\n    if (factor > 0)\n      mColumnStretchFactors[column] = factor;\n    else\n      qDebug() << Q_FUNC_INFO << \"Invalid stretch factor, must be positive:\" << factor;\n  } else\n    qDebug() << Q_FUNC_INFO << \"Invalid column:\" << column;\n}\n\n/*!\n  Sets the stretch \\a factors of all columns. \\a factors must have the size \\ref columnCount.\n  \n  Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond\n  their minimum and maximum widths/heights, regardless of the stretch factor. (see \\ref\n  QCPLayoutElement::setMinimumSize, \\ref QCPLayoutElement::setMaximumSize, \\ref\n  QCPLayoutElement::setSizeConstraintRect.)\n  \n  The default stretch factor of newly created rows/columns is 1.\n  \n  \\see setColumnStretchFactor, setRowStretchFactors\n*/\nvoid QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors)\n{\n  if (factors.size() == mColumnStretchFactors.size())\n  {\n    mColumnStretchFactors = factors;\n    for (int i=0; i<mColumnStretchFactors.size(); ++i)\n    {\n      if (mColumnStretchFactors.at(i) <= 0)\n      {\n        qDebug() << Q_FUNC_INFO << \"Invalid stretch factor, must be positive:\" << mColumnStretchFactors.at(i);\n        mColumnStretchFactors[i] = 1;\n      }\n    }\n  } else\n    qDebug() << Q_FUNC_INFO << \"Column count not equal to passed stretch factor count:\" << factors;\n}\n\n/*!\n  Sets the stretch \\a factor of \\a row.\n  \n  Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond\n  their minimum and maximum widths/heights, regardless of the stretch factor. (see \\ref\n  QCPLayoutElement::setMinimumSize, \\ref QCPLayoutElement::setMaximumSize, \\ref\n  QCPLayoutElement::setSizeConstraintRect.)\n  \n  The default stretch factor of newly created rows/columns is 1.\n  \n  \\see setColumnStretchFactors, setRowStretchFactor\n*/\nvoid QCPLayoutGrid::setRowStretchFactor(int row, double factor)\n{\n  if (row >= 0 && row < rowCount())\n  {\n    if (factor > 0)\n      mRowStretchFactors[row] = factor;\n    else\n      qDebug() << Q_FUNC_INFO << \"Invalid stretch factor, must be positive:\" << factor;\n  } else\n    qDebug() << Q_FUNC_INFO << \"Invalid row:\" << row;\n}\n\n/*!\n  Sets the stretch \\a factors of all rows. \\a factors must have the size \\ref rowCount.\n  \n  Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond\n  their minimum and maximum widths/heights, regardless of the stretch factor. (see \\ref\n  QCPLayoutElement::setMinimumSize, \\ref QCPLayoutElement::setMaximumSize, \\ref\n  QCPLayoutElement::setSizeConstraintRect.)\n  \n  The default stretch factor of newly created rows/columns is 1.\n  \n  \\see setRowStretchFactor, setColumnStretchFactors\n*/\nvoid QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors)\n{\n  if (factors.size() == mRowStretchFactors.size())\n  {\n    mRowStretchFactors = factors;\n    for (int i=0; i<mRowStretchFactors.size(); ++i)\n    {\n      if (mRowStretchFactors.at(i) <= 0)\n      {\n        qDebug() << Q_FUNC_INFO << \"Invalid stretch factor, must be positive:\" << mRowStretchFactors.at(i);\n        mRowStretchFactors[i] = 1;\n      }\n    }\n  } else\n    qDebug() << Q_FUNC_INFO << \"Row count not equal to passed stretch factor count:\" << factors;\n}\n\n/*!\n  Sets the gap that is left blank between columns to \\a pixels.\n  \n  \\see setRowSpacing\n*/\nvoid QCPLayoutGrid::setColumnSpacing(int pixels)\n{\n  mColumnSpacing = pixels;\n}\n\n/*!\n  Sets the gap that is left blank between rows to \\a pixels.\n  \n  \\see setColumnSpacing\n*/\nvoid QCPLayoutGrid::setRowSpacing(int pixels)\n{\n  mRowSpacing = pixels;\n}\n\n/*!\n  Sets the maximum number of columns or rows that are used, before new elements added with \\ref\n  addElement(QCPLayoutElement*) will start to fill the next row or column, respectively. It depends\n  on \\ref setFillOrder, whether rows or columns are wrapped.\n\n  If \\a count is set to zero, no wrapping will ever occur.\n  \n  If you wish to re-wrap the elements currently in the layout, call \\ref setFillOrder with \\a\n  rearrange set to true (the actual fill order doesn't need to be changed for the rearranging to be\n  done).\n\n  Note that the method \\ref addElement(int row, int column, QCPLayoutElement *element) with\n  explicitly stated row and column is not subject to wrapping and can place elements even beyond\n  the specified wrapping point.\n\n  \\see setFillOrder\n*/\nvoid QCPLayoutGrid::setWrap(int count)\n{\n  mWrap = qMax(0, count);\n}\n\n/*!\n  Sets the filling order and wrapping behaviour that is used when adding new elements with the\n  method \\ref addElement(QCPLayoutElement*).\n\n  The specified \\a order defines whether rows or columns are filled first. Using \\ref setWrap, you\n  can control at which row/column count wrapping into the next column/row will occur. If you set it\n  to zero, no wrapping will ever occur. Changing the fill order also changes the meaning of the\n  linear index used e.g. in \\ref elementAt and \\ref takeAt. The default fill order for \\ref\n  QCPLayoutGrid is \\ref foColumnsFirst.\n\n  If you want to have all current elements arranged in the new order, set \\a rearrange to true. The\n  elements will be rearranged in a way that tries to preserve their linear index. However, empty\n  cells are skipped during build-up of the new cell order, which shifts the succeeding element's\n  index. The rearranging is performed even if the specified \\a order is already the current fill\n  order. Thus this method can be used to re-wrap the current elements.\n\n  If \\a rearrange is false, the current element arrangement is not changed, which means the\n  linear indexes change (because the linear index is dependent on the fill order).\n\n  Note that the method \\ref addElement(int row, int column, QCPLayoutElement *element) with\n  explicitly stated row and column is not subject to wrapping and can place elements even beyond\n  the specified wrapping point.\n\n  \\see setWrap, addElement(QCPLayoutElement*)\n*/\nvoid QCPLayoutGrid::setFillOrder(FillOrder order, bool rearrange)\n{\n  // if rearranging, take all elements via linear index of old fill order:\n  const int elCount = elementCount();\n  QVector<QCPLayoutElement*> tempElements;\n  if (rearrange)\n  {\n    tempElements.reserve(elCount);\n    for (int i=0; i<elCount; ++i)\n    {\n      if (elementAt(i))\n        tempElements.append(takeAt(i));\n    }\n    simplify();\n  }\n  // change fill order as requested:\n  mFillOrder = order;\n  // if rearranging, re-insert via linear index according to new fill order:\n  if (rearrange)\n  {\n    foreach (QCPLayoutElement *tempElement, tempElements)\n      addElement(tempElement);\n  }\n}\n\n/*!\n  Expands the layout to have \\a newRowCount rows and \\a newColumnCount columns. So the last valid\n  row index will be \\a newRowCount-1, the last valid column index will be \\a newColumnCount-1.\n  \n  If the current column/row count is already larger or equal to \\a newColumnCount/\\a newRowCount,\n  this function does nothing in that dimension.\n  \n  Newly created cells are empty, new rows and columns have the stretch factor 1.\n  \n  Note that upon a call to \\ref addElement, the layout is expanded automatically to contain the\n  specified row and column, using this function.\n  \n  \\see simplify\n*/\nvoid QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount)\n{\n  // add rows as necessary:\n  while (rowCount() < newRowCount)\n  {\n    mElements.append(QList<QCPLayoutElement*>());\n    mRowStretchFactors.append(1);\n  }\n  // go through rows and expand columns as necessary:\n  int newColCount = qMax(columnCount(), newColumnCount);\n  for (int i=0; i<rowCount(); ++i)\n  {\n    while (mElements.at(i).size() < newColCount)\n      mElements[i].append(nullptr);\n  }\n  while (mColumnStretchFactors.size() < newColCount)\n    mColumnStretchFactors.append(1);\n}\n\n/*!\n  Inserts a new row with empty cells at the row index \\a newIndex. Valid values for \\a newIndex\n  range from 0 (inserts a row at the top) to \\a rowCount (appends a row at the bottom).\n  \n  \\see insertColumn\n*/\nvoid QCPLayoutGrid::insertRow(int newIndex)\n{\n  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell\n  {\n    expandTo(1, 1);\n    return;\n  }\n  \n  if (newIndex < 0)\n    newIndex = 0;\n  if (newIndex > rowCount())\n    newIndex = rowCount();\n  \n  mRowStretchFactors.insert(newIndex, 1);\n  QList<QCPLayoutElement*> newRow;\n  for (int col=0; col<columnCount(); ++col)\n    newRow.append(nullptr);\n  mElements.insert(newIndex, newRow);\n}\n\n/*!\n  Inserts a new column with empty cells at the column index \\a newIndex. Valid values for \\a\n  newIndex range from 0 (inserts a column at the left) to \\a columnCount (appends a column at the\n  right).\n  \n  \\see insertRow\n*/\nvoid QCPLayoutGrid::insertColumn(int newIndex)\n{\n  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell\n  {\n    expandTo(1, 1);\n    return;\n  }\n  \n  if (newIndex < 0)\n    newIndex = 0;\n  if (newIndex > columnCount())\n    newIndex = columnCount();\n  \n  mColumnStretchFactors.insert(newIndex, 1);\n  for (int row=0; row<rowCount(); ++row)\n    mElements[row].insert(newIndex, nullptr);\n}\n\n/*!\n  Converts the given \\a row and \\a column to the linear index used by some methods of \\ref\n  QCPLayoutGrid and \\ref QCPLayout.\n\n  The way the cells are indexed depends on \\ref setFillOrder. If it is \\ref foRowsFirst, the\n  indices increase left to right and then top to bottom. If it is \\ref foColumnsFirst, the indices\n  increase top to bottom and then left to right.\n\n  For the returned index to be valid, \\a row and \\a column must be valid indices themselves, i.e.\n  greater or equal to zero and smaller than the current \\ref rowCount/\\ref columnCount.\n\n  \\see indexToRowCol\n*/\nint QCPLayoutGrid::rowColToIndex(int row, int column) const\n{\n  if (row >= 0 && row < rowCount())\n  {\n    if (column >= 0 && column < columnCount())\n    {\n      switch (mFillOrder)\n      {\n        case foRowsFirst: return column*rowCount() + row;\n        case foColumnsFirst: return row*columnCount() + column;\n      }\n    } else\n      qDebug() << Q_FUNC_INFO << \"row index out of bounds:\" << row;\n  } else\n    qDebug() << Q_FUNC_INFO << \"column index out of bounds:\" << column;\n  return 0;\n}\n\n/*!\n  Converts the linear index to row and column indices and writes the result to \\a row and \\a\n  column.\n\n  The way the cells are indexed depends on \\ref setFillOrder. If it is \\ref foRowsFirst, the\n  indices increase left to right and then top to bottom. If it is \\ref foColumnsFirst, the indices\n  increase top to bottom and then left to right.\n\n  If there are no cells (i.e. column or row count is zero), sets \\a row and \\a column to -1.\n\n  For the retrieved \\a row and \\a column to be valid, the passed \\a index must be valid itself,\n  i.e. greater or equal to zero and smaller than the current \\ref elementCount.\n\n  \\see rowColToIndex\n*/\nvoid QCPLayoutGrid::indexToRowCol(int index, int &row, int &column) const\n{\n  row = -1;\n  column = -1;\n  const int nCols = columnCount();\n  const int nRows = rowCount();\n  if (nCols == 0 || nRows == 0)\n    return;\n  if (index < 0 || index >= elementCount())\n  {\n    qDebug() << Q_FUNC_INFO << \"index out of bounds:\" << index;\n    return;\n  }\n  \n  switch (mFillOrder)\n  {\n    case foRowsFirst:\n    {\n      column = index / nRows;\n      row = index % nRows;\n      break;\n    }\n    case foColumnsFirst:\n    {\n      row = index / nCols;\n      column = index % nCols;\n      break;\n    }\n  }\n}\n\n/* inherits documentation from base class */\nvoid QCPLayoutGrid::updateLayout()\n{\n  QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;\n  getMinimumRowColSizes(&minColWidths, &minRowHeights);\n  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);\n  \n  int totalRowSpacing = (rowCount()-1) * mRowSpacing;\n  int totalColSpacing = (columnCount()-1) * mColumnSpacing;\n  QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing);\n  QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing);\n  \n  // go through cells and set rects accordingly:\n  int yOffset = mRect.top();\n  for (int row=0; row<rowCount(); ++row)\n  {\n    if (row > 0)\n      yOffset += rowHeights.at(row-1)+mRowSpacing;\n    int xOffset = mRect.left();\n    for (int col=0; col<columnCount(); ++col)\n    {\n      if (col > 0)\n        xOffset += colWidths.at(col-1)+mColumnSpacing;\n      if (mElements.at(row).at(col))\n        mElements.at(row).at(col)->setOuterRect(QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));\n    }\n  }\n}\n\n/*!\n  \\seebaseclassmethod\n\n  Note that the association of the linear \\a index to the row/column based cells depends on the\n  current setting of \\ref setFillOrder.\n\n  \\see rowColToIndex\n*/\nQCPLayoutElement *QCPLayoutGrid::elementAt(int index) const\n{\n  if (index >= 0 && index < elementCount())\n  {\n    int row, col;\n    indexToRowCol(index, row, col);\n    return mElements.at(row).at(col);\n  } else\n    return nullptr;\n}\n\n/*!\n  \\seebaseclassmethod\n\n  Note that the association of the linear \\a index to the row/column based cells depends on the\n  current setting of \\ref setFillOrder.\n\n  \\see rowColToIndex\n*/\nQCPLayoutElement *QCPLayoutGrid::takeAt(int index)\n{\n  if (QCPLayoutElement *el = elementAt(index))\n  {\n    releaseElement(el);\n    int row, col;\n    indexToRowCol(index, row, col);\n    mElements[row][col] = nullptr;\n    return el;\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"Attempt to take invalid index:\" << index;\n    return nullptr;\n  }\n}\n\n/* inherits documentation from base class */\nbool QCPLayoutGrid::take(QCPLayoutElement *element)\n{\n  if (element)\n  {\n    for (int i=0; i<elementCount(); ++i)\n    {\n      if (elementAt(i) == element)\n      {\n        takeAt(i);\n        return true;\n      }\n    }\n    qDebug() << Q_FUNC_INFO << \"Element not in this layout, couldn't take\";\n  } else\n    qDebug() << Q_FUNC_INFO << \"Can't take nullptr element\";\n  return false;\n}\n\n/* inherits documentation from base class */\nQList<QCPLayoutElement*> QCPLayoutGrid::elements(bool recursive) const\n{\n  QList<QCPLayoutElement*> result;\n  const int elCount = elementCount();\n#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)\n  result.reserve(elCount);\n#endif\n  for (int i=0; i<elCount; ++i)\n    result.append(elementAt(i));\n  if (recursive)\n  {\n    for (int i=0; i<elCount; ++i)\n    {\n      if (result.at(i))\n        result << result.at(i)->elements(recursive);\n    }\n  }\n  return result;\n}\n\n/*!\n  Simplifies the layout by collapsing rows and columns which only contain empty cells.\n*/\nvoid QCPLayoutGrid::simplify()\n{\n  // remove rows with only empty cells:\n  for (int row=rowCount()-1; row>=0; --row)\n  {\n    bool hasElements = false;\n    for (int col=0; col<columnCount(); ++col)\n    {\n      if (mElements.at(row).at(col))\n      {\n        hasElements = true;\n        break;\n      }\n    }\n    if (!hasElements)\n    {\n      mRowStretchFactors.removeAt(row);\n      mElements.removeAt(row);\n      if (mElements.isEmpty()) // removed last element, also remove stretch factor (wouldn't happen below because also columnCount changed to 0 now)\n        mColumnStretchFactors.clear();\n    }\n  }\n  \n  // remove columns with only empty cells:\n  for (int col=columnCount()-1; col>=0; --col)\n  {\n    bool hasElements = false;\n    for (int row=0; row<rowCount(); ++row)\n    {\n      if (mElements.at(row).at(col))\n      {\n        hasElements = true;\n        break;\n      }\n    }\n    if (!hasElements)\n    {\n      mColumnStretchFactors.removeAt(col);\n      for (int row=0; row<rowCount(); ++row)\n        mElements[row].removeAt(col);\n    }\n  }\n}\n\n/* inherits documentation from base class */\nQSize QCPLayoutGrid::minimumOuterSizeHint() const\n{\n  QVector<int> minColWidths, minRowHeights;\n  getMinimumRowColSizes(&minColWidths, &minRowHeights);\n  QSize result(0, 0);\n  foreach (int w, minColWidths)\n    result.rwidth() += w;\n  foreach (int h, minRowHeights)\n    result.rheight() += h;\n  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing;\n  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing;\n  result.rwidth() += mMargins.left()+mMargins.right();\n  result.rheight() += mMargins.top()+mMargins.bottom();\n  return result;\n}\n\n/* inherits documentation from base class */\nQSize QCPLayoutGrid::maximumOuterSizeHint() const\n{\n  QVector<int> maxColWidths, maxRowHeights;\n  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);\n  \n  QSize result(0, 0);\n  foreach (int w, maxColWidths)\n    result.setWidth(qMin(result.width()+w, QWIDGETSIZE_MAX));\n  foreach (int h, maxRowHeights)\n    result.setHeight(qMin(result.height()+h, QWIDGETSIZE_MAX));\n  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing;\n  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing;\n  result.rwidth() += mMargins.left()+mMargins.right();\n  result.rheight() += mMargins.top()+mMargins.bottom();\n  if (result.height() > QWIDGETSIZE_MAX)\n    result.setHeight(QWIDGETSIZE_MAX);\n  if (result.width() > QWIDGETSIZE_MAX)\n    result.setWidth(QWIDGETSIZE_MAX);\n  return result;\n}\n\n/*! \\internal\n  \n  Places the minimum column widths and row heights into \\a minColWidths and \\a minRowHeights\n  respectively.\n  \n  The minimum height of a row is the largest minimum height of any element's outer rect in that\n  row. The minimum width of a column is the largest minimum width of any element's outer rect in\n  that column.\n  \n  This is a helper function for \\ref updateLayout.\n  \n  \\see getMaximumRowColSizes\n*/\nvoid QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const\n{\n  *minColWidths = QVector<int>(columnCount(), 0);\n  *minRowHeights = QVector<int>(rowCount(), 0);\n  for (int row=0; row<rowCount(); ++row)\n  {\n    for (int col=0; col<columnCount(); ++col)\n    {\n      if (QCPLayoutElement *el = mElements.at(row).at(col))\n      {\n        QSize minSize = getFinalMinimumOuterSize(el);\n        if (minColWidths->at(col) < minSize.width())\n          (*minColWidths)[col] = minSize.width();\n        if (minRowHeights->at(row) < minSize.height())\n          (*minRowHeights)[row] = minSize.height();\n      }\n    }\n  }\n}\n\n/*! \\internal\n  \n  Places the maximum column widths and row heights into \\a maxColWidths and \\a maxRowHeights\n  respectively.\n  \n  The maximum height of a row is the smallest maximum height of any element's outer rect in that\n  row. The maximum width of a column is the smallest maximum width of any element's outer rect in\n  that column.\n  \n  This is a helper function for \\ref updateLayout.\n  \n  \\see getMinimumRowColSizes\n*/\nvoid QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const\n{\n  *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);\n  *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);\n  for (int row=0; row<rowCount(); ++row)\n  {\n    for (int col=0; col<columnCount(); ++col)\n    {\n      if (QCPLayoutElement *el = mElements.at(row).at(col))\n      {\n        QSize maxSize = getFinalMaximumOuterSize(el);\n        if (maxColWidths->at(col) > maxSize.width())\n          (*maxColWidths)[col] = maxSize.width();\n        if (maxRowHeights->at(row) > maxSize.height())\n          (*maxRowHeights)[row] = maxSize.height();\n      }\n    }\n  }\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPLayoutInset\n////////////////////////////////////////////////////////////////////////////////////////////////////\n/*! \\class QCPLayoutInset\n  \\brief A layout that places child elements aligned to the border or arbitrarily positioned\n  \n  Elements are placed either aligned to the border or at arbitrary position in the area of the\n  layout. Which placement applies is controlled with the \\ref InsetPlacement (\\ref\n  setInsetPlacement).\n\n  Elements are added via \\ref addElement(QCPLayoutElement *element, Qt::Alignment alignment) or\n  addElement(QCPLayoutElement *element, const QRectF &rect). If the first method is used, the inset\n  placement will default to \\ref ipBorderAligned and the element will be aligned according to the\n  \\a alignment parameter. The second method defaults to \\ref ipFree and allows placing elements at\n  arbitrary position and size, defined by \\a rect.\n  \n  The alignment or rect can be set via \\ref setInsetAlignment or \\ref setInsetRect, respectively.\n  \n  This is the layout that every QCPAxisRect has as \\ref QCPAxisRect::insetLayout.\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn virtual void QCPLayoutInset::simplify()\n  \n  The QCPInsetLayout does not need simplification since it can never have empty cells due to its\n  linear index structure. This method does nothing.\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Creates an instance of QCPLayoutInset and sets default values.\n*/\nQCPLayoutInset::QCPLayoutInset()\n{\n}\n\nQCPLayoutInset::~QCPLayoutInset()\n{\n  // clear all child layout elements. This is important because only the specific layouts know how\n  // to handle removing elements (clear calls virtual removeAt method to do that).\n  clear();\n}\n\n/*!\n  Returns the placement type of the element with the specified \\a index.\n*/\nQCPLayoutInset::InsetPlacement QCPLayoutInset::insetPlacement(int index) const\n{\n  if (elementAt(index))\n    return mInsetPlacement.at(index);\n  else\n  {\n    qDebug() << Q_FUNC_INFO << \"Invalid element index:\" << index;\n    return ipFree;\n  }\n}\n\n/*!\n  Returns the alignment of the element with the specified \\a index. The alignment only has a\n  meaning, if the inset placement (\\ref setInsetPlacement) is \\ref ipBorderAligned.\n*/\nQt::Alignment QCPLayoutInset::insetAlignment(int index) const\n{\n  if (elementAt(index))\n    return mInsetAlignment.at(index);\n  else\n  {\n    qDebug() << Q_FUNC_INFO << \"Invalid element index:\" << index;\n#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)\n    return nullptr;\n#else\n    return {};\n#endif\n  }\n}\n\n/*!\n  Returns the rect of the element with the specified \\a index. The rect only has a\n  meaning, if the inset placement (\\ref setInsetPlacement) is \\ref ipFree.\n*/\nQRectF QCPLayoutInset::insetRect(int index) const\n{\n  if (elementAt(index))\n    return mInsetRect.at(index);\n  else\n  {\n    qDebug() << Q_FUNC_INFO << \"Invalid element index:\" << index;\n    return {};\n  }\n}\n\n/*!\n  Sets the inset placement type of the element with the specified \\a index to \\a placement.\n  \n  \\see InsetPlacement\n*/\nvoid QCPLayoutInset::setInsetPlacement(int index, QCPLayoutInset::InsetPlacement placement)\n{\n  if (elementAt(index))\n    mInsetPlacement[index] = placement;\n  else\n    qDebug() << Q_FUNC_INFO << \"Invalid element index:\" << index;\n}\n\n/*!\n  If the inset placement (\\ref setInsetPlacement) is \\ref ipBorderAligned, this function\n  is used to set the alignment of the element with the specified \\a index to \\a alignment.\n  \n  \\a alignment is an or combination of the following alignment flags: Qt::AlignLeft,\n  Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other\n  alignment flags will be ignored.\n*/\nvoid QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment)\n{\n  if (elementAt(index))\n    mInsetAlignment[index] = alignment;\n  else\n    qDebug() << Q_FUNC_INFO << \"Invalid element index:\" << index;\n}\n\n/*!\n  If the inset placement (\\ref setInsetPlacement) is \\ref ipFree, this function is used to set the\n  position and size of the element with the specified \\a index to \\a rect.\n  \n  \\a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1)\n  will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right\n  corner of the layout, with 35% width and height of the parent layout.\n  \n  Note that the minimum and maximum sizes of the embedded element (\\ref\n  QCPLayoutElement::setMinimumSize, \\ref QCPLayoutElement::setMaximumSize) are enforced.\n*/\nvoid QCPLayoutInset::setInsetRect(int index, const QRectF &rect)\n{\n  if (elementAt(index))\n    mInsetRect[index] = rect;\n  else\n    qDebug() << Q_FUNC_INFO << \"Invalid element index:\" << index;\n}\n\n/* inherits documentation from base class */\nvoid QCPLayoutInset::updateLayout()\n{\n  for (int i=0; i<mElements.size(); ++i)\n  {\n    QCPLayoutElement *el = mElements.at(i);\n    QRect insetRect;\n    QSize finalMinSize = getFinalMinimumOuterSize(el);\n    QSize finalMaxSize = getFinalMaximumOuterSize(el);\n    if (mInsetPlacement.at(i) == ipFree)\n    {\n      insetRect = QRect(int( rect().x()+rect().width()*mInsetRect.at(i).x() ),\n                        int( rect().y()+rect().height()*mInsetRect.at(i).y() ),\n                        int( rect().width()*mInsetRect.at(i).width() ),\n                        int( rect().height()*mInsetRect.at(i).height() ));\n      if (insetRect.size().width() < finalMinSize.width())\n        insetRect.setWidth(finalMinSize.width());\n      if (insetRect.size().height() < finalMinSize.height())\n        insetRect.setHeight(finalMinSize.height());\n      if (insetRect.size().width() > finalMaxSize.width())\n        insetRect.setWidth(finalMaxSize.width());\n      if (insetRect.size().height() > finalMaxSize.height())\n        insetRect.setHeight(finalMaxSize.height());\n    } else if (mInsetPlacement.at(i) == ipBorderAligned)\n    {\n      insetRect.setSize(finalMinSize);\n      Qt::Alignment al = mInsetAlignment.at(i);\n      if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x());\n      else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width());\n      else insetRect.moveLeft(int( rect().x()+rect().width()*0.5-finalMinSize.width()*0.5 )); // default to Qt::AlignHCenter\n      if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y());\n      else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height());\n      else insetRect.moveTop(int( rect().y()+rect().height()*0.5-finalMinSize.height()*0.5 )); // default to Qt::AlignVCenter\n    }\n    mElements.at(i)->setOuterRect(insetRect);\n  }\n}\n\n/* inherits documentation from base class */\nint QCPLayoutInset::elementCount() const\n{\n  return mElements.size();\n}\n\n/* inherits documentation from base class */\nQCPLayoutElement *QCPLayoutInset::elementAt(int index) const\n{\n  if (index >= 0 && index < mElements.size())\n    return mElements.at(index);\n  else\n    return nullptr;\n}\n\n/* inherits documentation from base class */\nQCPLayoutElement *QCPLayoutInset::takeAt(int index)\n{\n  if (QCPLayoutElement *el = elementAt(index))\n  {\n    releaseElement(el);\n    mElements.removeAt(index);\n    mInsetPlacement.removeAt(index);\n    mInsetAlignment.removeAt(index);\n    mInsetRect.removeAt(index);\n    return el;\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"Attempt to take invalid index:\" << index;\n    return nullptr;\n  }\n}\n\n/* inherits documentation from base class */\nbool QCPLayoutInset::take(QCPLayoutElement *element)\n{\n  if (element)\n  {\n    for (int i=0; i<elementCount(); ++i)\n    {\n      if (elementAt(i) == element)\n      {\n        takeAt(i);\n        return true;\n      }\n    }\n    qDebug() << Q_FUNC_INFO << \"Element not in this layout, couldn't take\";\n  } else\n    qDebug() << Q_FUNC_INFO << \"Can't take nullptr element\";\n  return false;\n}\n\n/*!\n  The inset layout is sensitive to events only at areas where its (visible) child elements are\n  sensitive. If the selectTest method of any of the child elements returns a positive number for \\a\n  pos, this method returns a value corresponding to 0.99 times the parent plot's selection\n  tolerance. The inset layout is not selectable itself by default. So if \\a onlySelectable is true,\n  -1.0 is returned.\n  \n  See \\ref QCPLayerable::selectTest for a general explanation of this virtual method.\n*/\ndouble QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(details)\n  if (onlySelectable)\n    return -1;\n  \n  foreach (QCPLayoutElement *el, mElements)\n  {\n    // inset layout shall only return positive selectTest, if actually an inset object is at pos\n    // else it would block the entire underlying QCPAxisRect with its surface.\n    if (el->realVisibility() && el->selectTest(pos, onlySelectable) >= 0)\n      return mParentPlot->selectionTolerance()*0.99;\n  }\n  return -1;\n}\n\n/*!\n  Adds the specified \\a element to the layout as an inset aligned at the border (\\ref\n  setInsetAlignment is initialized with \\ref ipBorderAligned). The alignment is set to \\a\n  alignment.\n  \n  \\a alignment is an or combination of the following alignment flags: Qt::AlignLeft,\n  Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other\n  alignment flags will be ignored.\n  \n  \\see addElement(QCPLayoutElement *element, const QRectF &rect)\n*/\nvoid QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignment)\n{\n  if (element)\n  {\n    if (element->layout()) // remove from old layout first\n      element->layout()->take(element);\n    mElements.append(element);\n    mInsetPlacement.append(ipBorderAligned);\n    mInsetAlignment.append(alignment);\n    mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));\n    adoptElement(element);\n  } else\n    qDebug() << Q_FUNC_INFO << \"Can't add nullptr element\";\n}\n\n/*!\n  Adds the specified \\a element to the layout as an inset with free positioning/sizing (\\ref\n  setInsetAlignment is initialized with \\ref ipFree). The position and size is set to \\a\n  rect.\n  \n  \\a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1)\n  will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right\n  corner of the layout, with 35% width and height of the parent layout.\n  \n  \\see addElement(QCPLayoutElement *element, Qt::Alignment alignment)\n*/\nvoid QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect)\n{\n  if (element)\n  {\n    if (element->layout()) // remove from old layout first\n      element->layout()->take(element);\n    mElements.append(element);\n    mInsetPlacement.append(ipFree);\n    mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop);\n    mInsetRect.append(rect);\n    adoptElement(element);\n  } else\n    qDebug() << Q_FUNC_INFO << \"Can't add nullptr element\";\n}\n/* end of 'src/layout.cpp' */\n\n\n/* including file 'src/lineending.cpp'      */\n/* modified 2021-03-29T02:30:44, size 11189 */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPLineEnding\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPLineEnding\n  \\brief Handles the different ending decorations for line-like items\n  \n  \\image html QCPLineEnding.png \"The various ending styles currently supported\"\n  \n  For every ending a line-like item has, an instance of this class exists. For example, QCPItemLine\n  has two endings which can be set with QCPItemLine::setHead and QCPItemLine::setTail.\n \n  The styles themselves are defined via the enum QCPLineEnding::EndingStyle. Most decorations can\n  be modified regarding width and length, see \\ref setWidth and \\ref setLength. The direction of\n  the ending decoration (e.g. direction an arrow is pointing) is controlled by the line-like item.\n  For example, when both endings of a QCPItemLine are set to be arrows, they will point to opposite\n  directions, e.g. \"outward\". This can be changed by \\ref setInverted, which would make the\n  respective arrow point inward.\n  \n  Note that due to the overloaded QCPLineEnding constructor, you may directly specify a\n  QCPLineEnding::EndingStyle where actually a QCPLineEnding is expected, e.g.\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcplineending-sethead\n*/\n\n/*!\n  Creates a QCPLineEnding instance with default values (style \\ref esNone).\n*/\nQCPLineEnding::QCPLineEnding() :\n  mStyle(esNone),\n  mWidth(8),\n  mLength(10),\n  mInverted(false)\n{\n}\n\n/*!\n  Creates a QCPLineEnding instance with the specified values.\n*/\nQCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width, double length, bool inverted) :\n  mStyle(style),\n  mWidth(width),\n  mLength(length),\n  mInverted(inverted)\n{\n}\n\n/*!\n  Sets the style of the ending decoration.\n*/\nvoid QCPLineEnding::setStyle(QCPLineEnding::EndingStyle style)\n{\n  mStyle = style;\n}\n\n/*!\n  Sets the width of the ending decoration, if the style supports it. On arrows, for example, the\n  width defines the size perpendicular to the arrow's pointing direction.\n  \n  \\see setLength\n*/\nvoid QCPLineEnding::setWidth(double width)\n{\n  mWidth = width;\n}\n\n/*!\n  Sets the length of the ending decoration, if the style supports it. On arrows, for example, the\n  length defines the size in pointing direction.\n  \n  \\see setWidth\n*/\nvoid QCPLineEnding::setLength(double length)\n{\n  mLength = length;\n}\n\n/*!\n  Sets whether the ending decoration shall be inverted. For example, an arrow decoration will point\n  inward when \\a inverted is set to true.\n\n  Note that also the \\a width direction is inverted. For symmetrical ending styles like arrows or\n  discs, this doesn't make a difference. However, asymmetric styles like \\ref esHalfBar are\n  affected by it, which can be used to control to which side the half bar points to.\n*/\nvoid QCPLineEnding::setInverted(bool inverted)\n{\n  mInverted = inverted;\n}\n\n/*! \\internal\n  \n  Returns the maximum pixel radius the ending decoration might cover, starting from the position\n  the decoration is drawn at (typically a line ending/\\ref QCPItemPosition of an item).\n  \n  This is relevant for clipping. Only omit painting of the decoration when the position where the\n  decoration is supposed to be drawn is farther away from the clipping rect than the returned\n  distance.\n*/\ndouble QCPLineEnding::boundingDistance() const\n{\n  switch (mStyle)\n  {\n    case esNone:\n      return 0;\n      \n    case esFlatArrow:\n    case esSpikeArrow:\n    case esLineArrow:\n    case esSkewedBar:\n      return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length\n      \n    case esDisc:\n    case esSquare:\n    case esDiamond:\n    case esBar:\n    case esHalfBar:\n      return mWidth*1.42; // items that only have a width -> width*sqrt(2)\n\n  }\n  return 0;\n}\n\n/*!\n  Starting from the origin of this line ending (which is style specific), returns the length\n  covered by the line ending symbol, in backward direction.\n  \n  For example, the \\ref esSpikeArrow has a shorter real length than a \\ref esFlatArrow, even if\n  both have the same \\ref setLength value, because the spike arrow has an inward curved back, which\n  reduces the length along its center axis (the drawing origin for arrows is at the tip).\n  \n  This function is used for precise, style specific placement of line endings, for example in\n  QCPAxes.\n*/\ndouble QCPLineEnding::realLength() const\n{\n  switch (mStyle)\n  {\n    case esNone:\n    case esLineArrow:\n    case esSkewedBar:\n    case esBar:\n    case esHalfBar:\n      return 0;\n      \n    case esFlatArrow:\n      return mLength;\n      \n    case esDisc:\n    case esSquare:\n    case esDiamond:\n      return mWidth*0.5;\n      \n    case esSpikeArrow:\n      return mLength*0.8;\n  }\n  return 0;\n}\n\n/*! \\internal\n  \n  Draws the line ending with the specified \\a painter at the position \\a pos. The direction of the\n  line ending is controlled with \\a dir.\n*/\nvoid QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, const QCPVector2D &dir) const\n{\n  if (mStyle == esNone)\n    return;\n  \n  QCPVector2D lengthVec = dir.normalized() * mLength*(mInverted ? -1 : 1);\n  if (lengthVec.isNull())\n    lengthVec = QCPVector2D(1, 0);\n  QCPVector2D widthVec = dir.normalized().perpendicular() * mWidth*0.5*(mInverted ? -1 : 1);\n  \n  QPen penBackup = painter->pen();\n  QBrush brushBackup = painter->brush();\n  QPen miterPen = penBackup;\n  miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey\n  QBrush brush(painter->pen().color(), Qt::SolidPattern);\n  switch (mStyle)\n  {\n    case esNone: break;\n    case esFlatArrow:\n    {\n      QPointF points[3] = {pos.toPointF(),\n                           (pos-lengthVec+widthVec).toPointF(),\n                           (pos-lengthVec-widthVec).toPointF()\n                          };\n      painter->setPen(miterPen);\n      painter->setBrush(brush);\n      painter->drawConvexPolygon(points, 3);\n      painter->setBrush(brushBackup);\n      painter->setPen(penBackup);\n      break;\n    }\n    case esSpikeArrow:\n    {\n      QPointF points[4] = {pos.toPointF(),\n                           (pos-lengthVec+widthVec).toPointF(),\n                           (pos-lengthVec*0.8).toPointF(),\n                           (pos-lengthVec-widthVec).toPointF()\n                          };\n      painter->setPen(miterPen);\n      painter->setBrush(brush);\n      painter->drawConvexPolygon(points, 4);\n      painter->setBrush(brushBackup);\n      painter->setPen(penBackup);\n      break;\n    }\n    case esLineArrow:\n    {\n      QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),\n                           pos.toPointF(),\n                           (pos-lengthVec-widthVec).toPointF()\n                          };\n      painter->setPen(miterPen);\n      painter->drawPolyline(points, 3);\n      painter->setPen(penBackup);\n      break;\n    }\n    case esDisc:\n    {\n      painter->setBrush(brush);\n      painter->drawEllipse(pos.toPointF(),  mWidth*0.5, mWidth*0.5);\n      painter->setBrush(brushBackup);\n      break;\n    }\n    case esSquare:\n    {\n      QCPVector2D widthVecPerp = widthVec.perpendicular();\n      QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),\n                           (pos-widthVecPerp-widthVec).toPointF(),\n                           (pos+widthVecPerp-widthVec).toPointF(),\n                           (pos+widthVecPerp+widthVec).toPointF()\n                          };\n      painter->setPen(miterPen);\n      painter->setBrush(brush);\n      painter->drawConvexPolygon(points, 4);\n      painter->setBrush(brushBackup);\n      painter->setPen(penBackup);\n      break;\n    }\n    case esDiamond:\n    {\n      QCPVector2D widthVecPerp = widthVec.perpendicular();\n      QPointF points[4] = {(pos-widthVecPerp).toPointF(),\n                           (pos-widthVec).toPointF(),\n                           (pos+widthVecPerp).toPointF(),\n                           (pos+widthVec).toPointF()\n                          };\n      painter->setPen(miterPen);\n      painter->setBrush(brush);\n      painter->drawConvexPolygon(points, 4);\n      painter->setBrush(brushBackup);\n      painter->setPen(penBackup);\n      break;\n    }\n    case esBar:\n    {\n      painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());\n      break;\n    }\n    case esHalfBar:\n    {\n      painter->drawLine((pos+widthVec).toPointF(), pos.toPointF());\n      break;\n    }\n    case esSkewedBar:\n    {\n      QCPVector2D shift;\n      if (!qFuzzyIsNull(painter->pen().widthF()) || painter->modes().testFlag(QCPPainter::pmNonCosmetic))\n        shift = dir.normalized()*qMax(qreal(1.0), painter->pen().widthF())*qreal(0.5);\n      // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly\n      painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)+shift).toPointF(),\n                        (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)+shift).toPointF());\n      break;\n    }\n  }\n}\n\n/*! \\internal\n  \\overload\n  \n  Draws the line ending. The direction is controlled with the \\a angle parameter in radians.\n*/\nvoid QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, double angle) const\n{\n  draw(painter, pos, QCPVector2D(qCos(angle), qSin(angle)));\n}\n/* end of 'src/lineending.cpp' */\n\n\n/* including file 'src/axis/labelpainter.cpp' */\n/* modified 2021-03-29T02:30:44, size 27296   */\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPLabelPainterPrivate\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPLabelPainterPrivate\n\n  \\internal\n  \\brief (Private)\n  \n  This is a private class and not part of the public QCustomPlot interface.\n  \n*/\n\nconst QChar QCPLabelPainterPrivate::SymbolDot(183);\nconst QChar QCPLabelPainterPrivate::SymbolCross(215);\n\n/*!\n  Constructs a QCPLabelPainterPrivate instance. Make sure to not create a new\n  instance on every redraw, to utilize the caching mechanisms.\n  \n  the \\a parentPlot does not take ownership of the label painter. Make sure\n  to delete it appropriately.\n*/\nQCPLabelPainterPrivate::QCPLabelPainterPrivate(QCustomPlot *parentPlot) :\n  mAnchorMode(amRectangular),\n  mAnchorSide(asLeft),\n  mAnchorReferenceType(artNormal),\n  mColor(Qt::black),\n  mPadding(0),\n  mRotation(0),\n  mSubstituteExponent(true),\n  mMultiplicationSymbol(QChar(215)),\n  mAbbreviateDecimalPowers(false),\n  mParentPlot(parentPlot),\n  mLabelCache(16)\n{\n  analyzeFontMetrics();\n}\n\nQCPLabelPainterPrivate::~QCPLabelPainterPrivate()\n{\n}\n\nvoid QCPLabelPainterPrivate::setAnchorSide(AnchorSide side)\n{\n  mAnchorSide = side;\n}\n\nvoid QCPLabelPainterPrivate::setAnchorMode(AnchorMode mode)\n{\n  mAnchorMode = mode;\n}\n\nvoid QCPLabelPainterPrivate::setAnchorReference(const QPointF &pixelPoint)\n{\n  mAnchorReference = pixelPoint;\n}\n\nvoid QCPLabelPainterPrivate::setAnchorReferenceType(AnchorReferenceType type)\n{\n  mAnchorReferenceType = type;\n}\n\nvoid QCPLabelPainterPrivate::setFont(const QFont &font)\n{\n  if (mFont != font)\n  {\n    mFont = font;\n    analyzeFontMetrics();\n  }\n}\n\nvoid QCPLabelPainterPrivate::setColor(const QColor &color)\n{\n  mColor = color;\n}\n\nvoid QCPLabelPainterPrivate::setPadding(int padding)\n{\n  mPadding = padding;\n}\n\nvoid QCPLabelPainterPrivate::setRotation(double rotation)\n{\n  mRotation = qBound(-90.0, rotation, 90.0);\n}\n\nvoid QCPLabelPainterPrivate::setSubstituteExponent(bool enabled)\n{\n  mSubstituteExponent = enabled;\n}\n\nvoid QCPLabelPainterPrivate::setMultiplicationSymbol(QChar symbol)\n{\n  mMultiplicationSymbol = symbol;\n}\n\nvoid QCPLabelPainterPrivate::setAbbreviateDecimalPowers(bool enabled)\n{\n  mAbbreviateDecimalPowers = enabled;\n}\n\nvoid QCPLabelPainterPrivate::setCacheSize(int labelCount)\n{\n  mLabelCache.setMaxCost(labelCount);\n}\n\nint QCPLabelPainterPrivate::cacheSize() const\n{\n  return mLabelCache.maxCost();\n}\n\nvoid QCPLabelPainterPrivate::drawTickLabel(QCPPainter *painter, const QPointF &tickPos, const QString &text)\n{\n  double realRotation = mRotation;\n  \n  AnchorSide realSide = mAnchorSide;\n  // for circular axes, the anchor side is determined depending on the quadrant of tickPos with respect to mCircularReference\n  if (mAnchorMode == amSkewedUpright)\n  {\n    realSide = skewedAnchorSide(tickPos, 0.2, 0.3); \n  } else if (mAnchorMode == amSkewedRotated) // in this mode every label is individually rotated to match circle tangent\n  {\n    realSide = skewedAnchorSide(tickPos, 0, 0);\n    realRotation += QCPVector2D(tickPos-mAnchorReference).angle()/M_PI*180.0;\n    if (realRotation > 90) realRotation -= 180;\n    else if (realRotation < -90) realRotation += 180;\n  }\n  \n  realSide = rotationCorrectedSide(realSide, realRotation); // rotation angles may change the true anchor side of the label\n  drawLabelMaybeCached(painter, mFont, mColor, getAnchorPos(tickPos), realSide, realRotation, text);\n}\n\n/*! \\internal\n  \n  Returns the size (\"margin\" in QCPAxisRect context, so measured perpendicular to the axis backbone\n  direction) needed to fit the axis.\n*/\n/* TODO: needed?\nint QCPLabelPainterPrivate::size() const\n{\n  int result = 0;\n  // get length of tick marks pointing outwards:\n  if (!tickPositions.isEmpty())\n    result += qMax(0, qMax(tickLengthOut, subTickLengthOut));\n  \n  // calculate size of tick labels:\n  if (tickLabelSide == QCPAxis::lsOutside)\n  {\n    QSize tickLabelsSize(0, 0);\n    if (!tickLabels.isEmpty())\n    {\n      for (int i=0; i<tickLabels.size(); ++i)\n        getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);\n      result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();\n    result += tickLabelPadding;\n    }\n  }\n  \n  // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):\n  if (!label.isEmpty())\n  {\n    QFontMetrics fontMetrics(labelFont);\n    QRect bounds;\n    bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);\n    result += bounds.height() + labelPadding;\n  }\n\n  return result;\n}\n*/\n\n/*! \\internal\n  \n  Clears the internal label cache. Upon the next \\ref draw, all labels will be created new. This\n  method is called automatically if any parameters have changed that invalidate the cached labels,\n  such as font, color, etc. Usually you won't need to call this method manually.\n*/\nvoid QCPLabelPainterPrivate::clearCache()\n{\n  mLabelCache.clear();\n}\n\n/*! \\internal\n  \n  Returns a hash that allows uniquely identifying whether the label parameters have changed such\n  that the cached labels must be refreshed (\\ref clearCache). It is used in \\ref draw. If the\n  return value of this method hasn't changed since the last redraw, the respective label parameters\n  haven't changed and cached labels may be used.\n*/\nQByteArray QCPLabelPainterPrivate::generateLabelParameterHash() const\n{\n  QByteArray result;\n  result.append(QByteArray::number(mParentPlot->bufferDevicePixelRatio()));\n  result.append(QByteArray::number(mRotation));\n  //result.append(QByteArray::number((int)tickLabelSide)); TODO: check whether this is really a cache-invalidating property\n  result.append(QByteArray::number((int)mSubstituteExponent));\n  result.append(QString(mMultiplicationSymbol).toUtf8());\n  result.append(mColor.name().toLatin1()+QByteArray::number(mColor.alpha(), 16));\n  result.append(mFont.toString().toLatin1());\n  return result;\n}\n\n/*! \\internal\n  \n  Draws a single tick label with the provided \\a painter, utilizing the internal label cache to\n  significantly speed up drawing of labels that were drawn in previous calls. The tick label is\n  always bound to an axis, the distance to the axis is controllable via \\a distanceToAxis in\n  pixels. The pixel position in the axis direction is passed in the \\a position parameter. Hence\n  for the bottom axis, \\a position would indicate the horizontal pixel position (not coordinate),\n  at which the label should be drawn.\n  \n  In order to later draw the axis label in a place that doesn't overlap with the tick labels, the\n  largest tick label size is needed. This is acquired by passing a \\a tickLabelsSize to the \\ref\n  drawTickLabel calls during the process of drawing all tick labels of one axis. In every call, \\a\n  tickLabelsSize is expanded, if the drawn label exceeds the value \\a tickLabelsSize currently\n  holds.\n  \n  The label is drawn with the font and pen that are currently set on the \\a painter. To draw\n  superscripted powers, the font is temporarily made smaller by a fixed factor (see \\ref\n  getTickLabelData).\n*/\nvoid QCPLabelPainterPrivate::drawLabelMaybeCached(QCPPainter *painter, const QFont &font, const QColor &color, const QPointF &pos, AnchorSide side, double rotation, const QString &text)\n{\n  // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!\n  if (text.isEmpty()) return;\n  QSize finalSize;\n\n  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled\n  {\n    QByteArray key = cacheKey(text, color, rotation, side);\n    CachedLabel *cachedLabel = mLabelCache.take(QString::fromUtf8(key)); // attempt to take label from cache (don't use object() because we want ownership/prevent deletion during our operations, we re-insert it afterwards)\n    if (!cachedLabel)  // no cached label existed, create it\n    {\n      LabelData labelData = getTickLabelData(font, color, rotation, side, text);\n      cachedLabel = createCachedLabel(labelData);\n    }\n    // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):\n    bool labelClippedByBorder = false;\n    /*\n    if (tickLabelSide == QCPAxis::lsOutside)\n    {\n      if (QCPAxis::orientation(type) == Qt::Horizontal)\n        labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width()/mParentPlot->bufferDevicePixelRatio() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left();\n      else\n        labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height()/mParentPlot->bufferDevicePixelRatio() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top();\n    }\n    */\n    if (!labelClippedByBorder)\n    {\n      painter->drawPixmap(pos+cachedLabel->offset, cachedLabel->pixmap);\n      finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio(); // TODO: collect this in a member rect list?\n    }\n    mLabelCache.insert(QString::fromUtf8(key), cachedLabel);\n  } else // label caching disabled, draw text directly on surface:\n  {\n    LabelData labelData = getTickLabelData(font, color, rotation, side, text);\n    // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):\n     bool labelClippedByBorder = false;\n     /*\n    if (tickLabelSide == QCPAxis::lsOutside)\n    {\n      if (QCPAxis::orientation(type) == Qt::Horizontal)\n        labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left();\n      else\n        labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top();\n    }\n    */\n    if (!labelClippedByBorder)\n    {\n      drawText(painter, pos, labelData);\n      finalSize = labelData.rotatedTotalBounds.size();\n    }\n  }\n  /*\n  // expand passed tickLabelsSize if current tick label is larger:\n  if (finalSize.width() > tickLabelsSize->width())\n    tickLabelsSize->setWidth(finalSize.width());\n  if (finalSize.height() > tickLabelsSize->height())\n    tickLabelsSize->setHeight(finalSize.height());\n  */\n}\n\nQPointF QCPLabelPainterPrivate::getAnchorPos(const QPointF &tickPos)\n{\n  switch (mAnchorMode)\n  {\n    case amRectangular:\n    {\n      switch (mAnchorSide)\n      {\n        case asLeft:   return tickPos+QPointF(mPadding, 0);\n        case asRight:  return tickPos+QPointF(-mPadding, 0);\n        case asTop:    return tickPos+QPointF(0, mPadding);\n        case asBottom: return tickPos+QPointF(0, -mPadding);\n        case asTopLeft:     return tickPos+QPointF(mPadding*M_SQRT1_2, mPadding*M_SQRT1_2);\n        case asTopRight:    return tickPos+QPointF(-mPadding*M_SQRT1_2, mPadding*M_SQRT1_2);\n        case asBottomRight: return tickPos+QPointF(-mPadding*M_SQRT1_2, -mPadding*M_SQRT1_2);\n        case asBottomLeft:  return tickPos+QPointF(mPadding*M_SQRT1_2, -mPadding*M_SQRT1_2);\n      }\n    }\n    case amSkewedUpright:\n    case amSkewedRotated:\n    {\n      QCPVector2D anchorNormal(tickPos-mAnchorReference);\n      if (mAnchorReferenceType == artTangent)\n        anchorNormal = anchorNormal.perpendicular();\n      anchorNormal.normalize();\n      return tickPos+(anchorNormal*mPadding).toPointF();\n    }\n  }\n  return tickPos;\n}\n\n/*! \\internal\n  \n  This is a \\ref placeTickLabel helper function.\n  \n  Draws the tick label specified in \\a labelData with \\a painter at the pixel positions \\a x and \\a\n  y. This function is used by \\ref placeTickLabel to create new tick labels for the cache, or to\n  directly draw the labels on the QCustomPlot surface when label caching is disabled, i.e. when\n  QCP::phCacheLabels plotting hint is not set.\n*/\nvoid QCPLabelPainterPrivate::drawText(QCPPainter *painter, const QPointF &pos, const LabelData &labelData) const\n{\n  // backup painter settings that we're about to change:\n  QTransform oldTransform = painter->transform();\n  QFont oldFont = painter->font();\n  QPen oldPen = painter->pen();\n  \n  // transform painter to position/rotation:\n  painter->translate(pos);\n  painter->setTransform(labelData.transform, true);\n  \n  // draw text:\n  painter->setFont(labelData.baseFont);\n  painter->setPen(QPen(labelData.color));\n  if (!labelData.expPart.isEmpty()) // use superscripted exponent typesetting\n  {\n    painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);\n    if (!labelData.suffixPart.isEmpty())\n      painter->drawText(labelData.baseBounds.width()+1+labelData.expBounds.width(), 0, 0, 0, Qt::TextDontClip, labelData.suffixPart);\n    painter->setFont(labelData.expFont);\n    painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip,  labelData.expPart);\n  } else\n  {\n    painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);\n  }\n  \n  /* Debug code to draw label bounding boxes, baseline, and capheight\n  painter->save();\n  painter->setPen(QPen(QColor(0, 0, 0, 150)));\n  painter->drawRect(labelData.totalBounds);\n  const int baseline = labelData.totalBounds.height()-mLetterDescent;\n  painter->setPen(QPen(QColor(255, 0, 0, 150)));\n  painter->drawLine(QLineF(0, baseline, labelData.totalBounds.width(), baseline));\n  painter->setPen(QPen(QColor(0, 0, 255, 150)));\n  painter->drawLine(QLineF(0, baseline-mLetterCapHeight, labelData.totalBounds.width(), baseline-mLetterCapHeight));\n  painter->restore();\n  */\n  \n  // reset painter settings to what it was before:\n  painter->setTransform(oldTransform);\n  painter->setFont(oldFont);\n  painter->setPen(oldPen);\n}\n\n/*! \\internal\n  \n  This is a \\ref placeTickLabel helper function.\n  \n  Transforms the passed \\a text and \\a font to a tickLabelData structure that can then be further\n  processed by \\ref getTickLabelDrawOffset and \\ref drawTickLabel. It splits the text into base and\n  exponent if necessary (member substituteExponent) and calculates appropriate bounding boxes.\n*/\nQCPLabelPainterPrivate::LabelData QCPLabelPainterPrivate::getTickLabelData(const QFont &font, const QColor &color, double rotation, AnchorSide side, const QString &text) const\n{\n  LabelData result;\n  result.rotation = rotation;\n  result.side = side;\n  result.color = color;\n  \n  // determine whether beautiful decimal powers should be used\n  bool useBeautifulPowers = false;\n  int ePos = -1; // first index of exponent part, text before that will be basePart, text until eLast will be expPart\n  int eLast = -1; // last index of exponent part, rest of text after this will be suffixPart\n  if (mSubstituteExponent)\n  {\n    ePos = text.indexOf(QLatin1Char('e'));\n    if (ePos > 0 && text.at(ePos-1).isDigit())\n    {\n      eLast = ePos;\n      while (eLast+1 < text.size() && (text.at(eLast+1) == QLatin1Char('+') || text.at(eLast+1) == QLatin1Char('-') || text.at(eLast+1).isDigit()))\n        ++eLast;\n      if (eLast > ePos) // only if also to right of 'e' is a digit/+/- interpret it as beautifiable power\n        useBeautifulPowers = true;\n    }\n  }\n  \n  // calculate text bounding rects and do string preparation for beautiful decimal powers:\n  result.baseFont = font;\n  if (result.baseFont.pointSizeF() > 0) // might return -1 if specified with setPixelSize, in that case we can't do correction in next line\n    result.baseFont.setPointSizeF(result.baseFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding\n  \n  QFontMetrics baseFontMetrics(result.baseFont);\n  if (useBeautifulPowers)\n  {\n    // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:\n    result.basePart = text.left(ePos);\n    result.suffixPart = text.mid(eLast+1); // also drawn normally but after exponent\n    // in log scaling, we want to turn \"1*10^n\" into \"10^n\", else add multiplication sign and decimal base:\n    if (mAbbreviateDecimalPowers && result.basePart == QLatin1String(\"1\"))\n      result.basePart = QLatin1String(\"10\");\n    else\n      result.basePart += QString(mMultiplicationSymbol) + QLatin1String(\"10\");\n    result.expPart = text.mid(ePos+1, eLast-ePos);\n    // clip \"+\" and leading zeros off expPart:\n    while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e'\n      result.expPart.remove(1, 1);\n    if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))\n      result.expPart.remove(0, 1);\n    // prepare smaller font for exponent:\n    result.expFont = font;\n    if (result.expFont.pointSize() > 0)\n      result.expFont.setPointSize(result.expFont.pointSize()*0.75);\n    else\n      result.expFont.setPixelSize(result.expFont.pixelSize()*0.75);\n    // calculate bounding rects of base part(s), exponent part and total one:\n    result.baseBounds = baseFontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);\n    result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);\n    if (!result.suffixPart.isEmpty())\n      result.suffixBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.suffixPart);\n    result.totalBounds = result.baseBounds.adjusted(0, 0, result.expBounds.width()+result.suffixBounds.width()+2, 0); // +2 consists of the 1 pixel spacing between base and exponent (see drawTickLabel) and an extra pixel to include AA\n  } else // useBeautifulPowers == false\n  {\n    result.basePart = text;\n    result.totalBounds = baseFontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);\n  }\n  result.totalBounds.moveTopLeft(QPoint(0, 0));\n  applyAnchorTransform(result);\n  result.rotatedTotalBounds = result.transform.mapRect(result.totalBounds);\n  \n  return result;\n}\n\nvoid QCPLabelPainterPrivate::applyAnchorTransform(LabelData &labelData) const\n{\n  if (!qFuzzyIsNull(labelData.rotation))\n    labelData.transform.rotate(labelData.rotation); // rotates effectively clockwise (due to flipped y axis of painter vs widget coordinate system)\n  \n  // from now on we translate in rotated label-local coordinate system.\n  // shift origin of coordinate system to appropriate point on label:\n  labelData.transform.translate(0, -labelData.totalBounds.height()+mLetterDescent+mLetterCapHeight); // shifts origin to true top of capital (or number) characters\n  \n  if (labelData.side == asLeft || labelData.side == asRight) // anchor is centered vertically\n    labelData.transform.translate(0, -mLetterCapHeight/2.0);\n  else if (labelData.side == asTop || labelData.side == asBottom) // anchor is centered horizontally\n    labelData.transform.translate(-labelData.totalBounds.width()/2.0, 0);\n  \n  if (labelData.side == asTopRight || labelData.side == asRight || labelData.side == asBottomRight) // anchor is at right\n    labelData.transform.translate(-labelData.totalBounds.width(), 0);\n  if (labelData.side == asBottomLeft || labelData.side == asBottom || labelData.side == asBottomRight) // anchor is at bottom (no elseif!)\n    labelData.transform.translate(0, -mLetterCapHeight);\n}\n\n/*! \\internal\n  \n  Simulates the steps done by \\ref placeTickLabel by calculating bounding boxes of the text label\n  to be drawn, depending on number format etc. Since only the largest tick label is wanted for the\n  margin calculation, the passed \\a tickLabelsSize is only expanded, if it's currently set to a\n  smaller width/height.\n*/\n/*\nvoid QCPLabelPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text,  QSize *tickLabelsSize) const\n{\n  // note: this function must return the same tick label sizes as the placeTickLabel function.\n  QSize finalSize;\n  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label\n  {\n    const CachedLabel *cachedLabel = mLabelCache.object(text);\n    finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio();\n  } else // label caching disabled or no label with this text cached:\n  {\n    // TODO: LabelData labelData = getTickLabelData(font, text);\n    // TODO: finalSize = labelData.rotatedTotalBounds.size();\n  }\n  \n  // expand passed tickLabelsSize if current tick label is larger:\n  if (finalSize.width() > tickLabelsSize->width())\n    tickLabelsSize->setWidth(finalSize.width());\n  if (finalSize.height() > tickLabelsSize->height())\n    tickLabelsSize->setHeight(finalSize.height());\n}\n*/\n\nQCPLabelPainterPrivate::CachedLabel *QCPLabelPainterPrivate::createCachedLabel(const LabelData &labelData) const\n{\n  CachedLabel *result = new CachedLabel;\n  \n  // allocate pixmap with the correct size and pixel ratio:\n  if (!qFuzzyCompare(1.0, mParentPlot->bufferDevicePixelRatio()))\n  {\n    result->pixmap = QPixmap(labelData.rotatedTotalBounds.size()*mParentPlot->bufferDevicePixelRatio());\n#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED\n#  ifdef QCP_DEVICEPIXELRATIO_FLOAT\n    result->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatioF());\n#  else\n    result->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatio());\n#  endif\n#endif\n  } else\n    result->pixmap = QPixmap(labelData.rotatedTotalBounds.size());\n  result->pixmap.fill(Qt::transparent);\n  \n  // draw the label into the pixmap\n  // offset is between label anchor and topleft of cache pixmap, so pixmap can be drawn at pos+offset to make the label anchor appear at pos.\n  // We use rotatedTotalBounds.topLeft() because rotatedTotalBounds is in a coordinate system where the label anchor is at (0, 0)\n  result->offset = labelData.rotatedTotalBounds.topLeft();\n  QCPPainter cachePainter(&result->pixmap);\n  drawText(&cachePainter, -result->offset, labelData);\n  return result;\n}\n\nQByteArray QCPLabelPainterPrivate::cacheKey(const QString &text, const QColor &color, double rotation, AnchorSide side) const\n{\n  return text.toUtf8()+\n      QByteArray::number(color.red()+256*color.green()+65536*color.blue(), 36)+\n      QByteArray::number(color.alpha()+256*(int)side, 36)+\n      QByteArray::number((int)(rotation*100)%36000, 36);\n}\n\nQCPLabelPainterPrivate::AnchorSide QCPLabelPainterPrivate::skewedAnchorSide(const QPointF &tickPos, double sideExpandHorz, double sideExpandVert) const\n{\n  QCPVector2D anchorNormal = QCPVector2D(tickPos-mAnchorReference);\n  if (mAnchorReferenceType == artTangent)\n    anchorNormal = anchorNormal.perpendicular();\n  const double radius = anchorNormal.length();\n  const double sideHorz = sideExpandHorz*radius;\n  const double sideVert = sideExpandVert*radius;\n  if (anchorNormal.x() > sideHorz)\n  {\n    if (anchorNormal.y() > sideVert) return asTopLeft;\n    else if (anchorNormal.y() < -sideVert) return asBottomLeft;\n    else return asLeft;\n  } else if (anchorNormal.x() < -sideHorz)\n  {\n    if (anchorNormal.y() > sideVert) return asTopRight;\n    else if (anchorNormal.y() < -sideVert) return asBottomRight;\n    else return asRight;\n  } else\n  {\n    if (anchorNormal.y() > 0) return asTop;\n    else return asBottom;\n  }\n  return asBottom; // should never be reached\n}\n\nQCPLabelPainterPrivate::AnchorSide QCPLabelPainterPrivate::rotationCorrectedSide(AnchorSide side, double rotation) const\n{\n  AnchorSide result = side;\n  const bool rotateClockwise = rotation > 0;\n  if (!qFuzzyIsNull(rotation))\n  {\n    if (!qFuzzyCompare(qAbs(rotation), 90)) // avoid graphical collision with anchor tangent (e.g. axis line) when rotating, so change anchor side appropriately:\n    {\n      if (side == asTop) result = rotateClockwise ? asLeft : asRight;\n      else if (side == asBottom) result = rotateClockwise ? asRight : asLeft;\n      else if (side == asTopLeft) result = rotateClockwise ? asLeft : asTop;\n      else if (side == asTopRight) result = rotateClockwise ? asTop : asRight;\n      else if (side == asBottomLeft) result = rotateClockwise ? asBottom : asLeft;\n      else if (side == asBottomRight) result = rotateClockwise ? asRight : asBottom;\n    } else // for full rotation by +/-90 degrees, other sides are more appropriate for centering on anchor:\n    {\n      if (side == asLeft) result = rotateClockwise ? asBottom : asTop;\n      else if (side == asRight) result = rotateClockwise ? asTop : asBottom;\n      else if (side == asTop) result = rotateClockwise ? asLeft : asRight;\n      else if (side == asBottom) result = rotateClockwise ? asRight : asLeft;\n      else if (side == asTopLeft) result = rotateClockwise ? asBottomLeft : asTopRight;\n      else if (side == asTopRight) result = rotateClockwise ? asTopLeft : asBottomRight;\n      else if (side == asBottomLeft) result = rotateClockwise ? asBottomRight : asTopLeft;\n      else if (side == asBottomRight) result = rotateClockwise ? asTopRight : asBottomLeft;\n    }\n  }\n  return result;\n}\n\nvoid QCPLabelPainterPrivate::analyzeFontMetrics()\n{\n  const QFontMetrics fm(mFont);\n  mLetterCapHeight = fm.tightBoundingRect(QLatin1String(\"8\")).height(); // this method is slow, that's why we query it only upon font change\n  mLetterDescent = fm.descent();\n}\n/* end of 'src/axis/labelpainter.cpp' */\n\n\n/* including file 'src/axis/axisticker.cpp' */\n/* modified 2021-03-29T02:30:44, size 18688 */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPAxisTicker\n////////////////////////////////////////////////////////////////////////////////////////////////////\n/*! \\class QCPAxisTicker\n  \\brief The base class tick generator used by QCPAxis to create tick positions and tick labels\n  \n  Each QCPAxis has an internal QCPAxisTicker (or a subclass) in order to generate tick positions\n  and tick labels for the current axis range. The ticker of an axis can be set via \\ref\n  QCPAxis::setTicker. Since that method takes a <tt>QSharedPointer<QCPAxisTicker></tt>, multiple\n  axes can share the same ticker instance.\n  \n  This base class generates normal tick coordinates and numeric labels for linear axes. It picks a\n  reasonable tick step (the separation between ticks) which results in readable tick labels. The\n  number of ticks that should be approximately generated can be set via \\ref setTickCount.\n  Depending on the current tick step strategy (\\ref setTickStepStrategy), the algorithm either\n  sacrifices readability to better match the specified tick count (\\ref\n  QCPAxisTicker::tssMeetTickCount) or relaxes the tick count in favor of better tick steps (\\ref\n  QCPAxisTicker::tssReadability), which is the default.\n  \n  The following more specialized axis ticker subclasses are available, see details in the\n  respective class documentation:\n  \n  <center>\n  <table>\n  <tr><td style=\"text-align:right; padding: 0 1em\">QCPAxisTickerFixed</td><td>\\image html axisticker-fixed.png</td></tr>\n  <tr><td style=\"text-align:right; padding: 0 1em\">QCPAxisTickerLog</td><td>\\image html axisticker-log.png</td></tr>\n  <tr><td style=\"text-align:right; padding: 0 1em\">QCPAxisTickerPi</td><td>\\image html axisticker-pi.png</td></tr>\n  <tr><td style=\"text-align:right; padding: 0 1em\">QCPAxisTickerText</td><td>\\image html axisticker-text.png</td></tr>\n  <tr><td style=\"text-align:right; padding: 0 1em\">QCPAxisTickerDateTime</td><td>\\image html axisticker-datetime.png</td></tr>\n  <tr><td style=\"text-align:right; padding: 0 1em\">QCPAxisTickerTime</td><td>\\image html axisticker-time.png\n    \\image html axisticker-time2.png</td></tr>\n  </table>\n  </center>\n  \n  \\section axisticker-subclassing Creating own axis tickers\n  \n  Creating own axis tickers can be achieved very easily by sublassing QCPAxisTicker and\n  reimplementing some or all of the available virtual methods.\n\n  In the simplest case you might wish to just generate different tick steps than the other tickers,\n  so you only reimplement the method \\ref getTickStep. If you additionally want control over the\n  string that will be shown as tick label, reimplement \\ref getTickLabel.\n  \n  If you wish to have complete control, you can generate the tick vectors and tick label vectors\n  yourself by reimplementing \\ref createTickVector and \\ref createLabelVector. The default\n  implementations use the previously mentioned virtual methods \\ref getTickStep and \\ref\n  getTickLabel, but your reimplementations don't necessarily need to do so. For example in the case\n  of unequal tick steps, the method \\ref getTickStep loses its usefulness and can be ignored.\n  \n  The sub tick count between major ticks can be controlled with \\ref getSubTickCount. Full sub tick\n  placement control is obtained by reimplementing \\ref createSubTickVector.\n  \n  See the documentation of all these virtual methods in QCPAxisTicker for detailed information\n  about the parameters and expected return values.\n*/\n\n/*!\n  Constructs the ticker and sets reasonable default values. Axis tickers are commonly created\n  managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker.\n*/\nQCPAxisTicker::QCPAxisTicker() :\n  mTickStepStrategy(tssReadability),\n  mTickCount(5),\n  mTickOrigin(0)\n{\n}\n\nQCPAxisTicker::~QCPAxisTicker()\n{\n  \n}\n\n/*!\n  Sets which strategy the axis ticker follows when choosing the size of the tick step. For the\n  available strategies, see \\ref TickStepStrategy.\n*/\nvoid QCPAxisTicker::setTickStepStrategy(QCPAxisTicker::TickStepStrategy strategy)\n{\n  mTickStepStrategy = strategy;\n}\n\n/*!\n  Sets how many ticks this ticker shall aim to generate across the axis range. Note that \\a count\n  is not guaranteed to be matched exactly, as generating readable tick intervals may conflict with\n  the requested number of ticks.\n\n  Whether the readability has priority over meeting the requested \\a count can be specified with\n  \\ref setTickStepStrategy.\n*/\nvoid QCPAxisTicker::setTickCount(int count)\n{\n  if (count > 0)\n    mTickCount = count;\n  else\n    qDebug() << Q_FUNC_INFO << \"tick count must be greater than zero:\" << count;\n}\n\n/*!\n  Sets the mathematical coordinate (or \"offset\") of the zeroth tick. This tick coordinate is just a\n  concept and doesn't need to be inside the currently visible axis range.\n  \n  By default \\a origin is zero, which for example yields ticks {-5, 0, 5, 10, 15,...} when the tick\n  step is five. If \\a origin is now set to 1 instead, the correspondingly generated ticks would be\n  {-4, 1, 6, 11, 16,...}.\n*/\nvoid QCPAxisTicker::setTickOrigin(double origin)\n{\n  mTickOrigin = origin;\n}\n\n/*!\n  This is the method called by QCPAxis in order to actually generate tick coordinates (\\a ticks),\n  tick label strings (\\a tickLabels) and sub tick coordinates (\\a subTicks).\n  \n  The ticks are generated for the specified \\a range. The generated labels typically follow the\n  specified \\a locale, \\a formatChar and number \\a precision, however this might be different (or\n  even irrelevant) for certain QCPAxisTicker subclasses.\n  \n  The output parameter \\a ticks is filled with the generated tick positions in axis coordinates.\n  The output parameters \\a subTicks and \\a tickLabels are optional (set them to \\c nullptr if not\n  needed) and are respectively filled with sub tick coordinates, and tick label strings belonging\n  to \\a ticks by index.\n*/\nvoid QCPAxisTicker::generate(const QCPRange &range, const QLocale &locale, QChar formatChar, int precision, QVector<double> &ticks, QVector<double> *subTicks, QVector<QString> *tickLabels)\n{\n  // generate (major) ticks:\n  double tickStep = getTickStep(range);\n  ticks = createTickVector(tickStep, range);\n  trimTicks(range, ticks, true); // trim ticks to visible range plus one outer tick on each side (incase a subclass createTickVector creates more)\n  \n  // generate sub ticks between major ticks:\n  if (subTicks)\n  {\n    if (!ticks.isEmpty())\n    {\n      *subTicks = createSubTickVector(getSubTickCount(tickStep), ticks);\n      trimTicks(range, *subTicks, false);\n    } else\n      *subTicks = QVector<double>();\n  }\n  \n  // finally trim also outliers (no further clipping happens in axis drawing):\n  trimTicks(range, ticks, false);\n  // generate labels for visible ticks if requested:\n  if (tickLabels)\n    *tickLabels = createLabelVector(ticks, locale, formatChar, precision);\n}\n\n/*! \\internal\n  \n  Takes the entire currently visible axis range and returns a sensible tick step in\n  order to provide readable tick labels as well as a reasonable number of tick counts (see \\ref\n  setTickCount, \\ref setTickStepStrategy).\n  \n  If a QCPAxisTicker subclass only wants a different tick step behaviour than the default\n  implementation, it should reimplement this method. See \\ref cleanMantissa for a possible helper\n  function.\n*/\ndouble QCPAxisTicker::getTickStep(const QCPRange &range)\n{\n  double exactStep = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers\n  return cleanMantissa(exactStep);\n}\n\n/*! \\internal\n  \n  Takes the \\a tickStep, i.e. the distance between two consecutive ticks, and returns\n  an appropriate number of sub ticks for that specific tick step.\n  \n  Note that a returned sub tick count of e.g. 4 will split each tick interval into 5 sections.\n*/\nint QCPAxisTicker::getSubTickCount(double tickStep)\n{\n  int result = 1; // default to 1, if no proper value can be found\n  \n  // separate integer and fractional part of mantissa:\n  double epsilon = 0.01;\n  double intPartf;\n  int intPart;\n  double fracPart = modf(getMantissa(tickStep), &intPartf);\n  intPart = int(intPartf);\n  \n  // handle cases with (almost) integer mantissa:\n  if (fracPart < epsilon || 1.0-fracPart < epsilon)\n  {\n    if (1.0-fracPart < epsilon)\n      ++intPart;\n    switch (intPart)\n    {\n      case 1: result = 4; break; // 1.0 -> 0.2 substep\n      case 2: result = 3; break; // 2.0 -> 0.5 substep\n      case 3: result = 2; break; // 3.0 -> 1.0 substep\n      case 4: result = 3; break; // 4.0 -> 1.0 substep\n      case 5: result = 4; break; // 5.0 -> 1.0 substep\n      case 6: result = 2; break; // 6.0 -> 2.0 substep\n      case 7: result = 6; break; // 7.0 -> 1.0 substep\n      case 8: result = 3; break; // 8.0 -> 2.0 substep\n      case 9: result = 2; break; // 9.0 -> 3.0 substep\n    }\n  } else\n  {\n    // handle cases with significantly fractional mantissa:\n    if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa\n    {\n      switch (intPart)\n      {\n        case 1: result = 2; break; // 1.5 -> 0.5 substep\n        case 2: result = 4; break; // 2.5 -> 0.5 substep\n        case 3: result = 4; break; // 3.5 -> 0.7 substep\n        case 4: result = 2; break; // 4.5 -> 1.5 substep\n        case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with default getTickStep from here on)\n        case 6: result = 4; break; // 6.5 -> 1.3 substep\n        case 7: result = 2; break; // 7.5 -> 2.5 substep\n        case 8: result = 4; break; // 8.5 -> 1.7 substep\n        case 9: result = 4; break; // 9.5 -> 1.9 substep\n      }\n    }\n    // if mantissa fraction isn't 0.0 or 0.5, don't bother finding good sub tick marks, leave default\n  }\n  \n  return result;\n}\n\n/*! \\internal\n  \n  This method returns the tick label string as it should be printed under the \\a tick coordinate.\n  If a textual number is returned, it should respect the provided \\a locale, \\a formatChar and \\a\n  precision.\n  \n  If the returned value contains exponentials of the form \"2e5\" and beautifully typeset powers is\n  enabled in the QCPAxis number format (\\ref QCPAxis::setNumberFormat), the exponential part will\n  be formatted accordingly using multiplication symbol and superscript during rendering of the\n  label automatically.\n*/\nQString QCPAxisTicker::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)\n{\n  return locale.toString(tick, formatChar.toLatin1(), precision);\n}\n\n/*! \\internal\n  \n  Returns a vector containing all coordinates of sub ticks that should be drawn. It generates \\a\n  subTickCount sub ticks between each tick pair given in \\a ticks.\n  \n  If a QCPAxisTicker subclass needs maximal control over the generated sub ticks, it should\n  reimplement this method. Depending on the purpose of the subclass it doesn't necessarily need to\n  base its result on \\a subTickCount or \\a ticks.\n*/\nQVector<double> QCPAxisTicker::createSubTickVector(int subTickCount, const QVector<double> &ticks)\n{\n  QVector<double> result;\n  if (subTickCount <= 0 || ticks.size() < 2)\n    return result;\n  \n  result.reserve((ticks.size()-1)*subTickCount);\n  for (int i=1; i<ticks.size(); ++i)\n  {\n    double subTickStep = (ticks.at(i)-ticks.at(i-1))/double(subTickCount+1);\n    for (int k=1; k<=subTickCount; ++k)\n      result.append(ticks.at(i-1) + k*subTickStep);\n  }\n  return result;\n}\n\n/*! \\internal\n  \n  Returns a vector containing all coordinates of ticks that should be drawn. The default\n  implementation generates ticks with a spacing of \\a tickStep (mathematically starting at the tick\n  step origin, see \\ref setTickOrigin) distributed over the passed \\a range.\n  \n  In order for the axis ticker to generate proper sub ticks, it is necessary that the first and\n  last tick coordinates returned by this method are just below/above the provided \\a range.\n  Otherwise the outer intervals won't contain any sub ticks.\n  \n  If a QCPAxisTicker subclass needs maximal control over the generated ticks, it should reimplement\n  this method. Depending on the purpose of the subclass it doesn't necessarily need to base its\n  result on \\a tickStep, e.g. when the ticks are spaced unequally like in the case of\n  QCPAxisTickerLog.\n*/\nQVector<double> QCPAxisTicker::createTickVector(double tickStep, const QCPRange &range)\n{\n  QVector<double> result;\n  // Generate tick positions according to tickStep:\n  qint64 firstStep = qint64(floor((range.lower-mTickOrigin)/tickStep)); // do not use qFloor here, or we'll lose 64 bit precision\n  qint64 lastStep = qint64(ceil((range.upper-mTickOrigin)/tickStep)); // do not use qCeil here, or we'll lose 64 bit precision\n  int tickcount = int(lastStep-firstStep+1);\n  if (tickcount < 0) tickcount = 0;\n  result.resize(tickcount);\n  for (int i=0; i<tickcount; ++i)\n    result[i] = mTickOrigin + (firstStep+i)*tickStep;\n  return result;\n}\n\n/*! \\internal\n  \n  Returns a vector containing all tick label strings corresponding to the tick coordinates provided\n  in \\a ticks. The default implementation calls \\ref getTickLabel to generate the respective\n  strings.\n  \n  It is possible but uncommon for QCPAxisTicker subclasses to reimplement this method, as\n  reimplementing \\ref getTickLabel often achieves the intended result easier.\n*/\nQVector<QString> QCPAxisTicker::createLabelVector(const QVector<double> &ticks, const QLocale &locale, QChar formatChar, int precision)\n{\n  QVector<QString> result;\n  result.reserve(ticks.size());\n  foreach (double tickCoord, ticks)\n    result.append(getTickLabel(tickCoord, locale, formatChar, precision));\n  return result;\n}\n\n/*! \\internal\n  \n  Removes tick coordinates from \\a ticks which lie outside the specified \\a range. If \\a\n  keepOneOutlier is true, it preserves one tick just outside the range on both sides, if present.\n  \n  The passed \\a ticks must be sorted in ascending order.\n*/\nvoid QCPAxisTicker::trimTicks(const QCPRange &range, QVector<double> &ticks, bool keepOneOutlier) const\n{\n  bool lowFound = false;\n  bool highFound = false;\n  int lowIndex = 0;\n  int highIndex = -1;\n  \n  for (int i=0; i < ticks.size(); ++i)\n  {\n    if (ticks.at(i) >= range.lower)\n    {\n      lowFound = true;\n      lowIndex = i;\n      break;\n    }\n  }\n  for (int i=ticks.size()-1; i >= 0; --i)\n  {\n    if (ticks.at(i) <= range.upper)\n    {\n      highFound = true;\n      highIndex = i;\n      break;\n    }\n  }\n  \n  if (highFound && lowFound)\n  {\n    int trimFront = qMax(0, lowIndex-(keepOneOutlier ? 1 : 0));\n    int trimBack = qMax(0, ticks.size()-(keepOneOutlier ? 2 : 1)-highIndex);\n    if (trimFront > 0 || trimBack > 0)\n      ticks = ticks.mid(trimFront, ticks.size()-trimFront-trimBack);\n  } else // all ticks are either all below or all above the range\n    ticks.clear();\n}\n\n/*! \\internal\n  \n  Returns the coordinate contained in \\a candidates which is closest to the provided \\a target.\n  \n  This method assumes \\a candidates is not empty and sorted in ascending order.\n*/\ndouble QCPAxisTicker::pickClosest(double target, const QVector<double> &candidates) const\n{\n  if (candidates.size() == 1)\n    return candidates.first();\n  QVector<double>::const_iterator it = std::lower_bound(candidates.constBegin(), candidates.constEnd(), target);\n  if (it == candidates.constEnd())\n    return *(it-1);\n  else if (it == candidates.constBegin())\n    return *it;\n  else\n    return target-*(it-1) < *it-target ? *(it-1) : *it;\n}\n\n/*! \\internal\n  \n  Returns the decimal mantissa of \\a input. Optionally, if \\a magnitude is not set to zero, it also\n  returns the magnitude of \\a input as a power of 10.\n  \n  For example, an input of 142.6 will return a mantissa of 1.426 and a magnitude of 100.\n*/\ndouble QCPAxisTicker::getMantissa(double input, double *magnitude) const\n{\n  const double mag = qPow(10.0, qFloor(qLn(input)/qLn(10.0)));\n  if (magnitude) *magnitude = mag;\n  return input/mag;\n}\n\n/*! \\internal\n  \n  Returns a number that is close to \\a input but has a clean, easier human readable mantissa. How\n  strongly the mantissa is altered, and thus how strong the result deviates from the original \\a\n  input, depends on the current tick step strategy (see \\ref setTickStepStrategy).\n*/\ndouble QCPAxisTicker::cleanMantissa(double input) const\n{\n  double magnitude;\n  const double mantissa = getMantissa(input, &magnitude);\n  switch (mTickStepStrategy)\n  {\n    case tssReadability:\n    {\n      return pickClosest(mantissa, QVector<double>() << 1.0 << 2.0 << 2.5 << 5.0 << 10.0)*magnitude;\n    }\n    case tssMeetTickCount:\n    {\n      // this gives effectively a mantissa of 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 8.0, 10.0\n      if (mantissa <= 5.0)\n        return int(mantissa*2)/2.0*magnitude; // round digit after decimal point to 0.5\n      else\n        return int(mantissa/2.0)*2.0*magnitude; // round to first digit in multiples of 2\n    }\n  }\n  return input;\n}\n/* end of 'src/axis/axisticker.cpp' */\n\n\n/* including file 'src/axis/axistickerdatetime.cpp' */\n/* modified 2021-03-29T02:30:44, size 18829         */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPAxisTickerDateTime\n////////////////////////////////////////////////////////////////////////////////////////////////////\n/*! \\class QCPAxisTickerDateTime\n  \\brief Specialized axis ticker for calendar dates and times as axis ticks\n  \n  \\image html axisticker-datetime.png\n  \n  This QCPAxisTicker subclass generates ticks that correspond to real calendar dates and times. The\n  plot axis coordinate is interpreted as Unix Time, so seconds since Epoch (January 1, 1970, 00:00\n  UTC). This is also used for example by QDateTime in the <tt>toTime_t()/setTime_t()</tt> methods\n  with a precision of one second. Since Qt 4.7, millisecond accuracy can be obtained from QDateTime\n  by using <tt>QDateTime::fromMSecsSinceEpoch()/1000.0</tt>. The static methods \\ref dateTimeToKey\n  and \\ref keyToDateTime conveniently perform this conversion achieving a precision of one\n  millisecond on all Qt versions.\n  \n  The format of the date/time display in the tick labels is controlled with \\ref setDateTimeFormat.\n  If a different time spec or time zone shall be used for the tick label appearance, see \\ref\n  setDateTimeSpec or \\ref setTimeZone, respectively.\n  \n  This ticker produces unequal tick spacing in order to provide intuitive date and time-of-day\n  ticks. For example, if the axis range spans a few years such that there is one tick per year,\n  ticks will be positioned on 1. January of every year. This is intuitive but, due to leap years,\n  will result in slightly unequal tick intervals (visually unnoticeable). The same can be seen in\n  the image above: even though the number of days varies month by month, this ticker generates\n  ticks on the same day of each month.\n  \n  If you would like to change the date/time that is used as a (mathematical) starting date for the\n  ticks, use the \\ref setTickOrigin(const QDateTime &origin) method overload, which takes a\n  QDateTime. If you pass 15. July, 9:45 to this method, the yearly ticks will end up on 15. July at\n  9:45 of every year.\n  \n  The ticker can be created and assigned to an axis like this:\n  \\snippet documentation/doc-image-generator/mainwindow.cpp axistickerdatetime-creation\n  \n  \\note If you rather wish to display relative times in terms of days, hours, minutes, seconds and\n  milliseconds, and are not interested in the intricacies of real calendar dates with months and\n  (leap) years, have a look at QCPAxisTickerTime instead.\n*/\n\n/*!\n  Constructs the ticker and sets reasonable default values. Axis tickers are commonly created\n  managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker.\n*/\nQCPAxisTickerDateTime::QCPAxisTickerDateTime() :\n  mDateTimeFormat(QLatin1String(\"hh:mm:ss\\ndd.MM.yy\")),\n  mDateTimeSpec(Qt::LocalTime),\n  mDateStrategy(dsNone)\n{\n  setTickCount(4);\n}\n\n/*!\n  Sets the format in which dates and times are displayed as tick labels. For details about the \\a\n  format string, see the documentation of QDateTime::toString().\n  \n  Typical expressions are\n  <table>\n    <tr><td>\\c d</td><td>The day as a number without a leading zero (1 to 31)</td></tr>\n    <tr><td>\\c dd</td><td>The day as a number with a leading zero (01 to 31)</td></tr>\n    <tr><td>\\c ddd</td><td>The abbreviated localized day name (e.g. 'Mon' to 'Sun'). Uses the system locale to localize the name, i.e. QLocale::system().</td></tr>\n    <tr><td>\\c dddd</td><td>The long localized day name (e.g. 'Monday' to 'Sunday'). Uses the system locale to localize the name, i.e. QLocale::system().</td></tr>\n    <tr><td>\\c M</td><td>The month as a number without a leading zero (1 to 12)</td></tr>\n    <tr><td>\\c MM</td><td>The month as a number with a leading zero (01 to 12)</td></tr>\n    <tr><td>\\c MMM</td><td>The abbreviated localized month name (e.g. 'Jan' to 'Dec'). Uses the system locale to localize the name, i.e. QLocale::system().</td></tr>\n    <tr><td>\\c MMMM</td><td>The long localized month name (e.g. 'January' to 'December'). Uses the system locale to localize the name, i.e. QLocale::system().</td></tr>\n    <tr><td>\\c yy</td><td>The year as a two digit number (00 to 99)</td></tr>\n    <tr><td>\\c yyyy</td><td>The year as a four digit number. If the year is negative, a minus sign is prepended, making five characters.</td></tr>\n    <tr><td>\\c h</td><td>The hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display)</td></tr>\n    <tr><td>\\c hh</td><td>The hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display)</td></tr>\n    <tr><td>\\c H</td><td>The hour without a leading zero (0 to 23, even with AM/PM display)</td></tr>\n    <tr><td>\\c HH</td><td>The hour with a leading zero (00 to 23, even with AM/PM display)</td></tr>\n    <tr><td>\\c m</td><td>The minute without a leading zero (0 to 59)</td></tr>\n    <tr><td>\\c mm</td><td>The minute with a leading zero (00 to 59)</td></tr>\n    <tr><td>\\c s</td><td>The whole second, without any leading zero (0 to 59)</td></tr>\n    <tr><td>\\c ss</td><td>The whole second, with a leading zero where applicable (00 to 59)</td></tr>\n    <tr><td>\\c z</td><td>The fractional part of the second, to go after a decimal point, without trailing zeroes (0 to 999). Thus \"s.z\" reports the seconds to full available (millisecond) precision without trailing zeroes.</td></tr>\n    <tr><td>\\c zzz</td><td>The fractional part of the second, to millisecond precision, including trailing zeroes where applicable (000 to 999).</td></tr>\n    <tr><td>\\c AP or \\c A</td><td>Use AM/PM display. A/AP will be replaced by an upper-case version of either QLocale::amText() or QLocale::pmText().</td></tr>\n    <tr><td>\\c ap or \\c a</td><td>Use am/pm display. a/ap will be replaced by a lower-case version of either QLocale::amText() or QLocale::pmText().</td></tr>\n    <tr><td>\\c t</td><td>The timezone (for example \"CEST\")</td></tr>\n  </table>\n  \n  Newlines can be inserted with \\c \"\\n\", literal strings (even when containing above expressions)\n  by encapsulating them using single-quotes. A literal single quote can be generated by using two\n  consecutive single quotes in the format.\n  \n  \\see setDateTimeSpec, setTimeZone\n*/\nvoid QCPAxisTickerDateTime::setDateTimeFormat(const QString &format)\n{\n  mDateTimeFormat = format;\n}\n\n/*!\n  Sets the time spec that is used for creating the tick labels from corresponding dates/times.\n\n  The default value of QDateTime objects (and also QCPAxisTickerDateTime) is\n  <tt>Qt::LocalTime</tt>. However, if the displayed tick labels shall be given in UTC, set \\a spec\n  to <tt>Qt::UTC</tt>.\n  \n  Tick labels corresponding to other time zones can be achieved with \\ref setTimeZone (which sets\n  \\a spec to \\c Qt::TimeZone internally). Note that if \\a spec is afterwards set to not be \\c\n  Qt::TimeZone again, the \\ref setTimeZone setting will be ignored accordingly.\n  \n  \\see setDateTimeFormat, setTimeZone\n*/\nvoid QCPAxisTickerDateTime::setDateTimeSpec(Qt::TimeSpec spec)\n{\n  mDateTimeSpec = spec;\n}\n\n# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)\n/*!\n  Sets the time zone that is used for creating the tick labels from corresponding dates/times. The\n  time spec (\\ref setDateTimeSpec) is set to \\c Qt::TimeZone.\n  \n  \\see setDateTimeFormat, setTimeZone\n*/\nvoid QCPAxisTickerDateTime::setTimeZone(const QTimeZone &zone)\n{\n  mTimeZone = zone;\n  mDateTimeSpec = Qt::TimeZone;\n}\n#endif\n\n/*!\n  Sets the tick origin (see \\ref QCPAxisTicker::setTickOrigin) in seconds since Epoch (1. Jan 1970,\n  00:00 UTC). For the date time ticker it might be more intuitive to use the overload which\n  directly takes a QDateTime, see \\ref setTickOrigin(const QDateTime &origin).\n  \n  This is useful to define the month/day/time recurring at greater tick interval steps. For\n  example, If you pass 15. July, 9:45 to this method and the tick interval happens to be one tick\n  per year, the ticks will end up on 15. July at 9:45 of every year.\n*/\nvoid QCPAxisTickerDateTime::setTickOrigin(double origin)\n{\n  QCPAxisTicker::setTickOrigin(origin);\n}\n\n/*!\n  Sets the tick origin (see \\ref QCPAxisTicker::setTickOrigin) as a QDateTime \\a origin.\n  \n  This is useful to define the month/day/time recurring at greater tick interval steps. For\n  example, If you pass 15. July, 9:45 to this method and the tick interval happens to be one tick\n  per year, the ticks will end up on 15. July at 9:45 of every year.\n*/\nvoid QCPAxisTickerDateTime::setTickOrigin(const QDateTime &origin)\n{\n  setTickOrigin(dateTimeToKey(origin));\n}\n\n/*! \\internal\n  \n  Returns a sensible tick step with intervals appropriate for a date-time-display, such as weekly,\n  monthly, bi-monthly, etc.\n  \n  Note that this tick step isn't used exactly when generating the tick vector in \\ref\n  createTickVector, but only as a guiding value requiring some correction for each individual tick\n  interval. Otherwise this would lead to unintuitive date displays, e.g. jumping between first day\n  in the month to the last day in the previous month from tick to tick, due to the non-uniform\n  length of months. The same problem arises with leap years.\n  \n  \\seebaseclassmethod\n*/\ndouble QCPAxisTickerDateTime::getTickStep(const QCPRange &range)\n{\n  double result = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers\n  \n  mDateStrategy = dsNone; // leaving it at dsNone means tick coordinates will not be tuned in any special way in createTickVector\n  if (result < 1) // ideal tick step is below 1 second -> use normal clean mantissa algorithm in units of seconds\n  {\n    result = cleanMantissa(result);\n  } else if (result < 86400*30.4375*12) // below a year\n  {\n    result = pickClosest(result, QVector<double>()\n                             << 1 << 2.5 << 5 << 10 << 15 << 30 << 60 << 2.5*60 << 5*60 << 10*60 << 15*60 << 30*60 << 60*60 // second, minute, hour range\n                             << 3600*2 << 3600*3 << 3600*6 << 3600*12 << 3600*24 // hour to day range\n                             << 86400*2 << 86400*5 << 86400*7 << 86400*14 << 86400*30.4375 << 86400*30.4375*2 << 86400*30.4375*3 << 86400*30.4375*6 << 86400*30.4375*12); // day, week, month range (avg. days per month includes leap years)\n    if (result > 86400*30.4375-1) // month tick intervals or larger\n      mDateStrategy = dsUniformDayInMonth;\n    else if (result > 3600*24-1) // day tick intervals or larger\n      mDateStrategy = dsUniformTimeInDay;\n  } else // more than a year, go back to normal clean mantissa algorithm but in units of years\n  {\n    const double secondsPerYear = 86400*30.4375*12; // average including leap years\n    result = cleanMantissa(result/secondsPerYear)*secondsPerYear;\n    mDateStrategy = dsUniformDayInMonth;\n  }\n  return result;\n}\n\n/*! \\internal\n  \n  Returns a sensible sub tick count with intervals appropriate for a date-time-display, such as weekly,\n  monthly, bi-monthly, etc.\n  \n  \\seebaseclassmethod\n*/\nint QCPAxisTickerDateTime::getSubTickCount(double tickStep)\n{\n  int result = QCPAxisTicker::getSubTickCount(tickStep);\n  switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day/week/month range (as specified in getTickStep)\n  {\n    case 5*60: result = 4; break;\n    case 10*60: result = 1; break;\n    case 15*60: result = 2; break;\n    case 30*60: result = 1; break;\n    case 60*60: result = 3; break;\n    case 3600*2: result = 3; break;\n    case 3600*3: result = 2; break;\n    case 3600*6: result = 1; break;\n    case 3600*12: result = 3; break;\n    case 3600*24: result = 3; break;\n    case 86400*2: result = 1; break;\n    case 86400*5: result = 4; break;\n    case 86400*7: result = 6; break;\n    case 86400*14: result = 1; break;\n    case int(86400*30.4375+0.5): result = 3; break;\n    case int(86400*30.4375*2+0.5): result = 1; break;\n    case int(86400*30.4375*3+0.5): result = 2; break;\n    case int(86400*30.4375*6+0.5): result = 5; break;\n    case int(86400*30.4375*12+0.5): result = 3; break;\n  }\n  return result;\n}\n\n/*! \\internal\n  \n  Generates a date/time tick label for tick coordinate \\a tick, based on the currently set format\n  (\\ref setDateTimeFormat), time spec (\\ref setDateTimeSpec), and possibly time zone (\\ref\n  setTimeZone).\n  \n  \\seebaseclassmethod\n*/\nQString QCPAxisTickerDateTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)\n{\n  Q_UNUSED(precision)\n  Q_UNUSED(formatChar)\n# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)\n  if (mDateTimeSpec == Qt::TimeZone)\n    return locale.toString(keyToDateTime(tick).toTimeZone(mTimeZone), mDateTimeFormat);\n  else\n    return locale.toString(keyToDateTime(tick).toTimeSpec(mDateTimeSpec), mDateTimeFormat);\n# else\n  return locale.toString(keyToDateTime(tick).toTimeSpec(mDateTimeSpec), mDateTimeFormat);\n# endif\n}\n\n/*! \\internal\n  \n  Uses the passed \\a tickStep as a guiding value and applies corrections in order to obtain\n  non-uniform tick intervals but intuitive tick labels, e.g. falling on the same day of each month.\n  \n  \\seebaseclassmethod\n*/\nQVector<double> QCPAxisTickerDateTime::createTickVector(double tickStep, const QCPRange &range)\n{\n  QVector<double> result = QCPAxisTicker::createTickVector(tickStep, range);\n  if (!result.isEmpty())\n  {\n    if (mDateStrategy == dsUniformTimeInDay)\n    {\n      QDateTime uniformDateTime = keyToDateTime(mTickOrigin); // the time of this datetime will be set for all other ticks, if possible\n      QDateTime tickDateTime;\n      for (int i=0; i<result.size(); ++i)\n      {\n        tickDateTime = keyToDateTime(result.at(i));\n        tickDateTime.setTime(uniformDateTime.time());\n        result[i] = dateTimeToKey(tickDateTime);\n      }\n    } else if (mDateStrategy == dsUniformDayInMonth)\n    {\n      QDateTime uniformDateTime = keyToDateTime(mTickOrigin); // this day (in month) and time will be set for all other ticks, if possible\n      QDateTime tickDateTime;\n      for (int i=0; i<result.size(); ++i)\n      {\n        tickDateTime = keyToDateTime(result.at(i));\n        tickDateTime.setTime(uniformDateTime.time());\n        int thisUniformDay = uniformDateTime.date().day() <= tickDateTime.date().daysInMonth() ? uniformDateTime.date().day() : tickDateTime.date().daysInMonth(); // don't exceed month (e.g. try to set day 31 in February)\n        if (thisUniformDay-tickDateTime.date().day() < -15) // with leap years involved, date month may jump backwards or forwards, and needs to be corrected before setting day\n          tickDateTime = tickDateTime.addMonths(1);\n        else if (thisUniformDay-tickDateTime.date().day() > 15) // with leap years involved, date month may jump backwards or forwards, and needs to be corrected before setting day\n          tickDateTime = tickDateTime.addMonths(-1);\n        tickDateTime.setDate(QDate(tickDateTime.date().year(), tickDateTime.date().month(), thisUniformDay));\n        result[i] = dateTimeToKey(tickDateTime);\n      }\n    }\n  }\n  return result;\n}\n\n/*!\n  A convenience method which turns \\a key (in seconds since Epoch 1. Jan 1970, 00:00 UTC) into a\n  QDateTime object. This can be used to turn axis coordinates to actual QDateTimes.\n  \n  The accuracy achieved by this method is one millisecond, irrespective of the used Qt version (it\n  works around the lack of a QDateTime::fromMSecsSinceEpoch in Qt 4.6)\n  \n  \\see dateTimeToKey\n*/\nQDateTime QCPAxisTickerDateTime::keyToDateTime(double key)\n{\n# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)\n  return QDateTime::fromTime_t(key).addMSecs((key-(qint64)key)*1000);\n# else\n  return QDateTime::fromMSecsSinceEpoch(qint64(key*1000.0));\n# endif\n}\n\n/*! \\overload\n  \n  A convenience method which turns a QDateTime object into a double value that corresponds to\n  seconds since Epoch (1. Jan 1970, 00:00 UTC). This is the format used as axis coordinates by\n  QCPAxisTickerDateTime.\n  \n  The accuracy achieved by this method is one millisecond, irrespective of the used Qt version (it\n  works around the lack of a QDateTime::toMSecsSinceEpoch in Qt 4.6)\n  \n  \\see keyToDateTime\n*/\ndouble QCPAxisTickerDateTime::dateTimeToKey(const QDateTime &dateTime)\n{\n# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)\n  return dateTime.toTime_t()+dateTime.time().msec()/1000.0;\n# else\n  return dateTime.toMSecsSinceEpoch()/1000.0;\n# endif\n}\n\n/*! \\overload\n  \n  A convenience method which turns a QDate object into a double value that corresponds to seconds\n  since Epoch (1. Jan 1970, 00:00 UTC). This is the format used\n  as axis coordinates by QCPAxisTickerDateTime.\n  \n  The returned value will be the start of the passed day of \\a date, interpreted in the given \\a\n  timeSpec.\n  \n  \\see keyToDateTime\n*/\ndouble QCPAxisTickerDateTime::dateTimeToKey(const QDate &date, Qt::TimeSpec timeSpec)\n{\n# if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)\n  return QDateTime(date, QTime(0, 0), timeSpec).toTime_t();\n# elif QT_VERSION < QT_VERSION_CHECK(5, 14, 0)\n  return QDateTime(date, QTime(0, 0), timeSpec).toMSecsSinceEpoch()/1000.0;\n# else\n  return date.startOfDay(timeSpec).toMSecsSinceEpoch()/1000.0;\n# endif\n}\n/* end of 'src/axis/axistickerdatetime.cpp' */\n\n\n/* including file 'src/axis/axistickertime.cpp' */\n/* modified 2021-03-29T02:30:44, size 11745     */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPAxisTickerTime\n////////////////////////////////////////////////////////////////////////////////////////////////////\n/*! \\class QCPAxisTickerTime\n  \\brief Specialized axis ticker for time spans in units of milliseconds to days\n  \n  \\image html axisticker-time.png\n  \n  This QCPAxisTicker subclass generates ticks that corresponds to time intervals.\n  \n  The format of the time display in the tick labels is controlled with \\ref setTimeFormat and \\ref\n  setFieldWidth. The time coordinate is in the unit of seconds with respect to the time coordinate\n  zero. Unlike with QCPAxisTickerDateTime, the ticks don't correspond to a specific calendar date\n  and time.\n  \n  The time can be displayed in milliseconds, seconds, minutes, hours and days. Depending on the\n  largest available unit in the format specified with \\ref setTimeFormat, any time spans above will\n  be carried in that largest unit. So for example if the format string is \"%m:%s\" and a tick at\n  coordinate value 7815 (being 2 hours, 10 minutes and 15 seconds) is created, the resulting tick\n  label will show \"130:15\" (130 minutes, 15 seconds). If the format string is \"%h:%m:%s\", the hour\n  unit will be used and the label will thus be \"02:10:15\". Negative times with respect to the axis\n  zero will carry a leading minus sign.\n  \n  The ticker can be created and assigned to an axis like this:\n  \\snippet documentation/doc-image-generator/mainwindow.cpp axistickertime-creation\n  \n  Here is an example of a time axis providing time information in days, hours and minutes. Due to\n  the axis range spanning a few days and the wanted tick count (\\ref setTickCount), the ticker\n  decided to use tick steps of 12 hours:\n  \n  \\image html axisticker-time2.png\n  \n  The format string for this example is\n  \\snippet documentation/doc-image-generator/mainwindow.cpp axistickertime-creation-2\n  \n  \\note If you rather wish to display calendar dates and times, have a look at QCPAxisTickerDateTime\n  instead.\n*/\n\n/*!\n  Constructs the ticker and sets reasonable default values. Axis tickers are commonly created\n  managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker.\n*/\nQCPAxisTickerTime::QCPAxisTickerTime() :\n  mTimeFormat(QLatin1String(\"%h:%m:%s\")),\n  mSmallestUnit(tuSeconds),\n  mBiggestUnit(tuHours)\n{\n  setTickCount(4);\n  mFieldWidth[tuMilliseconds] = 3;\n  mFieldWidth[tuSeconds] = 2;\n  mFieldWidth[tuMinutes] = 2;\n  mFieldWidth[tuHours] = 2;\n  mFieldWidth[tuDays] = 1;\n  \n  mFormatPattern[tuMilliseconds] = QLatin1String(\"%z\");\n  mFormatPattern[tuSeconds] = QLatin1String(\"%s\");\n  mFormatPattern[tuMinutes] = QLatin1String(\"%m\");\n  mFormatPattern[tuHours] = QLatin1String(\"%h\");\n  mFormatPattern[tuDays] = QLatin1String(\"%d\");\n}\n\n/*!\n  Sets the format that will be used to display time in the tick labels.\n  \n  The available patterns are:\n  - %%z for milliseconds\n  - %%s for seconds\n  - %%m for minutes\n  - %%h for hours\n  - %%d for days\n  \n  The field width (zero padding) can be controlled for each unit with \\ref setFieldWidth.\n  \n  The largest unit that appears in \\a format will carry all the remaining time of a certain tick\n  coordinate, even if it overflows the natural limit of the unit. For example, if %%m is the\n  largest unit it might become larger than 59 in order to consume larger time values. If on the\n  other hand %%h is available, the minutes will wrap around to zero after 59 and the time will\n  carry to the hour digit.\n*/\nvoid QCPAxisTickerTime::setTimeFormat(const QString &format)\n{\n  mTimeFormat = format;\n  \n  // determine smallest and biggest unit in format, to optimize unit replacement and allow biggest\n  // unit to consume remaining time of a tick value and grow beyond its modulo (e.g. min > 59)\n  mSmallestUnit = tuMilliseconds;\n  mBiggestUnit = tuMilliseconds;\n  bool hasSmallest = false;\n  for (int i = tuMilliseconds; i <= tuDays; ++i)\n  {\n    TimeUnit unit = static_cast<TimeUnit>(i);\n    if (mTimeFormat.contains(mFormatPattern.value(unit)))\n    {\n      if (!hasSmallest)\n      {\n        mSmallestUnit = unit;\n        hasSmallest = true;\n      }\n      mBiggestUnit = unit;\n    }\n  }\n}\n\n/*!\n  Sets the field widh of the specified \\a unit to be \\a width digits, when displayed in the tick\n  label. If the number for the specific unit is shorter than \\a width, it will be padded with an\n  according number of zeros to the left in order to reach the field width.\n  \n  \\see setTimeFormat\n*/\nvoid QCPAxisTickerTime::setFieldWidth(QCPAxisTickerTime::TimeUnit unit, int width)\n{\n  mFieldWidth[unit] = qMax(width, 1);\n}\n\n/*! \\internal\n\n  Returns the tick step appropriate for time displays, depending on the provided \\a range and the\n  smallest available time unit in the current format (\\ref setTimeFormat). For example if the unit\n  of seconds isn't available in the format, this method will not generate steps (like 2.5 minutes)\n  that require sub-minute precision to be displayed correctly.\n  \n  \\seebaseclassmethod\n*/\ndouble QCPAxisTickerTime::getTickStep(const QCPRange &range)\n{\n  double result = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers\n  \n  if (result < 1) // ideal tick step is below 1 second -> use normal clean mantissa algorithm in units of seconds\n  {\n    if (mSmallestUnit == tuMilliseconds)\n      result = qMax(cleanMantissa(result), 0.001); // smallest tick step is 1 millisecond\n    else // have no milliseconds available in format, so stick with 1 second tickstep\n      result = 1.0;\n  } else if (result < 3600*24) // below a day\n  {\n    // the filling of availableSteps seems a bit contorted but it fills in a sorted fashion and thus saves a post-fill sorting run\n    QVector<double> availableSteps;\n    // seconds range:\n    if (mSmallestUnit <= tuSeconds)\n      availableSteps << 1;\n    if (mSmallestUnit == tuMilliseconds)\n      availableSteps << 2.5; // only allow half second steps if milliseconds are there to display it\n    else if (mSmallestUnit == tuSeconds)\n      availableSteps << 2;\n    if (mSmallestUnit <= tuSeconds)\n      availableSteps << 5 << 10 << 15 << 30;\n    // minutes range:\n    if (mSmallestUnit <= tuMinutes)\n      availableSteps << 1*60;\n    if (mSmallestUnit <= tuSeconds)\n      availableSteps << 2.5*60; // only allow half minute steps if seconds are there to display it\n    else if (mSmallestUnit == tuMinutes)\n      availableSteps << 2*60;\n    if (mSmallestUnit <= tuMinutes)\n      availableSteps << 5*60 << 10*60 << 15*60 << 30*60;\n    // hours range:\n    if (mSmallestUnit <= tuHours)\n      availableSteps << 1*3600 << 2*3600 << 3*3600 << 6*3600 << 12*3600 << 24*3600;\n    // pick available step that is most appropriate to approximate ideal step:\n    result = pickClosest(result, availableSteps);\n  } else // more than a day, go back to normal clean mantissa algorithm but in units of days\n  {\n    const double secondsPerDay = 3600*24;\n    result = cleanMantissa(result/secondsPerDay)*secondsPerDay;\n  }\n  return result;\n}\n\n/*! \\internal\n\n  Returns the sub tick count appropriate for the provided \\a tickStep and time displays.\n  \n  \\seebaseclassmethod\n*/\nint QCPAxisTickerTime::getSubTickCount(double tickStep)\n{\n  int result = QCPAxisTicker::getSubTickCount(tickStep);\n  switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day range (as specified in getTickStep)\n  {\n    case 5*60: result = 4; break;\n    case 10*60: result = 1; break;\n    case 15*60: result = 2; break;\n    case 30*60: result = 1; break;\n    case 60*60: result = 3; break;\n    case 3600*2: result = 3; break;\n    case 3600*3: result = 2; break;\n    case 3600*6: result = 1; break;\n    case 3600*12: result = 3; break;\n    case 3600*24: result = 3; break;\n  }\n  return result;\n}\n\n/*! \\internal\n  \n  Returns the tick label corresponding to the provided \\a tick and the configured format and field\n  widths (\\ref setTimeFormat, \\ref setFieldWidth).\n  \n  \\seebaseclassmethod\n*/\nQString QCPAxisTickerTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)\n{\n  Q_UNUSED(precision)\n  Q_UNUSED(formatChar)\n  Q_UNUSED(locale)\n  bool negative = tick < 0;\n  if (negative) tick *= -1;\n  double values[tuDays+1]; // contains the msec/sec/min/... value with its respective modulo (e.g. minute 0..59)\n  double restValues[tuDays+1]; // contains the msec/sec/min/... value as if it's the largest available unit and thus consumes the remaining time\n  \n  restValues[tuMilliseconds] = tick*1000;\n  values[tuMilliseconds] = modf(restValues[tuMilliseconds]/1000, &restValues[tuSeconds])*1000;\n  values[tuSeconds] = modf(restValues[tuSeconds]/60, &restValues[tuMinutes])*60;\n  values[tuMinutes] = modf(restValues[tuMinutes]/60, &restValues[tuHours])*60;\n  values[tuHours] = modf(restValues[tuHours]/24, &restValues[tuDays])*24;\n  // no need to set values[tuDays] because days are always a rest value (there is no higher unit so it consumes all remaining time)\n  \n  QString result = mTimeFormat;\n  for (int i = mSmallestUnit; i <= mBiggestUnit; ++i)\n  {\n    TimeUnit iUnit = static_cast<TimeUnit>(i);\n    replaceUnit(result, iUnit, qRound(iUnit == mBiggestUnit ? restValues[iUnit] : values[iUnit]));\n  }\n  if (negative)\n    result.prepend(QLatin1Char('-'));\n  return result;\n}\n\n/*! \\internal\n  \n  Replaces all occurrences of the format pattern belonging to \\a unit in \\a text with the specified\n  \\a value, using the field width as specified with \\ref setFieldWidth for the \\a unit.\n*/\nvoid QCPAxisTickerTime::replaceUnit(QString &text, QCPAxisTickerTime::TimeUnit unit, int value) const\n{\n  QString valueStr = QString::number(value);\n  while (valueStr.size() < mFieldWidth.value(unit))\n    valueStr.prepend(QLatin1Char('0'));\n  \n  text.replace(mFormatPattern.value(unit), valueStr);\n}\n/* end of 'src/axis/axistickertime.cpp' */\n\n\n/* including file 'src/axis/axistickerfixed.cpp' */\n/* modified 2021-03-29T02:30:44, size 5575       */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPAxisTickerFixed\n////////////////////////////////////////////////////////////////////////////////////////////////////\n/*! \\class QCPAxisTickerFixed\n  \\brief Specialized axis ticker with a fixed tick step\n  \n  \\image html axisticker-fixed.png\n  \n  This QCPAxisTicker subclass generates ticks with a fixed tick step set with \\ref setTickStep. It\n  is also possible to allow integer multiples and integer powers of the specified tick step with\n  \\ref setScaleStrategy.\n  \n  A typical application of this ticker is to make an axis only display integers, by setting the\n  tick step of the ticker to 1.0 and the scale strategy to \\ref ssMultiples.\n  \n  Another case is when a certain number has a special meaning and axis ticks should only appear at\n  multiples of that value. In this case you might also want to consider \\ref QCPAxisTickerPi\n  because despite the name it is not limited to only pi symbols/values.\n  \n  The ticker can be created and assigned to an axis like this:\n  \\snippet documentation/doc-image-generator/mainwindow.cpp axistickerfixed-creation\n*/\n\n/*!\n  Constructs the ticker and sets reasonable default values. Axis tickers are commonly created\n  managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker.\n*/\nQCPAxisTickerFixed::QCPAxisTickerFixed() :\n  mTickStep(1.0),\n  mScaleStrategy(ssNone)\n{\n}\n\n/*!\n  Sets the fixed tick interval to \\a step.\n  \n  The axis ticker will only use this tick step when generating axis ticks. This might cause a very\n  high tick density and overlapping labels if the axis range is zoomed out. Using \\ref\n  setScaleStrategy it is possible to relax the fixed step and also allow multiples or powers of \\a\n  step. This will enable the ticker to reduce the number of ticks to a reasonable amount (see \\ref\n  setTickCount).\n*/\nvoid QCPAxisTickerFixed::setTickStep(double step)\n{\n  if (step > 0)\n    mTickStep = step;\n  else\n    qDebug() << Q_FUNC_INFO << \"tick step must be greater than zero:\" << step;\n}\n\n/*!\n  Sets whether the specified tick step (\\ref setTickStep) is absolutely fixed or whether\n  modifications may be applied to it before calculating the finally used tick step, such as\n  permitting multiples or powers. See \\ref ScaleStrategy for details.\n  \n  The default strategy is \\ref ssNone, which means the tick step is absolutely fixed.\n*/\nvoid QCPAxisTickerFixed::setScaleStrategy(QCPAxisTickerFixed::ScaleStrategy strategy)\n{\n  mScaleStrategy = strategy;\n}\n\n/*! \\internal\n  \n  Determines the actually used tick step from the specified tick step and scale strategy (\\ref\n  setTickStep, \\ref setScaleStrategy).\n  \n  This method either returns the specified tick step exactly, or, if the scale strategy is not \\ref\n  ssNone, a modification of it to allow varying the number of ticks in the current axis range.\n  \n  \\seebaseclassmethod\n*/\ndouble QCPAxisTickerFixed::getTickStep(const QCPRange &range)\n{\n  switch (mScaleStrategy)\n  {\n    case ssNone:\n    {\n      return mTickStep;\n    }\n    case ssMultiples:\n    {\n      double exactStep = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers\n      if (exactStep < mTickStep)\n        return mTickStep;\n      else\n        return qint64(cleanMantissa(exactStep/mTickStep)+0.5)*mTickStep;\n    }\n    case ssPowers:\n    {\n      double exactStep = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers\n      return qPow(mTickStep, int(qLn(exactStep)/qLn(mTickStep)+0.5));\n    }\n  }\n  return mTickStep;\n}\n/* end of 'src/axis/axistickerfixed.cpp' */\n\n\n/* including file 'src/axis/axistickertext.cpp' */\n/* modified 2021-03-29T02:30:44, size 8742      */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPAxisTickerText\n////////////////////////////////////////////////////////////////////////////////////////////////////\n/*! \\class QCPAxisTickerText\n  \\brief Specialized axis ticker which allows arbitrary labels at specified coordinates\n  \n  \\image html axisticker-text.png\n  \n  This QCPAxisTicker subclass generates ticks which can be directly specified by the user as\n  coordinates and associated strings. They can be passed as a whole with \\ref setTicks or one at a\n  time with \\ref addTick. Alternatively you can directly access the internal storage via \\ref ticks\n  and modify the tick/label data there.\n  \n  This is useful for cases where the axis represents categories rather than numerical values.\n  \n  If you are updating the ticks of this ticker regularly and in a dynamic fasion (e.g. dependent on\n  the axis range), it is a sign that you should probably create an own ticker by subclassing\n  QCPAxisTicker, instead of using this one.\n  \n  The ticker can be created and assigned to an axis like this:\n  \\snippet documentation/doc-image-generator/mainwindow.cpp axistickertext-creation\n*/\n\n/* start of documentation of inline functions */\n\n/*! \\fn QMap<double, QString> &QCPAxisTickerText::ticks()\n  \n  Returns a non-const reference to the internal map which stores the tick coordinates and their\n  labels.\n\n  You can access the map directly in order to add, remove or manipulate ticks, as an alternative to\n  using the methods provided by QCPAxisTickerText, such as \\ref setTicks and \\ref addTick.\n*/\n\n/* end of documentation of inline functions */\n\n/*!\n  Constructs the ticker and sets reasonable default values. Axis tickers are commonly created\n  managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker.\n*/\nQCPAxisTickerText::QCPAxisTickerText() :\n  mSubTickCount(0)\n{\n}\n\n/*! \\overload\n  \n  Sets the ticks that shall appear on the axis. The map key of \\a ticks corresponds to the axis\n  coordinate, and the map value is the string that will appear as tick label.\n  \n  An alternative to manipulate ticks is to directly access the internal storage with the \\ref ticks\n  getter.\n  \n  \\see addTicks, addTick, clear\n*/\nvoid QCPAxisTickerText::setTicks(const QMap<double, QString> &ticks)\n{\n  mTicks = ticks;\n}\n\n/*! \\overload\n  \n  Sets the ticks that shall appear on the axis. The entries of \\a positions correspond to the axis\n  coordinates, and the entries of \\a labels are the respective strings that will appear as tick\n  labels.\n  \n  \\see addTicks, addTick, clear\n*/\nvoid QCPAxisTickerText::setTicks(const QVector<double> &positions, const QVector<QString> &labels)\n{\n  clear();\n  addTicks(positions, labels);\n}\n\n/*!\n  Sets the number of sub ticks that shall appear between ticks. For QCPAxisTickerText, there is no\n  automatic sub tick count calculation. So if sub ticks are needed, they must be configured with this\n  method.\n*/\nvoid QCPAxisTickerText::setSubTickCount(int subTicks)\n{\n  if (subTicks >= 0)\n    mSubTickCount = subTicks;\n  else\n    qDebug() << Q_FUNC_INFO << \"sub tick count can't be negative:\" << subTicks;\n}\n\n/*!\n  Clears all ticks.\n  \n  An alternative to manipulate ticks is to directly access the internal storage with the \\ref ticks\n  getter.\n  \n  \\see setTicks, addTicks, addTick\n*/\nvoid QCPAxisTickerText::clear()\n{\n  mTicks.clear();\n}\n\n/*!\n  Adds a single tick to the axis at the given axis coordinate \\a position, with the provided tick \\a\n  label.\n  \n  \\see addTicks, setTicks, clear\n*/\nvoid QCPAxisTickerText::addTick(double position, const QString &label)\n{\n  mTicks.insert(position, label);\n}\n\n/*! \\overload\n  \n  Adds the provided \\a ticks to the ones already existing. The map key of \\a ticks corresponds to\n  the axis coordinate, and the map value is the string that will appear as tick label.\n  \n  An alternative to manipulate ticks is to directly access the internal storage with the \\ref ticks\n  getter.\n  \n  \\see addTick, setTicks, clear\n*/\nvoid QCPAxisTickerText::addTicks(const QMap<double, QString> &ticks)\n{\n#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)\n  mTicks.unite(ticks);\n#else\n  mTicks.insert(ticks);\n#endif\n}\n\n/*! \\overload\n  \n  Adds the provided ticks to the ones already existing. The entries of \\a positions correspond to\n  the axis coordinates, and the entries of \\a labels are the respective strings that will appear as\n  tick labels.\n  \n  An alternative to manipulate ticks is to directly access the internal storage with the \\ref ticks\n  getter.\n  \n  \\see addTick, setTicks, clear\n*/\nvoid QCPAxisTickerText::addTicks(const QVector<double> &positions, const QVector<QString> &labels)\n{\n  if (positions.size() != labels.size())\n    qDebug() << Q_FUNC_INFO << \"passed unequal length vectors for positions and labels:\" << positions.size() << labels.size();\n  int n = qMin(positions.size(), labels.size());\n  for (int i=0; i<n; ++i)\n    mTicks.insert(positions.at(i), labels.at(i));\n}\n\n/*!\n  Since the tick coordinates are provided externally, this method implementation does nothing.\n  \n  \\seebaseclassmethod\n*/\ndouble QCPAxisTickerText::getTickStep(const QCPRange &range)\n{\n  // text axis ticker has manual tick positions, so doesn't need this method\n  Q_UNUSED(range)\n  return 1.0;\n}\n\n/*!\n  Returns the sub tick count that was configured with \\ref setSubTickCount.\n  \n  \\seebaseclassmethod\n*/\nint QCPAxisTickerText::getSubTickCount(double tickStep)\n{\n  Q_UNUSED(tickStep)\n  return mSubTickCount;\n}\n\n/*!\n  Returns the tick label which corresponds to the key \\a tick in the internal tick storage. Since\n  the labels are provided externally, \\a locale, \\a formatChar, and \\a precision are ignored.\n  \n  \\seebaseclassmethod\n*/\nQString QCPAxisTickerText::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)\n{\n  Q_UNUSED(locale)\n  Q_UNUSED(formatChar)\n  Q_UNUSED(precision)\n  return mTicks.value(tick);\n}\n\n/*!\n  Returns the externally provided tick coordinates which are in the specified \\a range. If\n  available, one tick above and below the range is provided in addition, to allow possible sub tick\n  calculation. The parameter \\a tickStep is ignored.\n  \n  \\seebaseclassmethod\n*/\nQVector<double> QCPAxisTickerText::createTickVector(double tickStep, const QCPRange &range)\n{\n  Q_UNUSED(tickStep)\n  QVector<double> result;\n  if (mTicks.isEmpty())\n    return result;\n  \n  QMap<double, QString>::const_iterator start = mTicks.lowerBound(range.lower);\n  QMap<double, QString>::const_iterator end = mTicks.upperBound(range.upper);\n  // this method should try to give one tick outside of range so proper subticks can be generated:\n  if (start != mTicks.constBegin()) --start;\n  if (end != mTicks.constEnd()) ++end;\n  for (QMap<double, QString>::const_iterator it = start; it != end; ++it)\n    result.append(it.key());\n  \n  return result;\n}\n/* end of 'src/axis/axistickertext.cpp' */\n\n\n/* including file 'src/axis/axistickerpi.cpp' */\n/* modified 2021-03-29T02:30:44, size 11177   */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPAxisTickerPi\n////////////////////////////////////////////////////////////////////////////////////////////////////\n/*! \\class QCPAxisTickerPi\n  \\brief Specialized axis ticker to display ticks in units of an arbitrary constant, for example pi\n  \n  \\image html axisticker-pi.png\n  \n  This QCPAxisTicker subclass generates ticks that are expressed with respect to a given symbolic\n  constant with a numerical value specified with \\ref setPiValue and an appearance in the tick\n  labels specified with \\ref setPiSymbol.\n  \n  Ticks may be generated at fractions of the symbolic constant. How these fractions appear in the\n  tick label can be configured with \\ref setFractionStyle.\n  \n  The ticker can be created and assigned to an axis like this:\n  \\snippet documentation/doc-image-generator/mainwindow.cpp axistickerpi-creation\n*/\n\n/*!\n  Constructs the ticker and sets reasonable default values. Axis tickers are commonly created\n  managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker.\n*/\nQCPAxisTickerPi::QCPAxisTickerPi() :\n  mPiSymbol(QLatin1String(\" \")+QChar(0x03C0)),\n  mPiValue(M_PI),\n  mPeriodicity(0),\n  mFractionStyle(fsUnicodeFractions),\n  mPiTickStep(0)\n{\n  setTickCount(4);\n}\n\n/*!\n  Sets how the symbol part (which is always a suffix to the number) shall appear in the axis tick\n  label.\n  \n  If a space shall appear between the number and the symbol, make sure the space is contained in \\a\n  symbol.\n*/\nvoid QCPAxisTickerPi::setPiSymbol(QString symbol)\n{\n  mPiSymbol = symbol;\n}\n\n/*!\n  Sets the numerical value that the symbolic constant has.\n\n  This will be used to place the appropriate fractions of the symbol at the respective axis\n  coordinates.\n*/\nvoid QCPAxisTickerPi::setPiValue(double pi)\n{\n  mPiValue = pi;\n}\n\n/*!\n  Sets whether the axis labels shall appear periodicly and if so, at which multiplicity of the\n  symbolic constant.\n  \n  To disable periodicity, set \\a multiplesOfPi to zero.\n  \n  For example, an axis that identifies 0 with 2pi would set \\a multiplesOfPi to two.\n*/\nvoid QCPAxisTickerPi::setPeriodicity(int multiplesOfPi)\n{\n  mPeriodicity = qAbs(multiplesOfPi);\n}\n\n/*!\n  Sets how the numerical/fractional part preceding the symbolic constant is displayed in tick\n  labels. See \\ref FractionStyle for the various options.\n*/\nvoid QCPAxisTickerPi::setFractionStyle(QCPAxisTickerPi::FractionStyle style)\n{\n  mFractionStyle = style;\n}\n\n/*! \\internal\n  \n  Returns the tick step, using the constant's value (\\ref setPiValue) as base unit. In consequence\n  the numerical/fractional part preceding the symbolic constant is made to have a readable\n  mantissa.\n  \n  \\seebaseclassmethod\n*/\ndouble QCPAxisTickerPi::getTickStep(const QCPRange &range)\n{\n  mPiTickStep = range.size()/mPiValue/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers\n  mPiTickStep = cleanMantissa(mPiTickStep);\n  return mPiTickStep*mPiValue;\n}\n\n/*! \\internal\n  \n  Returns the sub tick count, using the constant's value (\\ref setPiValue) as base unit. In\n  consequence the sub ticks divide the numerical/fractional part preceding the symbolic constant\n  reasonably, and not the total tick coordinate.\n  \n  \\seebaseclassmethod\n*/\nint QCPAxisTickerPi::getSubTickCount(double tickStep)\n{\n  return QCPAxisTicker::getSubTickCount(tickStep/mPiValue);\n}\n\n/*! \\internal\n  \n  Returns the tick label as a fractional/numerical part and a symbolic string as suffix. The\n  formatting of the fraction is done according to the specified \\ref setFractionStyle. The appended\n  symbol is specified with \\ref setPiSymbol.\n  \n  \\seebaseclassmethod\n*/\nQString QCPAxisTickerPi::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)\n{\n  double tickInPis = tick/mPiValue;\n  if (mPeriodicity > 0)\n    tickInPis = fmod(tickInPis, mPeriodicity);\n  \n  if (mFractionStyle != fsFloatingPoint && mPiTickStep > 0.09 && mPiTickStep < 50)\n  {\n    // simply construct fraction from decimal like 1.234 -> 1234/1000 and then simplify fraction, smaller digits are irrelevant due to mPiTickStep conditional above\n    int denominator = 1000;\n    int numerator = qRound(tickInPis*denominator);\n    simplifyFraction(numerator, denominator);\n    if (qAbs(numerator) == 1 && denominator == 1)\n      return (numerator < 0 ? QLatin1String(\"-\") : QLatin1String(\"\")) + mPiSymbol.trimmed();\n    else if (numerator == 0)\n      return QLatin1String(\"0\");\n    else\n      return fractionToString(numerator, denominator) + mPiSymbol;\n  } else\n  {\n    if (qFuzzyIsNull(tickInPis))\n      return QLatin1String(\"0\");\n    else if (qFuzzyCompare(qAbs(tickInPis), 1.0))\n      return (tickInPis < 0 ? QLatin1String(\"-\") : QLatin1String(\"\")) + mPiSymbol.trimmed();\n    else\n      return QCPAxisTicker::getTickLabel(tickInPis, locale, formatChar, precision) + mPiSymbol;\n  }\n}\n\n/*! \\internal\n  \n  Takes the fraction given by \\a numerator and \\a denominator and modifies the values to make sure\n  the fraction is in irreducible form, i.e. numerator and denominator don't share any common\n  factors which could be cancelled.\n*/\nvoid QCPAxisTickerPi::simplifyFraction(int &numerator, int &denominator) const\n{\n  if (numerator == 0 || denominator == 0)\n    return;\n  \n  int num = numerator;\n  int denom = denominator;\n  while (denom != 0) // euclidean gcd algorithm\n  {\n    int oldDenom = denom;\n    denom = num % denom;\n    num = oldDenom;\n  }\n  // num is now gcd of numerator and denominator\n  numerator /= num;\n  denominator /= num;\n}\n\n/*! \\internal\n  \n  Takes the fraction given by \\a numerator and \\a denominator and returns a string representation.\n  The result depends on the configured fraction style (\\ref setFractionStyle).\n  \n  This method is used to format the numerical/fractional part when generating tick labels. It\n  simplifies the passed fraction to an irreducible form using \\ref simplifyFraction and factors out\n  any integer parts of the fraction (e.g. \"10/4\" becomes \"2 1/2\").\n*/\nQString QCPAxisTickerPi::fractionToString(int numerator, int denominator) const\n{\n  if (denominator == 0)\n  {\n    qDebug() << Q_FUNC_INFO << \"called with zero denominator\";\n    return QString();\n  }\n  if (mFractionStyle == fsFloatingPoint) // should never be the case when calling this function\n  {\n    qDebug() << Q_FUNC_INFO << \"shouldn't be called with fraction style fsDecimal\";\n    return QString::number(numerator/double(denominator)); // failsafe\n  }\n  int sign = numerator*denominator < 0 ? -1 : 1;\n  numerator = qAbs(numerator);\n  denominator = qAbs(denominator);\n  \n  if (denominator == 1)\n  {\n    return QString::number(sign*numerator);\n  } else\n  {\n    int integerPart = numerator/denominator;\n    int remainder = numerator%denominator;\n    if (remainder == 0)\n    {\n      return QString::number(sign*integerPart);\n    } else\n    {\n      if (mFractionStyle == fsAsciiFractions)\n      {\n        return QString(QLatin1String(\"%1%2%3/%4\"))\n            .arg(sign == -1 ? QLatin1String(\"-\") : QLatin1String(\"\"))\n            .arg(integerPart > 0 ? QString::number(integerPart)+QLatin1String(\" \") : QString(QLatin1String(\"\")))\n            .arg(remainder)\n            .arg(denominator);\n      } else if (mFractionStyle == fsUnicodeFractions)\n      {\n        return QString(QLatin1String(\"%1%2%3\"))\n            .arg(sign == -1 ? QLatin1String(\"-\") : QLatin1String(\"\"))\n            .arg(integerPart > 0 ? QString::number(integerPart) : QLatin1String(\"\"))\n            .arg(unicodeFraction(remainder, denominator));\n      }\n    }\n  }\n  return QString();\n}\n\n/*! \\internal\n  \n  Returns the unicode string representation of the fraction given by \\a numerator and \\a\n  denominator. This is the representation used in \\ref fractionToString when the fraction style\n  (\\ref setFractionStyle) is \\ref fsUnicodeFractions.\n  \n  This method doesn't use the single-character common fractions but builds each fraction from a\n  superscript unicode number, the unicode fraction character, and a subscript unicode number.\n*/\nQString QCPAxisTickerPi::unicodeFraction(int numerator, int denominator) const\n{\n  return unicodeSuperscript(numerator)+QChar(0x2044)+unicodeSubscript(denominator);\n}\n\n/*! \\internal\n  \n  Returns the unicode string representing \\a number as superscript. This is used to build\n  unicode fractions in \\ref unicodeFraction.\n*/\nQString QCPAxisTickerPi::unicodeSuperscript(int number) const\n{\n  if (number == 0)\n    return QString(QChar(0x2070));\n  \n  QString result;\n  while (number > 0)\n  {\n    const int digit = number%10;\n    switch (digit)\n    {\n      case 1: { result.prepend(QChar(0x00B9)); break; }\n      case 2: { result.prepend(QChar(0x00B2)); break; }\n      case 3: { result.prepend(QChar(0x00B3)); break; }\n      default: { result.prepend(QChar(0x2070+digit)); break; }\n    }\n    number /= 10;\n  }\n  return result;\n}\n\n/*! \\internal\n  \n  Returns the unicode string representing \\a number as subscript. This is used to build unicode\n  fractions in \\ref unicodeFraction.\n*/\nQString QCPAxisTickerPi::unicodeSubscript(int number) const\n{\n  if (number == 0)\n    return QString(QChar(0x2080));\n  \n  QString result;\n  while (number > 0)\n  {\n    result.prepend(QChar(0x2080+number%10));\n    number /= 10;\n  }\n  return result;\n}\n/* end of 'src/axis/axistickerpi.cpp' */\n\n\n/* including file 'src/axis/axistickerlog.cpp' */\n/* modified 2021-03-29T02:30:44, size 7890     */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPAxisTickerLog\n////////////////////////////////////////////////////////////////////////////////////////////////////\n/*! \\class QCPAxisTickerLog\n  \\brief Specialized axis ticker suited for logarithmic axes\n  \n  \\image html axisticker-log.png\n  \n  This QCPAxisTicker subclass generates ticks with unequal tick intervals suited for logarithmic\n  axis scales. The ticks are placed at powers of the specified log base (\\ref setLogBase).\n  \n  Especially in the case of a log base equal to 10 (the default), it might be desirable to have\n  tick labels in the form of powers of ten without mantissa display. To achieve this, set the\n  number precision (\\ref QCPAxis::setNumberPrecision) to zero and the number format (\\ref\n  QCPAxis::setNumberFormat) to scientific (exponential) display with beautifully typeset decimal\n  powers, so a format string of <tt>\"eb\"</tt>. This will result in the following axis tick labels:\n  \n  \\image html axisticker-log-powers.png\n\n  The ticker can be created and assigned to an axis like this:\n  \\snippet documentation/doc-image-generator/mainwindow.cpp axistickerlog-creation\n  \n  Note that the nature of logarithmic ticks imply that there exists a smallest possible tick step,\n  corresponding to one multiplication by the log base. If the user zooms in further than that, no\n  new ticks would appear, leading to very sparse or even no axis ticks on the axis. To prevent this\n  situation, this ticker falls back to regular tick generation if the axis range would be covered\n  by too few logarithmically placed ticks.\n*/\n\n/*!\n  Constructs the ticker and sets reasonable default values. Axis tickers are commonly created\n  managed by a QSharedPointer, which then can be passed to QCPAxis::setTicker.\n*/\nQCPAxisTickerLog::QCPAxisTickerLog() :\n  mLogBase(10.0),\n  mSubTickCount(8), // generates 10 intervals\n  mLogBaseLnInv(1.0/qLn(mLogBase))\n{\n}\n\n/*!\n  Sets the logarithm base used for tick coordinate generation. The ticks will be placed at integer\n  powers of \\a base.\n*/\nvoid QCPAxisTickerLog::setLogBase(double base)\n{\n  if (base > 0)\n  {\n    mLogBase = base;\n    mLogBaseLnInv = 1.0/qLn(mLogBase);\n  } else\n    qDebug() << Q_FUNC_INFO << \"log base has to be greater than zero:\" << base;\n}\n\n/*!\n  Sets the number of sub ticks in a tick interval. Within each interval, the sub ticks are spaced\n  linearly to provide a better visual guide, so the sub tick density increases toward the higher\n  tick.\n  \n  Note that \\a subTicks is the number of sub ticks (not sub intervals) in one tick interval. So in\n  the case of logarithm base 10 an intuitive sub tick spacing would be achieved with eight sub\n  ticks (the default). This means e.g. between the ticks 10 and 100 there will be eight ticks,\n  namely at 20, 30, 40, 50, 60, 70, 80 and 90.\n*/\nvoid QCPAxisTickerLog::setSubTickCount(int subTicks)\n{\n  if (subTicks >= 0)\n    mSubTickCount = subTicks;\n  else\n    qDebug() << Q_FUNC_INFO << \"sub tick count can't be negative:\" << subTicks;\n}\n\n/*! \\internal\n  \n  Returns the sub tick count specified in \\ref setSubTickCount. For QCPAxisTickerLog, there is no\n  automatic sub tick count calculation necessary.\n  \n  \\seebaseclassmethod\n*/\nint QCPAxisTickerLog::getSubTickCount(double tickStep)\n{\n  Q_UNUSED(tickStep)\n  return mSubTickCount;\n}\n\n/*! \\internal\n  \n  Creates ticks with a spacing given by the logarithm base and an increasing integer power in the\n  provided \\a range. The step in which the power increases tick by tick is chosen in order to keep\n  the total number of ticks as close as possible to the tick count (\\ref setTickCount).\n\n  The parameter \\a tickStep is ignored for the normal logarithmic ticker generation. Only when\n  zoomed in very far such that not enough logarithmically placed ticks would be visible, this\n  function falls back to the regular QCPAxisTicker::createTickVector, which then uses \\a tickStep.\n  \n  \\seebaseclassmethod\n*/\nQVector<double> QCPAxisTickerLog::createTickVector(double tickStep, const QCPRange &range)\n{\n  QVector<double> result;\n  if (range.lower > 0 && range.upper > 0) // positive range\n  {\n    const double baseTickCount = qLn(range.upper/range.lower)*mLogBaseLnInv;\n    if (baseTickCount < 1.6) // if too few log ticks would be visible in axis range, fall back to regular tick vector generation\n      return QCPAxisTicker::createTickVector(tickStep, range);\n    const double exactPowerStep =  baseTickCount/double(mTickCount+1e-10);\n    const double newLogBase = qPow(mLogBase, qMax(int(cleanMantissa(exactPowerStep)), 1));\n    double currentTick = qPow(newLogBase, qFloor(qLn(range.lower)/qLn(newLogBase)));\n    result.append(currentTick);\n    while (currentTick < range.upper && currentTick > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case\n    {\n      currentTick *= newLogBase;\n      result.append(currentTick);\n    }\n  } else if (range.lower < 0 && range.upper < 0) // negative range\n  {\n    const double baseTickCount = qLn(range.lower/range.upper)*mLogBaseLnInv;\n    if (baseTickCount < 1.6) // if too few log ticks would be visible in axis range, fall back to regular tick vector generation\n      return QCPAxisTicker::createTickVector(tickStep, range);\n    const double exactPowerStep =  baseTickCount/double(mTickCount+1e-10);\n    const double newLogBase = qPow(mLogBase, qMax(int(cleanMantissa(exactPowerStep)), 1));\n    double currentTick = -qPow(newLogBase, qCeil(qLn(-range.lower)/qLn(newLogBase)));\n    result.append(currentTick);\n    while (currentTick < range.upper && currentTick < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case\n    {\n      currentTick /= newLogBase;\n      result.append(currentTick);\n    }\n  } else // invalid range for logarithmic scale, because lower and upper have different sign\n  {\n    qDebug() << Q_FUNC_INFO << \"Invalid range for logarithmic plot: \" << range.lower << \"..\" << range.upper;\n  }\n  \n  return result;\n}\n/* end of 'src/axis/axistickerlog.cpp' */\n\n\n/* including file 'src/axis/axis.cpp'       */\n/* modified 2021-03-29T02:30:44, size 99883 */\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPGrid\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPGrid\n  \\brief Responsible for drawing the grid of a QCPAxis.\n  \n  This class is tightly bound to QCPAxis. Every axis owns a grid instance and uses it to draw the\n  grid lines, sub grid lines and zero-line. You can interact with the grid of an axis via \\ref\n  QCPAxis::grid. Normally, you don't need to create an instance of QCPGrid yourself.\n  \n  The axis and grid drawing was split into two classes to allow them to be placed on different\n  layers (both QCPAxis and QCPGrid inherit from QCPLayerable). Thus it is possible to have the grid\n  in the background and the axes in the foreground, and any plottables/items in between. This\n  described situation is the default setup, see the QCPLayer documentation.\n*/\n\n/*!\n  Creates a QCPGrid instance and sets default values.\n  \n  You shouldn't instantiate grids on their own, since every QCPAxis brings its own QCPGrid.\n*/\nQCPGrid::QCPGrid(QCPAxis *parentAxis) :\n  QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),\n  mSubGridVisible{},\n  mAntialiasedSubGrid{},\n  mAntialiasedZeroLine{},\n  mParentAxis(parentAxis)\n{\n  // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called\n  setParent(parentAxis);\n  setPen(QPen(QColor(200,200,200), 0, Qt::DotLine));\n  setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));\n  setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));\n  setSubGridVisible(false);\n  setAntialiased(false);\n  setAntialiasedSubGrid(false);\n  setAntialiasedZeroLine(false);\n}\n\n/*!\n  Sets whether grid lines at sub tick marks are drawn.\n  \n  \\see setSubGridPen\n*/\nvoid QCPGrid::setSubGridVisible(bool visible)\n{\n  mSubGridVisible = visible;\n}\n\n/*!\n  Sets whether sub grid lines are drawn antialiased.\n*/\nvoid QCPGrid::setAntialiasedSubGrid(bool enabled)\n{\n  mAntialiasedSubGrid = enabled;\n}\n\n/*!\n  Sets whether zero lines are drawn antialiased.\n*/\nvoid QCPGrid::setAntialiasedZeroLine(bool enabled)\n{\n  mAntialiasedZeroLine = enabled;\n}\n\n/*!\n  Sets the pen with which (major) grid lines are drawn.\n*/\nvoid QCPGrid::setPen(const QPen &pen)\n{\n  mPen = pen;\n}\n\n/*!\n  Sets the pen with which sub grid lines are drawn.\n*/\nvoid QCPGrid::setSubGridPen(const QPen &pen)\n{\n  mSubGridPen = pen;\n}\n\n/*!\n  Sets the pen with which zero lines are drawn.\n  \n  Zero lines are lines at value coordinate 0 which may be drawn with a different pen than other grid\n  lines. To disable zero lines and just draw normal grid lines at zero, set \\a pen to Qt::NoPen.\n*/\nvoid QCPGrid::setZeroLinePen(const QPen &pen)\n{\n  mZeroLinePen = pen;\n}\n\n/*! \\internal\n\n  A convenience function to easily set the QPainter::Antialiased hint on the provided \\a painter\n  before drawing the major grid lines.\n\n  This is the antialiasing state the painter passed to the \\ref draw method is in by default.\n  \n  This function takes into account the local setting of the antialiasing flag as well as the\n  overrides set with \\ref QCustomPlot::setAntialiasedElements and \\ref\n  QCustomPlot::setNotAntialiasedElements.\n  \n  \\see setAntialiased\n*/\nvoid QCPGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const\n{\n  applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid);\n}\n\n/*! \\internal\n  \n  Draws grid lines and sub grid lines at the positions of (sub) ticks of the parent axis, spanning\n  over the complete axis rect. Also draws the zero line, if appropriate (\\ref setZeroLinePen).\n*/\nvoid QCPGrid::draw(QCPPainter *painter)\n{\n  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << \"invalid parent axis\"; return; }\n  \n  if (mParentAxis->subTicks() && mSubGridVisible)\n    drawSubGridLines(painter);\n  drawGridLines(painter);\n}\n\n/*! \\internal\n  \n  Draws the main grid lines and possibly a zero line with the specified painter.\n  \n  This is a helper function called by \\ref draw.\n*/\nvoid QCPGrid::drawGridLines(QCPPainter *painter) const\n{\n  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << \"invalid parent axis\"; return; }\n  \n  const int tickCount = mParentAxis->mTickVector.size();\n  double t; // helper variable, result of coordinate-to-pixel transforms\n  if (mParentAxis->orientation() == Qt::Horizontal)\n  {\n    // draw zeroline:\n    int zeroLineIndex = -1;\n    if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)\n    {\n      applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);\n      painter->setPen(mZeroLinePen);\n      double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero\n      for (int i=0; i<tickCount; ++i)\n      {\n        if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)\n        {\n          zeroLineIndex = i;\n          t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x\n          painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));\n          break;\n        }\n      }\n    }\n    // draw grid lines:\n    applyDefaultAntialiasingHint(painter);\n    painter->setPen(mPen);\n    for (int i=0; i<tickCount; ++i)\n    {\n      if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline\n      t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x\n      painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));\n    }\n  } else\n  {\n    // draw zeroline:\n    int zeroLineIndex = -1;\n    if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)\n    {\n      applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);\n      painter->setPen(mZeroLinePen);\n      double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero\n      for (int i=0; i<tickCount; ++i)\n      {\n        if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)\n        {\n          zeroLineIndex = i;\n          t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y\n          painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));\n          break;\n        }\n      }\n    }\n    // draw grid lines:\n    applyDefaultAntialiasingHint(painter);\n    painter->setPen(mPen);\n    for (int i=0; i<tickCount; ++i)\n    {\n      if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline\n      t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y\n      painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));\n    }\n  }\n}\n\n/*! \\internal\n  \n  Draws the sub grid lines with the specified painter.\n  \n  This is a helper function called by \\ref draw.\n*/\nvoid QCPGrid::drawSubGridLines(QCPPainter *painter) const\n{\n  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << \"invalid parent axis\"; return; }\n  \n  applyAntialiasingHint(painter, mAntialiasedSubGrid, QCP::aeSubGrid);\n  double t; // helper variable, result of coordinate-to-pixel transforms\n  painter->setPen(mSubGridPen);\n  if (mParentAxis->orientation() == Qt::Horizontal)\n  {\n    foreach (double tickCoord, mParentAxis->mSubTickVector)\n    {\n      t = mParentAxis->coordToPixel(tickCoord); // x\n      painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));\n    }\n  } else\n  {\n    foreach (double tickCoord, mParentAxis->mSubTickVector)\n    {\n      t = mParentAxis->coordToPixel(tickCoord); // y\n      painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));\n    }\n  }\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPAxis\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPAxis\n  \\brief Manages a single axis inside a QCustomPlot.\n\n  Usually doesn't need to be instantiated externally. Access %QCustomPlot's default four axes via\n  QCustomPlot::xAxis (bottom), QCustomPlot::yAxis (left), QCustomPlot::xAxis2 (top) and\n  QCustomPlot::yAxis2 (right).\n  \n  Axes are always part of an axis rect, see QCPAxisRect.\n  \\image html AxisNamesOverview.png\n  <center>Naming convention of axis parts</center>\n  \\n\n    \n  \\image html AxisRectSpacingOverview.png\n  <center>Overview of the spacings and paddings that define the geometry of an axis. The dashed gray line\n  on the left represents the QCustomPlot widget border.</center>\n  \n  Each axis holds an instance of QCPAxisTicker which is used to generate the tick coordinates and\n  tick labels. You can access the currently installed \\ref ticker or set a new one (possibly one of\n  the specialized subclasses, or your own subclass) via \\ref setTicker. For details, see the\n  documentation of QCPAxisTicker.\n*/\n\n/* start of documentation of inline functions */\n\n/*! \\fn Qt::Orientation QCPAxis::orientation() const\n\n  Returns the orientation of this axis. The axis orientation (horizontal or vertical) is deduced\n  from the axis type (left, top, right or bottom).\n\n  \\see orientation(AxisType type), pixelOrientation\n*/\n\n/*! \\fn QCPGrid *QCPAxis::grid() const\n  \n  Returns the \\ref QCPGrid instance belonging to this axis. Access it to set details about the way the\n  grid is displayed.\n*/\n\n/*! \\fn static Qt::Orientation QCPAxis::orientation(AxisType type)\n\n  Returns the orientation of the specified axis type\n\n  \\see orientation(), pixelOrientation\n*/\n\n/*! \\fn int QCPAxis::pixelOrientation() const\n\n  Returns which direction points towards higher coordinate values/keys, in pixel space.\n\n  This method returns either 1 or -1. If it returns 1, then going in the positive direction along\n  the orientation of the axis in pixels corresponds to going from lower to higher axis coordinates.\n  On the other hand, if this method returns -1, going to smaller pixel values corresponds to going\n  from lower to higher axis coordinates.\n\n  For example, this is useful to easily shift axis coordinates by a certain amount given in pixels,\n  without having to care about reversed or vertically aligned axes:\n\n  \\code\n  double newKey = keyAxis->pixelToCoord(keyAxis->coordToPixel(oldKey)+10*keyAxis->pixelOrientation());\n  \\endcode\n\n  \\a newKey will then contain a key that is ten pixels towards higher keys, starting from \\a oldKey.\n*/\n\n/*! \\fn QSharedPointer<QCPAxisTicker> QCPAxis::ticker() const\n\n  Returns a modifiable shared pointer to the currently installed axis ticker. The axis ticker is\n  responsible for generating the tick positions and tick labels of this axis. You can access the\n  \\ref QCPAxisTicker with this method and modify basic properties such as the approximate tick count\n  (\\ref QCPAxisTicker::setTickCount).\n\n  You can gain more control over the axis ticks by setting a different \\ref QCPAxisTicker subclass, see\n  the documentation there. A new axis ticker can be set with \\ref setTicker.\n\n  Since the ticker is stored in the axis as a shared pointer, multiple axes may share the same axis\n  ticker simply by passing the same shared pointer to multiple axes.\n\n  \\see setTicker\n*/\n\n/* end of documentation of inline functions */\n/* start of documentation of signals */\n\n/*! \\fn void QCPAxis::rangeChanged(const QCPRange &newRange)\n\n  This signal is emitted when the range of this axis has changed. You can connect it to the \\ref\n  setRange slot of another axis to communicate the new range to the other axis, in order for it to\n  be synchronized.\n  \n  You may also manipulate/correct the range with \\ref setRange in a slot connected to this signal.\n  This is useful if for example a maximum range span shall not be exceeded, or if the lower/upper\n  range shouldn't go beyond certain values (see \\ref QCPRange::bounded). For example, the following\n  slot would limit the x axis to ranges between 0 and 10:\n  \\code\n  customPlot->xAxis->setRange(newRange.bounded(0, 10))\n  \\endcode\n*/\n\n/*! \\fn void QCPAxis::rangeChanged(const QCPRange &newRange, const QCPRange &oldRange)\n  \\overload\n  \n  Additionally to the new range, this signal also provides the previous range held by the axis as\n  \\a oldRange.\n*/\n\n/*! \\fn void QCPAxis::scaleTypeChanged(QCPAxis::ScaleType scaleType);\n  \n  This signal is emitted when the scale type changes, by calls to \\ref setScaleType\n*/\n\n/*! \\fn void QCPAxis::selectionChanged(QCPAxis::SelectableParts selection)\n  \n  This signal is emitted when the selection state of this axis has changed, either by user interaction\n  or by a direct call to \\ref setSelectedParts.\n*/\n\n/*! \\fn void QCPAxis::selectableChanged(const QCPAxis::SelectableParts &parts);\n  \n  This signal is emitted when the selectability changes, by calls to \\ref setSelectableParts\n*/\n\n/* end of documentation of signals */\n\n/*!\n  Constructs an Axis instance of Type \\a type for the axis rect \\a parent.\n  \n  Usually it isn't necessary to instantiate axes directly, because you can let QCustomPlot create\n  them for you with \\ref QCPAxisRect::addAxis. If you want to use own QCPAxis-subclasses however,\n  create them manually and then inject them also via \\ref QCPAxisRect::addAxis.\n*/\nQCPAxis::QCPAxis(QCPAxisRect *parent, AxisType type) :\n  QCPLayerable(parent->parentPlot(), QString(), parent),\n  // axis base:\n  mAxisType(type),\n  mAxisRect(parent),\n  mPadding(5),\n  mOrientation(orientation(type)),\n  mSelectableParts(spAxis | spTickLabels | spAxisLabel),\n  mSelectedParts(spNone),\n  mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),\n  mSelectedBasePen(QPen(Qt::blue, 2)),\n  // axis label:\n  mLabel(),\n  mLabelFont(mParentPlot->font()),\n  mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),\n  mLabelColor(Qt::black),\n  mSelectedLabelColor(Qt::blue),\n  // tick labels:\n  mTickLabels(true),\n  mTickLabelFont(mParentPlot->font()),\n  mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),\n  mTickLabelColor(Qt::black),\n  mSelectedTickLabelColor(Qt::blue),\n  mNumberPrecision(6),\n  mNumberFormatChar('g'),\n  mNumberBeautifulPowers(true),\n  // ticks and subticks:\n  mTicks(true),\n  mSubTicks(true),\n  mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),\n  mSelectedTickPen(QPen(Qt::blue, 2)),\n  mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),\n  mSelectedSubTickPen(QPen(Qt::blue, 2)),\n  // scale and range:\n  mRange(0, 5),\n  mRangeReversed(false),\n  mScaleType(stLinear),\n  // internal members:\n  mGrid(new QCPGrid(this)),\n  mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),\n  mTicker(new QCPAxisTicker),\n  mCachedMarginValid(false),\n  mCachedMargin(0),\n  mDragging(false)\n{\n  setParent(parent);\n  mGrid->setVisible(false);\n  setAntialiased(false);\n  setLayer(mParentPlot->currentLayer()); // it's actually on that layer already, but we want it in front of the grid, so we place it on there again\n  \n  if (type == atTop)\n  {\n    setTickLabelPadding(3);\n    setLabelPadding(6);\n  } else if (type == atRight)\n  {\n    setTickLabelPadding(7);\n    setLabelPadding(12);\n  } else if (type == atBottom)\n  {\n    setTickLabelPadding(3);\n    setLabelPadding(3);\n  } else if (type == atLeft)\n  {\n    setTickLabelPadding(5);\n    setLabelPadding(10);\n  }\n}\n\nQCPAxis::~QCPAxis()\n{\n  delete mAxisPainter;\n  delete mGrid; // delete grid here instead of via parent ~QObject for better defined deletion order\n}\n\n/* No documentation as it is a property getter */\nint QCPAxis::tickLabelPadding() const\n{\n  return mAxisPainter->tickLabelPadding;\n}\n\n/* No documentation as it is a property getter */\ndouble QCPAxis::tickLabelRotation() const\n{\n  return mAxisPainter->tickLabelRotation;\n}\n\n/* No documentation as it is a property getter */\nQCPAxis::LabelSide QCPAxis::tickLabelSide() const\n{\n  return mAxisPainter->tickLabelSide;\n}\n\n/* No documentation as it is a property getter */\nQString QCPAxis::numberFormat() const\n{\n  QString result;\n  result.append(mNumberFormatChar);\n  if (mNumberBeautifulPowers)\n  {\n    result.append(QLatin1Char('b'));\n    if (mAxisPainter->numberMultiplyCross)\n      result.append(QLatin1Char('c'));\n  }\n  return result;\n}\n\n/* No documentation as it is a property getter */\nint QCPAxis::tickLengthIn() const\n{\n  return mAxisPainter->tickLengthIn;\n}\n\n/* No documentation as it is a property getter */\nint QCPAxis::tickLengthOut() const\n{\n  return mAxisPainter->tickLengthOut;\n}\n\n/* No documentation as it is a property getter */\nint QCPAxis::subTickLengthIn() const\n{\n  return mAxisPainter->subTickLengthIn;\n}\n\n/* No documentation as it is a property getter */\nint QCPAxis::subTickLengthOut() const\n{\n  return mAxisPainter->subTickLengthOut;\n}\n\n/* No documentation as it is a property getter */\nint QCPAxis::labelPadding() const\n{\n  return mAxisPainter->labelPadding;\n}\n\n/* No documentation as it is a property getter */\nint QCPAxis::offset() const\n{\n  return mAxisPainter->offset;\n}\n\n/* No documentation as it is a property getter */\nQCPLineEnding QCPAxis::lowerEnding() const\n{\n  return mAxisPainter->lowerEnding;\n}\n\n/* No documentation as it is a property getter */\nQCPLineEnding QCPAxis::upperEnding() const\n{\n  return mAxisPainter->upperEnding;\n}\n\n/*!\n  Sets whether the axis uses a linear scale or a logarithmic scale.\n  \n  Note that this method controls the coordinate transformation. For logarithmic scales, you will\n  likely also want to use a logarithmic tick spacing and labeling, which can be achieved by setting\n  the axis ticker to an instance of \\ref QCPAxisTickerLog :\n  \n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpaxisticker-log-creation\n  \n  See the documentation of \\ref QCPAxisTickerLog about the details of logarithmic axis tick\n  creation.\n  \n  \\ref setNumberPrecision\n*/\nvoid QCPAxis::setScaleType(QCPAxis::ScaleType type)\n{\n  if (mScaleType != type)\n  {\n    mScaleType = type;\n    if (mScaleType == stLogarithmic)\n      setRange(mRange.sanitizedForLogScale());\n    mCachedMarginValid = false;\n    emit scaleTypeChanged(mScaleType);\n  }\n}\n\n/*!\n  Sets the range of the axis.\n  \n  This slot may be connected with the \\ref rangeChanged signal of another axis so this axis\n  is always synchronized with the other axis range, when it changes.\n  \n  To invert the direction of an axis, use \\ref setRangeReversed.\n*/\nvoid QCPAxis::setRange(const QCPRange &range)\n{\n  if (range.lower == mRange.lower && range.upper == mRange.upper)\n    return;\n  \n  if (!QCPRange::validRange(range)) return;\n  QCPRange oldRange = mRange;\n  if (mScaleType == stLogarithmic)\n  {\n    mRange = range.sanitizedForLogScale();\n  } else\n  {\n    mRange = range.sanitizedForLinScale();\n  }\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  Sets whether the user can (de-)select the parts in \\a selectable by clicking on the QCustomPlot surface.\n  (When \\ref QCustomPlot::setInteractions contains iSelectAxes.)\n  \n  However, even when \\a selectable is set to a value not allowing the selection of a specific part,\n  it is still possible to set the selection of this part manually, by calling \\ref setSelectedParts\n  directly.\n  \n  \\see SelectablePart, setSelectedParts\n*/\nvoid QCPAxis::setSelectableParts(const SelectableParts &selectable)\n{\n  if (mSelectableParts != selectable)\n  {\n    mSelectableParts = selectable;\n    emit selectableChanged(mSelectableParts);\n  }\n}\n\n/*!\n  Sets the selected state of the respective axis parts described by \\ref SelectablePart. When a part\n  is selected, it uses a different pen/font.\n  \n  The entire selection mechanism for axes is handled automatically when \\ref\n  QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you\n  wish to change the selection state manually.\n  \n  This function can change the selection state of a part, independent of the \\ref setSelectableParts setting.\n  \n  emits the \\ref selectionChanged signal when \\a selected is different from the previous selection state.\n  \n  \\see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen,\n  setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor\n*/\nvoid QCPAxis::setSelectedParts(const SelectableParts &selected)\n{\n  if (mSelectedParts != selected)\n  {\n    mSelectedParts = selected;\n    emit selectionChanged(mSelectedParts);\n  }\n}\n\n/*!\n  \\overload\n  \n  Sets the lower and upper bound of the axis range.\n  \n  To invert the direction of an axis, use \\ref setRangeReversed.\n  \n  There is also a slot to set a range, see \\ref setRange(const QCPRange &range).\n*/\nvoid QCPAxis::setRange(double lower, double upper)\n{\n  if (lower == mRange.lower && upper == mRange.upper)\n    return;\n  \n  if (!QCPRange::validRange(lower, upper)) return;\n  QCPRange oldRange = mRange;\n  mRange.lower = lower;\n  mRange.upper = upper;\n  if (mScaleType == stLogarithmic)\n  {\n    mRange = mRange.sanitizedForLogScale();\n  } else\n  {\n    mRange = mRange.sanitizedForLinScale();\n  }\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  \\overload\n  \n  Sets the range of the axis.\n  \n  The \\a position coordinate indicates together with the \\a alignment parameter, where the new\n  range will be positioned. \\a size defines the size of the new axis range. \\a alignment may be\n  Qt::AlignLeft, Qt::AlignRight or Qt::AlignCenter. This will cause the left border, right border,\n  or center of the range to be aligned with \\a position. Any other values of \\a alignment will\n  default to Qt::AlignCenter.\n*/\nvoid QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment)\n{\n  if (alignment == Qt::AlignLeft)\n    setRange(position, position+size);\n  else if (alignment == Qt::AlignRight)\n    setRange(position-size, position);\n  else // alignment == Qt::AlignCenter\n    setRange(position-size/2.0, position+size/2.0);\n}\n\n/*!\n  Sets the lower bound of the axis range. The upper bound is not changed.\n  \\see setRange\n*/\nvoid QCPAxis::setRangeLower(double lower)\n{\n  if (mRange.lower == lower)\n    return;\n  \n  QCPRange oldRange = mRange;\n  mRange.lower = lower;\n  if (mScaleType == stLogarithmic)\n  {\n    mRange = mRange.sanitizedForLogScale();\n  } else\n  {\n    mRange = mRange.sanitizedForLinScale();\n  }\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  Sets the upper bound of the axis range. The lower bound is not changed.\n  \\see setRange\n*/\nvoid QCPAxis::setRangeUpper(double upper)\n{\n  if (mRange.upper == upper)\n    return;\n  \n  QCPRange oldRange = mRange;\n  mRange.upper = upper;\n  if (mScaleType == stLogarithmic)\n  {\n    mRange = mRange.sanitizedForLogScale();\n  } else\n  {\n    mRange = mRange.sanitizedForLinScale();\n  }\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal\n  axes increase left to right, on vertical axes bottom to top. When \\a reversed is set to true, the\n  direction of increasing values is inverted.\n\n  Note that the range and data interface stays the same for reversed axes, e.g. the \\a lower part\n  of the \\ref setRange interface will still reference the mathematically smaller number than the \\a\n  upper part.\n*/\nvoid QCPAxis::setRangeReversed(bool reversed)\n{\n  mRangeReversed = reversed;\n}\n\n/*!\n  The axis ticker is responsible for generating the tick positions and tick labels. See the\n  documentation of QCPAxisTicker for details on how to work with axis tickers.\n  \n  You can change the tick positioning/labeling behaviour of this axis by setting a different\n  QCPAxisTicker subclass using this method. If you only wish to modify the currently installed axis\n  ticker, access it via \\ref ticker.\n  \n  Since the ticker is stored in the axis as a shared pointer, multiple axes may share the same axis\n  ticker simply by passing the same shared pointer to multiple axes.\n  \n  \\see ticker\n*/\nvoid QCPAxis::setTicker(QSharedPointer<QCPAxisTicker> ticker)\n{\n  if (ticker)\n    mTicker = ticker;\n  else\n    qDebug() << Q_FUNC_INFO << \"can not set nullptr as axis ticker\";\n  // no need to invalidate margin cache here because produced tick labels are checked for changes in setupTickVector\n}\n\n/*!\n  Sets whether tick marks are displayed.\n\n  Note that setting \\a show to false does not imply that tick labels are invisible, too. To achieve\n  that, see \\ref setTickLabels.\n  \n  \\see setSubTicks\n*/\nvoid QCPAxis::setTicks(bool show)\n{\n  if (mTicks != show)\n  {\n    mTicks = show;\n    mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets whether tick labels are displayed. Tick labels are the numbers drawn next to tick marks.\n*/\nvoid QCPAxis::setTickLabels(bool show)\n{\n  if (mTickLabels != show)\n  {\n    mTickLabels = show;\n    mCachedMarginValid = false;\n    if (!mTickLabels)\n      mTickVectorLabels.clear();\n  }\n}\n\n/*!\n  Sets the distance between the axis base line (including any outward ticks) and the tick labels.\n  \\see setLabelPadding, setPadding\n*/\nvoid QCPAxis::setTickLabelPadding(int padding)\n{\n  if (mAxisPainter->tickLabelPadding != padding)\n  {\n    mAxisPainter->tickLabelPadding = padding;\n    mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the font of the tick labels.\n  \n  \\see setTickLabels, setTickLabelColor\n*/\nvoid QCPAxis::setTickLabelFont(const QFont &font)\n{\n  if (font != mTickLabelFont)\n  {\n    mTickLabelFont = font;\n    mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the color of the tick labels.\n  \n  \\see setTickLabels, setTickLabelFont\n*/\nvoid QCPAxis::setTickLabelColor(const QColor &color)\n{\n  mTickLabelColor = color;\n}\n\n/*!\n  Sets the rotation of the tick labels. If \\a degrees is zero, the labels are drawn normally. Else,\n  the tick labels are drawn rotated by \\a degrees clockwise. The specified angle is bound to values\n  from -90 to 90 degrees.\n  \n  If \\a degrees is exactly -90, 0 or 90, the tick labels are centered on the tick coordinate. For\n  other angles, the label is drawn with an offset such that it seems to point toward or away from\n  the tick mark.\n*/\nvoid QCPAxis::setTickLabelRotation(double degrees)\n{\n  if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))\n  {\n    mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);\n    mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets whether the tick labels (numbers) shall appear inside or outside the axis rect.\n  \n  The usual and default setting is \\ref lsOutside. Very compact plots sometimes require tick labels\n  to be inside the axis rect, to save space. If \\a side is set to \\ref lsInside, the tick labels\n  appear on the inside are additionally clipped to the axis rect.\n*/\nvoid QCPAxis::setTickLabelSide(LabelSide side)\n{\n  mAxisPainter->tickLabelSide = side;\n  mCachedMarginValid = false;\n}\n\n/*!\n  Sets the number format for the numbers in tick labels. This \\a formatCode is an extended version\n  of the format code used e.g. by QString::number() and QLocale::toString(). For reference about\n  that, see the \"Argument Formats\" section in the detailed description of the QString class.\n  \n  \\a formatCode is a string of one, two or three characters.\n\n  <b>The first character</b> is identical to\n  the normal format code used by Qt. In short, this means: 'e'/'E' scientific format, 'f' fixed\n  format, 'g'/'G' scientific or fixed, whichever is shorter. For the 'e', 'E', and 'f' formats,\n  the precision set by \\ref setNumberPrecision represents the number of digits after the decimal\n  point. For the 'g' and 'G' formats, the precision represents the maximum number of significant\n  digits, trailing zeroes are omitted.\n\n  <b>The second and third characters</b> are optional and specific to QCustomPlot:\\n\n  If the first char was 'e' or 'g', numbers are/might be displayed in the scientific format, e.g.\n  \"5.5e9\", which is ugly in a plot. So when the second char of \\a formatCode is set to 'b' (for\n  \"beautiful\"), those exponential numbers are formatted in a more natural way, i.e. \"5.5\n  [multiplication sign] 10 [superscript] 9\". By default, the multiplication sign is a centered dot.\n  If instead a cross should be shown (as is usual in the USA), the third char of \\a formatCode can\n  be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the\n  cross and 183 (0xB7) for the dot.\n  \n  Examples for \\a formatCode:\n  \\li \\c g normal format code behaviour. If number is small, fixed format is used, if number is large,\n  normal scientific format is used\n  \\li \\c gb If number is small, fixed format is used, if number is large, scientific format is used with\n  beautifully typeset decimal powers and a dot as multiplication sign\n  \\li \\c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as\n  multiplication sign\n  \\li \\c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal\n  powers. Format code will be reduced to 'f'.\n  \\li \\c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format\n  code will not be changed.\n*/\nvoid QCPAxis::setNumberFormat(const QString &formatCode)\n{\n  if (formatCode.isEmpty())\n  {\n    qDebug() << Q_FUNC_INFO << \"Passed formatCode is empty\";\n    return;\n  }\n  mCachedMarginValid = false;\n  \n  // interpret first char as number format char:\n  QString allowedFormatChars(QLatin1String(\"eEfgG\"));\n  if (allowedFormatChars.contains(formatCode.at(0)))\n  {\n    mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"Invalid number format code (first char not in 'eEfgG'):\" << formatCode;\n    return;\n  }\n  if (formatCode.length() < 2)\n  {\n    mNumberBeautifulPowers = false;\n    mAxisPainter->numberMultiplyCross = false;\n    return;\n  }\n  \n  // interpret second char as indicator for beautiful decimal powers:\n  if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))\n  {\n    mNumberBeautifulPowers = true;\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):\" << formatCode;\n    return;\n  }\n  if (formatCode.length() < 3)\n  {\n    mAxisPainter->numberMultiplyCross = false;\n    return;\n  }\n  \n  // interpret third char as indicator for dot or cross multiplication symbol:\n  if (formatCode.at(2) == QLatin1Char('c'))\n  {\n    mAxisPainter->numberMultiplyCross = true;\n  } else if (formatCode.at(2) == QLatin1Char('d'))\n  {\n    mAxisPainter->numberMultiplyCross = false;\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"Invalid number format code (third char neither 'c' nor 'd'):\" << formatCode;\n    return;\n  }\n}\n\n/*!\n  Sets the precision of the tick label numbers. See QLocale::toString(double i, char f, int prec)\n  for details. The effect of precisions are most notably for number Formats starting with 'e', see\n  \\ref setNumberFormat\n*/\nvoid QCPAxis::setNumberPrecision(int precision)\n{\n  if (mNumberPrecision != precision)\n  {\n    mNumberPrecision = precision;\n    mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the length of the ticks in pixels. \\a inside is the length the ticks will reach inside the\n  plot and \\a outside is the length they will reach outside the plot. If \\a outside is greater than\n  zero, the tick labels and axis label will increase their distance to the axis accordingly, so\n  they won't collide with the ticks.\n  \n  \\see setSubTickLength, setTickLengthIn, setTickLengthOut\n*/\nvoid QCPAxis::setTickLength(int inside, int outside)\n{\n  setTickLengthIn(inside);\n  setTickLengthOut(outside);\n}\n\n/*!\n  Sets the length of the inward ticks in pixels. \\a inside is the length the ticks will reach\n  inside the plot.\n  \n  \\see setTickLengthOut, setTickLength, setSubTickLength\n*/\nvoid QCPAxis::setTickLengthIn(int inside)\n{\n  if (mAxisPainter->tickLengthIn != inside)\n  {\n    mAxisPainter->tickLengthIn = inside;\n  }\n}\n\n/*!\n  Sets the length of the outward ticks in pixels. \\a outside is the length the ticks will reach\n  outside the plot. If \\a outside is greater than zero, the tick labels and axis label will\n  increase their distance to the axis accordingly, so they won't collide with the ticks.\n  \n  \\see setTickLengthIn, setTickLength, setSubTickLength\n*/\nvoid QCPAxis::setTickLengthOut(int outside)\n{\n  if (mAxisPainter->tickLengthOut != outside)\n  {\n    mAxisPainter->tickLengthOut = outside;\n    mCachedMarginValid = false; // only outside tick length can change margin\n  }\n}\n\n/*!\n  Sets whether sub tick marks are displayed.\n  \n  Sub ticks are only potentially visible if (major) ticks are also visible (see \\ref setTicks)\n  \n  \\see setTicks\n*/\nvoid QCPAxis::setSubTicks(bool show)\n{\n  if (mSubTicks != show)\n  {\n    mSubTicks = show;\n    mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the length of the subticks in pixels. \\a inside is the length the subticks will reach inside\n  the plot and \\a outside is the length they will reach outside the plot. If \\a outside is greater\n  than zero, the tick labels and axis label will increase their distance to the axis accordingly,\n  so they won't collide with the ticks.\n  \n  \\see setTickLength, setSubTickLengthIn, setSubTickLengthOut\n*/\nvoid QCPAxis::setSubTickLength(int inside, int outside)\n{\n  setSubTickLengthIn(inside);\n  setSubTickLengthOut(outside);\n}\n\n/*!\n  Sets the length of the inward subticks in pixels. \\a inside is the length the subticks will reach inside\n  the plot.\n  \n  \\see setSubTickLengthOut, setSubTickLength, setTickLength\n*/\nvoid QCPAxis::setSubTickLengthIn(int inside)\n{\n  if (mAxisPainter->subTickLengthIn != inside)\n  {\n    mAxisPainter->subTickLengthIn = inside;\n  }\n}\n\n/*!\n  Sets the length of the outward subticks in pixels. \\a outside is the length the subticks will reach\n  outside the plot. If \\a outside is greater than zero, the tick labels will increase their\n  distance to the axis accordingly, so they won't collide with the ticks.\n  \n  \\see setSubTickLengthIn, setSubTickLength, setTickLength\n*/\nvoid QCPAxis::setSubTickLengthOut(int outside)\n{\n  if (mAxisPainter->subTickLengthOut != outside)\n  {\n    mAxisPainter->subTickLengthOut = outside;\n    mCachedMarginValid = false; // only outside tick length can change margin\n  }\n}\n\n/*!\n  Sets the pen, the axis base line is drawn with.\n  \n  \\see setTickPen, setSubTickPen\n*/\nvoid QCPAxis::setBasePen(const QPen &pen)\n{\n  mBasePen = pen;\n}\n\n/*!\n  Sets the pen, tick marks will be drawn with.\n  \n  \\see setTickLength, setBasePen\n*/\nvoid QCPAxis::setTickPen(const QPen &pen)\n{\n  mTickPen = pen;\n}\n\n/*!\n  Sets the pen, subtick marks will be drawn with.\n  \n  \\see setSubTickCount, setSubTickLength, setBasePen\n*/\nvoid QCPAxis::setSubTickPen(const QPen &pen)\n{\n  mSubTickPen = pen;\n}\n\n/*!\n  Sets the font of the axis label.\n  \n  \\see setLabelColor\n*/\nvoid QCPAxis::setLabelFont(const QFont &font)\n{\n  if (mLabelFont != font)\n  {\n    mLabelFont = font;\n    mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the color of the axis label.\n  \n  \\see setLabelFont\n*/\nvoid QCPAxis::setLabelColor(const QColor &color)\n{\n  mLabelColor = color;\n}\n\n/*!\n  Sets the text of the axis label that will be shown below/above or next to the axis, depending on\n  its orientation. To disable axis labels, pass an empty string as \\a str.\n*/\nvoid QCPAxis::setLabel(const QString &str)\n{\n  if (mLabel != str)\n  {\n    mLabel = str;\n    mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the distance between the tick labels and the axis label.\n  \n  \\see setTickLabelPadding, setPadding\n*/\nvoid QCPAxis::setLabelPadding(int padding)\n{\n  if (mAxisPainter->labelPadding != padding)\n  {\n    mAxisPainter->labelPadding = padding;\n    mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the padding of the axis.\n\n  When \\ref QCPAxisRect::setAutoMargins is enabled, the padding is the additional outer most space,\n  that is left blank.\n  \n  The axis padding has no meaning if \\ref QCPAxisRect::setAutoMargins is disabled.\n  \n  \\see setLabelPadding, setTickLabelPadding\n*/\nvoid QCPAxis::setPadding(int padding)\n{\n  if (mPadding != padding)\n  {\n    mPadding = padding;\n    mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the offset the axis has to its axis rect side.\n  \n  If an axis rect side has multiple axes and automatic margin calculation is enabled for that side,\n  only the offset of the inner most axis has meaning (even if it is set to be invisible). The\n  offset of the other, outer axes is controlled automatically, to place them at appropriate\n  positions.\n*/\nvoid QCPAxis::setOffset(int offset)\n{\n  mAxisPainter->offset = offset;\n}\n\n/*!\n  Sets the font that is used for tick labels when they are selected.\n  \n  \\see setTickLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPAxis::setSelectedTickLabelFont(const QFont &font)\n{\n  if (font != mSelectedTickLabelFont)\n  {\n    mSelectedTickLabelFont = font;\n    // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts\n  }\n}\n\n/*!\n  Sets the font that is used for the axis label when it is selected.\n  \n  \\see setLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPAxis::setSelectedLabelFont(const QFont &font)\n{\n  mSelectedLabelFont = font;\n  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts\n}\n\n/*!\n  Sets the color that is used for tick labels when they are selected.\n  \n  \\see setTickLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPAxis::setSelectedTickLabelColor(const QColor &color)\n{\n  if (color != mSelectedTickLabelColor)\n  {\n    mSelectedTickLabelColor = color;\n  }\n}\n\n/*!\n  Sets the color that is used for the axis label when it is selected.\n  \n  \\see setLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPAxis::setSelectedLabelColor(const QColor &color)\n{\n  mSelectedLabelColor = color;\n}\n\n/*!\n  Sets the pen that is used to draw the axis base line when selected.\n  \n  \\see setBasePen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPAxis::setSelectedBasePen(const QPen &pen)\n{\n  mSelectedBasePen = pen;\n}\n\n/*!\n  Sets the pen that is used to draw the (major) ticks when selected.\n  \n  \\see setTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPAxis::setSelectedTickPen(const QPen &pen)\n{\n  mSelectedTickPen = pen;\n}\n\n/*!\n  Sets the pen that is used to draw the subticks when selected.\n  \n  \\see setSubTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPAxis::setSelectedSubTickPen(const QPen &pen)\n{\n  mSelectedSubTickPen = pen;\n}\n\n/*!\n  Sets the style for the lower axis ending. See the documentation of QCPLineEnding for available\n  styles.\n  \n  For horizontal axes, this method refers to the left ending, for vertical axes the bottom ending.\n  Note that this meaning does not change when the axis range is reversed with \\ref\n  setRangeReversed.\n  \n  \\see setUpperEnding\n*/\nvoid QCPAxis::setLowerEnding(const QCPLineEnding &ending)\n{\n  mAxisPainter->lowerEnding = ending;\n}\n\n/*!\n  Sets the style for the upper axis ending. See the documentation of QCPLineEnding for available\n  styles.\n  \n  For horizontal axes, this method refers to the right ending, for vertical axes the top ending.\n  Note that this meaning does not change when the axis range is reversed with \\ref\n  setRangeReversed.\n  \n  \\see setLowerEnding\n*/\nvoid QCPAxis::setUpperEnding(const QCPLineEnding &ending)\n{\n  mAxisPainter->upperEnding = ending;\n}\n\n/*!\n  If the scale type (\\ref setScaleType) is \\ref stLinear, \\a diff is added to the lower and upper\n  bounds of the range. The range is simply moved by \\a diff.\n  \n  If the scale type is \\ref stLogarithmic, the range bounds are multiplied by \\a diff. This\n  corresponds to an apparent \"linear\" move in logarithmic scaling by a distance of log(diff).\n*/\nvoid QCPAxis::moveRange(double diff)\n{\n  QCPRange oldRange = mRange;\n  if (mScaleType == stLinear)\n  {\n    mRange.lower += diff;\n    mRange.upper += diff;\n  } else // mScaleType == stLogarithmic\n  {\n    mRange.lower *= diff;\n    mRange.upper *= diff;\n  }\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  Scales the range of this axis by \\a factor around the center of the current axis range. For\n  example, if \\a factor is 2.0, then the axis range will double its size, and the point at the axis\n  range center won't have changed its position in the QCustomPlot widget (i.e. coordinates around\n  the center will have moved symmetrically closer).\n\n  If you wish to scale around a different coordinate than the current axis range center, use the\n  overload \\ref scaleRange(double factor, double center).\n*/\nvoid QCPAxis::scaleRange(double factor)\n{\n  scaleRange(factor, range().center());\n}\n\n/*! \\overload\n\n  Scales the range of this axis by \\a factor around the coordinate \\a center. For example, if \\a\n  factor is 2.0, \\a center is 1.0, then the axis range will double its size, and the point at\n  coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates\n  around 1.0 will have moved symmetrically closer to 1.0).\n\n  \\see scaleRange(double factor)\n*/\nvoid QCPAxis::scaleRange(double factor, double center)\n{\n  QCPRange oldRange = mRange;\n  if (mScaleType == stLinear)\n  {\n    QCPRange newRange;\n    newRange.lower = (mRange.lower-center)*factor + center;\n    newRange.upper = (mRange.upper-center)*factor + center;\n    if (QCPRange::validRange(newRange))\n      mRange = newRange.sanitizedForLinScale();\n  } else // mScaleType == stLogarithmic\n  {\n    if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range\n    {\n      QCPRange newRange;\n      newRange.lower = qPow(mRange.lower/center, factor)*center;\n      newRange.upper = qPow(mRange.upper/center, factor)*center;\n      if (QCPRange::validRange(newRange))\n        mRange = newRange.sanitizedForLogScale();\n    } else\n      qDebug() << Q_FUNC_INFO << \"Center of scaling operation doesn't lie in same logarithmic sign domain as range:\" << center;\n  }\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  Scales the range of this axis to have a certain scale \\a ratio to \\a otherAxis. The scaling will\n  be done around the center of the current axis range.\n\n  For example, if \\a ratio is 1, this axis is the \\a yAxis and \\a otherAxis is \\a xAxis, graphs\n  plotted with those axes will appear in a 1:1 aspect ratio, independent of the aspect ratio the\n  axis rect has.\n\n  This is an operation that changes the range of this axis once, it doesn't fix the scale ratio\n  indefinitely. Note that calling this function in the constructor of the QCustomPlot's parent\n  won't have the desired effect, since the widget dimensions aren't defined yet, and a resizeEvent\n  will follow.\n*/\nvoid QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)\n{\n  int otherPixelSize, ownPixelSize;\n  \n  if (otherAxis->orientation() == Qt::Horizontal)\n    otherPixelSize = otherAxis->axisRect()->width();\n  else\n    otherPixelSize = otherAxis->axisRect()->height();\n  \n  if (orientation() == Qt::Horizontal)\n    ownPixelSize = axisRect()->width();\n  else\n    ownPixelSize = axisRect()->height();\n  \n  double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/double(otherPixelSize);\n  setRange(range().center(), newRangeSize, Qt::AlignCenter);\n}\n\n/*!\n  Changes the axis range such that all plottables associated with this axis are fully visible in\n  that dimension.\n  \n  \\see QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes\n*/\nvoid QCPAxis::rescale(bool onlyVisiblePlottables)\n{\n  QCPRange newRange;\n  bool haveRange = false;\n  foreach (QCPAbstractPlottable *plottable, plottables())\n  {\n    if (!plottable->realVisibility() && onlyVisiblePlottables)\n      continue;\n    QCPRange plottableRange;\n    bool currentFoundRange;\n    QCP::SignDomain signDomain = QCP::sdBoth;\n    if (mScaleType == stLogarithmic)\n      signDomain = (mRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive);\n    if (plottable->keyAxis() == this)\n      plottableRange = plottable->getKeyRange(currentFoundRange, signDomain);\n    else\n      plottableRange = plottable->getValueRange(currentFoundRange, signDomain);\n    if (currentFoundRange)\n    {\n      if (!haveRange)\n        newRange = plottableRange;\n      else\n        newRange.expand(plottableRange);\n      haveRange = true;\n    }\n  }\n  if (haveRange)\n  {\n    if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable\n    {\n      double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason\n      if (mScaleType == stLinear)\n      {\n        newRange.lower = center-mRange.size()/2.0;\n        newRange.upper = center+mRange.size()/2.0;\n      } else // mScaleType == stLogarithmic\n      {\n        newRange.lower = center/qSqrt(mRange.upper/mRange.lower);\n        newRange.upper = center*qSqrt(mRange.upper/mRange.lower);\n      }\n    }\n    setRange(newRange);\n  }\n}\n\n/*!\n  Transforms \\a value, in pixel coordinates of the QCustomPlot widget, to axis coordinates.\n*/\ndouble QCPAxis::pixelToCoord(double value) const\n{\n  if (orientation() == Qt::Horizontal)\n  {\n    if (mScaleType == stLinear)\n    {\n      if (!mRangeReversed)\n        return (value-mAxisRect->left())/double(mAxisRect->width())*mRange.size()+mRange.lower;\n      else\n        return -(value-mAxisRect->left())/double(mAxisRect->width())*mRange.size()+mRange.upper;\n    } else // mScaleType == stLogarithmic\n    {\n      if (!mRangeReversed)\n        return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/double(mAxisRect->width()))*mRange.lower;\n      else\n        return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/double(mAxisRect->width()))*mRange.upper;\n    }\n  } else // orientation() == Qt::Vertical\n  {\n    if (mScaleType == stLinear)\n    {\n      if (!mRangeReversed)\n        return (mAxisRect->bottom()-value)/double(mAxisRect->height())*mRange.size()+mRange.lower;\n      else\n        return -(mAxisRect->bottom()-value)/double(mAxisRect->height())*mRange.size()+mRange.upper;\n    } else // mScaleType == stLogarithmic\n    {\n      if (!mRangeReversed)\n        return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/double(mAxisRect->height()))*mRange.lower;\n      else\n        return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/double(mAxisRect->height()))*mRange.upper;\n    }\n  }\n}\n\n/*!\n  Transforms \\a value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget.\n*/\ndouble QCPAxis::coordToPixel(double value) const\n{\n  if (orientation() == Qt::Horizontal)\n  {\n    if (mScaleType == stLinear)\n    {\n      if (!mRangeReversed)\n        return (value-mRange.lower)/mRange.size()*mAxisRect->width()+mAxisRect->left();\n      else\n        return (mRange.upper-value)/mRange.size()*mAxisRect->width()+mAxisRect->left();\n    } else // mScaleType == stLogarithmic\n    {\n      if (value >= 0.0 && mRange.upper < 0.0) // invalid value for logarithmic scale, just draw it outside visible range\n        return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200;\n      else if (value <= 0.0 && mRange.upper >= 0.0) // invalid value for logarithmic scale, just draw it outside visible range\n        return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200;\n      else\n      {\n        if (!mRangeReversed)\n          return qLn(value/mRange.lower)/qLn(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();\n        else\n          return qLn(mRange.upper/value)/qLn(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();\n      }\n    }\n  } else // orientation() == Qt::Vertical\n  {\n    if (mScaleType == stLinear)\n    {\n      if (!mRangeReversed)\n        return mAxisRect->bottom()-(value-mRange.lower)/mRange.size()*mAxisRect->height();\n      else\n        return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height();\n    } else // mScaleType == stLogarithmic\n    {\n      if (value >= 0.0 && mRange.upper < 0.0) // invalid value for logarithmic scale, just draw it outside visible range\n        return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200;\n      else if (value <= 0.0 && mRange.upper >= 0.0) // invalid value for logarithmic scale, just draw it outside visible range\n        return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200;\n      else\n      {\n        if (!mRangeReversed)\n          return mAxisRect->bottom()-qLn(value/mRange.lower)/qLn(mRange.upper/mRange.lower)*mAxisRect->height();\n        else\n          return mAxisRect->bottom()-qLn(mRange.upper/value)/qLn(mRange.upper/mRange.lower)*mAxisRect->height();\n      }\n    }\n  }\n}\n\n/*!\n  Returns the part of the axis that is hit by \\a pos (in pixels). The return value of this function\n  is independent of the user-selectable parts defined with \\ref setSelectableParts. Further, this\n  function does not change the current selection state of the axis.\n  \n  If the axis is not visible (\\ref setVisible), this function always returns \\ref spNone.\n  \n  \\see setSelectedParts, setSelectableParts, QCustomPlot::setInteractions\n*/\nQCPAxis::SelectablePart QCPAxis::getPartAt(const QPointF &pos) const\n{\n  if (!mVisible)\n    return spNone;\n  \n  if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))\n    return spAxis;\n  else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))\n    return spTickLabels;\n  else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))\n    return spAxisLabel;\n  else\n    return spNone;\n}\n\n/* inherits documentation from base class */\ndouble QCPAxis::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  if (!mParentPlot) return -1;\n  SelectablePart part = getPartAt(pos);\n  if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)\n    return -1;\n  \n  if (details)\n    details->setValue(part);\n  return mParentPlot->selectionTolerance()*0.99;\n}\n\n/*!\n  Returns a list of all the plottables that have this axis as key or value axis.\n  \n  If you are only interested in plottables of type QCPGraph, see \\ref graphs.\n  \n  \\see graphs, items\n*/\nQList<QCPAbstractPlottable*> QCPAxis::plottables() const\n{\n  QList<QCPAbstractPlottable*> result;\n  if (!mParentPlot) return result;\n  \n  foreach (QCPAbstractPlottable *plottable, mParentPlot->mPlottables)\n  {\n    if (plottable->keyAxis() == this || plottable->valueAxis() == this)\n      result.append(plottable);\n  }\n  return result;\n}\n\n/*!\n  Returns a list of all the graphs that have this axis as key or value axis.\n  \n  \\see plottables, items\n*/\nQList<QCPGraph*> QCPAxis::graphs() const\n{\n  QList<QCPGraph*> result;\n  if (!mParentPlot) return result;\n  \n  foreach (QCPGraph *graph, mParentPlot->mGraphs)\n  {\n    if (graph->keyAxis() == this || graph->valueAxis() == this)\n      result.append(graph);\n  }\n  return result;\n}\n\n/*!\n  Returns a list of all the items that are associated with this axis. An item is considered\n  associated with an axis if at least one of its positions uses the axis as key or value axis.\n  \n  \\see plottables, graphs\n*/\nQList<QCPAbstractItem*> QCPAxis::items() const\n{\n  QList<QCPAbstractItem*> result;\n  if (!mParentPlot) return result;\n  \n  foreach (QCPAbstractItem *item, mParentPlot->mItems)\n  {\n    foreach (QCPItemPosition *position, item->positions())\n    {\n      if (position->keyAxis() == this || position->valueAxis() == this)\n      {\n        result.append(item);\n        break;\n      }\n    }\n  }\n  return result;\n}\n\n/*!\n  Transforms a margin side to the logically corresponding axis type. (QCP::msLeft to\n  QCPAxis::atLeft, QCP::msRight to QCPAxis::atRight, etc.)\n*/\nQCPAxis::AxisType QCPAxis::marginSideToAxisType(QCP::MarginSide side)\n{\n  switch (side)\n  {\n    case QCP::msLeft: return atLeft;\n    case QCP::msRight: return atRight;\n    case QCP::msTop: return atTop;\n    case QCP::msBottom: return atBottom;\n    default: break;\n  }\n  qDebug() << Q_FUNC_INFO << \"Invalid margin side passed:\" << static_cast<int>(side);\n  return atLeft;\n}\n\n/*!\n  Returns the axis type that describes the opposite axis of an axis with the specified \\a type.\n*/\nQCPAxis::AxisType QCPAxis::opposite(QCPAxis::AxisType type)\n{\n  switch (type)\n  {\n    case atLeft: return atRight;\n    case atRight: return atLeft;\n    case atBottom: return atTop;\n    case atTop: return atBottom;\n  }\n  qDebug() << Q_FUNC_INFO << \"invalid axis type\";\n  return atLeft;\n}\n\n/* inherits documentation from base class */\nvoid QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)\n{\n  Q_UNUSED(event)\n  SelectablePart part = details.value<SelectablePart>();\n  if (mSelectableParts.testFlag(part))\n  {\n    SelectableParts selBefore = mSelectedParts;\n    setSelectedParts(additive ? mSelectedParts^part : part);\n    if (selectionStateChanged)\n      *selectionStateChanged = mSelectedParts != selBefore;\n  }\n}\n\n/* inherits documentation from base class */\nvoid QCPAxis::deselectEvent(bool *selectionStateChanged)\n{\n  SelectableParts selBefore = mSelectedParts;\n  setSelectedParts(mSelectedParts & ~mSelectableParts);\n  if (selectionStateChanged)\n    *selectionStateChanged = mSelectedParts != selBefore;\n}\n\n/*! \\internal\n  \n  This mouse event reimplementation provides the functionality to let the user drag individual axes\n  exclusively, by startig the drag on top of the axis.\n\n  For the axis to accept this event and perform the single axis drag, the parent \\ref QCPAxisRect\n  must be configured accordingly, i.e. it must allow range dragging in the orientation of this axis\n  (\\ref QCPAxisRect::setRangeDrag) and this axis must be a draggable axis (\\ref\n  QCPAxisRect::setRangeDragAxes)\n  \n  \\seebaseclassmethod\n  \n  \\note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis\n  rect is handled by the axis rect's mouse event, e.g. \\ref QCPAxisRect::mousePressEvent.\n*/\nvoid QCPAxis::mousePressEvent(QMouseEvent *event, const QVariant &details)\n{\n  Q_UNUSED(details)\n  if (!mParentPlot->interactions().testFlag(QCP::iRangeDrag) ||\n      !mAxisRect->rangeDrag().testFlag(orientation()) ||\n      !mAxisRect->rangeDragAxes(orientation()).contains(this))\n  {\n    event->ignore();\n    return;\n  }\n  \n  if (event->buttons() & Qt::LeftButton)\n  {\n    mDragging = true;\n    // initialize antialiasing backup in case we start dragging:\n    if (mParentPlot->noAntialiasingOnDrag())\n    {\n      mAADragBackup = mParentPlot->antialiasedElements();\n      mNotAADragBackup = mParentPlot->notAntialiasedElements();\n    }\n    // Mouse range dragging interaction:\n    if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))\n      mDragStartRange = mRange;\n  }\n}\n\n/*! \\internal\n  \n  This mouse event reimplementation provides the functionality to let the user drag individual axes\n  exclusively, by startig the drag on top of the axis.\n  \n  \\seebaseclassmethod\n  \n  \\note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis\n  rect is handled by the axis rect's mouse event, e.g. \\ref QCPAxisRect::mousePressEvent.\n  \n  \\see QCPAxis::mousePressEvent\n*/\nvoid QCPAxis::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)\n{\n  if (mDragging)\n  {\n    const double startPixel = orientation() == Qt::Horizontal ? startPos.x() : startPos.y();\n    const double currentPixel = orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y();\n    if (mScaleType == QCPAxis::stLinear)\n    {\n      const double diff = pixelToCoord(startPixel) - pixelToCoord(currentPixel);\n      setRange(mDragStartRange.lower+diff, mDragStartRange.upper+diff);\n    } else if (mScaleType == QCPAxis::stLogarithmic)\n    {\n      const double diff = pixelToCoord(startPixel) / pixelToCoord(currentPixel);\n      setRange(mDragStartRange.lower*diff, mDragStartRange.upper*diff);\n    }\n    \n    if (mParentPlot->noAntialiasingOnDrag())\n      mParentPlot->setNotAntialiasedElements(QCP::aeAll);\n    mParentPlot->replot(QCustomPlot::rpQueuedReplot);\n  }\n}\n\n/*! \\internal\n  \n  This mouse event reimplementation provides the functionality to let the user drag individual axes\n  exclusively, by startig the drag on top of the axis.\n  \n  \\seebaseclassmethod\n  \n  \\note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis\n  rect is handled by the axis rect's mouse event, e.g. \\ref QCPAxisRect::mousePressEvent.\n  \n  \\see QCPAxis::mousePressEvent\n*/\nvoid QCPAxis::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)\n{\n  Q_UNUSED(event)\n  Q_UNUSED(startPos)\n  mDragging = false;\n  if (mParentPlot->noAntialiasingOnDrag())\n  {\n    mParentPlot->setAntialiasedElements(mAADragBackup);\n    mParentPlot->setNotAntialiasedElements(mNotAADragBackup);\n  }\n}\n\n/*! \\internal\n  \n  This mouse event reimplementation provides the functionality to let the user zoom individual axes\n  exclusively, by performing the wheel event on top of the axis.\n\n  For the axis to accept this event and perform the single axis zoom, the parent \\ref QCPAxisRect\n  must be configured accordingly, i.e. it must allow range zooming in the orientation of this axis\n  (\\ref QCPAxisRect::setRangeZoom) and this axis must be a zoomable axis (\\ref\n  QCPAxisRect::setRangeZoomAxes)\n  \n  \\seebaseclassmethod\n  \n  \\note The zooming of possibly multiple axes at once by performing the wheel event anywhere in the\n  axis rect is handled by the axis rect's mouse event, e.g. \\ref QCPAxisRect::wheelEvent.\n*/\nvoid QCPAxis::wheelEvent(QWheelEvent *event)\n{\n  // Mouse range zooming interaction:\n  if (!mParentPlot->interactions().testFlag(QCP::iRangeZoom) ||\n      !mAxisRect->rangeZoom().testFlag(orientation()) ||\n      !mAxisRect->rangeZoomAxes(orientation()).contains(this))\n  {\n    event->ignore();\n    return;\n  }\n  \n#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)\n  const double delta = event->delta();\n#else\n  const double delta = event->angleDelta().y();\n#endif\n  \n#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)\n  const QPointF pos = event->pos();\n#else\n  const QPointF pos = event->position();\n#endif\n  \n  const double wheelSteps = delta/120.0; // a single step delta is +/-120 usually\n  const double factor = qPow(mAxisRect->rangeZoomFactor(orientation()), wheelSteps);\n  scaleRange(factor, pixelToCoord(orientation() == Qt::Horizontal ? pos.x() : pos.y()));\n  mParentPlot->replot();\n}\n\n/*! \\internal\n\n  A convenience function to easily set the QPainter::Antialiased hint on the provided \\a painter\n  before drawing axis lines.\n\n  This is the antialiasing state the painter passed to the \\ref draw method is in by default.\n  \n  This function takes into account the local setting of the antialiasing flag as well as the\n  overrides set with \\ref QCustomPlot::setAntialiasedElements and \\ref\n  QCustomPlot::setNotAntialiasedElements.\n  \n  \\seebaseclassmethod\n  \n  \\see setAntialiased\n*/\nvoid QCPAxis::applyDefaultAntialiasingHint(QCPPainter *painter) const\n{\n  applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes);\n}\n\n/*! \\internal\n  \n  Draws the axis with the specified \\a painter, using the internal QCPAxisPainterPrivate instance.\n\n  \\seebaseclassmethod\n*/\nvoid QCPAxis::draw(QCPPainter *painter)\n{\n  QVector<double> subTickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter\n  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter\n  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter\n  tickPositions.reserve(mTickVector.size());\n  tickLabels.reserve(mTickVector.size());\n  subTickPositions.reserve(mSubTickVector.size());\n  \n  if (mTicks)\n  {\n    for (int i=0; i<mTickVector.size(); ++i)\n    {\n      tickPositions.append(coordToPixel(mTickVector.at(i)));\n      if (mTickLabels)\n        tickLabels.append(mTickVectorLabels.at(i));\n    }\n\n    if (mSubTicks)\n    {\n      const int subTickCount = mSubTickVector.size();\n      for (int i=0; i<subTickCount; ++i)\n        subTickPositions.append(coordToPixel(mSubTickVector.at(i)));\n    }\n  }\n  \n  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to draw the axis.\n  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters\n  mAxisPainter->type = mAxisType;\n  mAxisPainter->basePen = getBasePen();\n  mAxisPainter->labelFont = getLabelFont();\n  mAxisPainter->labelColor = getLabelColor();\n  mAxisPainter->label = mLabel;\n  mAxisPainter->substituteExponent = mNumberBeautifulPowers;\n  mAxisPainter->tickPen = getTickPen();\n  mAxisPainter->subTickPen = getSubTickPen();\n  mAxisPainter->tickLabelFont = getTickLabelFont();\n  mAxisPainter->tickLabelColor = getTickLabelColor();\n  mAxisPainter->axisRect = mAxisRect->rect();\n  mAxisPainter->viewportRect = mParentPlot->viewport();\n  mAxisPainter->abbreviateDecimalPowers = mScaleType == stLogarithmic;\n  mAxisPainter->reversedEndings = mRangeReversed;\n  mAxisPainter->tickPositions = tickPositions;\n  mAxisPainter->tickLabels = tickLabels;\n  mAxisPainter->subTickPositions = subTickPositions;\n  mAxisPainter->draw(painter);\n}\n\n/*! \\internal\n  \n  Prepares the internal tick vector, sub tick vector and tick label vector. This is done by calling\n  QCPAxisTicker::generate on the currently installed ticker.\n  \n  If a change in the label text/count is detected, the cached axis margin is invalidated to make\n  sure the next margin calculation recalculates the label sizes and returns an up-to-date value.\n*/\nvoid QCPAxis::setupTickVectors()\n{\n  if (!mParentPlot) return;\n  if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;\n  \n  QVector<QString> oldLabels = mTickVectorLabels;\n  mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar, mNumberPrecision, mTickVector, mSubTicks ? &mSubTickVector : nullptr, mTickLabels ? &mTickVectorLabels : nullptr);\n  mCachedMarginValid &= mTickVectorLabels == oldLabels; // if labels have changed, margin might have changed, too\n}\n\n/*! \\internal\n  \n  Returns the pen that is used to draw the axis base line. Depending on the selection state, this\n  is either mSelectedBasePen or mBasePen.\n*/\nQPen QCPAxis::getBasePen() const\n{\n  return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;\n}\n\n/*! \\internal\n  \n  Returns the pen that is used to draw the (major) ticks. Depending on the selection state, this\n  is either mSelectedTickPen or mTickPen.\n*/\nQPen QCPAxis::getTickPen() const\n{\n  return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;\n}\n\n/*! \\internal\n  \n  Returns the pen that is used to draw the subticks. Depending on the selection state, this\n  is either mSelectedSubTickPen or mSubTickPen.\n*/\nQPen QCPAxis::getSubTickPen() const\n{\n  return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen;\n}\n\n/*! \\internal\n  \n  Returns the font that is used to draw the tick labels. Depending on the selection state, this\n  is either mSelectedTickLabelFont or mTickLabelFont.\n*/\nQFont QCPAxis::getTickLabelFont() const\n{\n  return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont;\n}\n\n/*! \\internal\n  \n  Returns the font that is used to draw the axis label. Depending on the selection state, this\n  is either mSelectedLabelFont or mLabelFont.\n*/\nQFont QCPAxis::getLabelFont() const\n{\n  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont;\n}\n\n/*! \\internal\n  \n  Returns the color that is used to draw the tick labels. Depending on the selection state, this\n  is either mSelectedTickLabelColor or mTickLabelColor.\n*/\nQColor QCPAxis::getTickLabelColor() const\n{\n  return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor;\n}\n\n/*! \\internal\n  \n  Returns the color that is used to draw the axis label. Depending on the selection state, this\n  is either mSelectedLabelColor or mLabelColor.\n*/\nQColor QCPAxis::getLabelColor() const\n{\n  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor;\n}\n\n/*! \\internal\n  \n  Returns the appropriate outward margin for this axis. It is needed if \\ref\n  QCPAxisRect::setAutoMargins is set to true on the parent axis rect. An axis with axis type \\ref\n  atLeft will return an appropriate left margin, \\ref atBottom will return an appropriate bottom\n  margin and so forth. For the calculation, this function goes through similar steps as \\ref draw,\n  so changing one function likely requires the modification of the other one as well.\n  \n  The margin consists of the outward tick length, tick label padding, tick label size, label\n  padding, label size, and padding.\n  \n  The margin is cached internally, so repeated calls while leaving the axis range, fonts, etc.\n  unchanged are very fast.\n*/\nint QCPAxis::calculateMargin()\n{\n  if (!mVisible) // if not visible, directly return 0, don't cache 0 because we can't react to setVisible in QCPAxis\n    return 0;\n  \n  if (mCachedMarginValid)\n    return mCachedMargin;\n  \n  // run through similar steps as QCPAxis::draw, and calculate margin needed to fit axis and its labels\n  int margin = 0;\n  \n  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter\n  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter\n  tickPositions.reserve(mTickVector.size());\n  tickLabels.reserve(mTickVector.size());\n  \n  if (mTicks)\n  {\n    for (int i=0; i<mTickVector.size(); ++i)\n    {\n      tickPositions.append(coordToPixel(mTickVector.at(i)));\n      if (mTickLabels)\n        tickLabels.append(mTickVectorLabels.at(i));\n    }\n  }\n  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to calculate the size.\n  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters\n  mAxisPainter->type = mAxisType;\n  mAxisPainter->labelFont = getLabelFont();\n  mAxisPainter->label = mLabel;\n  mAxisPainter->tickLabelFont = mTickLabelFont;\n  mAxisPainter->axisRect = mAxisRect->rect();\n  mAxisPainter->viewportRect = mParentPlot->viewport();\n  mAxisPainter->tickPositions = tickPositions;\n  mAxisPainter->tickLabels = tickLabels;\n  margin += mAxisPainter->size();\n  margin += mPadding;\n\n  mCachedMargin = margin;\n  mCachedMarginValid = true;\n  return margin;\n}\n\n/* inherits documentation from base class */\nQCP::Interaction QCPAxis::selectionCategory() const\n{\n  return QCP::iSelectAxes;\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPAxisPainterPrivate\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPAxisPainterPrivate\n\n  \\internal\n  \\brief (Private)\n  \n  This is a private class and not part of the public QCustomPlot interface.\n  \n  It is used by QCPAxis to do the low-level drawing of axis backbone, tick marks, tick labels and\n  axis label. It also buffers the labels to reduce replot times. The parameters are configured by\n  directly accessing the public member variables.\n*/\n\n/*!\n  Constructs a QCPAxisPainterPrivate instance. Make sure to not create a new instance on every\n  redraw, to utilize the caching mechanisms.\n*/\nQCPAxisPainterPrivate::QCPAxisPainterPrivate(QCustomPlot *parentPlot) :\n  type(QCPAxis::atLeft),\n  basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),\n  lowerEnding(QCPLineEnding::esNone),\n  upperEnding(QCPLineEnding::esNone),\n  labelPadding(0),\n  tickLabelPadding(0),\n  tickLabelRotation(0),\n  tickLabelSide(QCPAxis::lsOutside),\n  substituteExponent(true),\n  numberMultiplyCross(false),\n  tickLengthIn(5),\n  tickLengthOut(0),\n  subTickLengthIn(2),\n  subTickLengthOut(0),\n  tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),\n  subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),\n  offset(0),\n  abbreviateDecimalPowers(false),\n  reversedEndings(false),\n  mParentPlot(parentPlot),\n  mLabelCache(16) // cache at most 16 (tick) labels\n{\n}\n\nQCPAxisPainterPrivate::~QCPAxisPainterPrivate()\n{\n}\n\n/*! \\internal\n  \n  Draws the axis with the specified \\a painter.\n  \n  The selection boxes (mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox) are set\n  here, too.\n*/\nvoid QCPAxisPainterPrivate::draw(QCPPainter *painter)\n{\n  QByteArray newHash = generateLabelParameterHash();\n  if (newHash != mLabelParameterHash)\n  {\n    mLabelCache.clear();\n    mLabelParameterHash = newHash;\n  }\n  \n  QPoint origin;\n  switch (type)\n  {\n    case QCPAxis::atLeft:   origin = axisRect.bottomLeft() +QPoint(-offset, 0); break;\n    case QCPAxis::atRight:  origin = axisRect.bottomRight()+QPoint(+offset, 0); break;\n    case QCPAxis::atTop:    origin = axisRect.topLeft()    +QPoint(0, -offset); break;\n    case QCPAxis::atBottom: origin = axisRect.bottomLeft() +QPoint(0, +offset); break;\n  }\n\n  double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)\n  switch (type)\n  {\n    case QCPAxis::atTop: yCor = -1; break;\n    case QCPAxis::atRight: xCor = 1; break;\n    default: break;\n  }\n  int margin = 0;\n  // draw baseline:\n  QLineF baseLine;\n  painter->setPen(basePen);\n  if (QCPAxis::orientation(type) == Qt::Horizontal)\n    baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(axisRect.width()+xCor, yCor));\n  else\n    baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -axisRect.height()+yCor));\n  if (reversedEndings)\n    baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later\n  painter->drawLine(baseLine);\n  \n  // draw ticks:\n  if (!tickPositions.isEmpty())\n  {\n    painter->setPen(tickPen);\n    int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks (\"inward\" is right for left axis and left for right axis)\n    if (QCPAxis::orientation(type) == Qt::Horizontal)\n    {\n      foreach (double tickPos, tickPositions)\n        painter->drawLine(QLineF(tickPos+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPos+xCor, origin.y()+tickLengthIn*tickDir+yCor));\n    } else\n    {\n      foreach (double tickPos, tickPositions)\n        painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPos+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPos+yCor));\n    }\n  }\n  \n  // draw subticks:\n  if (!subTickPositions.isEmpty())\n  {\n    painter->setPen(subTickPen);\n    // direction of ticks (\"inward\" is right for left axis and left for right axis)\n    int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;\n    if (QCPAxis::orientation(type) == Qt::Horizontal)\n    {\n      foreach (double subTickPos, subTickPositions)\n        painter->drawLine(QLineF(subTickPos+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPos+xCor, origin.y()+subTickLengthIn*tickDir+yCor));\n    } else\n    {\n      foreach (double subTickPos, subTickPositions)\n        painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPos+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPos+yCor));\n    }\n  }\n  margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));\n  \n  // draw axis base endings:\n  bool antialiasingBackup = painter->antialiasing();\n  painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't\n  painter->setBrush(QBrush(basePen.color()));\n  QCPVector2D baseLineVector(baseLine.dx(), baseLine.dy());\n  if (lowerEnding.style() != QCPLineEnding::esNone)\n    lowerEnding.draw(painter, QCPVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector);\n  if (upperEnding.style() != QCPLineEnding::esNone)\n    upperEnding.draw(painter, QCPVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector);\n  painter->setAntialiasing(antialiasingBackup);\n  \n  // tick labels:\n  QRect oldClipRect;\n  if (tickLabelSide == QCPAxis::lsInside) // if using inside labels, clip them to the axis rect\n  {\n    oldClipRect = painter->clipRegion().boundingRect();\n    painter->setClipRect(axisRect);\n  }\n  QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label\n  if (!tickLabels.isEmpty())\n  {\n    if (tickLabelSide == QCPAxis::lsOutside)\n      margin += tickLabelPadding;\n    painter->setFont(tickLabelFont);\n    painter->setPen(QPen(tickLabelColor));\n    const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());\n    int distanceToAxis = margin;\n    if (tickLabelSide == QCPAxis::lsInside)\n      distanceToAxis = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);\n    for (int i=0; i<maxLabelIndex; ++i)\n      placeTickLabel(painter, tickPositions.at(i), distanceToAxis, tickLabels.at(i), &tickLabelsSize);\n    if (tickLabelSide == QCPAxis::lsOutside)\n      margin += (QCPAxis::orientation(type) == Qt::Horizontal) ? tickLabelsSize.height() : tickLabelsSize.width();\n  }\n  if (tickLabelSide == QCPAxis::lsInside)\n    painter->setClipRect(oldClipRect);\n  \n  // axis label:\n  QRect labelBounds;\n  if (!label.isEmpty())\n  {\n    margin += labelPadding;\n    painter->setFont(labelFont);\n    painter->setPen(QPen(labelColor));\n    labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label);\n    if (type == QCPAxis::atLeft)\n    {\n      QTransform oldTransform = painter->transform();\n      painter->translate((origin.x()-margin-labelBounds.height()), origin.y());\n      painter->rotate(-90);\n      painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);\n      painter->setTransform(oldTransform);\n    }\n    else if (type == QCPAxis::atRight)\n    {\n      QTransform oldTransform = painter->transform();\n      painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-axisRect.height());\n      painter->rotate(90);\n      painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);\n      painter->setTransform(oldTransform);\n    }\n    else if (type == QCPAxis::atTop)\n      painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);\n    else if (type == QCPAxis::atBottom)\n      painter->drawText(origin.x(), origin.y()+margin, axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);\n  }\n  \n  // set selection boxes:\n  int selectionTolerance = 0;\n  if (mParentPlot)\n    selectionTolerance = mParentPlot->selectionTolerance();\n  else\n    qDebug() << Q_FUNC_INFO << \"mParentPlot is null\";\n  int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);\n  int selAxisInSize = selectionTolerance;\n  int selTickLabelSize;\n  int selTickLabelOffset;\n  if (tickLabelSide == QCPAxis::lsOutside)\n  {\n    selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());\n    selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding;\n  } else\n  {\n    selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());\n    selTickLabelOffset = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);\n  }\n  int selLabelSize = labelBounds.height();\n  int selLabelOffset = qMax(tickLengthOut, subTickLengthOut)+(!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside ? tickLabelPadding+selTickLabelSize : 0)+labelPadding;\n  if (type == QCPAxis::atLeft)\n  {\n    mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, axisRect.top(), origin.x()+selAxisInSize, axisRect.bottom());\n    mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, axisRect.top(), origin.x()-selTickLabelOffset, axisRect.bottom());\n    mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, axisRect.top(), origin.x()-selLabelOffset, axisRect.bottom());\n  } else if (type == QCPAxis::atRight)\n  {\n    mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, axisRect.top(), origin.x()+selAxisOutSize, axisRect.bottom());\n    mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, axisRect.top(), origin.x()+selTickLabelOffset, axisRect.bottom());\n    mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, axisRect.top(), origin.x()+selLabelOffset, axisRect.bottom());\n  } else if (type == QCPAxis::atTop)\n  {\n    mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisOutSize, axisRect.right(), origin.y()+selAxisInSize);\n    mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()-selTickLabelOffset-selTickLabelSize, axisRect.right(), origin.y()-selTickLabelOffset);\n    mLabelSelectionBox.setCoords(axisRect.left(), origin.y()-selLabelOffset-selLabelSize, axisRect.right(), origin.y()-selLabelOffset);\n  } else if (type == QCPAxis::atBottom)\n  {\n    mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisInSize, axisRect.right(), origin.y()+selAxisOutSize);\n    mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, axisRect.right(), origin.y()+selTickLabelOffset);\n    mLabelSelectionBox.setCoords(axisRect.left(), origin.y()+selLabelOffset+selLabelSize, axisRect.right(), origin.y()+selLabelOffset);\n  }\n  mAxisSelectionBox = mAxisSelectionBox.normalized();\n  mTickLabelsSelectionBox = mTickLabelsSelectionBox.normalized();\n  mLabelSelectionBox = mLabelSelectionBox.normalized();\n  // draw hitboxes for debug purposes:\n  //painter->setBrush(Qt::NoBrush);\n  //painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);\n}\n\n/*! \\internal\n  \n  Returns the size (\"margin\" in QCPAxisRect context, so measured perpendicular to the axis backbone\n  direction) needed to fit the axis.\n*/\nint QCPAxisPainterPrivate::size()\n{\n  int result = 0;\n\n  QByteArray newHash = generateLabelParameterHash();\n  if (newHash != mLabelParameterHash)\n  {\n    mLabelCache.clear();\n    mLabelParameterHash = newHash;\n  }\n  \n  // get length of tick marks pointing outwards:\n  if (!tickPositions.isEmpty())\n    result += qMax(0, qMax(tickLengthOut, subTickLengthOut));\n  \n  // calculate size of tick labels:\n  if (tickLabelSide == QCPAxis::lsOutside)\n  {\n    QSize tickLabelsSize(0, 0);\n    if (!tickLabels.isEmpty())\n    {\n      foreach (const QString &tickLabel, tickLabels)\n        getMaxTickLabelSize(tickLabelFont, tickLabel, &tickLabelsSize);\n      result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();\n    result += tickLabelPadding;\n    }\n  }\n  \n  // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):\n  if (!label.isEmpty())\n  {\n    QFontMetrics fontMetrics(labelFont);\n    QRect bounds;\n    bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);\n    result += bounds.height() + labelPadding;\n  }\n\n  return result;\n}\n\n/*! \\internal\n  \n  Clears the internal label cache. Upon the next \\ref draw, all labels will be created new. This\n  method is called automatically in \\ref draw, if any parameters have changed that invalidate the\n  cached labels, such as font, color, etc.\n*/\nvoid QCPAxisPainterPrivate::clearCache()\n{\n  mLabelCache.clear();\n}\n\n/*! \\internal\n  \n  Returns a hash that allows uniquely identifying whether the label parameters have changed such\n  that the cached labels must be refreshed (\\ref clearCache). It is used in \\ref draw. If the\n  return value of this method hasn't changed since the last redraw, the respective label parameters\n  haven't changed and cached labels may be used.\n*/\nQByteArray QCPAxisPainterPrivate::generateLabelParameterHash() const\n{\n  QByteArray result;\n  result.append(QByteArray::number(mParentPlot->bufferDevicePixelRatio()));\n  result.append(QByteArray::number(tickLabelRotation));\n  result.append(QByteArray::number(int(tickLabelSide)));\n  result.append(QByteArray::number(int(substituteExponent)));\n  result.append(QByteArray::number(int(numberMultiplyCross)));\n  result.append(tickLabelColor.name().toLatin1()+QByteArray::number(tickLabelColor.alpha(), 16));\n  result.append(tickLabelFont.toString().toLatin1());\n  return result;\n}\n\n/*! \\internal\n  \n  Draws a single tick label with the provided \\a painter, utilizing the internal label cache to\n  significantly speed up drawing of labels that were drawn in previous calls. The tick label is\n  always bound to an axis, the distance to the axis is controllable via \\a distanceToAxis in\n  pixels. The pixel position in the axis direction is passed in the \\a position parameter. Hence\n  for the bottom axis, \\a position would indicate the horizontal pixel position (not coordinate),\n  at which the label should be drawn.\n  \n  In order to later draw the axis label in a place that doesn't overlap with the tick labels, the\n  largest tick label size is needed. This is acquired by passing a \\a tickLabelsSize to the \\ref\n  drawTickLabel calls during the process of drawing all tick labels of one axis. In every call, \\a\n  tickLabelsSize is expanded, if the drawn label exceeds the value \\a tickLabelsSize currently\n  holds.\n  \n  The label is drawn with the font and pen that are currently set on the \\a painter. To draw\n  superscripted powers, the font is temporarily made smaller by a fixed factor (see \\ref\n  getTickLabelData).\n*/\nvoid QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)\n{\n  // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!\n  if (text.isEmpty()) return;\n  QSize finalSize;\n  QPointF labelAnchor;\n  switch (type)\n  {\n    case QCPAxis::atLeft:   labelAnchor = QPointF(axisRect.left()-distanceToAxis-offset, position); break;\n    case QCPAxis::atRight:  labelAnchor = QPointF(axisRect.right()+distanceToAxis+offset, position); break;\n    case QCPAxis::atTop:    labelAnchor = QPointF(position, axisRect.top()-distanceToAxis-offset); break;\n    case QCPAxis::atBottom: labelAnchor = QPointF(position, axisRect.bottom()+distanceToAxis+offset); break;\n  }\n  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled\n  {\n    CachedLabel *cachedLabel = mLabelCache.take(text); // attempt to get label from cache\n    if (!cachedLabel)  // no cached label existed, create it\n    {\n      cachedLabel = new CachedLabel;\n      TickLabelData labelData = getTickLabelData(painter->font(), text);\n      cachedLabel->offset = getTickLabelDrawOffset(labelData)+labelData.rotatedTotalBounds.topLeft();\n      if (!qFuzzyCompare(1.0, mParentPlot->bufferDevicePixelRatio()))\n      {\n        cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size()*mParentPlot->bufferDevicePixelRatio());\n#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED\n#  ifdef QCP_DEVICEPIXELRATIO_FLOAT\n        cachedLabel->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatioF());\n#  else\n        cachedLabel->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatio());\n#  endif\n#endif\n      } else\n        cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());\n      cachedLabel->pixmap.fill(Qt::transparent);\n      QCPPainter cachePainter(&cachedLabel->pixmap);\n      cachePainter.setPen(painter->pen());\n      drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);\n    }\n    // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):\n    bool labelClippedByBorder = false;\n    if (tickLabelSide == QCPAxis::lsOutside)\n    {\n      if (QCPAxis::orientation(type) == Qt::Horizontal)\n        labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width()/mParentPlot->bufferDevicePixelRatio() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left();\n      else\n        labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height()/mParentPlot->bufferDevicePixelRatio() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top();\n    }\n    if (!labelClippedByBorder)\n    {\n      painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);\n      finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio();\n    }\n    mLabelCache.insert(text, cachedLabel); // return label to cache or insert for the first time if newly created\n  } else // label caching disabled, draw text directly on surface:\n  {\n    TickLabelData labelData = getTickLabelData(painter->font(), text);\n    QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);\n    // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):\n     bool labelClippedByBorder = false;\n    if (tickLabelSide == QCPAxis::lsOutside)\n    {\n      if (QCPAxis::orientation(type) == Qt::Horizontal)\n        labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left();\n      else\n        labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top();\n    }\n    if (!labelClippedByBorder)\n    {\n      drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);\n      finalSize = labelData.rotatedTotalBounds.size();\n    }\n  }\n  \n  // expand passed tickLabelsSize if current tick label is larger:\n  if (finalSize.width() > tickLabelsSize->width())\n    tickLabelsSize->setWidth(finalSize.width());\n  if (finalSize.height() > tickLabelsSize->height())\n    tickLabelsSize->setHeight(finalSize.height());\n}\n\n/*! \\internal\n  \n  This is a \\ref placeTickLabel helper function.\n  \n  Draws the tick label specified in \\a labelData with \\a painter at the pixel positions \\a x and \\a\n  y. This function is used by \\ref placeTickLabel to create new tick labels for the cache, or to\n  directly draw the labels on the QCustomPlot surface when label caching is disabled, i.e. when\n  QCP::phCacheLabels plotting hint is not set.\n*/\nvoid QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const\n{\n  // backup painter settings that we're about to change:\n  QTransform oldTransform = painter->transform();\n  QFont oldFont = painter->font();\n  \n  // transform painter to position/rotation:\n  painter->translate(x, y);\n  if (!qFuzzyIsNull(tickLabelRotation))\n    painter->rotate(tickLabelRotation);\n  \n  // draw text:\n  if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used\n  {\n    painter->setFont(labelData.baseFont);\n    painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);\n    if (!labelData.suffixPart.isEmpty())\n      painter->drawText(labelData.baseBounds.width()+1+labelData.expBounds.width(), 0, 0, 0, Qt::TextDontClip, labelData.suffixPart);\n    painter->setFont(labelData.expFont);\n    painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip,  labelData.expPart);\n  } else\n  {\n    painter->setFont(labelData.baseFont);\n    painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);\n  }\n  \n  // reset painter settings to what it was before:\n  painter->setTransform(oldTransform);\n  painter->setFont(oldFont);\n}\n\n/*! \\internal\n  \n  This is a \\ref placeTickLabel helper function.\n  \n  Transforms the passed \\a text and \\a font to a tickLabelData structure that can then be further\n  processed by \\ref getTickLabelDrawOffset and \\ref drawTickLabel. It splits the text into base and\n  exponent if necessary (member substituteExponent) and calculates appropriate bounding boxes.\n*/\nQCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(const QFont &font, const QString &text) const\n{\n  TickLabelData result;\n  \n  // determine whether beautiful decimal powers should be used\n  bool useBeautifulPowers = false;\n  int ePos = -1; // first index of exponent part, text before that will be basePart, text until eLast will be expPart\n  int eLast = -1; // last index of exponent part, rest of text after this will be suffixPart\n  if (substituteExponent)\n  {\n    ePos = text.indexOf(QLatin1Char('e'));\n    if (ePos > 0 && text.at(ePos-1).isDigit())\n    {\n      eLast = ePos;\n      while (eLast+1 < text.size() && (text.at(eLast+1) == QLatin1Char('+') || text.at(eLast+1) == QLatin1Char('-') || text.at(eLast+1).isDigit()))\n        ++eLast;\n      if (eLast > ePos) // only if also to right of 'e' is a digit/+/- interpret it as beautifiable power\n        useBeautifulPowers = true;\n    }\n  }\n  \n  // calculate text bounding rects and do string preparation for beautiful decimal powers:\n  result.baseFont = font;\n  if (result.baseFont.pointSizeF() > 0) // might return -1 if specified with setPixelSize, in that case we can't do correction in next line\n    result.baseFont.setPointSizeF(result.baseFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding\n  if (useBeautifulPowers)\n  {\n    // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:\n    result.basePart = text.left(ePos);\n    result.suffixPart = text.mid(eLast+1); // also drawn normally but after exponent\n    // in log scaling, we want to turn \"1*10^n\" into \"10^n\", else add multiplication sign and decimal base:\n    if (abbreviateDecimalPowers && result.basePart == QLatin1String(\"1\"))\n      result.basePart = QLatin1String(\"10\");\n    else\n      result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + QLatin1String(\"10\");\n    result.expPart = text.mid(ePos+1, eLast-ePos);\n    // clip \"+\" and leading zeros off expPart:\n    while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e'\n      result.expPart.remove(1, 1);\n    if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))\n      result.expPart.remove(0, 1);\n    // prepare smaller font for exponent:\n    result.expFont = font;\n    if (result.expFont.pointSize() > 0)\n      result.expFont.setPointSize(int(result.expFont.pointSize()*0.75));\n    else\n      result.expFont.setPixelSize(int(result.expFont.pixelSize()*0.75));\n    // calculate bounding rects of base part(s), exponent part and total one:\n    result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);\n    result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);\n    if (!result.suffixPart.isEmpty())\n      result.suffixBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.suffixPart);\n    result.totalBounds = result.baseBounds.adjusted(0, 0, result.expBounds.width()+result.suffixBounds.width()+2, 0); // +2 consists of the 1 pixel spacing between base and exponent (see drawTickLabel) and an extra pixel to include AA\n  } else // useBeautifulPowers == false\n  {\n    result.basePart = text;\n    result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);\n  }\n  result.totalBounds.moveTopLeft(QPoint(0, 0)); // want bounding box aligned top left at origin, independent of how it was created, to make further processing simpler\n  \n  // calculate possibly different bounding rect after rotation:\n  result.rotatedTotalBounds = result.totalBounds;\n  if (!qFuzzyIsNull(tickLabelRotation))\n  {\n    QTransform transform;\n    transform.rotate(tickLabelRotation);\n    result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);\n  }\n  \n  return result;\n}\n\n/*! \\internal\n  \n  This is a \\ref placeTickLabel helper function.\n  \n  Calculates the offset at which the top left corner of the specified tick label shall be drawn.\n  The offset is relative to a point right next to the tick the label belongs to.\n  \n  This function is thus responsible for e.g. centering tick labels under ticks and positioning them\n  appropriately when they are rotated.\n*/\nQPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(const TickLabelData &labelData) const\n{\n  /*\n    calculate label offset from base point at tick (non-trivial, for best visual appearance): short\n    explanation for bottom axis: The anchor, i.e. the point in the label that is placed\n    horizontally under the corresponding tick is always on the label side that is closer to the\n    axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height\n    is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text\n    will be centered under the tick (i.e. displaced horizontally by half its height). At the same\n    time, a 45 degree rotated text will \"point toward\" its tick, as is typical for rotated tick\n    labels.\n  */\n  bool doRotation = !qFuzzyIsNull(tickLabelRotation);\n  bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.\n  double radians = tickLabelRotation/180.0*M_PI;\n  double x = 0;\n  double y = 0;\n  if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsInside)) // Anchor at right side of tick label\n  {\n    if (doRotation)\n    {\n      if (tickLabelRotation > 0)\n      {\n        x = -qCos(radians)*labelData.totalBounds.width();\n        y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;\n      } else\n      {\n        x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();\n        y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;\n      }\n    } else\n    {\n      x = -labelData.totalBounds.width();\n      y = -labelData.totalBounds.height()/2.0;\n    }\n  } else if ((type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsInside)) // Anchor at left side of tick label\n  {\n    if (doRotation)\n    {\n      if (tickLabelRotation > 0)\n      {\n        x = +qSin(radians)*labelData.totalBounds.height();\n        y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;\n      } else\n      {\n        x = 0;\n        y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;\n      }\n    } else\n    {\n      x = 0;\n      y = -labelData.totalBounds.height()/2.0;\n    }\n  } else if ((type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsInside)) // Anchor at bottom side of tick label\n  {\n    if (doRotation)\n    {\n      if (tickLabelRotation > 0)\n      {\n        x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;\n        y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();\n      } else\n      {\n        x = -qSin(-radians)*labelData.totalBounds.height()/2.0;\n        y = -qCos(-radians)*labelData.totalBounds.height();\n      }\n    } else\n    {\n      x = -labelData.totalBounds.width()/2.0;\n      y = -labelData.totalBounds.height();\n    }\n  } else if ((type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsInside)) // Anchor at top side of tick label\n  {\n    if (doRotation)\n    {\n      if (tickLabelRotation > 0)\n      {\n        x = +qSin(radians)*labelData.totalBounds.height()/2.0;\n        y = 0;\n      } else\n      {\n        x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;\n        y = +qSin(-radians)*labelData.totalBounds.width();\n      }\n    } else\n    {\n      x = -labelData.totalBounds.width()/2.0;\n      y = 0;\n    }\n  }\n  \n  return {x, y};\n}\n\n/*! \\internal\n  \n  Simulates the steps done by \\ref placeTickLabel by calculating bounding boxes of the text label\n  to be drawn, depending on number format etc. Since only the largest tick label is wanted for the\n  margin calculation, the passed \\a tickLabelsSize is only expanded, if it's currently set to a\n  smaller width/height.\n*/\nvoid QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text,  QSize *tickLabelsSize) const\n{\n  // note: this function must return the same tick label sizes as the placeTickLabel function.\n  QSize finalSize;\n  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label\n  {\n    const CachedLabel *cachedLabel = mLabelCache.object(text);\n    finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio();\n  } else // label caching disabled or no label with this text cached:\n  {\n    TickLabelData labelData = getTickLabelData(font, text);\n    finalSize = labelData.rotatedTotalBounds.size();\n  }\n  \n  // expand passed tickLabelsSize if current tick label is larger:\n  if (finalSize.width() > tickLabelsSize->width())\n    tickLabelsSize->setWidth(finalSize.width());\n  if (finalSize.height() > tickLabelsSize->height())\n    tickLabelsSize->setHeight(finalSize.height());\n}\n/* end of 'src/axis/axis.cpp' */\n\n\n/* including file 'src/scatterstyle.cpp'    */\n/* modified 2021-03-29T02:30:44, size 17466 */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPScatterStyle\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPScatterStyle\n  \\brief Represents the visual appearance of scatter points\n  \n  This class holds information about shape, color and size of scatter points. In plottables like\n  QCPGraph it is used to store how scatter points shall be drawn. For example, \\ref\n  QCPGraph::setScatterStyle takes a QCPScatterStyle instance.\n  \n  A scatter style consists of a shape (\\ref setShape), a line color (\\ref setPen) and possibly a\n  fill (\\ref setBrush), if the shape provides a fillable area. Further, the size of the shape can\n  be controlled with \\ref setSize.\n\n  \\section QCPScatterStyle-defining Specifying a scatter style\n  \n  You can set all these configurations either by calling the respective functions on an instance:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-creation-1\n  \n  Or you can use one of the various constructors that take different parameter combinations, making\n  it easy to specify a scatter style in a single call, like so:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-creation-2\n  \n  \\section QCPScatterStyle-undefinedpen Leaving the color/pen up to the plottable\n  \n  There are two constructors which leave the pen undefined: \\ref QCPScatterStyle() and \\ref\n  QCPScatterStyle(ScatterShape shape, double size). If those constructors are used, a call to \\ref\n  isPenDefined will return false. It leads to scatter points that inherit the pen from the\n  plottable that uses the scatter style. Thus, if such a scatter style is passed to QCPGraph, the line\n  color of the graph (\\ref QCPGraph::setPen) will be used by the scatter points. This makes\n  it very convenient to set up typical scatter settings:\n  \n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-shortcreation\n\n  Notice that it wasn't even necessary to explicitly call a QCPScatterStyle constructor. This works\n  because QCPScatterStyle provides a constructor that can transform a \\ref ScatterShape directly\n  into a QCPScatterStyle instance (that's the \\ref QCPScatterStyle(ScatterShape shape, double size)\n  constructor with a default for \\a size). In those cases, C++ allows directly supplying a \\ref\n  ScatterShape, where actually a QCPScatterStyle is expected.\n  \n  \\section QCPScatterStyle-custompath-and-pixmap Custom shapes and pixmaps\n  \n  QCPScatterStyle supports drawing custom shapes and arbitrary pixmaps as scatter points.\n\n  For custom shapes, you can provide a QPainterPath with the desired shape to the \\ref\n  setCustomPath function or call the constructor that takes a painter path. The scatter shape will\n  automatically be set to \\ref ssCustom.\n  \n  For pixmaps, you call \\ref setPixmap with the desired QPixmap. Alternatively you can use the\n  constructor that takes a QPixmap. The scatter shape will automatically be set to \\ref ssPixmap.\n  Note that \\ref setSize does not influence the appearance of the pixmap.\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn bool QCPScatterStyle::isNone() const\n  \n  Returns whether the scatter shape is \\ref ssNone.\n  \n  \\see setShape\n*/\n\n/*! \\fn bool QCPScatterStyle::isPenDefined() const\n  \n  Returns whether a pen has been defined for this scatter style.\n  \n  The pen is undefined if a constructor is called that does not carry \\a pen as parameter. Those\n  are \\ref QCPScatterStyle() and \\ref QCPScatterStyle(ScatterShape shape, double size). If the pen\n  is undefined, the pen of the respective plottable will be used for drawing scatters.\n  \n  If a pen was defined for this scatter style instance, and you now wish to undefine the pen, call\n  \\ref undefinePen.\n  \n  \\see setPen\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Creates a new QCPScatterStyle instance with size set to 6. No shape, pen or brush is defined.\n  \n  Since the pen is undefined (\\ref isPenDefined returns false), the scatter color will be inherited\n  from the plottable that uses this scatter style.\n*/\nQCPScatterStyle::QCPScatterStyle() :\n  mSize(6),\n  mShape(ssNone),\n  mPen(Qt::NoPen),\n  mBrush(Qt::NoBrush),\n  mPenDefined(false)\n{\n}\n\n/*!\n  Creates a new QCPScatterStyle instance with shape set to \\a shape and size to \\a size. No pen or\n  brush is defined.\n  \n  Since the pen is undefined (\\ref isPenDefined returns false), the scatter color will be inherited\n  from the plottable that uses this scatter style.\n*/\nQCPScatterStyle::QCPScatterStyle(ScatterShape shape, double size) :\n  mSize(size),\n  mShape(shape),\n  mPen(Qt::NoPen),\n  mBrush(Qt::NoBrush),\n  mPenDefined(false)\n{\n}\n\n/*!\n  Creates a new QCPScatterStyle instance with shape set to \\a shape, the pen color set to \\a color,\n  and size to \\a size. No brush is defined, i.e. the scatter point will not be filled.\n*/\nQCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, double size) :\n  mSize(size),\n  mShape(shape),\n  mPen(QPen(color)),\n  mBrush(Qt::NoBrush),\n  mPenDefined(true)\n{\n}\n\n/*!\n  Creates a new QCPScatterStyle instance with shape set to \\a shape, the pen color set to \\a color,\n  the brush color to \\a fill (with a solid pattern), and size to \\a size.\n*/\nQCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) :\n  mSize(size),\n  mShape(shape),\n  mPen(QPen(color)),\n  mBrush(QBrush(fill)),\n  mPenDefined(true)\n{\n}\n\n/*!\n  Creates a new QCPScatterStyle instance with shape set to \\a shape, the pen set to \\a pen, the\n  brush to \\a brush, and size to \\a size.\n  \n  \\warning In some cases it might be tempting to directly use a pen style like <tt>Qt::NoPen</tt> as \\a pen\n  and a color like <tt>Qt::blue</tt> as \\a brush. Notice however, that the corresponding call\\n\n  <tt>QCPScatterStyle(QCPScatterShape::ssCircle, Qt::NoPen, Qt::blue, 5)</tt>\\n\n  doesn't necessarily lead C++ to use this constructor in some cases, but might mistake\n  <tt>Qt::NoPen</tt> for a QColor and use the\n  \\ref QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size)\n  constructor instead (which will lead to an unexpected look of the scatter points). To prevent\n  this, be more explicit with the parameter types. For example, use <tt>QBrush(Qt::blue)</tt>\n  instead of just <tt>Qt::blue</tt>, to clearly point out to the compiler that this constructor is\n  wanted.\n*/\nQCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size) :\n  mSize(size),\n  mShape(shape),\n  mPen(pen),\n  mBrush(brush),\n  mPenDefined(pen.style() != Qt::NoPen)\n{\n}\n\n/*!\n  Creates a new QCPScatterStyle instance which will show the specified \\a pixmap. The scatter shape\n  is set to \\ref ssPixmap.\n*/\nQCPScatterStyle::QCPScatterStyle(const QPixmap &pixmap) :\n  mSize(5),\n  mShape(ssPixmap),\n  mPen(Qt::NoPen),\n  mBrush(Qt::NoBrush),\n  mPixmap(pixmap),\n  mPenDefined(false)\n{\n}\n\n/*!\n  Creates a new QCPScatterStyle instance with a custom shape that is defined via \\a customPath. The\n  scatter shape is set to \\ref ssCustom.\n  \n  The custom shape line will be drawn with \\a pen and filled with \\a brush. The size has a slightly\n  different meaning than for built-in scatter points: The custom path will be drawn scaled by a\n  factor of \\a size/6.0. Since the default \\a size is 6, the custom path will appear in its\n  original size by default. To for example double the size of the path, set \\a size to 12.\n*/\nQCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush, double size) :\n  mSize(size),\n  mShape(ssCustom),\n  mPen(pen),\n  mBrush(brush),\n  mCustomPath(customPath),\n  mPenDefined(pen.style() != Qt::NoPen)\n{\n}\n\n/*!\n  Copies the specified \\a properties from the \\a other scatter style to this scatter style.\n*/\nvoid QCPScatterStyle::setFromOther(const QCPScatterStyle &other, ScatterProperties properties)\n{\n  if (properties.testFlag(spPen))\n  {\n    setPen(other.pen());\n    if (!other.isPenDefined())\n      undefinePen();\n  }\n  if (properties.testFlag(spBrush))\n    setBrush(other.brush());\n  if (properties.testFlag(spSize))\n    setSize(other.size());\n  if (properties.testFlag(spShape))\n  {\n    setShape(other.shape());\n    if (other.shape() == ssPixmap)\n      setPixmap(other.pixmap());\n    else if (other.shape() == ssCustom)\n      setCustomPath(other.customPath());\n  }\n}\n\n/*!\n  Sets the size (pixel diameter) of the drawn scatter points to \\a size.\n  \n  \\see setShape\n*/\nvoid QCPScatterStyle::setSize(double size)\n{\n  mSize = size;\n}\n\n/*!\n  Sets the shape to \\a shape.\n  \n  Note that the calls \\ref setPixmap and \\ref setCustomPath automatically set the shape to \\ref\n  ssPixmap and \\ref ssCustom, respectively.\n  \n  \\see setSize\n*/\nvoid QCPScatterStyle::setShape(QCPScatterStyle::ScatterShape shape)\n{\n  mShape = shape;\n}\n\n/*!\n  Sets the pen that will be used to draw scatter points to \\a pen.\n  \n  If the pen was previously undefined (see \\ref isPenDefined), the pen is considered defined after\n  a call to this function, even if \\a pen is <tt>Qt::NoPen</tt>. If you have defined a pen\n  previously by calling this function and now wish to undefine the pen, call \\ref undefinePen.\n  \n  \\see setBrush\n*/\nvoid QCPScatterStyle::setPen(const QPen &pen)\n{\n  mPenDefined = true;\n  mPen = pen;\n}\n\n/*!\n  Sets the brush that will be used to fill scatter points to \\a brush. Note that not all scatter\n  shapes have fillable areas. For example, \\ref ssPlus does not while \\ref ssCircle does.\n  \n  \\see setPen\n*/\nvoid QCPScatterStyle::setBrush(const QBrush &brush)\n{\n  mBrush = brush;\n}\n\n/*!\n  Sets the pixmap that will be drawn as scatter point to \\a pixmap.\n  \n  Note that \\ref setSize does not influence the appearance of the pixmap.\n  \n  The scatter shape is automatically set to \\ref ssPixmap.\n*/\nvoid QCPScatterStyle::setPixmap(const QPixmap &pixmap)\n{\n  setShape(ssPixmap);\n  mPixmap = pixmap;\n}\n\n/*!\n  Sets the custom shape that will be drawn as scatter point to \\a customPath.\n  \n  The scatter shape is automatically set to \\ref ssCustom.\n*/\nvoid QCPScatterStyle::setCustomPath(const QPainterPath &customPath)\n{\n  setShape(ssCustom);\n  mCustomPath = customPath;\n}\n\n/*!\n  Sets this scatter style to have an undefined pen (see \\ref isPenDefined for what an undefined pen\n  implies).\n\n  A call to \\ref setPen will define a pen.\n*/\nvoid QCPScatterStyle::undefinePen()\n{\n  mPenDefined = false;\n}\n\n/*!\n  Applies the pen and the brush of this scatter style to \\a painter. If this scatter style has an\n  undefined pen (\\ref isPenDefined), sets the pen of \\a painter to \\a defaultPen instead.\n  \n  This function is used by plottables (or any class that wants to draw scatters) just before a\n  number of scatters with this style shall be drawn with the \\a painter.\n  \n  \\see drawShape\n*/\nvoid QCPScatterStyle::applyTo(QCPPainter *painter, const QPen &defaultPen) const\n{\n  painter->setPen(mPenDefined ? mPen : defaultPen);\n  painter->setBrush(mBrush);\n}\n\n/*!\n  Draws the scatter shape with \\a painter at position \\a pos.\n  \n  This function does not modify the pen or the brush on the painter, as \\ref applyTo is meant to be\n  called before scatter points are drawn with \\ref drawShape.\n  \n  \\see applyTo\n*/\nvoid QCPScatterStyle::drawShape(QCPPainter *painter, const QPointF &pos) const\n{\n  drawShape(painter, pos.x(), pos.y());\n}\n\n/*! \\overload\n  Draws the scatter shape with \\a painter at position \\a x and \\a y.\n*/\nvoid QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const\n{\n  double w = mSize/2.0;\n  switch (mShape)\n  {\n    case ssNone: break;\n    case ssDot:\n    {\n      painter->drawLine(QPointF(x, y), QPointF(x+0.0001, y));\n      break;\n    }\n    case ssCross:\n    {\n      painter->drawLine(QLineF(x-w, y-w, x+w, y+w));\n      painter->drawLine(QLineF(x-w, y+w, x+w, y-w));\n      break;\n    }\n    case ssPlus:\n    {\n      painter->drawLine(QLineF(x-w,   y, x+w,   y));\n      painter->drawLine(QLineF(  x, y+w,   x, y-w));\n      break;\n    }\n    case ssCircle:\n    {\n      painter->drawEllipse(QPointF(x , y), w, w);\n      break;\n    }\n    case ssDisc:\n    {\n      QBrush b = painter->brush();\n      painter->setBrush(painter->pen().color());\n      painter->drawEllipse(QPointF(x , y), w, w);\n      painter->setBrush(b);\n      break;\n    }\n    case ssSquare:\n    {\n      painter->drawRect(QRectF(x-w, y-w, mSize, mSize));\n      break;\n    }\n    case ssDiamond:\n    {\n      QPointF lineArray[4] = {QPointF(x-w,   y),\n                              QPointF(  x, y-w),\n                              QPointF(x+w,   y),\n                              QPointF(  x, y+w)};\n      painter->drawPolygon(lineArray, 4);\n      break;\n    }\n    case ssStar:\n    {\n      painter->drawLine(QLineF(x-w,   y, x+w,   y));\n      painter->drawLine(QLineF(  x, y+w,   x, y-w));\n      painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707));\n      painter->drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707));\n      break;\n    }\n    case ssTriangle:\n    {\n      QPointF lineArray[3] = {QPointF(x-w, y+0.755*w),\n                              QPointF(x+w, y+0.755*w),\n                              QPointF(  x, y-0.977*w)};\n      painter->drawPolygon(lineArray, 3);\n      break;\n    }\n    case ssTriangleInverted:\n    {\n      QPointF lineArray[3] = {QPointF(x-w, y-0.755*w),\n                              QPointF(x+w, y-0.755*w),\n                              QPointF(  x, y+0.977*w)};\n      painter->drawPolygon(lineArray, 3);\n      break;\n    }\n    case ssCrossSquare:\n    {\n      painter->drawRect(QRectF(x-w, y-w, mSize, mSize));\n      painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95));\n      painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w));\n      break;\n    }\n    case ssPlusSquare:\n    {\n      painter->drawRect(QRectF(x-w, y-w, mSize, mSize));\n      painter->drawLine(QLineF(x-w,   y, x+w*0.95,   y));\n      painter->drawLine(QLineF(  x, y+w,        x, y-w));\n      break;\n    }\n    case ssCrossCircle:\n    {\n      painter->drawEllipse(QPointF(x, y), w, w);\n      painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670));\n      painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707));\n      break;\n    }\n    case ssPlusCircle:\n    {\n      painter->drawEllipse(QPointF(x, y), w, w);\n      painter->drawLine(QLineF(x-w,   y, x+w,   y));\n      painter->drawLine(QLineF(  x, y+w,   x, y-w));\n      break;\n    }\n    case ssPeace:\n    {\n      painter->drawEllipse(QPointF(x, y), w, w);\n      painter->drawLine(QLineF(x, y-w,         x,       y+w));\n      painter->drawLine(QLineF(x,   y, x-w*0.707, y+w*0.707));\n      painter->drawLine(QLineF(x,   y, x+w*0.707, y+w*0.707));\n      break;\n    }\n    case ssPixmap:\n    {\n      const double widthHalf = mPixmap.width()*0.5;\n      const double heightHalf = mPixmap.height()*0.5;\n#if QT_VERSION < QT_VERSION_CHECK(4, 8, 0)\n      const QRectF clipRect = painter->clipRegion().boundingRect().adjusted(-widthHalf, -heightHalf, widthHalf, heightHalf);\n#else\n      const QRectF clipRect = painter->clipBoundingRect().adjusted(-widthHalf, -heightHalf, widthHalf, heightHalf);\n#endif\n      if (clipRect.contains(x, y))\n        painter->drawPixmap(qRound(x-widthHalf), qRound(y-heightHalf), mPixmap);\n      break;\n    }\n    case ssCustom:\n    {\n      QTransform oldTransform = painter->transform();\n      painter->translate(x, y);\n      painter->scale(mSize/6.0, mSize/6.0);\n      painter->drawPath(mCustomPath);\n      painter->setTransform(oldTransform);\n      break;\n    }\n  }\n}\n/* end of 'src/scatterstyle.cpp' */\n\n\n/* including file 'src/plottable.cpp'       */\n/* modified 2021-03-29T02:30:44, size 38818 */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPSelectionDecorator\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPSelectionDecorator\n  \\brief Controls how a plottable's data selection is drawn\n  \n  Each \\ref QCPAbstractPlottable instance has one \\ref QCPSelectionDecorator (accessible via \\ref\n  QCPAbstractPlottable::selectionDecorator) and uses it when drawing selected segments of its data.\n  \n  The selection decorator controls both pen (\\ref setPen) and brush (\\ref setBrush), as well as the\n  scatter style (\\ref setScatterStyle) if the plottable draws scatters. Since a \\ref\n  QCPScatterStyle is itself composed of different properties such as color shape and size, the\n  decorator allows specifying exactly which of those properties shall be used for the selected data\n  point, via \\ref setUsedScatterProperties.\n  \n  A \\ref QCPSelectionDecorator subclass instance can be passed to a plottable via \\ref\n  QCPAbstractPlottable::setSelectionDecorator, allowing greater customizability of the appearance\n  of selected segments.\n  \n  Use \\ref copyFrom to easily transfer the settings of one decorator to another one. This is\n  especially useful since plottables take ownership of the passed selection decorator, and thus the\n  same decorator instance can not be passed to multiple plottables.\n  \n  Selection decorators can also themselves perform drawing operations by reimplementing \\ref\n  drawDecoration, which is called by the plottable's draw method. The base class \\ref\n  QCPSelectionDecorator does not make use of this however. For example, \\ref\n  QCPSelectionDecoratorBracket draws brackets around selected data segments.\n*/\n\n/*!\n  Creates a new QCPSelectionDecorator instance with default values\n*/\nQCPSelectionDecorator::QCPSelectionDecorator() :\n  mPen(QColor(80, 80, 255), 2.5),\n  mBrush(Qt::NoBrush),\n  mUsedScatterProperties(QCPScatterStyle::spNone),\n  mPlottable(nullptr)\n{\n}\n\nQCPSelectionDecorator::~QCPSelectionDecorator()\n{\n}\n\n/*!\n  Sets the pen that will be used by the parent plottable to draw selected data segments.\n*/\nvoid QCPSelectionDecorator::setPen(const QPen &pen)\n{\n  mPen = pen;\n}\n\n/*!\n  Sets the brush that will be used by the parent plottable to draw selected data segments.\n*/\nvoid QCPSelectionDecorator::setBrush(const QBrush &brush)\n{\n  mBrush = brush;\n}\n\n/*!\n  Sets the scatter style that will be used by the parent plottable to draw scatters in selected\n  data segments.\n  \n  \\a usedProperties specifies which parts of the passed \\a scatterStyle will be used by the\n  plottable. The used properties can also be changed via \\ref setUsedScatterProperties.\n*/\nvoid QCPSelectionDecorator::setScatterStyle(const QCPScatterStyle &scatterStyle, QCPScatterStyle::ScatterProperties usedProperties)\n{\n  mScatterStyle = scatterStyle;\n  setUsedScatterProperties(usedProperties);\n}\n\n/*!\n  Use this method to define which properties of the scatter style (set via \\ref setScatterStyle)\n  will be used for selected data segments. All properties of the scatter style that are not\n  specified in \\a properties will remain as specified in the plottable's original scatter style.\n  \n  \\see QCPScatterStyle::ScatterProperty\n*/\nvoid QCPSelectionDecorator::setUsedScatterProperties(const QCPScatterStyle::ScatterProperties &properties)\n{\n  mUsedScatterProperties = properties;\n}\n\n/*!\n  Sets the pen of \\a painter to the pen of this selection decorator.\n  \n  \\see applyBrush, getFinalScatterStyle\n*/\nvoid QCPSelectionDecorator::applyPen(QCPPainter *painter) const\n{\n  painter->setPen(mPen);\n}\n\n/*!\n  Sets the brush of \\a painter to the brush of this selection decorator.\n  \n  \\see applyPen, getFinalScatterStyle\n*/\nvoid QCPSelectionDecorator::applyBrush(QCPPainter *painter) const\n{\n  painter->setBrush(mBrush);\n}\n\n/*!\n  Returns the scatter style that the parent plottable shall use for selected scatter points. The\n  plottable's original (unselected) scatter style must be passed as \\a unselectedStyle. Depending\n  on the setting of \\ref setUsedScatterProperties, the returned scatter style is a mixture of this\n  selecion decorator's scatter style (\\ref setScatterStyle), and \\a unselectedStyle.\n  \n  \\see applyPen, applyBrush, setScatterStyle\n*/\nQCPScatterStyle QCPSelectionDecorator::getFinalScatterStyle(const QCPScatterStyle &unselectedStyle) const\n{\n  QCPScatterStyle result(unselectedStyle);\n  result.setFromOther(mScatterStyle, mUsedScatterProperties);\n  \n  // if style shall inherit pen from plottable (has no own pen defined), give it the selected\n  // plottable pen explicitly, so it doesn't use the unselected plottable pen when used in the\n  // plottable:\n  if (!result.isPenDefined())\n    result.setPen(mPen);\n  \n  return result;\n}\n\n/*!\n  Copies all properties (e.g. color, fill, scatter style) of the \\a other selection decorator to\n  this selection decorator.\n*/\nvoid QCPSelectionDecorator::copyFrom(const QCPSelectionDecorator *other)\n{\n  setPen(other->pen());\n  setBrush(other->brush());\n  setScatterStyle(other->scatterStyle(), other->usedScatterProperties());\n}\n\n/*!\n  This method is called by all plottables' draw methods to allow custom selection decorations to be\n  drawn. Use the passed \\a painter to perform the drawing operations. \\a selection carries the data\n  selection for which the decoration shall be drawn.\n  \n  The default base class implementation of \\ref QCPSelectionDecorator has no special decoration, so\n  this method does nothing.\n*/\nvoid QCPSelectionDecorator::drawDecoration(QCPPainter *painter, QCPDataSelection selection)\n{\n  Q_UNUSED(painter)\n  Q_UNUSED(selection)\n}\n\n/*! \\internal\n  \n  This method is called as soon as a selection decorator is associated with a plottable, by a call\n  to \\ref QCPAbstractPlottable::setSelectionDecorator. This way the selection decorator can obtain a pointer to the plottable that uses it (e.g. to access\n  data points via the \\ref QCPAbstractPlottable::interface1D interface).\n  \n  If the selection decorator was already added to a different plottable before, this method aborts\n  the registration and returns false.\n*/\nbool QCPSelectionDecorator::registerWithPlottable(QCPAbstractPlottable *plottable)\n{\n  if (!mPlottable)\n  {\n    mPlottable = plottable;\n    return true;\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"This selection decorator is already registered with plottable:\" << reinterpret_cast<quintptr>(mPlottable);\n    return false;\n  }\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPAbstractPlottable\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPAbstractPlottable\n  \\brief The abstract base class for all data representing objects in a plot.\n\n  It defines a very basic interface like name, pen, brush, visibility etc. Since this class is\n  abstract, it can't be instantiated. Use one of the subclasses or create a subclass yourself to\n  create new ways of displaying data (see \"Creating own plottables\" below). Plottables that display\n  one-dimensional data (i.e. data points have a single key dimension and one or multiple values at\n  each key) are based off of the template subclass \\ref QCPAbstractPlottable1D, see details\n  there.\n  \n  All further specifics are in the subclasses, for example:\n  \\li A normal graph with possibly a line and/or scatter points \\ref QCPGraph\n  (typically created with \\ref QCustomPlot::addGraph)\n  \\li A parametric curve: \\ref QCPCurve\n  \\li A bar chart: \\ref QCPBars\n  \\li A statistical box plot: \\ref QCPStatisticalBox\n  \\li A color encoded two-dimensional map: \\ref QCPColorMap\n  \\li An OHLC/Candlestick chart: \\ref QCPFinancial\n  \n  \\section plottables-subclassing Creating own plottables\n  \n  Subclassing directly from QCPAbstractPlottable is only recommended if you wish to display\n  two-dimensional data like \\ref QCPColorMap, i.e. two logical key dimensions and one (or more)\n  data dimensions. If you want to display data with only one logical key dimension, you should\n  rather derive from \\ref QCPAbstractPlottable1D.\n  \n  If subclassing QCPAbstractPlottable directly, these are the pure virtual functions you must\n  implement:\n  \\li \\ref selectTest\n  \\li \\ref draw\n  \\li \\ref drawLegendIcon\n  \\li \\ref getKeyRange\n  \\li \\ref getValueRange\n  \n  See the documentation of those functions for what they need to do.\n  \n  For drawing your plot, you can use the \\ref coordsToPixels functions to translate a point in plot\n  coordinates to pixel coordinates. This function is quite convenient, because it takes the\n  orientation of the key and value axes into account for you (x and y are swapped when the key axis\n  is vertical and the value axis horizontal). If you are worried about performance (i.e. you need\n  to translate many points in a loop like QCPGraph), you can directly use \\ref\n  QCPAxis::coordToPixel. However, you must then take care about the orientation of the axis\n  yourself.\n  \n  Here are some important members you inherit from QCPAbstractPlottable:\n  <table>\n  <tr>\n    <td>QCustomPlot *\\b mParentPlot</td>\n    <td>A pointer to the parent QCustomPlot instance. The parent plot is inferred from the axes that are passed in the constructor.</td>\n  </tr><tr>\n    <td>QString \\b mName</td>\n    <td>The name of the plottable.</td>\n  </tr><tr>\n    <td>QPen \\b mPen</td>\n    <td>The generic pen of the plottable. You should use this pen for the most prominent data representing lines in the plottable\n        (e.g QCPGraph uses this pen for its graph lines and scatters)</td>\n  </tr><tr>\n    <td>QBrush \\b mBrush</td>\n    <td>The generic brush of the plottable. You should use this brush for the most prominent fillable structures in the plottable\n        (e.g. QCPGraph uses this brush to control filling under the graph)</td>\n  </tr><tr>\n    <td>QPointer<\\ref QCPAxis> \\b mKeyAxis, \\b mValueAxis</td>\n    <td>The key and value axes this plottable is attached to. Call their QCPAxis::coordToPixel functions to translate coordinates\n        to pixels in either the key or value dimension. Make sure to check whether the pointer is \\c nullptr before using it. If one of\n        the axes is null, don't draw the plottable.</td>\n  </tr><tr>\n    <td>\\ref QCPSelectionDecorator \\b mSelectionDecorator</td>\n    <td>The currently set selection decorator which specifies how selected data of the plottable shall be drawn and decorated.\n        When drawing your data, you must consult this decorator for the appropriate pen/brush before drawing unselected/selected data segments.\n        Finally, you should call its \\ref QCPSelectionDecorator::drawDecoration method at the end of your \\ref draw implementation.</td>\n  </tr><tr>\n    <td>\\ref QCP::SelectionType \\b mSelectable</td>\n    <td>In which composition, if at all, this plottable's data may be selected. Enforcing this setting on the data selection is done\n        by QCPAbstractPlottable automatically.</td>\n  </tr><tr>\n    <td>\\ref QCPDataSelection \\b mSelection</td>\n    <td>Holds the current selection state of the plottable's data, i.e. the selected data ranges (\\ref QCPDataRange).</td>\n  </tr>\n  </table>\n*/\n\n/* start of documentation of inline functions */\n\n/*! \\fn QCPSelectionDecorator *QCPAbstractPlottable::selectionDecorator() const\n  \n  Provides access to the selection decorator of this plottable. The selection decorator controls\n  how selected data ranges are drawn (e.g. their pen color and fill), see \\ref\n  QCPSelectionDecorator for details.\n  \n  If you wish to use an own \\ref QCPSelectionDecorator subclass, pass an instance of it to \\ref\n  setSelectionDecorator.\n*/\n\n/*! \\fn bool QCPAbstractPlottable::selected() const\n  \n  Returns true if there are any data points of the plottable currently selected. Use \\ref selection\n  to retrieve the current \\ref QCPDataSelection.\n*/\n\n/*! \\fn QCPDataSelection QCPAbstractPlottable::selection() const\n  \n  Returns a \\ref QCPDataSelection encompassing all the data points that are currently selected on\n  this plottable.\n  \n  \\see selected, setSelection, setSelectable\n*/\n\n/*! \\fn virtual QCPPlottableInterface1D *QCPAbstractPlottable::interface1D()\n  \n  If this plottable is a one-dimensional plottable, i.e. it implements the \\ref\n  QCPPlottableInterface1D, returns the \\a this pointer with that type. Otherwise (e.g. in the case\n  of a \\ref QCPColorMap) returns zero.\n  \n  You can use this method to gain read access to data coordinates while holding a pointer to the\n  abstract base class only.\n*/\n\n/* end of documentation of inline functions */\n/* start of documentation of pure virtual functions */\n\n/*! \\fn void QCPAbstractPlottable::drawLegendIcon(QCPPainter *painter, const QRect &rect) const = 0\n  \\internal\n  \n  called by QCPLegend::draw (via QCPPlottableLegendItem::draw) to create a graphical representation\n  of this plottable inside \\a rect, next to the plottable name.\n  \n  The passed \\a painter has its cliprect set to \\a rect, so painting outside of \\a rect won't\n  appear outside the legend icon border.\n*/\n\n/*! \\fn QCPRange QCPAbstractPlottable::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const = 0\n  \n  Returns the coordinate range that all data in this plottable span in the key axis dimension. For\n  logarithmic plots, one can set \\a inSignDomain to either \\ref QCP::sdNegative or \\ref\n  QCP::sdPositive in order to restrict the returned range to that sign domain. E.g. when only\n  negative range is wanted, set \\a inSignDomain to \\ref QCP::sdNegative and all positive points\n  will be ignored for range calculation. For no restriction, just set \\a inSignDomain to \\ref\n  QCP::sdBoth (default). \\a foundRange is an output parameter that indicates whether a range could\n  be found or not. If this is false, you shouldn't use the returned range (e.g. no points in data).\n\n  Note that \\a foundRange is not the same as \\ref QCPRange::validRange, since the range returned by\n  this function may have size zero (e.g. when there is only one data point). In this case \\a\n  foundRange would return true, but the returned range is not a valid range in terms of \\ref\n  QCPRange::validRange.\n  \n  \\see rescaleAxes, getValueRange\n*/\n\n/*! \\fn QCPRange QCPAbstractPlottable::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const = 0\n  \n  Returns the coordinate range that the data points in the specified key range (\\a inKeyRange) span\n  in the value axis dimension. For logarithmic plots, one can set \\a inSignDomain to either \\ref\n  QCP::sdNegative or \\ref QCP::sdPositive in order to restrict the returned range to that sign\n  domain. E.g. when only negative range is wanted, set \\a inSignDomain to \\ref QCP::sdNegative and\n  all positive points will be ignored for range calculation. For no restriction, just set \\a\n  inSignDomain to \\ref QCP::sdBoth (default). \\a foundRange is an output parameter that indicates\n  whether a range could be found or not. If this is false, you shouldn't use the returned range\n  (e.g. no points in data).\n  \n  If \\a inKeyRange has both lower and upper bound set to zero (is equal to <tt>QCPRange()</tt>),\n  all data points are considered, without any restriction on the keys.\n\n  Note that \\a foundRange is not the same as \\ref QCPRange::validRange, since the range returned by\n  this function may have size zero (e.g. when there is only one data point). In this case \\a\n  foundRange would return true, but the returned range is not a valid range in terms of \\ref\n  QCPRange::validRange.\n  \n  \\see rescaleAxes, getKeyRange\n*/\n\n/* end of documentation of pure virtual functions */\n/* start of documentation of signals */\n\n/*! \\fn void QCPAbstractPlottable::selectionChanged(bool selected)\n  \n  This signal is emitted when the selection state of this plottable has changed, either by user\n  interaction or by a direct call to \\ref setSelection. The parameter \\a selected indicates whether\n  there are any points selected or not.\n  \n  \\see selectionChanged(const QCPDataSelection &selection)\n*/\n\n/*! \\fn void QCPAbstractPlottable::selectionChanged(const QCPDataSelection &selection)\n  \n  This signal is emitted when the selection state of this plottable has changed, either by user\n  interaction or by a direct call to \\ref setSelection. The parameter \\a selection holds the\n  currently selected data ranges.\n  \n  \\see selectionChanged(bool selected)\n*/\n\n/*! \\fn void QCPAbstractPlottable::selectableChanged(QCP::SelectionType selectable);\n  \n  This signal is emitted when the selectability of this plottable has changed.\n  \n  \\see setSelectable\n*/\n\n/* end of documentation of signals */\n\n/*!\n  Constructs an abstract plottable which uses \\a keyAxis as its key axis (\"x\") and \\a valueAxis as\n  its value axis (\"y\"). \\a keyAxis and \\a valueAxis must reside in the same QCustomPlot instance\n  and have perpendicular orientations. If either of these restrictions is violated, a corresponding\n  message is printed to the debug output (qDebug), the construction is not aborted, though.\n  \n  Since QCPAbstractPlottable is an abstract class that defines the basic interface to plottables,\n  it can't be directly instantiated.\n  \n  You probably want one of the subclasses like \\ref QCPGraph or \\ref QCPCurve instead.\n*/\nQCPAbstractPlottable::QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis) :\n  QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()),\n  mName(),\n  mAntialiasedFill(true),\n  mAntialiasedScatters(true),\n  mPen(Qt::black),\n  mBrush(Qt::NoBrush),\n  mKeyAxis(keyAxis),\n  mValueAxis(valueAxis),\n  mSelectable(QCP::stWhole),\n  mSelectionDecorator(nullptr)\n{\n  if (keyAxis->parentPlot() != valueAxis->parentPlot())\n    qDebug() << Q_FUNC_INFO << \"Parent plot of keyAxis is not the same as that of valueAxis.\";\n  if (keyAxis->orientation() == valueAxis->orientation())\n    qDebug() << Q_FUNC_INFO << \"keyAxis and valueAxis must be orthogonal to each other.\";\n  \n  mParentPlot->registerPlottable(this);\n  setSelectionDecorator(new QCPSelectionDecorator);\n}\n\nQCPAbstractPlottable::~QCPAbstractPlottable()\n{\n  if (mSelectionDecorator)\n  {\n    delete mSelectionDecorator;\n    mSelectionDecorator = nullptr;\n  }\n}\n\n/*!\n   The name is the textual representation of this plottable as it is displayed in the legend\n   (\\ref QCPLegend). It may contain any UTF-8 characters, including newlines.\n*/\nvoid QCPAbstractPlottable::setName(const QString &name)\n{\n  mName = name;\n}\n\n/*!\n  Sets whether fills of this plottable are drawn antialiased or not.\n  \n  Note that this setting may be overridden by \\ref QCustomPlot::setAntialiasedElements and \\ref\n  QCustomPlot::setNotAntialiasedElements.\n*/\nvoid QCPAbstractPlottable::setAntialiasedFill(bool enabled)\n{\n  mAntialiasedFill = enabled;\n}\n\n/*!\n  Sets whether the scatter symbols of this plottable are drawn antialiased or not.\n  \n  Note that this setting may be overridden by \\ref QCustomPlot::setAntialiasedElements and \\ref\n  QCustomPlot::setNotAntialiasedElements.\n*/\nvoid QCPAbstractPlottable::setAntialiasedScatters(bool enabled)\n{\n  mAntialiasedScatters = enabled;\n}\n\n/*!\n  The pen is used to draw basic lines that make up the plottable representation in the\n  plot.\n  \n  For example, the \\ref QCPGraph subclass draws its graph lines with this pen.\n\n  \\see setBrush\n*/\nvoid QCPAbstractPlottable::setPen(const QPen &pen)\n{\n  mPen = pen;\n}\n\n/*!\n  The brush is used to draw basic fills of the plottable representation in the\n  plot. The Fill can be a color, gradient or texture, see the usage of QBrush.\n  \n  For example, the \\ref QCPGraph subclass draws the fill under the graph with this brush, when\n  it's not set to Qt::NoBrush.\n\n  \\see setPen\n*/\nvoid QCPAbstractPlottable::setBrush(const QBrush &brush)\n{\n  mBrush = brush;\n}\n\n/*!\n  The key axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal\n  to the plottable's value axis. This function performs no checks to make sure this is the case.\n  The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and the\n  y-axis (QCustomPlot::yAxis) as value axis.\n  \n  Normally, the key and value axes are set in the constructor of the plottable (or \\ref\n  QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).\n\n  \\see setValueAxis\n*/\nvoid QCPAbstractPlottable::setKeyAxis(QCPAxis *axis)\n{\n  mKeyAxis = axis;\n}\n\n/*!\n  The value axis of a plottable can be set to any axis of a QCustomPlot, as long as it is\n  orthogonal to the plottable's key axis. This function performs no checks to make sure this is the\n  case. The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and\n  the y-axis (QCustomPlot::yAxis) as value axis.\n\n  Normally, the key and value axes are set in the constructor of the plottable (or \\ref\n  QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).\n  \n  \\see setKeyAxis\n*/\nvoid QCPAbstractPlottable::setValueAxis(QCPAxis *axis)\n{\n  mValueAxis = axis;\n}\n\n\n/*!\n  Sets which data ranges of this plottable are selected. Selected data ranges are drawn differently\n  (e.g. color) in the plot. This can be controlled via the selection decorator (see \\ref\n  selectionDecorator).\n  \n  The entire selection mechanism for plottables is handled automatically when \\ref\n  QCustomPlot::setInteractions contains iSelectPlottables. You only need to call this function when\n  you wish to change the selection state programmatically.\n  \n  Using \\ref setSelectable you can further specify for each plottable whether and to which\n  granularity it is selectable. If \\a selection is not compatible with the current \\ref\n  QCP::SelectionType set via \\ref setSelectable, the resulting selection will be adjusted\n  accordingly (see \\ref QCPDataSelection::enforceType).\n  \n  emits the \\ref selectionChanged signal when \\a selected is different from the previous selection state.\n  \n  \\see setSelectable, selectTest\n*/\nvoid QCPAbstractPlottable::setSelection(QCPDataSelection selection)\n{\n  selection.enforceType(mSelectable);\n  if (mSelection != selection)\n  {\n    mSelection = selection;\n    emit selectionChanged(selected());\n    emit selectionChanged(mSelection);\n  }\n}\n\n/*!\n  Use this method to set an own QCPSelectionDecorator (subclass) instance. This allows you to\n  customize the visual representation of selected data ranges further than by using the default\n  QCPSelectionDecorator.\n  \n  The plottable takes ownership of the \\a decorator.\n  \n  The currently set decorator can be accessed via \\ref selectionDecorator.\n*/\nvoid QCPAbstractPlottable::setSelectionDecorator(QCPSelectionDecorator *decorator)\n{\n  if (decorator)\n  {\n    if (decorator->registerWithPlottable(this))\n    {\n      delete mSelectionDecorator; // delete old decorator if necessary\n      mSelectionDecorator = decorator;\n    }\n  } else if (mSelectionDecorator) // just clear decorator\n  {\n    delete mSelectionDecorator;\n    mSelectionDecorator = nullptr;\n  }\n}\n\n/*!\n  Sets whether and to which granularity this plottable can be selected.\n\n  A selection can happen by clicking on the QCustomPlot surface (When \\ref\n  QCustomPlot::setInteractions contains \\ref QCP::iSelectPlottables), by dragging a selection rect\n  (When \\ref QCustomPlot::setSelectionRectMode is \\ref QCP::srmSelect), or programmatically by\n  calling \\ref setSelection.\n  \n  \\see setSelection, QCP::SelectionType\n*/\nvoid QCPAbstractPlottable::setSelectable(QCP::SelectionType selectable)\n{\n  if (mSelectable != selectable)\n  {\n    mSelectable = selectable;\n    QCPDataSelection oldSelection = mSelection;\n    mSelection.enforceType(mSelectable);\n    emit selectableChanged(mSelectable);\n    if (mSelection != oldSelection)\n    {\n      emit selectionChanged(selected());\n      emit selectionChanged(mSelection);\n    }\n  }\n}\n\n\n/*!\n  Convenience function for transforming a key/value pair to pixels on the QCustomPlot surface,\n  taking the orientations of the axes associated with this plottable into account (e.g. whether key\n  represents x or y).\n\n  \\a key and \\a value are transformed to the coodinates in pixels and are written to \\a x and \\a y.\n\n  \\see pixelsToCoords, QCPAxis::coordToPixel\n*/\nvoid QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const\n{\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n  \n  if (keyAxis->orientation() == Qt::Horizontal)\n  {\n    x = keyAxis->coordToPixel(key);\n    y = valueAxis->coordToPixel(value);\n  } else\n  {\n    y = keyAxis->coordToPixel(key);\n    x = valueAxis->coordToPixel(value);\n  }\n}\n\n/*! \\overload\n\n  Transforms the given \\a key and \\a value to pixel coordinates and returns them in a QPointF.\n*/\nconst QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const\n{\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return QPointF(); }\n  \n  if (keyAxis->orientation() == Qt::Horizontal)\n    return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value));\n  else\n    return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key));\n}\n\n/*!\n  Convenience function for transforming a x/y pixel pair on the QCustomPlot surface to plot coordinates,\n  taking the orientations of the axes associated with this plottable into account (e.g. whether key\n  represents x or y).\n\n  \\a x and \\a y are transformed to the plot coodinates and are written to \\a key and \\a value.\n\n  \\see coordsToPixels, QCPAxis::coordToPixel\n*/\nvoid QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const\n{\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n  \n  if (keyAxis->orientation() == Qt::Horizontal)\n  {\n    key = keyAxis->pixelToCoord(x);\n    value = valueAxis->pixelToCoord(y);\n  } else\n  {\n    key = keyAxis->pixelToCoord(y);\n    value = valueAxis->pixelToCoord(x);\n  }\n}\n\n/*! \\overload\n\n  Returns the pixel input \\a pixelPos as plot coordinates \\a key and \\a value.\n*/\nvoid QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const\n{\n  pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);\n}\n\n/*!\n  Rescales the key and value axes associated with this plottable to contain all displayed data, so\n  the whole plottable is visible. If the scaling of an axis is logarithmic, rescaleAxes will make\n  sure not to rescale to an illegal range i.e. a range containing different signs and/or zero.\n  Instead it will stay in the current sign domain and ignore all parts of the plottable that lie\n  outside of that domain.\n  \n  \\a onlyEnlarge makes sure the ranges are only expanded, never reduced. So it's possible to show\n  multiple plottables in their entirety by multiple calls to rescaleAxes where the first call has\n  \\a onlyEnlarge set to false (the default), and all subsequent set to true.\n  \n  \\see rescaleKeyAxis, rescaleValueAxis, QCustomPlot::rescaleAxes, QCPAxis::rescale\n*/\nvoid QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const\n{\n  rescaleKeyAxis(onlyEnlarge);\n  rescaleValueAxis(onlyEnlarge);\n}\n\n/*!\n  Rescales the key axis of the plottable so the whole plottable is visible.\n  \n  See \\ref rescaleAxes for detailed behaviour.\n*/\nvoid QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const\n{\n  QCPAxis *keyAxis = mKeyAxis.data();\n  if (!keyAxis) { qDebug() << Q_FUNC_INFO << \"invalid key axis\"; return; }\n  \n  QCP::SignDomain signDomain = QCP::sdBoth;\n  if (keyAxis->scaleType() == QCPAxis::stLogarithmic)\n    signDomain = (keyAxis->range().upper < 0 ? QCP::sdNegative : QCP::sdPositive);\n  \n  bool foundRange;\n  QCPRange newRange = getKeyRange(foundRange, signDomain);\n  if (foundRange)\n  {\n    if (onlyEnlarge)\n      newRange.expand(keyAxis->range());\n    if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable\n    {\n      double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason\n      if (keyAxis->scaleType() == QCPAxis::stLinear)\n      {\n        newRange.lower = center-keyAxis->range().size()/2.0;\n        newRange.upper = center+keyAxis->range().size()/2.0;\n      } else // scaleType() == stLogarithmic\n      {\n        newRange.lower = center/qSqrt(keyAxis->range().upper/keyAxis->range().lower);\n        newRange.upper = center*qSqrt(keyAxis->range().upper/keyAxis->range().lower);\n      }\n    }\n    keyAxis->setRange(newRange);\n  }\n}\n\n/*!\n  Rescales the value axis of the plottable so the whole plottable is visible. If \\a inKeyRange is\n  set to true, only the data points which are in the currently visible key axis range are\n  considered.\n\n  Returns true if the axis was actually scaled. This might not be the case if this plottable has an\n  invalid range, e.g. because it has no data points.\n\n  See \\ref rescaleAxes for detailed behaviour.\n*/\nvoid QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge, bool inKeyRange) const\n{\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n  \n  QCP::SignDomain signDomain = QCP::sdBoth;\n  if (valueAxis->scaleType() == QCPAxis::stLogarithmic)\n    signDomain = (valueAxis->range().upper < 0 ? QCP::sdNegative : QCP::sdPositive);\n  \n  bool foundRange;\n  QCPRange newRange = getValueRange(foundRange, signDomain, inKeyRange ? keyAxis->range() : QCPRange());\n  if (foundRange)\n  {\n    if (onlyEnlarge)\n      newRange.expand(valueAxis->range());\n    if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable\n    {\n      double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason\n      if (valueAxis->scaleType() == QCPAxis::stLinear)\n      {\n        newRange.lower = center-valueAxis->range().size()/2.0;\n        newRange.upper = center+valueAxis->range().size()/2.0;\n      } else // scaleType() == stLogarithmic\n      {\n        newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);\n        newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);\n      }\n    }\n    valueAxis->setRange(newRange);\n  }\n}\n\n/*! \\overload\n\n  Adds this plottable to the specified \\a legend.\n\n  Creates a QCPPlottableLegendItem which is inserted into the legend. Returns true on success, i.e.\n  when the legend exists and a legend item associated with this plottable isn't already in the\n  legend.\n\n  If the plottable needs a more specialized representation in the legend, you can create a\n  corresponding subclass of \\ref QCPPlottableLegendItem and add it to the legend manually instead\n  of calling this method.\n\n  \\see removeFromLegend, QCPLegend::addItem\n*/\nbool QCPAbstractPlottable::addToLegend(QCPLegend *legend)\n{\n  if (!legend)\n  {\n    qDebug() << Q_FUNC_INFO << \"passed legend is null\";\n    return false;\n  }\n  if (legend->parentPlot() != mParentPlot)\n  {\n    qDebug() << Q_FUNC_INFO << \"passed legend isn't in the same QCustomPlot as this plottable\";\n    return false;\n  }\n  \n  if (!legend->hasItemWithPlottable(this))\n  {\n    legend->addItem(new QCPPlottableLegendItem(legend, this));\n    return true;\n  } else\n    return false;\n}\n\n/*! \\overload\n\n  Adds this plottable to the legend of the parent QCustomPlot (\\ref QCustomPlot::legend).\n\n  \\see removeFromLegend\n*/\nbool QCPAbstractPlottable::addToLegend()\n{\n  if (!mParentPlot || !mParentPlot->legend)\n    return false;\n  else\n    return addToLegend(mParentPlot->legend);\n}\n\n/*! \\overload\n\n  Removes the plottable from the specifed \\a legend. This means the \\ref QCPPlottableLegendItem\n  that is associated with this plottable is removed.\n\n  Returns true on success, i.e. if the legend exists and a legend item associated with this\n  plottable was found and removed.\n\n  \\see addToLegend, QCPLegend::removeItem\n*/\nbool QCPAbstractPlottable::removeFromLegend(QCPLegend *legend) const\n{\n  if (!legend)\n  {\n    qDebug() << Q_FUNC_INFO << \"passed legend is null\";\n    return false;\n  }\n  \n  if (QCPPlottableLegendItem *lip = legend->itemWithPlottable(this))\n    return legend->removeItem(lip);\n  else\n    return false;\n}\n\n/*! \\overload\n\n  Removes the plottable from the legend of the parent QCustomPlot.\n\n  \\see addToLegend\n*/\nbool QCPAbstractPlottable::removeFromLegend() const\n{\n  if (!mParentPlot || !mParentPlot->legend)\n    return false;\n  else\n    return removeFromLegend(mParentPlot->legend);\n}\n\n/* inherits documentation from base class */\nQRect QCPAbstractPlottable::clipRect() const\n{\n  if (mKeyAxis && mValueAxis)\n    return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect();\n  else\n    return {};\n}\n\n/* inherits documentation from base class */\nQCP::Interaction QCPAbstractPlottable::selectionCategory() const\n{\n  return QCP::iSelectPlottables;\n}\n\n/*! \\internal\n\n  A convenience function to easily set the QPainter::Antialiased hint on the provided \\a painter\n  before drawing plottable lines.\n\n  This is the antialiasing state the painter passed to the \\ref draw method is in by default.\n  \n  This function takes into account the local setting of the antialiasing flag as well as the\n  overrides set with \\ref QCustomPlot::setAntialiasedElements and \\ref\n  QCustomPlot::setNotAntialiasedElements.\n  \n  \\seebaseclassmethod\n  \n  \\see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint\n*/\nvoid QCPAbstractPlottable::applyDefaultAntialiasingHint(QCPPainter *painter) const\n{\n  applyAntialiasingHint(painter, mAntialiased, QCP::aePlottables);\n}\n\n/*! \\internal\n\n  A convenience function to easily set the QPainter::Antialiased hint on the provided \\a painter\n  before drawing plottable fills.\n  \n  This function takes into account the local setting of the antialiasing flag as well as the\n  overrides set with \\ref QCustomPlot::setAntialiasedElements and \\ref\n  QCustomPlot::setNotAntialiasedElements.\n  \n  \\see setAntialiased, applyDefaultAntialiasingHint, applyScattersAntialiasingHint\n*/\nvoid QCPAbstractPlottable::applyFillAntialiasingHint(QCPPainter *painter) const\n{\n  applyAntialiasingHint(painter, mAntialiasedFill, QCP::aeFills);\n}\n\n/*! \\internal\n\n  A convenience function to easily set the QPainter::Antialiased hint on the provided \\a painter\n  before drawing plottable scatter points.\n  \n  This function takes into account the local setting of the antialiasing flag as well as the\n  overrides set with \\ref QCustomPlot::setAntialiasedElements and \\ref\n  QCustomPlot::setNotAntialiasedElements.\n  \n  \\see setAntialiased, applyFillAntialiasingHint, applyDefaultAntialiasingHint\n*/\nvoid QCPAbstractPlottable::applyScattersAntialiasingHint(QCPPainter *painter) const\n{\n  applyAntialiasingHint(painter, mAntialiasedScatters, QCP::aeScatters);\n}\n\n/* inherits documentation from base class */\nvoid QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)\n{\n  Q_UNUSED(event)\n  \n  if (mSelectable != QCP::stNone)\n  {\n    QCPDataSelection newSelection = details.value<QCPDataSelection>();\n    QCPDataSelection selectionBefore = mSelection;\n    if (additive)\n    {\n      if (mSelectable == QCP::stWhole) // in whole selection mode, we toggle to no selection even if currently unselected point was hit\n      {\n        if (selected())\n          setSelection(QCPDataSelection());\n        else\n          setSelection(newSelection);\n      } else // in all other selection modes we toggle selections of homogeneously selected/unselected segments\n      {\n        if (mSelection.contains(newSelection)) // if entire newSelection is already selected, toggle selection\n          setSelection(mSelection-newSelection);\n        else\n          setSelection(mSelection+newSelection);\n      }\n    } else\n      setSelection(newSelection);\n    if (selectionStateChanged)\n      *selectionStateChanged = mSelection != selectionBefore;\n  }\n}\n\n/* inherits documentation from base class */\nvoid QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged)\n{\n  if (mSelectable != QCP::stNone)\n  {\n    QCPDataSelection selectionBefore = mSelection;\n    setSelection(QCPDataSelection());\n    if (selectionStateChanged)\n      *selectionStateChanged = mSelection != selectionBefore;\n  }\n}\n/* end of 'src/plottable.cpp' */\n\n\n/* including file 'src/item.cpp'            */\n/* modified 2021-03-29T02:30:44, size 49486 */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPItemAnchor\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPItemAnchor\n  \\brief An anchor of an item to which positions can be attached to.\n  \n  An item (QCPAbstractItem) may have one or more anchors. Unlike QCPItemPosition, an anchor doesn't\n  control anything on its item, but provides a way to tie other items via their positions to the\n  anchor.\n\n  For example, a QCPItemRect is defined by its positions \\a topLeft and \\a bottomRight.\n  Additionally it has various anchors like \\a top, \\a topRight or \\a bottomLeft etc. So you can\n  attach the \\a start (which is a QCPItemPosition) of a QCPItemLine to one of the anchors by\n  calling QCPItemPosition::setParentAnchor on \\a start, passing the wanted anchor of the\n  QCPItemRect. This way the start of the line will now always follow the respective anchor location\n  on the rect item.\n  \n  Note that QCPItemPosition derives from QCPItemAnchor, so every position can also serve as an\n  anchor to other positions.\n  \n  To learn how to provide anchors in your own item subclasses, see the subclassing section of the\n  QCPAbstractItem documentation.\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn virtual QCPItemPosition *QCPItemAnchor::toQCPItemPosition()\n  \n  Returns \\c nullptr if this instance is merely a QCPItemAnchor, and a valid pointer of type\n  QCPItemPosition* if it actually is a QCPItemPosition (which is a subclass of QCPItemAnchor).\n  \n  This safe downcast functionality could also be achieved with a dynamic_cast. However, QCustomPlot avoids\n  dynamic_cast to work with projects that don't have RTTI support enabled (e.g. -fno-rtti flag with\n  gcc compiler).\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Creates a new QCPItemAnchor. You shouldn't create QCPItemAnchor instances directly, even if\n  you want to make a new item subclass. Use \\ref QCPAbstractItem::createAnchor instead, as\n  explained in the subclassing section of the QCPAbstractItem documentation.\n*/\nQCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name, int anchorId) :\n  mName(name),\n  mParentPlot(parentPlot),\n  mParentItem(parentItem),\n  mAnchorId(anchorId)\n{\n}\n\nQCPItemAnchor::~QCPItemAnchor()\n{\n  // unregister as parent at children:\n  foreach (QCPItemPosition *child, mChildrenX.values())\n  {\n    if (child->parentAnchorX() == this)\n      child->setParentAnchorX(nullptr); // this acts back on this anchor and child removes itself from mChildrenX\n  }\n  foreach (QCPItemPosition *child, mChildrenY.values())\n  {\n    if (child->parentAnchorY() == this)\n      child->setParentAnchorY(nullptr); // this acts back on this anchor and child removes itself from mChildrenY\n  }\n}\n\n/*!\n  Returns the final absolute pixel position of the QCPItemAnchor on the QCustomPlot surface.\n  \n  The pixel information is internally retrieved via QCPAbstractItem::anchorPixelPosition of the\n  parent item, QCPItemAnchor is just an intermediary.\n*/\nQPointF QCPItemAnchor::pixelPosition() const\n{\n  if (mParentItem)\n  {\n    if (mAnchorId > -1)\n    {\n      return mParentItem->anchorPixelPosition(mAnchorId);\n    } else\n    {\n      qDebug() << Q_FUNC_INFO << \"no valid anchor id set:\" << mAnchorId;\n      return {};\n    }\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"no parent item set\";\n    return {};\n  }\n}\n\n/*! \\internal\n\n  Adds \\a pos to the childX list of this anchor, which keeps track of which children use this\n  anchor as parent anchor for the respective coordinate. This is necessary to notify the children\n  prior to destruction of the anchor.\n  \n  Note that this function does not change the parent setting in \\a pos.\n*/\nvoid QCPItemAnchor::addChildX(QCPItemPosition *pos)\n{\n  if (!mChildrenX.contains(pos))\n    mChildrenX.insert(pos);\n  else\n    qDebug() << Q_FUNC_INFO << \"provided pos is child already\" << reinterpret_cast<quintptr>(pos);\n}\n\n/*! \\internal\n\n  Removes \\a pos from the childX list of this anchor.\n  \n  Note that this function does not change the parent setting in \\a pos.\n*/\nvoid QCPItemAnchor::removeChildX(QCPItemPosition *pos)\n{\n  if (!mChildrenX.remove(pos))\n    qDebug() << Q_FUNC_INFO << \"provided pos isn't child\" << reinterpret_cast<quintptr>(pos);\n}\n\n/*! \\internal\n\n  Adds \\a pos to the childY list of this anchor, which keeps track of which children use this\n  anchor as parent anchor for the respective coordinate. This is necessary to notify the children\n  prior to destruction of the anchor.\n  \n  Note that this function does not change the parent setting in \\a pos.\n*/\nvoid QCPItemAnchor::addChildY(QCPItemPosition *pos)\n{\n  if (!mChildrenY.contains(pos))\n    mChildrenY.insert(pos);\n  else\n    qDebug() << Q_FUNC_INFO << \"provided pos is child already\" << reinterpret_cast<quintptr>(pos);\n}\n\n/*! \\internal\n\n  Removes \\a pos from the childY list of this anchor.\n  \n  Note that this function does not change the parent setting in \\a pos.\n*/\nvoid QCPItemAnchor::removeChildY(QCPItemPosition *pos)\n{\n  if (!mChildrenY.remove(pos))\n    qDebug() << Q_FUNC_INFO << \"provided pos isn't child\" << reinterpret_cast<quintptr>(pos);\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPItemPosition\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPItemPosition\n  \\brief Manages the position of an item.\n  \n  Every item has at least one public QCPItemPosition member pointer which provides ways to position the\n  item on the QCustomPlot surface. Some items have multiple positions, for example QCPItemRect has two:\n  \\a topLeft and \\a bottomRight.\n\n  QCPItemPosition has a type (\\ref PositionType) that can be set with \\ref setType. This type\n  defines how coordinates passed to \\ref setCoords are to be interpreted, e.g. as absolute pixel\n  coordinates, as plot coordinates of certain axes (\\ref QCPItemPosition::setAxes), as fractions of\n  the axis rect (\\ref QCPItemPosition::setAxisRect), etc. For more advanced plots it is also\n  possible to assign different types per X/Y coordinate of the position (see \\ref setTypeX, \\ref\n  setTypeY). This way an item could be positioned for example at a fixed pixel distance from the\n  top in the Y direction, while following a plot coordinate in the X direction.\n\n  A QCPItemPosition may have a parent QCPItemAnchor, see \\ref setParentAnchor. This way you can tie\n  multiple items together. If the QCPItemPosition has a parent, its coordinates (\\ref setCoords)\n  are considered to be absolute pixels in the reference frame of the parent anchor, where (0, 0)\n  means directly ontop of the parent anchor. For example, You could attach the \\a start position of\n  a QCPItemLine to the \\a bottom anchor of a QCPItemText to make the starting point of the line\n  always be centered under the text label, no matter where the text is moved to. For more advanced\n  plots, it is possible to assign different parent anchors per X/Y coordinate of the position, see\n  \\ref setParentAnchorX, \\ref setParentAnchorY. This way an item could follow another item in the X\n  direction but stay at a fixed position in the Y direction. Or even follow item A in X, and item B\n  in Y.\n\n  Note that every QCPItemPosition inherits from QCPItemAnchor and thus can itself be used as parent\n  anchor for other positions.\n\n  To set the apparent pixel position on the QCustomPlot surface directly, use \\ref setPixelPosition. This\n  works no matter what type this QCPItemPosition is or what parent-child situation it is in, as \\ref\n  setPixelPosition transforms the coordinates appropriately, to make the position appear at the specified\n  pixel values.\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn QCPItemPosition::PositionType *QCPItemPosition::type() const\n  \n  Returns the current position type.\n  \n  If different types were set for X and Y (\\ref setTypeX, \\ref setTypeY), this method returns the\n  type of the X coordinate. In that case rather use \\a typeX() and \\a typeY().\n  \n  \\see setType\n*/\n\n/*! \\fn QCPItemAnchor *QCPItemPosition::parentAnchor() const\n  \n  Returns the current parent anchor.\n  \n  If different parent anchors were set for X and Y (\\ref setParentAnchorX, \\ref setParentAnchorY),\n  this method returns the parent anchor of the Y coordinate. In that case rather use \\a\n  parentAnchorX() and \\a parentAnchorY().\n  \n  \\see setParentAnchor\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Creates a new QCPItemPosition. You shouldn't create QCPItemPosition instances directly, even if\n  you want to make a new item subclass. Use \\ref QCPAbstractItem::createPosition instead, as\n  explained in the subclassing section of the QCPAbstractItem documentation.\n*/\nQCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name) :\n  QCPItemAnchor(parentPlot, parentItem, name),\n  mPositionTypeX(ptAbsolute),\n  mPositionTypeY(ptAbsolute),\n  mKey(0),\n  mValue(0),\n  mParentAnchorX(nullptr),\n  mParentAnchorY(nullptr)\n{\n}\n\nQCPItemPosition::~QCPItemPosition()\n{\n  // unregister as parent at children:\n  // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then\n  //       the setParentAnchor(0) call the correct QCPItemPosition::pixelPosition function instead of QCPItemAnchor::pixelPosition\n  foreach (QCPItemPosition *child, mChildrenX.values())\n  {\n    if (child->parentAnchorX() == this)\n      child->setParentAnchorX(nullptr); // this acts back on this anchor and child removes itself from mChildrenX\n  }\n  foreach (QCPItemPosition *child, mChildrenY.values())\n  {\n    if (child->parentAnchorY() == this)\n      child->setParentAnchorY(nullptr); // this acts back on this anchor and child removes itself from mChildrenY\n  }\n  // unregister as child in parent:\n  if (mParentAnchorX)\n    mParentAnchorX->removeChildX(this);\n  if (mParentAnchorY)\n    mParentAnchorY->removeChildY(this);\n}\n\n/* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */\nQCPAxisRect *QCPItemPosition::axisRect() const\n{\n  return mAxisRect.data();\n}\n\n/*!\n  Sets the type of the position. The type defines how the coordinates passed to \\ref setCoords\n  should be handled and how the QCPItemPosition should behave in the plot.\n  \n  The possible values for \\a type can be separated in two main categories:\n\n  \\li The position is regarded as a point in plot coordinates. This corresponds to \\ref ptPlotCoords\n  and requires two axes that define the plot coordinate system. They can be specified with \\ref setAxes.\n  By default, the QCustomPlot's x- and yAxis are used.\n  \n  \\li The position is fixed on the QCustomPlot surface, i.e. independent of axis ranges. This\n  corresponds to all other types, i.e. \\ref ptAbsolute, \\ref ptViewportRatio and \\ref\n  ptAxisRectRatio. They differ only in the way the absolute position is described, see the\n  documentation of \\ref PositionType for details. For \\ref ptAxisRectRatio, note that you can specify\n  the axis rect with \\ref setAxisRect. By default this is set to the main axis rect.\n  \n  Note that the position type \\ref ptPlotCoords is only available (and sensible) when the position\n  has no parent anchor (\\ref setParentAnchor).\n  \n  If the type is changed, the apparent pixel position on the plot is preserved. This means\n  the coordinates as retrieved with coords() and set with \\ref setCoords may change in the process.\n  \n  This method sets the type for both X and Y directions. It is also possible to set different types\n  for X and Y, see \\ref setTypeX, \\ref setTypeY.\n*/\nvoid QCPItemPosition::setType(QCPItemPosition::PositionType type)\n{\n  setTypeX(type);\n  setTypeY(type);\n}\n\n/*!\n  This method sets the position type of the X coordinate to \\a type.\n  \n  For a detailed description of what a position type is, see the documentation of \\ref setType.\n  \n  \\see setType, setTypeY\n*/\nvoid QCPItemPosition::setTypeX(QCPItemPosition::PositionType type)\n{\n  if (mPositionTypeX != type)\n  {\n    // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect\n    // were deleted), don't try to recover the pixelPosition() because it would output a qDebug warning.\n    bool retainPixelPosition = true;\n    if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))\n      retainPixelPosition = false;\n    if ((mPositionTypeX == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))\n      retainPixelPosition = false;\n    \n    QPointF pixel;\n    if (retainPixelPosition)\n      pixel = pixelPosition();\n    \n    mPositionTypeX = type;\n    \n    if (retainPixelPosition)\n      setPixelPosition(pixel);\n  }\n}\n\n/*!\n  This method sets the position type of the Y coordinate to \\a type.\n  \n  For a detailed description of what a position type is, see the documentation of \\ref setType.\n  \n  \\see setType, setTypeX\n*/\nvoid QCPItemPosition::setTypeY(QCPItemPosition::PositionType type)\n{\n  if (mPositionTypeY != type)\n  {\n    // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect\n    // were deleted), don't try to recover the pixelPosition() because it would output a qDebug warning.\n    bool retainPixelPosition = true;\n    if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))\n      retainPixelPosition = false;\n    if ((mPositionTypeY == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))\n      retainPixelPosition = false;\n    \n    QPointF pixel;\n    if (retainPixelPosition)\n      pixel = pixelPosition();\n    \n    mPositionTypeY = type;\n    \n    if (retainPixelPosition)\n      setPixelPosition(pixel);\n  }\n}\n\n/*!\n  Sets the parent of this QCPItemPosition to \\a parentAnchor. This means the position will now\n  follow any position changes of the anchor. The local coordinate system of positions with a parent\n  anchor always is absolute pixels, with (0, 0) being exactly on top of the parent anchor. (Hence\n  the type shouldn't be set to \\ref ptPlotCoords for positions with parent anchors.)\n  \n  if \\a keepPixelPosition is true, the current pixel position of the QCPItemPosition is preserved\n  during reparenting. If it's set to false, the coordinates are set to (0, 0), i.e. the position\n  will be exactly on top of the parent anchor.\n  \n  To remove this QCPItemPosition from any parent anchor, set \\a parentAnchor to \\c nullptr.\n  \n  If the QCPItemPosition previously had no parent and the type is \\ref ptPlotCoords, the type is\n  set to \\ref ptAbsolute, to keep the position in a valid state.\n  \n  This method sets the parent anchor for both X and Y directions. It is also possible to set\n  different parents for X and Y, see \\ref setParentAnchorX, \\ref setParentAnchorY.\n*/\nbool QCPItemPosition::setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition)\n{\n  bool successX = setParentAnchorX(parentAnchor, keepPixelPosition);\n  bool successY = setParentAnchorY(parentAnchor, keepPixelPosition);\n  return successX && successY;\n}\n\n/*!\n  This method sets the parent anchor of the X coordinate to \\a parentAnchor.\n  \n  For a detailed description of what a parent anchor is, see the documentation of \\ref setParentAnchor.\n  \n  \\see setParentAnchor, setParentAnchorY\n*/\nbool QCPItemPosition::setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition)\n{\n  // make sure self is not assigned as parent:\n  if (parentAnchor == this)\n  {\n    qDebug() << Q_FUNC_INFO << \"can't set self as parent anchor\" << reinterpret_cast<quintptr>(parentAnchor);\n    return false;\n  }\n  // make sure no recursive parent-child-relationships are created:\n  QCPItemAnchor *currentParent = parentAnchor;\n  while (currentParent)\n  {\n    if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())\n    {\n      // is a QCPItemPosition, might have further parent, so keep iterating\n      if (currentParentPos == this)\n      {\n        qDebug() << Q_FUNC_INFO << \"can't create recursive parent-child-relationship\" << reinterpret_cast<quintptr>(parentAnchor);\n        return false;\n      }\n      currentParent = currentParentPos->parentAnchorX();\n    } else\n    {\n      // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the\n      // same, to prevent a position being child of an anchor which itself depends on the position,\n      // because they're both on the same item:\n      if (currentParent->mParentItem == mParentItem)\n      {\n        qDebug() << Q_FUNC_INFO << \"can't set parent to be an anchor which itself depends on this position\" << reinterpret_cast<quintptr>(parentAnchor);\n        return false;\n      }\n      break;\n    }\n  }\n  \n  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:\n  if (!mParentAnchorX && mPositionTypeX == ptPlotCoords)\n    setTypeX(ptAbsolute);\n  \n  // save pixel position:\n  QPointF pixelP;\n  if (keepPixelPosition)\n    pixelP = pixelPosition();\n  // unregister at current parent anchor:\n  if (mParentAnchorX)\n    mParentAnchorX->removeChildX(this);\n  // register at new parent anchor:\n  if (parentAnchor)\n    parentAnchor->addChildX(this);\n  mParentAnchorX = parentAnchor;\n  // restore pixel position under new parent:\n  if (keepPixelPosition)\n    setPixelPosition(pixelP);\n  else\n    setCoords(0, coords().y());\n  return true;\n}\n\n/*!\n  This method sets the parent anchor of the Y coordinate to \\a parentAnchor.\n  \n  For a detailed description of what a parent anchor is, see the documentation of \\ref setParentAnchor.\n  \n  \\see setParentAnchor, setParentAnchorX\n*/\nbool QCPItemPosition::setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition)\n{\n  // make sure self is not assigned as parent:\n  if (parentAnchor == this)\n  {\n    qDebug() << Q_FUNC_INFO << \"can't set self as parent anchor\" << reinterpret_cast<quintptr>(parentAnchor);\n    return false;\n  }\n  // make sure no recursive parent-child-relationships are created:\n  QCPItemAnchor *currentParent = parentAnchor;\n  while (currentParent)\n  {\n    if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())\n    {\n      // is a QCPItemPosition, might have further parent, so keep iterating\n      if (currentParentPos == this)\n      {\n        qDebug() << Q_FUNC_INFO << \"can't create recursive parent-child-relationship\" << reinterpret_cast<quintptr>(parentAnchor);\n        return false;\n      }\n      currentParent = currentParentPos->parentAnchorY();\n    } else\n    {\n      // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the\n      // same, to prevent a position being child of an anchor which itself depends on the position,\n      // because they're both on the same item:\n      if (currentParent->mParentItem == mParentItem)\n      {\n        qDebug() << Q_FUNC_INFO << \"can't set parent to be an anchor which itself depends on this position\" << reinterpret_cast<quintptr>(parentAnchor);\n        return false;\n      }\n      break;\n    }\n  }\n  \n  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:\n  if (!mParentAnchorY && mPositionTypeY == ptPlotCoords)\n    setTypeY(ptAbsolute);\n  \n  // save pixel position:\n  QPointF pixelP;\n  if (keepPixelPosition)\n    pixelP = pixelPosition();\n  // unregister at current parent anchor:\n  if (mParentAnchorY)\n    mParentAnchorY->removeChildY(this);\n  // register at new parent anchor:\n  if (parentAnchor)\n    parentAnchor->addChildY(this);\n  mParentAnchorY = parentAnchor;\n  // restore pixel position under new parent:\n  if (keepPixelPosition)\n    setPixelPosition(pixelP);\n  else\n    setCoords(coords().x(), 0);\n  return true;\n}\n\n/*!\n  Sets the coordinates of this QCPItemPosition. What the coordinates mean, is defined by the type\n  (\\ref setType, \\ref setTypeX, \\ref setTypeY).\n  \n  For example, if the type is \\ref ptAbsolute, \\a key and \\a value mean the x and y pixel position\n  on the QCustomPlot surface. In that case the origin (0, 0) is in the top left corner of the\n  QCustomPlot viewport. If the type is \\ref ptPlotCoords, \\a key and \\a value mean a point in the\n  plot coordinate system defined by the axes set by \\ref setAxes. By default those are the\n  QCustomPlot's xAxis and yAxis. See the documentation of \\ref setType for other available\n  coordinate types and their meaning.\n  \n  If different types were configured for X and Y (\\ref setTypeX, \\ref setTypeY), \\a key and \\a\n  value must also be provided in the different coordinate systems. Here, the X type refers to \\a\n  key, and the Y type refers to \\a value.\n\n  \\see setPixelPosition\n*/\nvoid QCPItemPosition::setCoords(double key, double value)\n{\n  mKey = key;\n  mValue = value;\n}\n\n/*! \\overload\n\n  Sets the coordinates as a QPointF \\a pos where pos.x has the meaning of \\a key and pos.y the\n  meaning of \\a value of the \\ref setCoords(double key, double value) method.\n*/\nvoid QCPItemPosition::setCoords(const QPointF &pos)\n{\n  setCoords(pos.x(), pos.y());\n}\n\n/*!\n  Returns the final absolute pixel position of the QCPItemPosition on the QCustomPlot surface. It\n  includes all effects of type (\\ref setType) and possible parent anchors (\\ref setParentAnchor).\n\n  \\see setPixelPosition\n*/\nQPointF QCPItemPosition::pixelPosition() const\n{\n  QPointF result;\n  \n  // determine X:\n  switch (mPositionTypeX)\n  {\n    case ptAbsolute:\n    {\n      result.rx() = mKey;\n      if (mParentAnchorX)\n        result.rx() += mParentAnchorX->pixelPosition().x();\n      break;\n    }\n    case ptViewportRatio:\n    {\n      result.rx() = mKey*mParentPlot->viewport().width();\n      if (mParentAnchorX)\n        result.rx() += mParentAnchorX->pixelPosition().x();\n      else\n        result.rx() += mParentPlot->viewport().left();\n      break;\n    }\n    case ptAxisRectRatio:\n    {\n      if (mAxisRect)\n      {\n        result.rx() = mKey*mAxisRect.data()->width();\n        if (mParentAnchorX)\n          result.rx() += mParentAnchorX->pixelPosition().x();\n        else\n          result.rx() += mAxisRect.data()->left();\n      } else\n        qDebug() << Q_FUNC_INFO << \"Item position type x is ptAxisRectRatio, but no axis rect was defined\";\n      break;\n    }\n    case ptPlotCoords:\n    {\n      if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)\n        result.rx() = mKeyAxis.data()->coordToPixel(mKey);\n      else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)\n        result.rx() = mValueAxis.data()->coordToPixel(mValue);\n      else\n        qDebug() << Q_FUNC_INFO << \"Item position type x is ptPlotCoords, but no axes were defined\";\n      break;\n    }\n  }\n  \n  // determine Y:\n  switch (mPositionTypeY)\n  {\n    case ptAbsolute:\n    {\n      result.ry() = mValue;\n      if (mParentAnchorY)\n        result.ry() += mParentAnchorY->pixelPosition().y();\n      break;\n    }\n    case ptViewportRatio:\n    {\n      result.ry() = mValue*mParentPlot->viewport().height();\n      if (mParentAnchorY)\n        result.ry() += mParentAnchorY->pixelPosition().y();\n      else\n        result.ry() += mParentPlot->viewport().top();\n      break;\n    }\n    case ptAxisRectRatio:\n    {\n      if (mAxisRect)\n      {\n        result.ry() = mValue*mAxisRect.data()->height();\n        if (mParentAnchorY)\n          result.ry() += mParentAnchorY->pixelPosition().y();\n        else\n          result.ry() += mAxisRect.data()->top();\n      } else\n        qDebug() << Q_FUNC_INFO << \"Item position type y is ptAxisRectRatio, but no axis rect was defined\";\n      break;\n    }\n    case ptPlotCoords:\n    {\n      if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)\n        result.ry() = mKeyAxis.data()->coordToPixel(mKey);\n      else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)\n        result.ry() = mValueAxis.data()->coordToPixel(mValue);\n      else\n        qDebug() << Q_FUNC_INFO << \"Item position type y is ptPlotCoords, but no axes were defined\";\n      break;\n    }\n  }\n  \n  return result;\n}\n\n/*!\n  When \\ref setType is \\ref ptPlotCoords, this function may be used to specify the axes the\n  coordinates set with \\ref setCoords relate to. By default they are set to the initial xAxis and\n  yAxis of the QCustomPlot.\n*/\nvoid QCPItemPosition::setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)\n{\n  mKeyAxis = keyAxis;\n  mValueAxis = valueAxis;\n}\n\n/*!\n  When \\ref setType is \\ref ptAxisRectRatio, this function may be used to specify the axis rect the\n  coordinates set with \\ref setCoords relate to. By default this is set to the main axis rect of\n  the QCustomPlot.\n*/\nvoid QCPItemPosition::setAxisRect(QCPAxisRect *axisRect)\n{\n  mAxisRect = axisRect;\n}\n\n/*!\n  Sets the apparent pixel position. This works no matter what type (\\ref setType) this\n  QCPItemPosition is or what parent-child situation it is in, as coordinates are transformed\n  appropriately, to make the position finally appear at the specified pixel values.\n\n  Only if the type is \\ref ptAbsolute and no parent anchor is set, this function's effect is\n  identical to that of \\ref setCoords.\n\n  \\see pixelPosition, setCoords\n*/\nvoid QCPItemPosition::setPixelPosition(const QPointF &pixelPosition)\n{\n  double x = pixelPosition.x();\n  double y = pixelPosition.y();\n  \n  switch (mPositionTypeX)\n  {\n    case ptAbsolute:\n    {\n      if (mParentAnchorX)\n        x -= mParentAnchorX->pixelPosition().x();\n      break;\n    }\n    case ptViewportRatio:\n    {\n      if (mParentAnchorX)\n        x -= mParentAnchorX->pixelPosition().x();\n      else\n        x -= mParentPlot->viewport().left();\n      x /= double(mParentPlot->viewport().width());\n      break;\n    }\n    case ptAxisRectRatio:\n    {\n      if (mAxisRect)\n      {\n        if (mParentAnchorX)\n          x -= mParentAnchorX->pixelPosition().x();\n        else\n          x -= mAxisRect.data()->left();\n        x /= double(mAxisRect.data()->width());\n      } else\n        qDebug() << Q_FUNC_INFO << \"Item position type x is ptAxisRectRatio, but no axis rect was defined\";\n      break;\n    }\n    case ptPlotCoords:\n    {\n      if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)\n        x = mKeyAxis.data()->pixelToCoord(x);\n      else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)\n        y = mValueAxis.data()->pixelToCoord(x);\n      else\n        qDebug() << Q_FUNC_INFO << \"Item position type x is ptPlotCoords, but no axes were defined\";\n      break;\n    }\n  }\n  \n  switch (mPositionTypeY)\n  {\n    case ptAbsolute:\n    {\n      if (mParentAnchorY)\n        y -= mParentAnchorY->pixelPosition().y();\n      break;\n    }\n    case ptViewportRatio:\n    {\n      if (mParentAnchorY)\n        y -= mParentAnchorY->pixelPosition().y();\n      else\n        y -= mParentPlot->viewport().top();\n      y /= double(mParentPlot->viewport().height());\n      break;\n    }\n    case ptAxisRectRatio:\n    {\n      if (mAxisRect)\n      {\n        if (mParentAnchorY)\n          y -= mParentAnchorY->pixelPosition().y();\n        else\n          y -= mAxisRect.data()->top();\n        y /= double(mAxisRect.data()->height());\n      } else\n        qDebug() << Q_FUNC_INFO << \"Item position type y is ptAxisRectRatio, but no axis rect was defined\";\n      break;\n    }\n    case ptPlotCoords:\n    {\n      if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)\n        x = mKeyAxis.data()->pixelToCoord(y);\n      else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)\n        y = mValueAxis.data()->pixelToCoord(y);\n      else\n        qDebug() << Q_FUNC_INFO << \"Item position type y is ptPlotCoords, but no axes were defined\";\n      break;\n    }\n  }\n  \n  setCoords(x, y);\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPAbstractItem\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPAbstractItem\n  \\brief The abstract base class for all items in a plot.\n  \n  In QCustomPlot, items are supplemental graphical elements that are neither plottables\n  (QCPAbstractPlottable) nor axes (QCPAxis). While plottables are always tied to two axes and thus\n  plot coordinates, items can also be placed in absolute coordinates independent of any axes. Each\n  specific item has at least one QCPItemPosition member which controls the positioning. Some items\n  are defined by more than one coordinate and thus have two or more QCPItemPosition members (For\n  example, QCPItemRect has \\a topLeft and \\a bottomRight).\n  \n  This abstract base class defines a very basic interface like visibility and clipping. Since this\n  class is abstract, it can't be instantiated. Use one of the subclasses or create a subclass\n  yourself to create new items.\n  \n  The built-in items are:\n  <table>\n  <tr><td>QCPItemLine</td><td>A line defined by a start and an end point. May have different ending styles on each side (e.g. arrows).</td></tr>\n  <tr><td>QCPItemStraightLine</td><td>A straight line defined by a start and a direction point. Unlike QCPItemLine, the straight line is infinitely long and has no endings.</td></tr>\n  <tr><td>QCPItemCurve</td><td>A curve defined by start, end and two intermediate control points. May have different ending styles on each side (e.g. arrows).</td></tr>\n  <tr><td>QCPItemRect</td><td>A rectangle</td></tr>\n  <tr><td>QCPItemEllipse</td><td>An ellipse</td></tr>\n  <tr><td>QCPItemPixmap</td><td>An arbitrary pixmap</td></tr>\n  <tr><td>QCPItemText</td><td>A text label</td></tr>\n  <tr><td>QCPItemBracket</td><td>A bracket which may be used to reference/highlight certain parts in the plot.</td></tr>\n  <tr><td>QCPItemTracer</td><td>An item that can be attached to a QCPGraph and sticks to its data points, given a key coordinate.</td></tr>\n  </table>\n  \n  \\section items-clipping Clipping\n\n  Items are by default clipped to the main axis rect (they are only visible inside the axis rect).\n  To make an item visible outside that axis rect, disable clipping via \\ref setClipToAxisRect\n  \"setClipToAxisRect(false)\".\n\n  On the other hand if you want the item to be clipped to a different axis rect, specify it via\n  \\ref setClipAxisRect. This clipAxisRect property of an item is only used for clipping behaviour, and\n  in principle is independent of the coordinate axes the item might be tied to via its position\n  members (\\ref QCPItemPosition::setAxes). However, it is common that the axis rect for clipping\n  also contains the axes used for the item positions.\n  \n  \\section items-using Using items\n  \n  First you instantiate the item you want to use and add it to the plot:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-1\n  by default, the positions of the item are bound to the x- and y-Axis of the plot. So we can just\n  set the plot coordinates where the line should start/end:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-2\n  If we don't want the line to be positioned in plot coordinates but a different coordinate system,\n  e.g. absolute pixel positions on the QCustomPlot surface, we need to change the position type like this:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-3\n  Then we can set the coordinates, this time in pixels:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-4\n  and make the line visible on the entire QCustomPlot, by disabling clipping to the axis rect:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-5\n  \n  For more advanced plots, it is even possible to set different types and parent anchors per X/Y\n  coordinate of an item position, using for example \\ref QCPItemPosition::setTypeX or \\ref\n  QCPItemPosition::setParentAnchorX. For details, see the documentation of \\ref QCPItemPosition.\n  \n  \\section items-subclassing Creating own items\n  \n  To create an own item, you implement a subclass of QCPAbstractItem. These are the pure\n  virtual functions, you must implement:\n  \\li \\ref selectTest\n  \\li \\ref draw\n  \n  See the documentation of those functions for what they need to do.\n  \n  \\subsection items-positioning Allowing the item to be positioned\n  \n  As mentioned, item positions are represented by QCPItemPosition members. Let's assume the new item shall\n  have only one point as its position (as opposed to two like a rect or multiple like a polygon). You then add\n  a public member of type QCPItemPosition like so:\n  \n  \\code QCPItemPosition * const myPosition;\\endcode\n  \n  the const makes sure the pointer itself can't be modified from the user of your new item (the QCPItemPosition\n  instance it points to, can be modified, of course).\n  The initialization of this pointer is made easy with the \\ref createPosition function. Just assign\n  the return value of this function to each QCPItemPosition in the constructor of your item. \\ref createPosition\n  takes a string which is the name of the position, typically this is identical to the variable name.\n  For example, the constructor of QCPItemExample could look like this:\n  \n  \\code\n  QCPItemExample::QCPItemExample(QCustomPlot *parentPlot) :\n    QCPAbstractItem(parentPlot),\n    myPosition(createPosition(\"myPosition\"))\n  {\n    // other constructor code\n  }\n  \\endcode\n  \n  \\subsection items-drawing The draw function\n  \n  To give your item a visual representation, reimplement the \\ref draw function and use the passed\n  QCPPainter to draw the item. You can retrieve the item position in pixel coordinates from the\n  position member(s) via \\ref QCPItemPosition::pixelPosition.\n\n  To optimize performance you should calculate a bounding rect first (don't forget to take the pen\n  width into account), check whether it intersects the \\ref clipRect, and only draw the item at all\n  if this is the case.\n  \n  \\subsection items-selection The selectTest function\n  \n  Your implementation of the \\ref selectTest function may use the helpers \\ref\n  QCPVector2D::distanceSquaredToLine and \\ref rectDistance. With these, the implementation of the\n  selection test becomes significantly simpler for most items. See the documentation of \\ref\n  selectTest for what the function parameters mean and what the function should return.\n  \n  \\subsection anchors Providing anchors\n  \n  Providing anchors (QCPItemAnchor) starts off like adding a position. First you create a public\n  member, e.g.\n  \n  \\code QCPItemAnchor * const bottom;\\endcode\n\n  and create it in the constructor with the \\ref createAnchor function, assigning it a name and an\n  anchor id (an integer enumerating all anchors on the item, you may create an own enum for this).\n  Since anchors can be placed anywhere, relative to the item's position(s), your item needs to\n  provide the position of every anchor with the reimplementation of the \\ref anchorPixelPosition(int\n  anchorId) function.\n  \n  In essence the QCPItemAnchor is merely an intermediary that itself asks your item for the pixel\n  position when anything attached to the anchor needs to know the coordinates.\n*/\n\n/* start of documentation of inline functions */\n\n/*! \\fn QList<QCPItemPosition*> QCPAbstractItem::positions() const\n  \n  Returns all positions of the item in a list.\n  \n  \\see anchors, position\n*/\n\n/*! \\fn QList<QCPItemAnchor*> QCPAbstractItem::anchors() const\n  \n  Returns all anchors of the item in a list. Note that since a position (QCPItemPosition) is always\n  also an anchor, the list will also contain the positions of this item.\n  \n  \\see positions, anchor\n*/\n\n/* end of documentation of inline functions */\n/* start documentation of pure virtual functions */\n\n/*! \\fn void QCPAbstractItem::draw(QCPPainter *painter) = 0\n  \\internal\n  \n  Draws this item with the provided \\a painter.\n  \n  The cliprect of the provided painter is set to the rect returned by \\ref clipRect before this\n  function is called. The clipRect depends on the clipping settings defined by \\ref\n  setClipToAxisRect and \\ref setClipAxisRect.\n*/\n\n/* end documentation of pure virtual functions */\n/* start documentation of signals */\n\n/*! \\fn void QCPAbstractItem::selectionChanged(bool selected)\n  This signal is emitted when the selection state of this item has changed, either by user interaction\n  or by a direct call to \\ref setSelected.\n*/\n\n/* end documentation of signals */\n\n/*!\n  Base class constructor which initializes base class members.\n*/\nQCPAbstractItem::QCPAbstractItem(QCustomPlot *parentPlot) :\n  QCPLayerable(parentPlot),\n  mClipToAxisRect(false),\n  mSelectable(true),\n  mSelected(false)\n{\n  parentPlot->registerItem(this);\n  \n  QList<QCPAxisRect*> rects = parentPlot->axisRects();\n  if (!rects.isEmpty())\n  {\n    setClipToAxisRect(true);\n    setClipAxisRect(rects.first());\n  }\n}\n\nQCPAbstractItem::~QCPAbstractItem()\n{\n  // don't delete mPositions because every position is also an anchor and thus in mAnchors\n  qDeleteAll(mAnchors);\n}\n\n/* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */\nQCPAxisRect *QCPAbstractItem::clipAxisRect() const\n{\n  return mClipAxisRect.data();\n}\n\n/*!\n  Sets whether the item shall be clipped to an axis rect or whether it shall be visible on the\n  entire QCustomPlot. The axis rect can be set with \\ref setClipAxisRect.\n  \n  \\see setClipAxisRect\n*/\nvoid QCPAbstractItem::setClipToAxisRect(bool clip)\n{\n  mClipToAxisRect = clip;\n  if (mClipToAxisRect)\n    setParentLayerable(mClipAxisRect.data());\n}\n\n/*!\n  Sets the clip axis rect. It defines the rect that will be used to clip the item when \\ref\n  setClipToAxisRect is set to true.\n  \n  \\see setClipToAxisRect\n*/\nvoid QCPAbstractItem::setClipAxisRect(QCPAxisRect *rect)\n{\n  mClipAxisRect = rect;\n  if (mClipToAxisRect)\n    setParentLayerable(mClipAxisRect.data());\n}\n\n/*!\n  Sets whether the user can (de-)select this item by clicking on the QCustomPlot surface.\n  (When \\ref QCustomPlot::setInteractions contains QCustomPlot::iSelectItems.)\n  \n  However, even when \\a selectable was set to false, it is possible to set the selection manually,\n  by calling \\ref setSelected.\n  \n  \\see QCustomPlot::setInteractions, setSelected\n*/\nvoid QCPAbstractItem::setSelectable(bool selectable)\n{\n  if (mSelectable != selectable)\n  {\n    mSelectable = selectable;\n    emit selectableChanged(mSelectable);\n  }\n}\n\n/*!\n  Sets whether this item is selected or not. When selected, it might use a different visual\n  appearance (e.g. pen and brush), this depends on the specific item though.\n\n  The entire selection mechanism for items is handled automatically when \\ref\n  QCustomPlot::setInteractions contains QCustomPlot::iSelectItems. You only need to call this\n  function when you wish to change the selection state manually.\n  \n  This function can change the selection state even when \\ref setSelectable was set to false.\n  \n  emits the \\ref selectionChanged signal when \\a selected is different from the previous selection state.\n  \n  \\see setSelectable, selectTest\n*/\nvoid QCPAbstractItem::setSelected(bool selected)\n{\n  if (mSelected != selected)\n  {\n    mSelected = selected;\n    emit selectionChanged(mSelected);\n  }\n}\n\n/*!\n  Returns the QCPItemPosition with the specified \\a name. If this item doesn't have a position by\n  that name, returns \\c nullptr.\n  \n  This function provides an alternative way to access item positions. Normally, you access\n  positions direcly by their member pointers (which typically have the same variable name as \\a\n  name).\n  \n  \\see positions, anchor\n*/\nQCPItemPosition *QCPAbstractItem::position(const QString &name) const\n{\n  foreach (QCPItemPosition *position, mPositions)\n  {\n    if (position->name() == name)\n      return position;\n  }\n  qDebug() << Q_FUNC_INFO << \"position with name not found:\" << name;\n  return nullptr;\n}\n\n/*!\n  Returns the QCPItemAnchor with the specified \\a name. If this item doesn't have an anchor by\n  that name, returns \\c nullptr.\n  \n  This function provides an alternative way to access item anchors. Normally, you access\n  anchors direcly by their member pointers (which typically have the same variable name as \\a\n  name).\n  \n  \\see anchors, position\n*/\nQCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const\n{\n  foreach (QCPItemAnchor *anchor, mAnchors)\n  {\n    if (anchor->name() == name)\n      return anchor;\n  }\n  qDebug() << Q_FUNC_INFO << \"anchor with name not found:\" << name;\n  return nullptr;\n}\n\n/*!\n  Returns whether this item has an anchor with the specified \\a name.\n  \n  Note that you can check for positions with this function, too. This is because every position is\n  also an anchor (QCPItemPosition inherits from QCPItemAnchor).\n  \n  \\see anchor, position\n*/\nbool QCPAbstractItem::hasAnchor(const QString &name) const\n{\n  foreach (QCPItemAnchor *anchor, mAnchors)\n  {\n    if (anchor->name() == name)\n      return true;\n  }\n  return false;\n}\n\n/*! \\internal\n  \n  Returns the rect the visual representation of this item is clipped to. This depends on the\n  current setting of \\ref setClipToAxisRect as well as the axis rect set with \\ref setClipAxisRect.\n  \n  If the item is not clipped to an axis rect, QCustomPlot's viewport rect is returned.\n  \n  \\see draw\n*/\nQRect QCPAbstractItem::clipRect() const\n{\n  if (mClipToAxisRect && mClipAxisRect)\n    return mClipAxisRect.data()->rect();\n  else\n    return mParentPlot->viewport();\n}\n\n/*! \\internal\n\n  A convenience function to easily set the QPainter::Antialiased hint on the provided \\a painter\n  before drawing item lines.\n\n  This is the antialiasing state the painter passed to the \\ref draw method is in by default.\n  \n  This function takes into account the local setting of the antialiasing flag as well as the\n  overrides set with \\ref QCustomPlot::setAntialiasedElements and \\ref\n  QCustomPlot::setNotAntialiasedElements.\n  \n  \\see setAntialiased\n*/\nvoid QCPAbstractItem::applyDefaultAntialiasingHint(QCPPainter *painter) const\n{\n  applyAntialiasingHint(painter, mAntialiased, QCP::aeItems);\n}\n\n/*! \\internal\n\n  A convenience function which returns the selectTest value for a specified \\a rect and a specified\n  click position \\a pos. \\a filledRect defines whether a click inside the rect should also be\n  considered a hit or whether only the rect border is sensitive to hits.\n  \n  This function may be used to help with the implementation of the \\ref selectTest function for\n  specific items.\n  \n  For example, if your item consists of four rects, call this function four times, once for each\n  rect, in your \\ref selectTest reimplementation. Finally, return the minimum (non -1) of all four\n  returned values.\n*/\ndouble QCPAbstractItem::rectDistance(const QRectF &rect, const QPointF &pos, bool filledRect) const\n{\n  double result = -1;\n\n  // distance to border:\n  const QList<QLineF> lines = QList<QLineF>() << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())\n                                              << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());\n  const QCPVector2D posVec(pos);\n  double minDistSqr = (std::numeric_limits<double>::max)();\n  foreach (const QLineF &line, lines)\n  {\n    double distSqr = posVec.distanceSquaredToLine(line.p1(), line.p2());\n    if (distSqr < minDistSqr)\n      minDistSqr = distSqr;\n  }\n  result = qSqrt(minDistSqr);\n  \n  // filled rect, allow click inside to count as hit:\n  if (filledRect && result > mParentPlot->selectionTolerance()*0.99)\n  {\n    if (rect.contains(pos))\n      result = mParentPlot->selectionTolerance()*0.99;\n  }\n  return result;\n}\n\n/*! \\internal\n\n  Returns the pixel position of the anchor with Id \\a anchorId. This function must be reimplemented in\n  item subclasses if they want to provide anchors (QCPItemAnchor).\n  \n  For example, if the item has two anchors with id 0 and 1, this function takes one of these anchor\n  ids and returns the respective pixel points of the specified anchor.\n  \n  \\see createAnchor\n*/\nQPointF QCPAbstractItem::anchorPixelPosition(int anchorId) const\n{\n  qDebug() << Q_FUNC_INFO << \"called on item which shouldn't have any anchors (this method not reimplemented). anchorId\" << anchorId;\n  return {};\n}\n\n/*! \\internal\n\n  Creates a QCPItemPosition, registers it with this item and returns a pointer to it. The specified\n  \\a name must be a unique string that is usually identical to the variable name of the position\n  member (This is needed to provide the name-based \\ref position access to positions).\n  \n  Don't delete positions created by this function manually, as the item will take care of it.\n  \n  Use this function in the constructor (initialization list) of the specific item subclass to\n  create each position member. Don't create QCPItemPositions with \\b new yourself, because they\n  won't be registered with the item properly.\n  \n  \\see createAnchor\n*/\nQCPItemPosition *QCPAbstractItem::createPosition(const QString &name)\n{\n  if (hasAnchor(name))\n    qDebug() << Q_FUNC_INFO << \"anchor/position with name exists already:\" << name;\n  QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);\n  mPositions.append(newPosition);\n  mAnchors.append(newPosition); // every position is also an anchor\n  newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);\n  newPosition->setType(QCPItemPosition::ptPlotCoords);\n  if (mParentPlot->axisRect())\n    newPosition->setAxisRect(mParentPlot->axisRect());\n  newPosition->setCoords(0, 0);\n  return newPosition;\n}\n\n/*! \\internal\n\n  Creates a QCPItemAnchor, registers it with this item and returns a pointer to it. The specified\n  \\a name must be a unique string that is usually identical to the variable name of the anchor\n  member (This is needed to provide the name based \\ref anchor access to anchors).\n  \n  The \\a anchorId must be a number identifying the created anchor. It is recommended to create an\n  enum (e.g. \"AnchorIndex\") for this on each item that uses anchors. This id is used by the anchor\n  to identify itself when it calls QCPAbstractItem::anchorPixelPosition. That function then returns\n  the correct pixel coordinates for the passed anchor id.\n  \n  Don't delete anchors created by this function manually, as the item will take care of it.\n  \n  Use this function in the constructor (initialization list) of the specific item subclass to\n  create each anchor member. Don't create QCPItemAnchors with \\b new yourself, because then they\n  won't be registered with the item properly.\n  \n  \\see createPosition\n*/\nQCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId)\n{\n  if (hasAnchor(name))\n    qDebug() << Q_FUNC_INFO << \"anchor/position with name exists already:\" << name;\n  QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId);\n  mAnchors.append(newAnchor);\n  return newAnchor;\n}\n\n/* inherits documentation from base class */\nvoid QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)\n{\n  Q_UNUSED(event)\n  Q_UNUSED(details)\n  if (mSelectable)\n  {\n    bool selBefore = mSelected;\n    setSelected(additive ? !mSelected : true);\n    if (selectionStateChanged)\n      *selectionStateChanged = mSelected != selBefore;\n  }\n}\n\n/* inherits documentation from base class */\nvoid QCPAbstractItem::deselectEvent(bool *selectionStateChanged)\n{\n  if (mSelectable)\n  {\n    bool selBefore = mSelected;\n    setSelected(false);\n    if (selectionStateChanged)\n      *selectionStateChanged = mSelected != selBefore;\n  }\n}\n\n/* inherits documentation from base class */\nQCP::Interaction QCPAbstractItem::selectionCategory() const\n{\n  return QCP::iSelectItems;\n}\n/* end of 'src/item.cpp' */\n\n\n/* including file 'src/core.cpp'             */\n/* modified 2021-03-29T02:30:44, size 127198 */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCustomPlot\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCustomPlot\n  \n  \\brief The central class of the library. This is the QWidget which displays the plot and\n  interacts with the user.\n  \n  For tutorials on how to use QCustomPlot, see the website\\n\n  http://www.qcustomplot.com/\n*/\n\n/* start of documentation of inline functions */\n\n/*! \\fn QCPSelectionRect *QCustomPlot::selectionRect() const\n  \n  Allows access to the currently used QCPSelectionRect instance (or subclass thereof), that is used\n  to handle and draw selection rect interactions (see \\ref setSelectionRectMode).\n  \n  \\see setSelectionRect\n*/\n\n/*! \\fn QCPLayoutGrid *QCustomPlot::plotLayout() const\n  \n  Returns the top level layout of this QCustomPlot instance. It is a \\ref QCPLayoutGrid, initially containing just\n  one cell with the main QCPAxisRect inside.\n*/\n\n/* end of documentation of inline functions */\n/* start of documentation of signals */\n\n/*! \\fn void QCustomPlot::mouseDoubleClick(QMouseEvent *event)\n\n  This signal is emitted when the QCustomPlot receives a mouse double click event.\n*/\n\n/*! \\fn void QCustomPlot::mousePress(QMouseEvent *event)\n\n  This signal is emitted when the QCustomPlot receives a mouse press event.\n  \n  It is emitted before QCustomPlot handles any other mechanism like range dragging. So a slot\n  connected to this signal can still influence the behaviour e.g. with \\ref QCPAxisRect::setRangeDrag or \\ref\n  QCPAxisRect::setRangeDragAxes.\n*/\n\n/*! \\fn void QCustomPlot::mouseMove(QMouseEvent *event)\n\n  This signal is emitted when the QCustomPlot receives a mouse move event.\n  \n  It is emitted before QCustomPlot handles any other mechanism like range dragging. So a slot\n  connected to this signal can still influence the behaviour e.g. with \\ref QCPAxisRect::setRangeDrag or \\ref\n  QCPAxisRect::setRangeDragAxes.\n  \n  \\warning It is discouraged to change the drag-axes with \\ref QCPAxisRect::setRangeDragAxes here,\n  because the dragging starting point was saved the moment the mouse was pressed. Thus it only has\n  a meaning for the range drag axes that were set at that moment. If you want to change the drag\n  axes, consider doing this in the \\ref mousePress signal instead.\n*/\n\n/*! \\fn void QCustomPlot::mouseRelease(QMouseEvent *event)\n\n  This signal is emitted when the QCustomPlot receives a mouse release event.\n  \n  It is emitted before QCustomPlot handles any other mechanisms like object selection. So a\n  slot connected to this signal can still influence the behaviour e.g. with \\ref setInteractions or\n  \\ref QCPAbstractPlottable::setSelectable.\n*/\n\n/*! \\fn void QCustomPlot::mouseWheel(QMouseEvent *event)\n\n  This signal is emitted when the QCustomPlot receives a mouse wheel event.\n  \n  It is emitted before QCustomPlot handles any other mechanisms like range zooming. So a slot\n  connected to this signal can still influence the behaviour e.g. with \\ref QCPAxisRect::setRangeZoom, \\ref\n  QCPAxisRect::setRangeZoomAxes or \\ref QCPAxisRect::setRangeZoomFactor.\n*/\n\n/*! \\fn void QCustomPlot::plottableClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event)\n\n  This signal is emitted when a plottable is clicked.\n\n  \\a event is the mouse event that caused the click and \\a plottable is the plottable that received\n  the click. The parameter \\a dataIndex indicates the data point that was closest to the click\n  position.\n\n  \\see plottableDoubleClick\n*/\n\n/*! \\fn void QCustomPlot::plottableDoubleClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event)\n\n  This signal is emitted when a plottable is double clicked.\n\n  \\a event is the mouse event that caused the click and \\a plottable is the plottable that received\n  the click. The parameter \\a dataIndex indicates the data point that was closest to the click\n  position.\n\n  \\see plottableClick\n*/\n\n/*! \\fn void QCustomPlot::itemClick(QCPAbstractItem *item, QMouseEvent *event)\n  \n  This signal is emitted when an item is clicked.\n\n  \\a event is the mouse event that caused the click and \\a item is the item that received the\n  click.\n  \n  \\see itemDoubleClick\n*/\n\n/*! \\fn void QCustomPlot::itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event)\n  \n  This signal is emitted when an item is double clicked.\n  \n  \\a event is the mouse event that caused the click and \\a item is the item that received the\n  click.\n  \n  \\see itemClick\n*/\n\n/*! \\fn void QCustomPlot::axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)\n  \n  This signal is emitted when an axis is clicked.\n  \n  \\a event is the mouse event that caused the click, \\a axis is the axis that received the click and\n  \\a part indicates the part of the axis that was clicked.\n  \n  \\see axisDoubleClick\n*/\n\n/*! \\fn void QCustomPlot::axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)\n\n  This signal is emitted when an axis is double clicked.\n  \n  \\a event is the mouse event that caused the click, \\a axis is the axis that received the click and\n  \\a part indicates the part of the axis that was clicked.\n  \n  \\see axisClick\n*/\n\n/*! \\fn void QCustomPlot::legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)\n\n  This signal is emitted when a legend (item) is clicked.\n  \n  \\a event is the mouse event that caused the click, \\a legend is the legend that received the\n  click and \\a item is the legend item that received the click. If only the legend and no item is\n  clicked, \\a item is \\c nullptr. This happens for a click inside the legend padding or the space\n  between two items.\n  \n  \\see legendDoubleClick\n*/\n\n/*! \\fn void QCustomPlot::legendDoubleClick(QCPLegend *legend,  QCPAbstractLegendItem *item, QMouseEvent *event)\n\n  This signal is emitted when a legend (item) is double clicked.\n  \n  \\a event is the mouse event that caused the click, \\a legend is the legend that received the\n  click and \\a item is the legend item that received the click. If only the legend and no item is\n  clicked, \\a item is \\c nullptr. This happens for a click inside the legend padding or the space\n  between two items.\n  \n  \\see legendClick\n*/\n\n/*! \\fn void QCustomPlot::selectionChangedByUser()\n  \n  This signal is emitted after the user has changed the selection in the QCustomPlot, e.g. by\n  clicking. It is not emitted when the selection state of an object has changed programmatically by\n  a direct call to <tt>setSelected()</tt>/<tt>setSelection()</tt> on an object or by calling \\ref\n  deselectAll.\n  \n  In addition to this signal, selectable objects also provide individual signals, for example \\ref\n  QCPAxis::selectionChanged or \\ref QCPAbstractPlottable::selectionChanged. Note that those signals\n  are emitted even if the selection state is changed programmatically.\n  \n  See the documentation of \\ref setInteractions for details about the selection mechanism.\n  \n  \\see selectedPlottables, selectedGraphs, selectedItems, selectedAxes, selectedLegends\n*/\n\n/*! \\fn void QCustomPlot::beforeReplot()\n  \n  This signal is emitted immediately before a replot takes place (caused by a call to the slot \\ref\n  replot).\n  \n  It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them\n  replot synchronously, it won't cause an infinite recursion.\n  \n  \\see replot, afterReplot, afterLayout\n*/\n\n/*! \\fn void QCustomPlot::afterLayout()\n\n  This signal is emitted immediately after the layout step has been completed, which occurs right\n  before drawing the plot. This is typically during a call to \\ref replot, and in such cases this\n  signal is emitted in between the signals \\ref beforeReplot and \\ref afterReplot. Unlike those\n  signals however, this signal is also emitted during off-screen painting, such as when calling\n  \\ref toPixmap or \\ref savePdf.\n\n  The layout step queries all layouts and layout elements in the plot for their proposed size and\n  arranges the objects accordingly as preparation for the subsequent drawing step. Through this\n  signal, you have the opportunity to update certain things in your plot that depend crucially on\n  the exact dimensions/positioning of layout elements such as axes and axis rects.\n\n  \\warning However, changing any parameters of this QCustomPlot instance which would normally\n  affect the layouting (e.g. axis range order of magnitudes, tick label sizes, etc.) will not issue\n  a second run of the layout step. It will propagate directly to the draw step and may cause\n  graphical inconsistencies such as overlapping objects, if sizes or positions have changed.\n\n  \\see updateLayout, beforeReplot, afterReplot\n*/\n\n/*! \\fn void QCustomPlot::afterReplot()\n  \n  This signal is emitted immediately after a replot has taken place (caused by a call to the slot \\ref\n  replot).\n  \n  It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them\n  replot synchronously, it won't cause an infinite recursion.\n  \n  \\see replot, beforeReplot, afterLayout\n*/\n\n/* end of documentation of signals */\n/* start of documentation of public members */\n\n/*! \\var QCPAxis *QCustomPlot::xAxis\n\n  A pointer to the primary x Axis (bottom) of the main axis rect of the plot.\n  \n  QCustomPlot offers convenient pointers to the axes (\\ref xAxis, \\ref yAxis, \\ref xAxis2, \\ref\n  yAxis2) and the \\ref legend. They make it very easy working with plots that only have a single\n  axis rect and at most one axis at each axis rect side. If you use \\link thelayoutsystem the\n  layout system\\endlink to add multiple axis rects or multiple axes to one side, use the \\ref\n  QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the\n  default legend is removed due to manipulation of the layout system (e.g. by removing the main\n  axis rect), the corresponding pointers become \\c nullptr.\n  \n  If an axis convenience pointer is currently \\c nullptr and a new axis rect or a corresponding\n  axis is added in the place of the main axis rect, QCustomPlot resets the convenience pointers to\n  the according new axes. Similarly the \\ref legend convenience pointer will be reset if a legend\n  is added after the main legend was removed before.\n*/\n\n/*! \\var QCPAxis *QCustomPlot::yAxis\n\n  A pointer to the primary y Axis (left) of the main axis rect of the plot.\n  \n  QCustomPlot offers convenient pointers to the axes (\\ref xAxis, \\ref yAxis, \\ref xAxis2, \\ref\n  yAxis2) and the \\ref legend. They make it very easy working with plots that only have a single\n  axis rect and at most one axis at each axis rect side. If you use \\link thelayoutsystem the\n  layout system\\endlink to add multiple axis rects or multiple axes to one side, use the \\ref\n  QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the\n  default legend is removed due to manipulation of the layout system (e.g. by removing the main\n  axis rect), the corresponding pointers become \\c nullptr.\n  \n  If an axis convenience pointer is currently \\c nullptr and a new axis rect or a corresponding\n  axis is added in the place of the main axis rect, QCustomPlot resets the convenience pointers to\n  the according new axes. Similarly the \\ref legend convenience pointer will be reset if a legend\n  is added after the main legend was removed before.\n*/\n\n/*! \\var QCPAxis *QCustomPlot::xAxis2\n\n  A pointer to the secondary x Axis (top) of the main axis rect of the plot. Secondary axes are\n  invisible by default. Use QCPAxis::setVisible to change this (or use \\ref\n  QCPAxisRect::setupFullAxesBox).\n  \n  QCustomPlot offers convenient pointers to the axes (\\ref xAxis, \\ref yAxis, \\ref xAxis2, \\ref\n  yAxis2) and the \\ref legend. They make it very easy working with plots that only have a single\n  axis rect and at most one axis at each axis rect side. If you use \\link thelayoutsystem the\n  layout system\\endlink to add multiple axis rects or multiple axes to one side, use the \\ref\n  QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the\n  default legend is removed due to manipulation of the layout system (e.g. by removing the main\n  axis rect), the corresponding pointers become \\c nullptr.\n  \n  If an axis convenience pointer is currently \\c nullptr and a new axis rect or a corresponding\n  axis is added in the place of the main axis rect, QCustomPlot resets the convenience pointers to\n  the according new axes. Similarly the \\ref legend convenience pointer will be reset if a legend\n  is added after the main legend was removed before.\n*/\n\n/*! \\var QCPAxis *QCustomPlot::yAxis2\n\n  A pointer to the secondary y Axis (right) of the main axis rect of the plot. Secondary axes are\n  invisible by default. Use QCPAxis::setVisible to change this (or use \\ref\n  QCPAxisRect::setupFullAxesBox).\n  \n  QCustomPlot offers convenient pointers to the axes (\\ref xAxis, \\ref yAxis, \\ref xAxis2, \\ref\n  yAxis2) and the \\ref legend. They make it very easy working with plots that only have a single\n  axis rect and at most one axis at each axis rect side. If you use \\link thelayoutsystem the\n  layout system\\endlink to add multiple axis rects or multiple axes to one side, use the \\ref\n  QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the\n  default legend is removed due to manipulation of the layout system (e.g. by removing the main\n  axis rect), the corresponding pointers become \\c nullptr.\n  \n  If an axis convenience pointer is currently \\c nullptr and a new axis rect or a corresponding\n  axis is added in the place of the main axis rect, QCustomPlot resets the convenience pointers to\n  the according new axes. Similarly the \\ref legend convenience pointer will be reset if a legend\n  is added after the main legend was removed before.\n*/\n\n/*! \\var QCPLegend *QCustomPlot::legend\n\n  A pointer to the default legend of the main axis rect. The legend is invisible by default. Use\n  QCPLegend::setVisible to change this.\n  \n  QCustomPlot offers convenient pointers to the axes (\\ref xAxis, \\ref yAxis, \\ref xAxis2, \\ref\n  yAxis2) and the \\ref legend. They make it very easy working with plots that only have a single\n  axis rect and at most one axis at each axis rect side. If you use \\link thelayoutsystem the\n  layout system\\endlink to add multiple legends to the plot, use the layout system interface to\n  access the new legend. For example, legends can be placed inside an axis rect's \\ref\n  QCPAxisRect::insetLayout \"inset layout\", and must then also be accessed via the inset layout. If\n  the default legend is removed due to manipulation of the layout system (e.g. by removing the main\n  axis rect), the corresponding pointer becomes \\c nullptr.\n  \n  If an axis convenience pointer is currently \\c nullptr and a new axis rect or a corresponding\n  axis is added in the place of the main axis rect, QCustomPlot resets the convenience pointers to\n  the according new axes. Similarly the \\ref legend convenience pointer will be reset if a legend\n  is added after the main legend was removed before.\n*/\n\n/* end of documentation of public members */\n\n/*!\n  Constructs a QCustomPlot and sets reasonable default values.\n*/\nQCustomPlot::QCustomPlot(QWidget *parent) :\n  QWidget(parent),\n  xAxis(nullptr),\n  yAxis(nullptr),\n  xAxis2(nullptr),\n  yAxis2(nullptr),\n  legend(nullptr),\n  mBufferDevicePixelRatio(1.0), // will be adapted to primary screen below\n  mPlotLayout(nullptr),\n  mAutoAddPlottableToLegend(true),\n  mAntialiasedElements(QCP::aeNone),\n  mNotAntialiasedElements(QCP::aeNone),\n  mInteractions(QCP::iNone),\n  mSelectionTolerance(8),\n  mNoAntialiasingOnDrag(false),\n  mBackgroundBrush(Qt::white, Qt::SolidPattern),\n  mBackgroundScaled(true),\n  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),\n  mCurrentLayer(nullptr),\n  mPlottingHints(QCP::phCacheLabels|QCP::phImmediateRefresh),\n  mMultiSelectModifier(Qt::ControlModifier),\n  mSelectionRectMode(QCP::srmNone),\n  mSelectionRect(nullptr),\n  mOpenGl(false),\n  mMouseHasMoved(false),\n  mMouseEventLayerable(nullptr),\n  mMouseSignalLayerable(nullptr),\n  mReplotting(false),\n  mReplotQueued(false),\n  mReplotTime(0),\n  mReplotTimeAverage(0),\n  mOpenGlMultisamples(16),\n  mOpenGlAntialiasedElementsBackup(QCP::aeNone),\n  mOpenGlCacheLabelsBackup(true)\n{\n  setAttribute(Qt::WA_NoMousePropagation);\n  setAttribute(Qt::WA_OpaquePaintEvent);\n  setFocusPolicy(Qt::ClickFocus);\n  setMouseTracking(true);\n  QLocale currentLocale = locale();\n  currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);\n  setLocale(currentLocale);\n#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED\n#  ifdef QCP_DEVICEPIXELRATIO_FLOAT\n  setBufferDevicePixelRatio(QWidget::devicePixelRatioF());\n#  else\n  setBufferDevicePixelRatio(QWidget::devicePixelRatio());\n#  endif\n#endif\n  \n  mOpenGlAntialiasedElementsBackup = mAntialiasedElements;\n  mOpenGlCacheLabelsBackup = mPlottingHints.testFlag(QCP::phCacheLabels);\n  // create initial layers:\n  mLayers.append(new QCPLayer(this, QLatin1String(\"background\")));\n  mLayers.append(new QCPLayer(this, QLatin1String(\"grid\")));\n  mLayers.append(new QCPLayer(this, QLatin1String(\"main\")));\n  mLayers.append(new QCPLayer(this, QLatin1String(\"axes\")));\n  mLayers.append(new QCPLayer(this, QLatin1String(\"legend\")));\n  mLayers.append(new QCPLayer(this, QLatin1String(\"overlay\")));\n  updateLayerIndices();\n  setCurrentLayer(QLatin1String(\"main\"));\n  layer(QLatin1String(\"overlay\"))->setMode(QCPLayer::lmBuffered);\n  \n  // create initial layout, axis rect and legend:\n  mPlotLayout = new QCPLayoutGrid;\n  mPlotLayout->initializeParentPlot(this);\n  mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry\n  mPlotLayout->setLayer(QLatin1String(\"main\"));\n  QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);\n  mPlotLayout->addElement(0, 0, defaultAxisRect);\n  xAxis = defaultAxisRect->axis(QCPAxis::atBottom);\n  yAxis = defaultAxisRect->axis(QCPAxis::atLeft);\n  xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);\n  yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);\n  legend = new QCPLegend;\n  legend->setVisible(false);\n  defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop);\n  defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));\n  \n  defaultAxisRect->setLayer(QLatin1String(\"background\"));\n  xAxis->setLayer(QLatin1String(\"axes\"));\n  yAxis->setLayer(QLatin1String(\"axes\"));\n  xAxis2->setLayer(QLatin1String(\"axes\"));\n  yAxis2->setLayer(QLatin1String(\"axes\"));\n  xAxis->grid()->setLayer(QLatin1String(\"grid\"));\n  yAxis->grid()->setLayer(QLatin1String(\"grid\"));\n  xAxis2->grid()->setLayer(QLatin1String(\"grid\"));\n  yAxis2->grid()->setLayer(QLatin1String(\"grid\"));\n  legend->setLayer(QLatin1String(\"legend\"));\n  \n  // create selection rect instance:\n  mSelectionRect = new QCPSelectionRect(this);\n  mSelectionRect->setLayer(QLatin1String(\"overlay\"));\n  \n  setViewport(rect()); // needs to be called after mPlotLayout has been created\n  \n  replot(rpQueuedReplot);\n}\n\nQCustomPlot::~QCustomPlot()\n{\n  clearPlottables();\n  clearItems();\n\n  if (mPlotLayout)\n  {\n    delete mPlotLayout;\n    mPlotLayout = nullptr;\n  }\n  \n  mCurrentLayer = nullptr;\n  qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the last layer to be removed\n  mLayers.clear();\n}\n\n/*!\n  Sets which elements are forcibly drawn antialiased as an \\a or combination of QCP::AntialiasedElement.\n  \n  This overrides the antialiasing settings for whole element groups, normally controlled with the\n  \\a setAntialiasing function on the individual elements. If an element is neither specified in\n  \\ref setAntialiasedElements nor in \\ref setNotAntialiasedElements, the antialiasing setting on\n  each individual element instance is used.\n  \n  For example, if \\a antialiasedElements contains \\ref QCP::aePlottables, all plottables will be\n  drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set\n  to.\n  \n  if an element in \\a antialiasedElements is already set in \\ref setNotAntialiasedElements, it is\n  removed from there.\n  \n  \\see setNotAntialiasedElements\n*/\nvoid QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)\n{\n  mAntialiasedElements = antialiasedElements;\n  \n  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:\n  if ((mNotAntialiasedElements & mAntialiasedElements) != 0)\n    mNotAntialiasedElements |= ~mAntialiasedElements;\n}\n\n/*!\n  Sets whether the specified \\a antialiasedElement is forcibly drawn antialiased.\n  \n  See \\ref setAntialiasedElements for details.\n  \n  \\see setNotAntialiasedElement\n*/\nvoid QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled)\n{\n  if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))\n    mAntialiasedElements &= ~antialiasedElement;\n  else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))\n    mAntialiasedElements |= antialiasedElement;\n  \n  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:\n  if ((mNotAntialiasedElements & mAntialiasedElements) != 0)\n    mNotAntialiasedElements |= ~mAntialiasedElements;\n}\n\n/*!\n  Sets which elements are forcibly drawn not antialiased as an \\a or combination of\n  QCP::AntialiasedElement.\n  \n  This overrides the antialiasing settings for whole element groups, normally controlled with the\n  \\a setAntialiasing function on the individual elements. If an element is neither specified in\n  \\ref setAntialiasedElements nor in \\ref setNotAntialiasedElements, the antialiasing setting on\n  each individual element instance is used.\n  \n  For example, if \\a notAntialiasedElements contains \\ref QCP::aePlottables, no plottables will be\n  drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set\n  to.\n  \n  if an element in \\a notAntialiasedElements is already set in \\ref setAntialiasedElements, it is\n  removed from there.\n  \n  \\see setAntialiasedElements\n*/\nvoid QCustomPlot::setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)\n{\n  mNotAntialiasedElements = notAntialiasedElements;\n  \n  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:\n  if ((mNotAntialiasedElements & mAntialiasedElements) != 0)\n    mAntialiasedElements |= ~mNotAntialiasedElements;\n}\n\n/*!\n  Sets whether the specified \\a notAntialiasedElement is forcibly drawn not antialiased.\n  \n  See \\ref setNotAntialiasedElements for details.\n  \n  \\see setAntialiasedElement\n*/\nvoid QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled)\n{\n  if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))\n    mNotAntialiasedElements &= ~notAntialiasedElement;\n  else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))\n    mNotAntialiasedElements |= notAntialiasedElement;\n  \n  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:\n  if ((mNotAntialiasedElements & mAntialiasedElements) != 0)\n    mAntialiasedElements |= ~mNotAntialiasedElements;\n}\n\n/*!\n  If set to true, adding a plottable (e.g. a graph) to the QCustomPlot automatically also adds the\n  plottable to the legend (QCustomPlot::legend).\n  \n  \\see addGraph, QCPLegend::addItem\n*/\nvoid QCustomPlot::setAutoAddPlottableToLegend(bool on)\n{\n  mAutoAddPlottableToLegend = on;\n}\n\n/*!\n  Sets the possible interactions of this QCustomPlot as an or-combination of \\ref QCP::Interaction\n  enums. There are the following types of interactions:\n  \n  <b>Axis range manipulation</b> is controlled via \\ref QCP::iRangeDrag and \\ref QCP::iRangeZoom. When the\n  respective interaction is enabled, the user may drag axes ranges and zoom with the mouse wheel.\n  For details how to control which axes the user may drag/zoom and in what orientations, see \\ref\n  QCPAxisRect::setRangeDrag, \\ref QCPAxisRect::setRangeZoom, \\ref QCPAxisRect::setRangeDragAxes,\n  \\ref QCPAxisRect::setRangeZoomAxes.\n  \n  <b>Plottable data selection</b> is controlled by \\ref QCP::iSelectPlottables. If \\ref\n  QCP::iSelectPlottables is set, the user may select plottables (graphs, curves, bars,...) and\n  their data by clicking on them or in their vicinity (\\ref setSelectionTolerance). Whether the\n  user can actually select a plottable and its data can further be restricted with the \\ref\n  QCPAbstractPlottable::setSelectable method on the specific plottable. For details, see the\n  special page about the \\ref dataselection \"data selection mechanism\". To retrieve a list of all\n  currently selected plottables, call \\ref selectedPlottables. If you're only interested in\n  QCPGraphs, you may use the convenience function \\ref selectedGraphs.\n  \n  <b>Item selection</b> is controlled by \\ref QCP::iSelectItems. If \\ref QCP::iSelectItems is set, the user\n  may select items (QCPItemLine, QCPItemText,...) by clicking on them or in their vicinity. To find\n  out whether a specific item is selected, call QCPAbstractItem::selected(). To retrieve a list of\n  all currently selected items, call \\ref selectedItems.\n  \n  <b>Axis selection</b> is controlled with \\ref QCP::iSelectAxes. If \\ref QCP::iSelectAxes is set, the user\n  may select parts of the axes by clicking on them. What parts exactly (e.g. Axis base line, tick\n  labels, axis label) are selectable can be controlled via \\ref QCPAxis::setSelectableParts for\n  each axis. To retrieve a list of all axes that currently contain selected parts, call \\ref\n  selectedAxes. Which parts of an axis are selected, can be retrieved with QCPAxis::selectedParts().\n  \n  <b>Legend selection</b> is controlled with \\ref QCP::iSelectLegend. If this is set, the user may\n  select the legend itself or individual items by clicking on them. What parts exactly are\n  selectable can be controlled via \\ref QCPLegend::setSelectableParts. To find out whether the\n  legend or any of its child items are selected, check the value of QCPLegend::selectedParts. To\n  find out which child items are selected, call \\ref QCPLegend::selectedItems.\n  \n  <b>All other selectable elements</b> The selection of all other selectable objects (e.g.\n  QCPTextElement, or your own layerable subclasses) is controlled with \\ref QCP::iSelectOther. If set, the\n  user may select those objects by clicking on them. To find out which are currently selected, you\n  need to check their selected state explicitly.\n  \n  If the selection state has changed by user interaction, the \\ref selectionChangedByUser signal is\n  emitted. Each selectable object additionally emits an individual selectionChanged signal whenever\n  their selection state has changed, i.e. not only by user interaction.\n  \n  To allow multiple objects to be selected by holding the selection modifier (\\ref\n  setMultiSelectModifier), set the flag \\ref QCP::iMultiSelect.\n  \n  \\note In addition to the selection mechanism presented here, QCustomPlot always emits\n  corresponding signals, when an object is clicked or double clicked. see \\ref plottableClick and\n  \\ref plottableDoubleClick for example.\n  \n  \\see setInteraction, setSelectionTolerance\n*/\nvoid QCustomPlot::setInteractions(const QCP::Interactions &interactions)\n{\n  mInteractions = interactions;\n}\n\n/*!\n  Sets the single \\a interaction of this QCustomPlot to \\a enabled.\n  \n  For details about the interaction system, see \\ref setInteractions.\n  \n  \\see setInteractions\n*/\nvoid QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled)\n{\n  if (!enabled && mInteractions.testFlag(interaction))\n    mInteractions &= ~interaction;\n  else if (enabled && !mInteractions.testFlag(interaction))\n    mInteractions |= interaction;\n}\n\n/*!\n  Sets the tolerance that is used to decide whether a click selects an object (e.g. a plottable) or\n  not.\n  \n  If the user clicks in the vicinity of the line of e.g. a QCPGraph, it's only regarded as a\n  potential selection when the minimum distance between the click position and the graph line is\n  smaller than \\a pixels. Objects that are defined by an area (e.g. QCPBars) only react to clicks\n  directly inside the area and ignore this selection tolerance. In other words, it only has meaning\n  for parts of objects that are too thin to exactly hit with a click and thus need such a\n  tolerance.\n  \n  \\see setInteractions, QCPLayerable::selectTest\n*/\nvoid QCustomPlot::setSelectionTolerance(int pixels)\n{\n  mSelectionTolerance = pixels;\n}\n\n/*!\n  Sets whether antialiasing is disabled for this QCustomPlot while the user is dragging axes\n  ranges. If many objects, especially plottables, are drawn antialiased, this greatly improves\n  performance during dragging. Thus it creates a more responsive user experience. As soon as the\n  user stops dragging, the last replot is done with normal antialiasing, to restore high image\n  quality.\n  \n  \\see setAntialiasedElements, setNotAntialiasedElements\n*/\nvoid QCustomPlot::setNoAntialiasingOnDrag(bool enabled)\n{\n  mNoAntialiasingOnDrag = enabled;\n}\n\n/*!\n  Sets the plotting hints for this QCustomPlot instance as an \\a or combination of QCP::PlottingHint.\n  \n  \\see setPlottingHint\n*/\nvoid QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints)\n{\n  mPlottingHints = hints;\n}\n\n/*!\n  Sets the specified plotting \\a hint to \\a enabled.\n  \n  \\see setPlottingHints\n*/\nvoid QCustomPlot::setPlottingHint(QCP::PlottingHint hint, bool enabled)\n{\n  QCP::PlottingHints newHints = mPlottingHints;\n  if (!enabled)\n    newHints &= ~hint;\n  else\n    newHints |= hint;\n  \n  if (newHints != mPlottingHints)\n    setPlottingHints(newHints);\n}\n\n/*!\n  Sets the keyboard modifier that will be recognized as multi-select-modifier.\n  \n  If \\ref QCP::iMultiSelect is specified in \\ref setInteractions, the user may select multiple\n  objects (or data points) by clicking on them one after the other while holding down \\a modifier.\n  \n  By default the multi-select-modifier is set to Qt::ControlModifier.\n  \n  \\see setInteractions\n*/\nvoid QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier)\n{\n  mMultiSelectModifier = modifier;\n}\n\n/*!\n  Sets how QCustomPlot processes mouse click-and-drag interactions by the user.\n\n  If \\a mode is \\ref QCP::srmNone, the mouse drag is forwarded to the underlying objects. For\n  example, QCPAxisRect may process a mouse drag by dragging axis ranges, see \\ref\n  QCPAxisRect::setRangeDrag. If \\a mode is not \\ref QCP::srmNone, the current selection rect (\\ref\n  selectionRect) becomes activated and allows e.g. rect zooming and data point selection.\n  \n  If you wish to provide your user both with axis range dragging and data selection/range zooming,\n  use this method to switch between the modes just before the interaction is processed, e.g. in\n  reaction to the \\ref mousePress or \\ref mouseMove signals. For example you could check whether\n  the user is holding a certain keyboard modifier, and then decide which \\a mode shall be set.\n  \n  If a selection rect interaction is currently active, and \\a mode is set to \\ref QCP::srmNone, the\n  interaction is canceled (\\ref QCPSelectionRect::cancel). Switching between any of the other modes\n  will keep the selection rect active. Upon completion of the interaction, the behaviour is as\n  defined by the currently set \\a mode, not the mode that was set when the interaction started.\n  \n  \\see setInteractions, setSelectionRect, QCPSelectionRect\n*/\nvoid QCustomPlot::setSelectionRectMode(QCP::SelectionRectMode mode)\n{\n  if (mSelectionRect)\n  {\n    if (mode == QCP::srmNone)\n      mSelectionRect->cancel(); // when switching to none, we immediately want to abort a potentially active selection rect\n    \n    // disconnect old connections:\n    if (mSelectionRectMode == QCP::srmSelect)\n      disconnect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*)));\n    else if (mSelectionRectMode == QCP::srmZoom)\n      disconnect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*)));\n    \n    // establish new ones:\n    if (mode == QCP::srmSelect)\n      connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*)));\n    else if (mode == QCP::srmZoom)\n      connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*)));\n  }\n  \n  mSelectionRectMode = mode;\n}\n\n/*!\n  Sets the \\ref QCPSelectionRect instance that QCustomPlot will use if \\a mode is not \\ref\n  QCP::srmNone and the user performs a click-and-drag interaction. QCustomPlot takes ownership of\n  the passed \\a selectionRect. It can be accessed later via \\ref selectionRect.\n  \n  This method is useful if you wish to replace the default QCPSelectionRect instance with an\n  instance of a QCPSelectionRect subclass, to introduce custom behaviour of the selection rect.\n  \n  \\see setSelectionRectMode\n*/\nvoid QCustomPlot::setSelectionRect(QCPSelectionRect *selectionRect)\n{\n  delete mSelectionRect;\n  \n  mSelectionRect = selectionRect;\n  \n  if (mSelectionRect)\n  {\n    // establish connections with new selection rect:\n    if (mSelectionRectMode == QCP::srmSelect)\n      connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*)));\n    else if (mSelectionRectMode == QCP::srmZoom)\n      connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*)));\n  }\n}\n\n/*!\n  \\warning This is still an experimental feature and its performance depends on the system that it\n  runs on. Having multiple QCustomPlot widgets in one application with enabled OpenGL rendering\n  might cause context conflicts on some systems.\n  \n  This method allows to enable OpenGL plot rendering, for increased plotting performance of\n  graphically demanding plots (thick lines, translucent fills, etc.).\n\n  If \\a enabled is set to true, QCustomPlot will try to initialize OpenGL and, if successful,\n  continue plotting with hardware acceleration. The parameter \\a multisampling controls how many\n  samples will be used per pixel, it essentially controls the antialiasing quality. If \\a\n  multisampling is set too high for the current graphics hardware, the maximum allowed value will\n  be used.\n\n  You can test whether switching to OpenGL rendering was successful by checking whether the\n  according getter \\a QCustomPlot::openGl() returns true. If the OpenGL initialization fails,\n  rendering continues with the regular software rasterizer, and an according qDebug output is\n  generated.\n\n  If switching to OpenGL was successful, this method disables label caching (\\ref setPlottingHint\n  \"setPlottingHint(QCP::phCacheLabels, false)\") and turns on QCustomPlot's antialiasing override\n  for all elements (\\ref setAntialiasedElements \"setAntialiasedElements(QCP::aeAll)\"), leading to a\n  higher quality output. The antialiasing override allows for pixel-grid aligned drawing in the\n  OpenGL paint device. As stated before, in OpenGL rendering the actual antialiasing of the plot is\n  controlled with \\a multisampling. If \\a enabled is set to false, the antialiasing/label caching\n  settings are restored to what they were before OpenGL was enabled, if they weren't altered in the\n  meantime.\n\n  \\note OpenGL support is only enabled if QCustomPlot is compiled with the macro \\c QCUSTOMPLOT_USE_OPENGL\n  defined. This define must be set before including the QCustomPlot header both during compilation\n  of the QCustomPlot library as well as when compiling your application. It is best to just include\n  the line <tt>DEFINES += QCUSTOMPLOT_USE_OPENGL</tt> in the respective qmake project files.\n  \\note If you are using a Qt version before 5.0, you must also add the module \"opengl\" to your \\c\n  QT variable in the qmake project files. For Qt versions 5.0 and higher, QCustomPlot switches to a\n  newer OpenGL interface which is already in the \"gui\" module.\n*/\nvoid QCustomPlot::setOpenGl(bool enabled, int multisampling)\n{\n  mOpenGlMultisamples = qMax(0, multisampling);\n#ifdef QCUSTOMPLOT_USE_OPENGL\n  mOpenGl = enabled;\n  if (mOpenGl)\n  {\n    if (setupOpenGl())\n    {\n      // backup antialiasing override and labelcaching setting so we can restore upon disabling OpenGL\n      mOpenGlAntialiasedElementsBackup = mAntialiasedElements;\n      mOpenGlCacheLabelsBackup = mPlottingHints.testFlag(QCP::phCacheLabels);\n      // set antialiasing override to antialias all (aligns gl pixel grid properly), and disable label caching (would use software rasterizer for pixmap caches):\n      setAntialiasedElements(QCP::aeAll);\n      setPlottingHint(QCP::phCacheLabels, false);\n    } else\n    {\n      qDebug() << Q_FUNC_INFO << \"Failed to enable OpenGL, continuing plotting without hardware acceleration.\";\n      mOpenGl = false;\n    }\n  } else\n  {\n    // restore antialiasing override and labelcaching to what it was before enabling OpenGL, if nobody changed it in the meantime:\n    if (mAntialiasedElements == QCP::aeAll)\n      setAntialiasedElements(mOpenGlAntialiasedElementsBackup);\n    if (!mPlottingHints.testFlag(QCP::phCacheLabels))\n      setPlottingHint(QCP::phCacheLabels, mOpenGlCacheLabelsBackup);\n    freeOpenGl();\n  }\n  // recreate all paint buffers:\n  mPaintBuffers.clear();\n  setupPaintBuffers();\n#else\n  Q_UNUSED(enabled)\n  qDebug() << Q_FUNC_INFO << \"QCustomPlot can't use OpenGL because QCUSTOMPLOT_USE_OPENGL was not defined during compilation (add 'DEFINES += QCUSTOMPLOT_USE_OPENGL' to your qmake .pro file)\";\n#endif\n}\n\n/*!\n  Sets the viewport of this QCustomPlot. Usually users of QCustomPlot don't need to change the\n  viewport manually.\n\n  The viewport is the area in which the plot is drawn. All mechanisms, e.g. margin calculation take\n  the viewport to be the outer border of the plot. The viewport normally is the rect() of the\n  QCustomPlot widget, i.e. a rect with top left (0, 0) and size of the QCustomPlot widget.\n\n  Don't confuse the viewport with the axis rect (QCustomPlot::axisRect). An axis rect is typically\n  an area enclosed by four axes, where the graphs/plottables are drawn in. The viewport is larger\n  and contains also the axes themselves, their tick numbers, their labels, or even additional axis\n  rects, color scales and other layout elements.\n\n  This function is used to allow arbitrary size exports with \\ref toPixmap, \\ref savePng, \\ref\n  savePdf, etc. by temporarily changing the viewport size.\n*/\nvoid QCustomPlot::setViewport(const QRect &rect)\n{\n  mViewport = rect;\n  if (mPlotLayout)\n    mPlotLayout->setOuterRect(mViewport);\n}\n\n/*!\n  Sets the device pixel ratio used by the paint buffers of this QCustomPlot instance.\n\n  Normally, this doesn't need to be set manually, because it is initialized with the regular \\a\n  QWidget::devicePixelRatio which is configured by Qt to fit the display device (e.g. 1 for normal\n  displays, 2 for High-DPI displays).\n\n  Device pixel ratios are supported by Qt only for Qt versions since 5.4. If this method is called\n  when QCustomPlot is being used with older Qt versions, outputs an according qDebug message and\n  leaves the internal buffer device pixel ratio at 1.0.\n*/\nvoid QCustomPlot::setBufferDevicePixelRatio(double ratio)\n{\n  if (!qFuzzyCompare(ratio, mBufferDevicePixelRatio))\n  {\n#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED\n    mBufferDevicePixelRatio = ratio;\n    foreach (QSharedPointer<QCPAbstractPaintBuffer> buffer, mPaintBuffers)\n      buffer->setDevicePixelRatio(mBufferDevicePixelRatio);\n    // Note: axis label cache has devicePixelRatio as part of cache hash, so no need to manually clear cache here\n#else\n    qDebug() << Q_FUNC_INFO << \"Device pixel ratios not supported for Qt versions before 5.4\";\n    mBufferDevicePixelRatio = 1.0;\n#endif\n  }\n}\n\n/*!\n  Sets \\a pm as the viewport background pixmap (see \\ref setViewport). The pixmap is always drawn\n  below all other objects in the plot.\n\n  For cases where the provided pixmap doesn't have the same size as the viewport, scaling can be\n  enabled with \\ref setBackgroundScaled and the scaling mode (whether and how the aspect ratio is\n  preserved) can be set with \\ref setBackgroundScaledMode. To set all these options in one call,\n  consider using the overloaded version of this function.\n  \n  If a background brush was set with \\ref setBackground(const QBrush &brush), the viewport will\n  first be filled with that brush, before drawing the background pixmap. This can be useful for\n  background pixmaps with translucent areas.\n\n  \\see setBackgroundScaled, setBackgroundScaledMode\n*/\nvoid QCustomPlot::setBackground(const QPixmap &pm)\n{\n  mBackgroundPixmap = pm;\n  mScaledBackgroundPixmap = QPixmap();\n}\n\n/*!\n  Sets the background brush of the viewport (see \\ref setViewport).\n\n  Before drawing everything else, the background is filled with \\a brush. If a background pixmap\n  was set with \\ref setBackground(const QPixmap &pm), this brush will be used to fill the viewport\n  before the background pixmap is drawn. This can be useful for background pixmaps with translucent\n  areas.\n  \n  Set \\a brush to Qt::NoBrush or Qt::Transparent to leave background transparent. This can be\n  useful for exporting to image formats which support transparency, e.g. \\ref savePng.\n\n  \\see setBackgroundScaled, setBackgroundScaledMode\n*/\nvoid QCustomPlot::setBackground(const QBrush &brush)\n{\n  mBackgroundBrush = brush;\n}\n\n/*! \\overload\n  \n  Allows setting the background pixmap of the viewport, whether it shall be scaled and how it\n  shall be scaled in one call.\n\n  \\see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode\n*/\nvoid QCustomPlot::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)\n{\n  mBackgroundPixmap = pm;\n  mScaledBackgroundPixmap = QPixmap();\n  mBackgroundScaled = scaled;\n  mBackgroundScaledMode = mode;\n}\n\n/*!\n  Sets whether the viewport background pixmap shall be scaled to fit the viewport. If \\a scaled is\n  set to true, control whether and how the aspect ratio of the original pixmap is preserved with\n  \\ref setBackgroundScaledMode.\n  \n  Note that the scaled version of the original pixmap is buffered, so there is no performance\n  penalty on replots. (Except when the viewport dimensions are changed continuously.)\n  \n  \\see setBackground, setBackgroundScaledMode\n*/\nvoid QCustomPlot::setBackgroundScaled(bool scaled)\n{\n  mBackgroundScaled = scaled;\n}\n\n/*!\n  If scaling of the viewport background pixmap is enabled (\\ref setBackgroundScaled), use this\n  function to define whether and how the aspect ratio of the original pixmap is preserved.\n  \n  \\see setBackground, setBackgroundScaled\n*/\nvoid QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode)\n{\n  mBackgroundScaledMode = mode;\n}\n\n/*!\n  Returns the plottable with \\a index. If the index is invalid, returns \\c nullptr.\n  \n  There is an overloaded version of this function with no parameter which returns the last added\n  plottable, see QCustomPlot::plottable()\n  \n  \\see plottableCount\n*/\nQCPAbstractPlottable *QCustomPlot::plottable(int index)\n{\n  if (index >= 0 && index < mPlottables.size())\n  {\n    return mPlottables.at(index);\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"index out of bounds:\" << index;\n    return nullptr;\n  }\n}\n\n/*! \\overload\n  \n  Returns the last plottable that was added to the plot. If there are no plottables in the plot,\n  returns \\c nullptr.\n  \n  \\see plottableCount\n*/\nQCPAbstractPlottable *QCustomPlot::plottable()\n{\n  if (!mPlottables.isEmpty())\n  {\n    return mPlottables.last();\n  } else\n    return nullptr;\n}\n\n/*!\n  Removes the specified plottable from the plot and deletes it. If necessary, the corresponding\n  legend item is also removed from the default legend (QCustomPlot::legend).\n  \n  Returns true on success.\n  \n  \\see clearPlottables\n*/\nbool QCustomPlot::removePlottable(QCPAbstractPlottable *plottable)\n{\n  if (!mPlottables.contains(plottable))\n  {\n    qDebug() << Q_FUNC_INFO << \"plottable not in list:\" << reinterpret_cast<quintptr>(plottable);\n    return false;\n  }\n  \n  // remove plottable from legend:\n  plottable->removeFromLegend();\n  // special handling for QCPGraphs to maintain the simple graph interface:\n  if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))\n    mGraphs.removeOne(graph);\n  // remove plottable:\n  delete plottable;\n  mPlottables.removeOne(plottable);\n  return true;\n}\n\n/*! \\overload\n  \n  Removes and deletes the plottable by its \\a index.\n*/\nbool QCustomPlot::removePlottable(int index)\n{\n  if (index >= 0 && index < mPlottables.size())\n    return removePlottable(mPlottables[index]);\n  else\n  {\n    qDebug() << Q_FUNC_INFO << \"index out of bounds:\" << index;\n    return false;\n  }\n}\n\n/*!\n  Removes all plottables from the plot and deletes them. Corresponding legend items are also\n  removed from the default legend (QCustomPlot::legend).\n  \n  Returns the number of plottables removed.\n  \n  \\see removePlottable\n*/\nint QCustomPlot::clearPlottables()\n{\n  int c = mPlottables.size();\n  for (int i=c-1; i >= 0; --i)\n    removePlottable(mPlottables[i]);\n  return c;\n}\n\n/*!\n  Returns the number of currently existing plottables in the plot\n  \n  \\see plottable\n*/\nint QCustomPlot::plottableCount() const\n{\n  return mPlottables.size();\n}\n\n/*!\n  Returns a list of the selected plottables. If no plottables are currently selected, the list is empty.\n  \n  There is a convenience function if you're only interested in selected graphs, see \\ref selectedGraphs.\n  \n  \\see setInteractions, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelection\n*/\nQList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const\n{\n  QList<QCPAbstractPlottable*> result;\n  foreach (QCPAbstractPlottable *plottable, mPlottables)\n  {\n    if (plottable->selected())\n      result.append(plottable);\n  }\n  return result;\n}\n\n/*!\n  Returns any plottable at the pixel position \\a pos. Since it can capture all plottables, the\n  return type is the abstract base class of all plottables, QCPAbstractPlottable.\n  \n  For details, and if you wish to specify a certain plottable type (e.g. QCPGraph), see the\n  template method plottableAt<PlottableType>()\n  \n  \\see plottableAt<PlottableType>(), itemAt, layoutElementAt\n*/\nQCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable, int *dataIndex) const\n{\n  return plottableAt<QCPAbstractPlottable>(pos, onlySelectable, dataIndex);\n}\n\n/*!\n  Returns whether this QCustomPlot instance contains the \\a plottable.\n*/\nbool QCustomPlot::hasPlottable(QCPAbstractPlottable *plottable) const\n{\n  return mPlottables.contains(plottable);\n}\n\n/*!\n  Returns the graph with \\a index. If the index is invalid, returns \\c nullptr.\n  \n  There is an overloaded version of this function with no parameter which returns the last created\n  graph, see QCustomPlot::graph()\n  \n  \\see graphCount, addGraph\n*/\nQCPGraph *QCustomPlot::graph(int index) const\n{\n  if (index >= 0 && index < mGraphs.size())\n  {\n    return mGraphs.at(index);\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"index out of bounds:\" << index;\n    return nullptr;\n  }\n}\n\n/*! \\overload\n  \n  Returns the last graph, that was created with \\ref addGraph. If there are no graphs in the plot,\n  returns \\c nullptr.\n  \n  \\see graphCount, addGraph\n*/\nQCPGraph *QCustomPlot::graph() const\n{\n  if (!mGraphs.isEmpty())\n  {\n    return mGraphs.last();\n  } else\n    return nullptr;\n}\n\n/*!\n  Creates a new graph inside the plot. If \\a keyAxis and \\a valueAxis are left unspecified (0), the\n  bottom (xAxis) is used as key and the left (yAxis) is used as value axis. If specified, \\a\n  keyAxis and \\a valueAxis must reside in this QCustomPlot.\n  \n  \\a keyAxis will be used as key axis (typically \"x\") and \\a valueAxis as value axis (typically\n  \"y\") for the graph.\n  \n  Returns a pointer to the newly created graph, or \\c nullptr if adding the graph failed.\n  \n  \\see graph, graphCount, removeGraph, clearGraphs\n*/\nQCPGraph *QCustomPlot::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)\n{\n  if (!keyAxis) keyAxis = xAxis;\n  if (!valueAxis) valueAxis = yAxis;\n  if (!keyAxis || !valueAxis)\n  {\n    qDebug() << Q_FUNC_INFO << \"can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)\";\n    return nullptr;\n  }\n  if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)\n  {\n    qDebug() << Q_FUNC_INFO << \"passed keyAxis or valueAxis doesn't have this QCustomPlot as parent\";\n    return nullptr;\n  }\n  \n  QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);\n  newGraph->setName(QLatin1String(\"Graph \")+QString::number(mGraphs.size()));\n  return newGraph;\n}\n\n/*!\n  Removes the specified \\a graph from the plot and deletes it. If necessary, the corresponding\n  legend item is also removed from the default legend (QCustomPlot::legend). If any other graphs in\n  the plot have a channel fill set towards the removed graph, the channel fill property of those\n  graphs is reset to \\c nullptr (no channel fill).\n  \n  Returns true on success.\n  \n  \\see clearGraphs\n*/\nbool QCustomPlot::removeGraph(QCPGraph *graph)\n{\n  return removePlottable(graph);\n}\n\n/*! \\overload\n  \n  Removes and deletes the graph by its \\a index.\n*/\nbool QCustomPlot::removeGraph(int index)\n{\n  if (index >= 0 && index < mGraphs.size())\n    return removeGraph(mGraphs[index]);\n  else\n    return false;\n}\n\n/*!\n  Removes all graphs from the plot and deletes them. Corresponding legend items are also removed\n  from the default legend (QCustomPlot::legend).\n\n  Returns the number of graphs removed.\n  \n  \\see removeGraph\n*/\nint QCustomPlot::clearGraphs()\n{\n  int c = mGraphs.size();\n  for (int i=c-1; i >= 0; --i)\n    removeGraph(mGraphs[i]);\n  return c;\n}\n\n/*!\n  Returns the number of currently existing graphs in the plot\n  \n  \\see graph, addGraph\n*/\nint QCustomPlot::graphCount() const\n{\n  return mGraphs.size();\n}\n\n/*!\n  Returns a list of the selected graphs. If no graphs are currently selected, the list is empty.\n  \n  If you are not only interested in selected graphs but other plottables like QCPCurve, QCPBars,\n  etc., use \\ref selectedPlottables.\n  \n  \\see setInteractions, selectedPlottables, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelection\n*/\nQList<QCPGraph*> QCustomPlot::selectedGraphs() const\n{\n  QList<QCPGraph*> result;\n  foreach (QCPGraph *graph, mGraphs)\n  {\n    if (graph->selected())\n      result.append(graph);\n  }\n  return result;\n}\n\n/*!\n  Returns the item with \\a index. If the index is invalid, returns \\c nullptr.\n  \n  There is an overloaded version of this function with no parameter which returns the last added\n  item, see QCustomPlot::item()\n  \n  \\see itemCount\n*/\nQCPAbstractItem *QCustomPlot::item(int index) const\n{\n  if (index >= 0 && index < mItems.size())\n  {\n    return mItems.at(index);\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"index out of bounds:\" << index;\n    return nullptr;\n  }\n}\n\n/*! \\overload\n  \n  Returns the last item that was added to this plot. If there are no items in the plot,\n  returns \\c nullptr.\n  \n  \\see itemCount\n*/\nQCPAbstractItem *QCustomPlot::item() const\n{\n  if (!mItems.isEmpty())\n  {\n    return mItems.last();\n  } else\n    return nullptr;\n}\n\n/*!\n  Removes the specified item from the plot and deletes it.\n  \n  Returns true on success.\n  \n  \\see clearItems\n*/\nbool QCustomPlot::removeItem(QCPAbstractItem *item)\n{\n  if (mItems.contains(item))\n  {\n    delete item;\n    mItems.removeOne(item);\n    return true;\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"item not in list:\" << reinterpret_cast<quintptr>(item);\n    return false;\n  }\n}\n\n/*! \\overload\n  \n  Removes and deletes the item by its \\a index.\n*/\nbool QCustomPlot::removeItem(int index)\n{\n  if (index >= 0 && index < mItems.size())\n    return removeItem(mItems[index]);\n  else\n  {\n    qDebug() << Q_FUNC_INFO << \"index out of bounds:\" << index;\n    return false;\n  }\n}\n\n/*!\n  Removes all items from the plot and deletes them.\n  \n  Returns the number of items removed.\n  \n  \\see removeItem\n*/\nint QCustomPlot::clearItems()\n{\n  int c = mItems.size();\n  for (int i=c-1; i >= 0; --i)\n    removeItem(mItems[i]);\n  return c;\n}\n\n/*!\n  Returns the number of currently existing items in the plot\n  \n  \\see item\n*/\nint QCustomPlot::itemCount() const\n{\n  return mItems.size();\n}\n\n/*!\n  Returns a list of the selected items. If no items are currently selected, the list is empty.\n  \n  \\see setInteractions, QCPAbstractItem::setSelectable, QCPAbstractItem::setSelected\n*/\nQList<QCPAbstractItem*> QCustomPlot::selectedItems() const\n{\n  QList<QCPAbstractItem*> result;\n  foreach (QCPAbstractItem *item, mItems)\n  {\n    if (item->selected())\n      result.append(item);\n  }\n  return result;\n}\n\n/*!\n  Returns the item at the pixel position \\a pos. Since it can capture all items, the\n  return type is the abstract base class of all items, QCPAbstractItem.\n  \n  For details, and if you wish to specify a certain item type (e.g. QCPItemLine), see the\n  template method itemAt<ItemType>()\n  \n  \\see itemAt<ItemType>(), plottableAt, layoutElementAt\n*/\nQCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const\n{\n  return itemAt<QCPAbstractItem>(pos, onlySelectable);\n}\n\n/*!\n  Returns whether this QCustomPlot contains the \\a item.\n  \n  \\see item\n*/\nbool QCustomPlot::hasItem(QCPAbstractItem *item) const\n{\n  return mItems.contains(item);\n}\n\n/*!\n  Returns the layer with the specified \\a name. If there is no layer with the specified name, \\c\n  nullptr is returned.\n  \n  Layer names are case-sensitive.\n  \n  \\see addLayer, moveLayer, removeLayer\n*/\nQCPLayer *QCustomPlot::layer(const QString &name) const\n{\n  foreach (QCPLayer *layer, mLayers)\n  {\n    if (layer->name() == name)\n      return layer;\n  }\n  return nullptr;\n}\n\n/*! \\overload\n  \n  Returns the layer by \\a index. If the index is invalid, \\c nullptr is returned.\n  \n  \\see addLayer, moveLayer, removeLayer\n*/\nQCPLayer *QCustomPlot::layer(int index) const\n{\n  if (index >= 0 && index < mLayers.size())\n  {\n    return mLayers.at(index);\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"index out of bounds:\" << index;\n    return nullptr;\n  }\n}\n\n/*!\n  Returns the layer that is set as current layer (see \\ref setCurrentLayer).\n*/\nQCPLayer *QCustomPlot::currentLayer() const\n{\n  return mCurrentLayer;\n}\n\n/*!\n  Sets the layer with the specified \\a name to be the current layer. All layerables (\\ref\n  QCPLayerable), e.g. plottables and items, are created on the current layer.\n  \n  Returns true on success, i.e. if there is a layer with the specified \\a name in the QCustomPlot.\n  \n  Layer names are case-sensitive.\n  \n  \\see addLayer, moveLayer, removeLayer, QCPLayerable::setLayer\n*/\nbool QCustomPlot::setCurrentLayer(const QString &name)\n{\n  if (QCPLayer *newCurrentLayer = layer(name))\n  {\n    return setCurrentLayer(newCurrentLayer);\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"layer with name doesn't exist:\" << name;\n    return false;\n  }\n}\n\n/*! \\overload\n  \n  Sets the provided \\a layer to be the current layer.\n  \n  Returns true on success, i.e. when \\a layer is a valid layer in the QCustomPlot.\n  \n  \\see addLayer, moveLayer, removeLayer\n*/\nbool QCustomPlot::setCurrentLayer(QCPLayer *layer)\n{\n  if (!mLayers.contains(layer))\n  {\n    qDebug() << Q_FUNC_INFO << \"layer not a layer of this QCustomPlot:\" << reinterpret_cast<quintptr>(layer);\n    return false;\n  }\n  \n  mCurrentLayer = layer;\n  return true;\n}\n\n/*!\n  Returns the number of currently existing layers in the plot\n  \n  \\see layer, addLayer\n*/\nint QCustomPlot::layerCount() const\n{\n  return mLayers.size();\n}\n\n/*!\n  Adds a new layer to this QCustomPlot instance. The new layer will have the name \\a name, which\n  must be unique. Depending on \\a insertMode, it is positioned either below or above \\a otherLayer.\n  \n  Returns true on success, i.e. if there is no other layer named \\a name and \\a otherLayer is a\n  valid layer inside this QCustomPlot.\n  \n  If \\a otherLayer is 0, the highest layer in the QCustomPlot will be used.\n  \n  For an explanation of what layers are in QCustomPlot, see the documentation of \\ref QCPLayer.\n  \n  \\see layer, moveLayer, removeLayer\n*/\nbool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)\n{\n  if (!otherLayer)\n    otherLayer = mLayers.last();\n  if (!mLayers.contains(otherLayer))\n  {\n    qDebug() << Q_FUNC_INFO << \"otherLayer not a layer of this QCustomPlot:\" << reinterpret_cast<quintptr>(otherLayer);\n    return false;\n  }\n  if (layer(name))\n  {\n    qDebug() << Q_FUNC_INFO << \"A layer exists already with the name\" << name;\n    return false;\n  }\n    \n  QCPLayer *newLayer = new QCPLayer(this, name);\n  mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer);\n  updateLayerIndices();\n  setupPaintBuffers(); // associates new layer with the appropriate paint buffer\n  return true;\n}\n\n/*!\n  Removes the specified \\a layer and returns true on success.\n  \n  All layerables (e.g. plottables and items) on the removed layer will be moved to the layer below\n  \\a layer. If \\a layer is the bottom layer, the layerables are moved to the layer above. In both\n  cases, the total rendering order of all layerables in the QCustomPlot is preserved.\n  \n  If \\a layer is the current layer (\\ref setCurrentLayer), the layer below (or above, if bottom\n  layer) becomes the new current layer.\n  \n  It is not possible to remove the last layer of the plot.\n  \n  \\see layer, addLayer, moveLayer\n*/\nbool QCustomPlot::removeLayer(QCPLayer *layer)\n{\n  if (!mLayers.contains(layer))\n  {\n    qDebug() << Q_FUNC_INFO << \"layer not a layer of this QCustomPlot:\" << reinterpret_cast<quintptr>(layer);\n    return false;\n  }\n  if (mLayers.size() < 2)\n  {\n    qDebug() << Q_FUNC_INFO << \"can't remove last layer\";\n    return false;\n  }\n  \n  // append all children of this layer to layer below (if this is lowest layer, prepend to layer above)\n  int removedIndex = layer->index();\n  bool isFirstLayer = removedIndex==0;\n  QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);\n  QList<QCPLayerable*> children = layer->children();\n  if (isFirstLayer) // prepend in reverse order (such that relative order stays the same)\n    std::reverse(children.begin(), children.end());\n  foreach (QCPLayerable *child, children)\n    child->moveToLayer(targetLayer, isFirstLayer); // prepend if isFirstLayer, otherwise append\n  \n  // if removed layer is current layer, change current layer to layer below/above:\n  if (layer == mCurrentLayer)\n    setCurrentLayer(targetLayer);\n  \n  // invalidate the paint buffer that was responsible for this layer:\n  if (QSharedPointer<QCPAbstractPaintBuffer> pb = layer->mPaintBuffer.toStrongRef())\n    pb->setInvalidated();\n  \n  // remove layer:\n  delete layer;\n  mLayers.removeOne(layer);\n  updateLayerIndices();\n  return true;\n}\n\n/*!\n  Moves the specified \\a layer either above or below \\a otherLayer. Whether it's placed above or\n  below is controlled with \\a insertMode.\n  \n  Returns true on success, i.e. when both \\a layer and \\a otherLayer are valid layers in the\n  QCustomPlot.\n  \n  \\see layer, addLayer, moveLayer\n*/\nbool QCustomPlot::moveLayer(QCPLayer *layer, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)\n{\n  if (!mLayers.contains(layer))\n  {\n    qDebug() << Q_FUNC_INFO << \"layer not a layer of this QCustomPlot:\" << reinterpret_cast<quintptr>(layer);\n    return false;\n  }\n  if (!mLayers.contains(otherLayer))\n  {\n    qDebug() << Q_FUNC_INFO << \"otherLayer not a layer of this QCustomPlot:\" << reinterpret_cast<quintptr>(otherLayer);\n    return false;\n  }\n  \n  if (layer->index() > otherLayer->index())\n    mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0));\n  else if (layer->index() < otherLayer->index())\n    mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 0:-1));\n  \n  // invalidate the paint buffers that are responsible for the layers:\n  if (QSharedPointer<QCPAbstractPaintBuffer> pb = layer->mPaintBuffer.toStrongRef())\n    pb->setInvalidated();\n  if (QSharedPointer<QCPAbstractPaintBuffer> pb = otherLayer->mPaintBuffer.toStrongRef())\n    pb->setInvalidated();\n  \n  updateLayerIndices();\n  return true;\n}\n\n/*!\n  Returns the number of axis rects in the plot.\n  \n  All axis rects can be accessed via QCustomPlot::axisRect().\n  \n  Initially, only one axis rect exists in the plot.\n  \n  \\see axisRect, axisRects\n*/\nint QCustomPlot::axisRectCount() const\n{\n  return axisRects().size();\n}\n\n/*!\n  Returns the axis rect with \\a index.\n  \n  Initially, only one axis rect (with index 0) exists in the plot. If multiple axis rects were\n  added, all of them may be accessed with this function in a linear fashion (even when they are\n  nested in a layout hierarchy or inside other axis rects via QCPAxisRect::insetLayout).\n  \n  The order of the axis rects is given by the fill order of the \\ref QCPLayout that is holding\n  them. For example, if the axis rects are in the top level grid layout (accessible via \\ref\n  QCustomPlot::plotLayout), they are ordered from left to right, top to bottom, if the layout's\n  default \\ref QCPLayoutGrid::setFillOrder \"setFillOrder\" of \\ref QCPLayoutGrid::foColumnsFirst\n  \"foColumnsFirst\" wasn't changed.\n  \n  If you want to access axis rects by their row and column index, use the layout interface. For\n  example, use \\ref QCPLayoutGrid::element of the top level grid layout, and \\c qobject_cast the\n  returned layout element to \\ref QCPAxisRect. (See also \\ref thelayoutsystem.)\n  \n  \\see axisRectCount, axisRects, QCPLayoutGrid::setFillOrder\n*/\nQCPAxisRect *QCustomPlot::axisRect(int index) const\n{\n  const QList<QCPAxisRect*> rectList = axisRects();\n  if (index >= 0 && index < rectList.size())\n  {\n    return rectList.at(index);\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"invalid axis rect index\" << index;\n    return nullptr;\n  }\n}\n\n/*!\n  Returns all axis rects in the plot.\n  \n  The order of the axis rects is given by the fill order of the \\ref QCPLayout that is holding\n  them. For example, if the axis rects are in the top level grid layout (accessible via \\ref\n  QCustomPlot::plotLayout), they are ordered from left to right, top to bottom, if the layout's\n  default \\ref QCPLayoutGrid::setFillOrder \"setFillOrder\" of \\ref QCPLayoutGrid::foColumnsFirst\n  \"foColumnsFirst\" wasn't changed.\n  \n  \\see axisRectCount, axisRect, QCPLayoutGrid::setFillOrder\n*/\nQList<QCPAxisRect*> QCustomPlot::axisRects() const\n{\n  QList<QCPAxisRect*> result;\n  QStack<QCPLayoutElement*> elementStack;\n  if (mPlotLayout)\n    elementStack.push(mPlotLayout);\n  \n  while (!elementStack.isEmpty())\n  {\n    foreach (QCPLayoutElement *element, elementStack.pop()->elements(false))\n    {\n      if (element)\n      {\n        elementStack.push(element);\n        if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))\n          result.append(ar);\n      }\n    }\n  }\n  \n  return result;\n}\n\n/*!\n  Returns the layout element at pixel position \\a pos. If there is no element at that position,\n  returns \\c nullptr.\n  \n  Only visible elements are used. If \\ref QCPLayoutElement::setVisible on the element itself or on\n  any of its parent elements is set to false, it will not be considered.\n  \n  \\see itemAt, plottableAt\n*/\nQCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const\n{\n  QCPLayoutElement *currentElement = mPlotLayout;\n  bool searchSubElements = true;\n  while (searchSubElements && currentElement)\n  {\n    searchSubElements = false;\n    foreach (QCPLayoutElement *subElement, currentElement->elements(false))\n    {\n      if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)\n      {\n        currentElement = subElement;\n        searchSubElements = true;\n        break;\n      }\n    }\n  }\n  return currentElement;\n}\n\n/*!\n  Returns the layout element of type \\ref QCPAxisRect at pixel position \\a pos. This method ignores\n  other layout elements even if they are visually in front of the axis rect (e.g. a \\ref\n  QCPLegend). If there is no axis rect at that position, returns \\c nullptr.\n\n  Only visible axis rects are used. If \\ref QCPLayoutElement::setVisible on the axis rect itself or\n  on any of its parent elements is set to false, it will not be considered.\n\n  \\see layoutElementAt\n*/\nQCPAxisRect *QCustomPlot::axisRectAt(const QPointF &pos) const\n{\n  QCPAxisRect *result = nullptr;\n  QCPLayoutElement *currentElement = mPlotLayout;\n  bool searchSubElements = true;\n  while (searchSubElements && currentElement)\n  {\n    searchSubElements = false;\n    foreach (QCPLayoutElement *subElement, currentElement->elements(false))\n    {\n      if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)\n      {\n        currentElement = subElement;\n        searchSubElements = true;\n        if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(currentElement))\n          result = ar;\n        break;\n      }\n    }\n  }\n  return result;\n}\n\n/*!\n  Returns the axes that currently have selected parts, i.e. whose selection state is not \\ref\n  QCPAxis::spNone.\n  \n  \\see selectedPlottables, selectedLegends, setInteractions, QCPAxis::setSelectedParts,\n  QCPAxis::setSelectableParts\n*/\nQList<QCPAxis*> QCustomPlot::selectedAxes() const\n{\n  QList<QCPAxis*> result, allAxes;\n  foreach (QCPAxisRect *rect, axisRects())\n    allAxes << rect->axes();\n  \n  foreach (QCPAxis *axis, allAxes)\n  {\n    if (axis->selectedParts() != QCPAxis::spNone)\n      result.append(axis);\n  }\n  \n  return result;\n}\n\n/*!\n  Returns the legends that currently have selected parts, i.e. whose selection state is not \\ref\n  QCPLegend::spNone.\n  \n  \\see selectedPlottables, selectedAxes, setInteractions, QCPLegend::setSelectedParts,\n  QCPLegend::setSelectableParts, QCPLegend::selectedItems\n*/\nQList<QCPLegend*> QCustomPlot::selectedLegends() const\n{\n  QList<QCPLegend*> result;\n  \n  QStack<QCPLayoutElement*> elementStack;\n  if (mPlotLayout)\n    elementStack.push(mPlotLayout);\n  \n  while (!elementStack.isEmpty())\n  {\n    foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false))\n    {\n      if (subElement)\n      {\n        elementStack.push(subElement);\n        if (QCPLegend *leg = qobject_cast<QCPLegend*>(subElement))\n        {\n          if (leg->selectedParts() != QCPLegend::spNone)\n            result.append(leg);\n        }\n      }\n    }\n  }\n  \n  return result;\n}\n\n/*!\n  Deselects all layerables (plottables, items, axes, legends,...) of the QCustomPlot.\n  \n  Since calling this function is not a user interaction, this does not emit the \\ref\n  selectionChangedByUser signal. The individual selectionChanged signals are emitted though, if the\n  objects were previously selected.\n  \n  \\see setInteractions, selectedPlottables, selectedItems, selectedAxes, selectedLegends\n*/\nvoid QCustomPlot::deselectAll()\n{\n  foreach (QCPLayer *layer, mLayers)\n  {\n    foreach (QCPLayerable *layerable, layer->children())\n      layerable->deselectEvent(nullptr);\n  }\n}\n\n/*!\n  Causes a complete replot into the internal paint buffer(s). Finally, the widget surface is\n  refreshed with the new buffer contents. This is the method that must be called to make changes to\n  the plot, e.g. on the axis ranges or data points of graphs, visible.\n\n  The parameter \\a refreshPriority can be used to fine-tune the timing of the replot. For example\n  if your application calls \\ref replot very quickly in succession (e.g. multiple independent\n  functions change some aspects of the plot and each wants to make sure the change gets replotted),\n  it is advisable to set \\a refreshPriority to \\ref QCustomPlot::rpQueuedReplot. This way, the\n  actual replotting is deferred to the next event loop iteration. Multiple successive calls of \\ref\n  replot with this priority will only cause a single replot, avoiding redundant replots and\n  improving performance.\n\n  Under a few circumstances, QCustomPlot causes a replot by itself. Those are resize events of the\n  QCustomPlot widget and user interactions (object selection and range dragging/zooming).\n\n  Before the replot happens, the signal \\ref beforeReplot is emitted. After the replot, \\ref\n  afterReplot is emitted. It is safe to mutually connect the replot slot with any of those two\n  signals on two QCustomPlots to make them replot synchronously, it won't cause an infinite\n  recursion.\n\n  If a layer is in mode \\ref QCPLayer::lmBuffered (\\ref QCPLayer::setMode), it is also possible to\n  replot only that specific layer via \\ref QCPLayer::replot. See the documentation there for\n  details.\n  \n  \\see replotTime\n*/\nvoid QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority)\n{\n  if (refreshPriority == QCustomPlot::rpQueuedReplot)\n  {\n    if (!mReplotQueued)\n    {\n      mReplotQueued = true;\n      QTimer::singleShot(0, this, SLOT(replot()));\n    }\n    return;\n  }\n  \n  if (mReplotting) // incase signals loop back to replot slot\n    return;\n  mReplotting = true;\n  mReplotQueued = false;\n  emit beforeReplot();\n  \n# if QT_VERSION < QT_VERSION_CHECK(4, 8, 0)\n  QTime replotTimer;\n  replotTimer.start();\n# else\n  QElapsedTimer replotTimer;\n  replotTimer.start();\n# endif\n  \n  updateLayout();\n  // draw all layered objects (grid, axes, plottables, items, legend,...) into their buffers:\n  setupPaintBuffers();\n  foreach (QCPLayer *layer, mLayers)\n    layer->drawToPaintBuffer();\n  foreach (QSharedPointer<QCPAbstractPaintBuffer> buffer, mPaintBuffers)\n    buffer->setInvalidated(false);\n  \n  if ((refreshPriority == rpRefreshHint && mPlottingHints.testFlag(QCP::phImmediateRefresh)) || refreshPriority==rpImmediateRefresh)\n    repaint();\n  else\n    update();\n  \n# if QT_VERSION < QT_VERSION_CHECK(4, 8, 0)\n  mReplotTime = replotTimer.elapsed();\n# else\n  mReplotTime = replotTimer.nsecsElapsed()*1e-6;\n# endif\n  if (!qFuzzyIsNull(mReplotTimeAverage))\n    mReplotTimeAverage = mReplotTimeAverage*0.9 + mReplotTime*0.1; // exponential moving average with a time constant of 10 last replots\n  else\n    mReplotTimeAverage = mReplotTime; // no previous replots to average with, so initialize with replot time\n  \n  emit afterReplot();\n  mReplotting = false;\n}\n\n/*!\n  Returns the time in milliseconds that the last replot took. If \\a average is set to true, an\n  exponential moving average over the last couple of replots is returned.\n  \n  \\see replot\n*/\ndouble QCustomPlot::replotTime(bool average) const\n{\n  return average ? mReplotTimeAverage : mReplotTime;\n}\n\n/*!\n  Rescales the axes such that all plottables (like graphs) in the plot are fully visible.\n  \n  if \\a onlyVisiblePlottables is set to true, only the plottables that have their visibility set to true\n  (QCPLayerable::setVisible), will be used to rescale the axes.\n  \n  \\see QCPAbstractPlottable::rescaleAxes, QCPAxis::rescale\n*/\nvoid QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)\n{\n  QList<QCPAxis*> allAxes;\n  foreach (QCPAxisRect *rect, axisRects())\n    allAxes << rect->axes();\n  \n  foreach (QCPAxis *axis, allAxes)\n    axis->rescale(onlyVisiblePlottables);\n}\n\n/*!\n  Saves a PDF with the vectorized plot to the file \\a fileName. The axis ratio as well as the scale\n  of texts and lines will be derived from the specified \\a width and \\a height. This means, the\n  output will look like the normal on-screen output of a QCustomPlot widget with the corresponding\n  pixel width and height. If either \\a width or \\a height is zero, the exported image will have the\n  same dimensions as the QCustomPlot widget currently has.\n\n  Setting \\a exportPen to \\ref QCP::epNoCosmetic allows to disable the use of cosmetic pens when\n  drawing to the PDF file. Cosmetic pens are pens with numerical width 0, which are always drawn as\n  a one pixel wide line, no matter what zoom factor is set in the PDF-Viewer. For more information\n  about cosmetic pens, see the QPainter and QPen documentation.\n\n  The objects of the plot will appear in the current selection state. If you don't want any\n  selected objects to be painted in their selected look, deselect everything with \\ref deselectAll\n  before calling this function.\n\n  Returns true on success.\n\n  \\warning\n  \\li If you plan on editing the exported PDF file with a vector graphics editor like Inkscape, it\n  is advised to set \\a exportPen to \\ref QCP::epNoCosmetic to avoid losing those cosmetic lines\n  (which might be quite many, because cosmetic pens are the default for e.g. axes and tick marks).\n  \\li If calling this function inside the constructor of the parent of the QCustomPlot widget\n  (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide\n  explicit non-zero widths and heights. If you leave \\a width or \\a height as 0 (default), this\n  function uses the current width and height of the QCustomPlot widget. However, in Qt, these\n  aren't defined yet inside the constructor, so you would get an image that has strange\n  widths/heights.\n\n  \\a pdfCreator and \\a pdfTitle may be used to set the according metadata fields in the resulting\n  PDF file.\n\n  \\note On Android systems, this method does nothing and issues an according qDebug warning\n  message. This is also the case if for other reasons the define flag \\c QT_NO_PRINTER is set.\n\n  \\see savePng, saveBmp, saveJpg, saveRastered\n*/\nbool QCustomPlot::savePdf(const QString &fileName, int width, int height, QCP::ExportPen exportPen, const QString &pdfCreator, const QString &pdfTitle)\n{\n  bool success = false;\n#ifdef QT_NO_PRINTER\n  Q_UNUSED(fileName)\n  Q_UNUSED(exportPen)\n  Q_UNUSED(width)\n  Q_UNUSED(height)\n  Q_UNUSED(pdfCreator)\n  Q_UNUSED(pdfTitle)\n  qDebug() << Q_FUNC_INFO << \"Qt was built without printer support (QT_NO_PRINTER). PDF not created.\";\n#else\n  int newWidth, newHeight;\n  if (width == 0 || height == 0)\n  {\n    newWidth = this->width();\n    newHeight = this->height();\n  } else\n  {\n    newWidth = width;\n    newHeight = height;\n  }\n  \n  QPrinter printer(QPrinter::ScreenResolution);\n  printer.setOutputFileName(fileName);\n  printer.setOutputFormat(QPrinter::PdfFormat);\n  printer.setColorMode(QPrinter::Color);\n  printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);\n  printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);\n  QRect oldViewport = viewport();\n  setViewport(QRect(0, 0, newWidth, newHeight));\n#if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)\n  printer.setFullPage(true);\n  printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);\n#else\n  QPageLayout pageLayout;\n  pageLayout.setMode(QPageLayout::FullPageMode);\n  pageLayout.setOrientation(QPageLayout::Portrait);\n  pageLayout.setMargins(QMarginsF(0, 0, 0, 0));\n  pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point, QString(), QPageSize::ExactMatch));\n  printer.setPageLayout(pageLayout);\n#endif\n  QCPPainter printpainter;\n  if (printpainter.begin(&printer))\n  {\n    printpainter.setMode(QCPPainter::pmVectorized);\n    printpainter.setMode(QCPPainter::pmNoCaching);\n    printpainter.setMode(QCPPainter::pmNonCosmetic, exportPen==QCP::epNoCosmetic);\n    printpainter.setWindow(mViewport);\n    if (mBackgroundBrush.style() != Qt::NoBrush &&\n        mBackgroundBrush.color() != Qt::white &&\n        mBackgroundBrush.color() != Qt::transparent &&\n        mBackgroundBrush.color().alpha() > 0) // draw pdf background color if not white/transparent\n      printpainter.fillRect(viewport(), mBackgroundBrush);\n    draw(&printpainter);\n    printpainter.end();\n    success = true;\n  }\n  setViewport(oldViewport);\n#endif // QT_NO_PRINTER\n  return success;\n}\n\n/*!\n  Saves a PNG image file to \\a fileName on disc. The output plot will have the dimensions \\a width\n  and \\a height in pixels, multiplied by \\a scale. If either \\a width or \\a height is zero, the\n  current width and height of the QCustomPlot widget is used instead. Line widths and texts etc.\n  are not scaled up when larger widths/heights are used. If you want that effect, use the \\a scale\n  parameter.\n\n  For example, if you set both \\a width and \\a height to 100 and \\a scale to 2, you will end up with an\n  image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,\n  texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full\n  200*200 pixel resolution.\n\n  If you use a high scaling factor, it is recommended to enable antialiasing for all elements by\n  temporarily setting \\ref QCustomPlot::setAntialiasedElements to \\ref QCP::aeAll as this allows\n  QCustomPlot to place objects with sub-pixel accuracy.\n\n  image compression can be controlled with the \\a quality parameter which must be between 0 and 100\n  or -1 to use the default setting.\n\n  The \\a resolution will be written to the image file header and has no direct consequence for the\n  quality or the pixel size. However, if opening the image with a tool which respects the metadata,\n  it will be able to scale the image to match either a given size in real units of length (inch,\n  centimeters, etc.), or the target display DPI. You can specify in which units \\a resolution is\n  given, by setting \\a resolutionUnit. The \\a resolution is converted to the format's expected\n  resolution unit internally.\n\n  Returns true on success. If this function fails, most likely the PNG format isn't supported by\n  the system, see Qt docs about QImageWriter::supportedImageFormats().\n\n  The objects of the plot will appear in the current selection state. If you don't want any selected\n  objects to be painted in their selected look, deselect everything with \\ref deselectAll before calling\n  this function.\n\n  If you want the PNG to have a transparent background, call \\ref setBackground(const QBrush &brush)\n  with no brush (Qt::NoBrush) or a transparent color (Qt::transparent), before saving.\n\n  \\warning If calling this function inside the constructor of the parent of the QCustomPlot widget\n  (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide\n  explicit non-zero widths and heights. If you leave \\a width or \\a height as 0 (default), this\n  function uses the current width and height of the QCustomPlot widget. However, in Qt, these\n  aren't defined yet inside the constructor, so you would get an image that has strange\n  widths/heights.\n\n  \\see savePdf, saveBmp, saveJpg, saveRastered\n*/\nbool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality, int resolution, QCP::ResolutionUnit resolutionUnit)\n{\n  return saveRastered(fileName, width, height, scale, \"PNG\", quality, resolution, resolutionUnit);\n}\n\n/*!\n  Saves a JPEG image file to \\a fileName on disc. The output plot will have the dimensions \\a width\n  and \\a height in pixels, multiplied by \\a scale. If either \\a width or \\a height is zero, the\n  current width and height of the QCustomPlot widget is used instead. Line widths and texts etc.\n  are not scaled up when larger widths/heights are used. If you want that effect, use the \\a scale\n  parameter.\n\n  For example, if you set both \\a width and \\a height to 100 and \\a scale to 2, you will end up with an\n  image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,\n  texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full\n  200*200 pixel resolution.\n\n  If you use a high scaling factor, it is recommended to enable antialiasing for all elements by\n  temporarily setting \\ref QCustomPlot::setAntialiasedElements to \\ref QCP::aeAll as this allows\n  QCustomPlot to place objects with sub-pixel accuracy.\n\n  image compression can be controlled with the \\a quality parameter which must be between 0 and 100\n  or -1 to use the default setting.\n\n  The \\a resolution will be written to the image file header and has no direct consequence for the\n  quality or the pixel size. However, if opening the image with a tool which respects the metadata,\n  it will be able to scale the image to match either a given size in real units of length (inch,\n  centimeters, etc.), or the target display DPI. You can specify in which units \\a resolution is\n  given, by setting \\a resolutionUnit. The \\a resolution is converted to the format's expected\n  resolution unit internally.\n\n  Returns true on success. If this function fails, most likely the JPEG format isn't supported by\n  the system, see Qt docs about QImageWriter::supportedImageFormats().\n\n  The objects of the plot will appear in the current selection state. If you don't want any selected\n  objects to be painted in their selected look, deselect everything with \\ref deselectAll before calling\n  this function.\n\n  \\warning If calling this function inside the constructor of the parent of the QCustomPlot widget\n  (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide\n  explicit non-zero widths and heights. If you leave \\a width or \\a height as 0 (default), this\n  function uses the current width and height of the QCustomPlot widget. However, in Qt, these\n  aren't defined yet inside the constructor, so you would get an image that has strange\n  widths/heights.\n\n  \\see savePdf, savePng, saveBmp, saveRastered\n*/\nbool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality, int resolution, QCP::ResolutionUnit resolutionUnit)\n{\n  return saveRastered(fileName, width, height, scale, \"JPG\", quality, resolution, resolutionUnit);\n}\n\n/*!\n  Saves a BMP image file to \\a fileName on disc. The output plot will have the dimensions \\a width\n  and \\a height in pixels, multiplied by \\a scale. If either \\a width or \\a height is zero, the\n  current width and height of the QCustomPlot widget is used instead. Line widths and texts etc.\n  are not scaled up when larger widths/heights are used. If you want that effect, use the \\a scale\n  parameter.\n\n  For example, if you set both \\a width and \\a height to 100 and \\a scale to 2, you will end up with an\n  image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,\n  texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full\n  200*200 pixel resolution.\n\n  If you use a high scaling factor, it is recommended to enable antialiasing for all elements by\n  temporarily setting \\ref QCustomPlot::setAntialiasedElements to \\ref QCP::aeAll as this allows\n  QCustomPlot to place objects with sub-pixel accuracy.\n\n  The \\a resolution will be written to the image file header and has no direct consequence for the\n  quality or the pixel size. However, if opening the image with a tool which respects the metadata,\n  it will be able to scale the image to match either a given size in real units of length (inch,\n  centimeters, etc.), or the target display DPI. You can specify in which units \\a resolution is\n  given, by setting \\a resolutionUnit. The \\a resolution is converted to the format's expected\n  resolution unit internally.\n\n  Returns true on success. If this function fails, most likely the BMP format isn't supported by\n  the system, see Qt docs about QImageWriter::supportedImageFormats().\n\n  The objects of the plot will appear in the current selection state. If you don't want any selected\n  objects to be painted in their selected look, deselect everything with \\ref deselectAll before calling\n  this function.\n\n  \\warning If calling this function inside the constructor of the parent of the QCustomPlot widget\n  (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide\n  explicit non-zero widths and heights. If you leave \\a width or \\a height as 0 (default), this\n  function uses the current width and height of the QCustomPlot widget. However, in Qt, these\n  aren't defined yet inside the constructor, so you would get an image that has strange\n  widths/heights.\n\n  \\see savePdf, savePng, saveJpg, saveRastered\n*/\nbool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale, int resolution, QCP::ResolutionUnit resolutionUnit)\n{\n  return saveRastered(fileName, width, height, scale, \"BMP\", -1, resolution, resolutionUnit);\n}\n\n/*! \\internal\n  \n  Returns a minimum size hint that corresponds to the minimum size of the top level layout\n  (\\ref plotLayout). To prevent QCustomPlot from being collapsed to size/width zero, set a minimum\n  size (setMinimumSize) either on the whole QCustomPlot or on any layout elements inside the plot.\n  This is especially important, when placed in a QLayout where other components try to take in as\n  much space as possible (e.g. QMdiArea).\n*/\nQSize QCustomPlot::minimumSizeHint() const\n{\n  return mPlotLayout->minimumOuterSizeHint();\n}\n\n/*! \\internal\n  \n  Returns a size hint that is the same as \\ref minimumSizeHint.\n  \n*/\nQSize QCustomPlot::sizeHint() const\n{\n  return mPlotLayout->minimumOuterSizeHint();\n}\n\n/*! \\internal\n  \n  Event handler for when the QCustomPlot widget needs repainting. This does not cause a \\ref replot, but\n  draws the internal buffer on the widget surface.\n*/\nvoid QCustomPlot::paintEvent(QPaintEvent *event)\n{\n  Q_UNUSED(event)\n  QCPPainter painter(this);\n  if (painter.isActive())\n  {\n#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)\n  painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem\n#endif\n    if (mBackgroundBrush.style() != Qt::NoBrush)\n      painter.fillRect(mViewport, mBackgroundBrush);\n    drawBackground(&painter);\n    foreach (QSharedPointer<QCPAbstractPaintBuffer> buffer, mPaintBuffers)\n      buffer->draw(&painter);\n  }\n}\n\n/*! \\internal\n  \n  Event handler for a resize of the QCustomPlot widget. The viewport (which becomes the outer rect\n  of mPlotLayout) is resized appropriately. Finally a \\ref replot is performed.\n*/\nvoid QCustomPlot::resizeEvent(QResizeEvent *event)\n{\n  Q_UNUSED(event)\n  // resize and repaint the buffer:\n  setViewport(rect());\n  replot(rpQueuedRefresh); // queued refresh is important here, to prevent painting issues in some contexts (e.g. MDI subwindow)\n}\n\n/*! \\internal\n  \n Event handler for when a double click occurs. Emits the \\ref mouseDoubleClick signal, then\n determines the layerable under the cursor and forwards the event to it. Finally, emits the\n specialized signals when certain objecs are clicked (e.g. \\ref plottableDoubleClick, \\ref\n axisDoubleClick, etc.).\n \n \\see mousePressEvent, mouseReleaseEvent\n*/\nvoid QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)\n{\n  emit mouseDoubleClick(event);\n  mMouseHasMoved = false;\n  mMousePressPos = event->pos();\n  \n  // determine layerable under the cursor (this event is called instead of the second press event in a double-click):\n  QList<QVariant> details;\n  QList<QCPLayerable*> candidates = layerableListAt(mMousePressPos, false, &details);\n  for (int i=0; i<candidates.size(); ++i)\n  {\n    event->accept(); // default impl of QCPLayerable's mouse events ignore the event, in that case propagate to next candidate in list\n    candidates.at(i)->mouseDoubleClickEvent(event, details.at(i));\n    if (event->isAccepted())\n    {\n      mMouseEventLayerable = candidates.at(i);\n      mMouseEventLayerableDetails = details.at(i);\n      break;\n    }\n  }\n  \n  // emit specialized object double click signals:\n  if (!candidates.isEmpty())\n  {\n    if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(candidates.first()))\n    {\n      int dataIndex = 0;\n      if (!details.first().value<QCPDataSelection>().isEmpty())\n        dataIndex = details.first().value<QCPDataSelection>().dataRange().begin();\n      emit plottableDoubleClick(ap, dataIndex, event);\n    } else if (QCPAxis *ax = qobject_cast<QCPAxis*>(candidates.first()))\n      emit axisDoubleClick(ax, details.first().value<QCPAxis::SelectablePart>(), event);\n    else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(candidates.first()))\n      emit itemDoubleClick(ai, event);\n    else if (QCPLegend *lg = qobject_cast<QCPLegend*>(candidates.first()))\n      emit legendDoubleClick(lg, nullptr, event);\n    else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(candidates.first()))\n      emit legendDoubleClick(li->parentLegend(), li, event);\n  }\n  \n  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.\n}\n\n/*! \\internal\n  \n  Event handler for when a mouse button is pressed. Emits the mousePress signal.\n\n  If the current \\ref setSelectionRectMode is not \\ref QCP::srmNone, passes the event to the\n  selection rect. Otherwise determines the layerable under the cursor and forwards the event to it.\n  \n  \\see mouseMoveEvent, mouseReleaseEvent\n*/\nvoid QCustomPlot::mousePressEvent(QMouseEvent *event)\n{\n  emit mousePress(event);\n  // save some state to tell in releaseEvent whether it was a click:\n  mMouseHasMoved = false;\n  mMousePressPos = event->pos();\n  \n  if (mSelectionRect && mSelectionRectMode != QCP::srmNone)\n  {\n    if (mSelectionRectMode != QCP::srmZoom || qobject_cast<QCPAxisRect*>(axisRectAt(mMousePressPos))) // in zoom mode only activate selection rect if on an axis rect\n      mSelectionRect->startSelection(event);\n  } else\n  {\n    // no selection rect interaction, prepare for click signal emission and forward event to layerable under the cursor:\n    QList<QVariant> details;\n    QList<QCPLayerable*> candidates = layerableListAt(mMousePressPos, false, &details);\n    if (!candidates.isEmpty())\n    {\n      mMouseSignalLayerable = candidates.first(); // candidate for signal emission is always topmost hit layerable (signal emitted in release event)\n      mMouseSignalLayerableDetails = details.first();\n    }\n    // forward event to topmost candidate which accepts the event:\n    for (int i=0; i<candidates.size(); ++i)\n    {\n      event->accept(); // default impl of QCPLayerable's mouse events call ignore() on the event, in that case propagate to next candidate in list\n      candidates.at(i)->mousePressEvent(event, details.at(i));\n      if (event->isAccepted())\n      {\n        mMouseEventLayerable = candidates.at(i);\n        mMouseEventLayerableDetails = details.at(i);\n        break;\n      }\n    }\n  }\n  \n  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.\n}\n\n/*! \\internal\n  \n  Event handler for when the cursor is moved. Emits the \\ref mouseMove signal.\n\n  If the selection rect (\\ref setSelectionRect) is currently active, the event is forwarded to it\n  in order to update the rect geometry.\n  \n  Otherwise, if a layout element has mouse capture focus (a mousePressEvent happened on top of the\n  layout element before), the mouseMoveEvent is forwarded to that element.\n  \n  \\see mousePressEvent, mouseReleaseEvent\n*/\nvoid QCustomPlot::mouseMoveEvent(QMouseEvent *event)\n{\n  emit mouseMove(event);\n  \n  if (!mMouseHasMoved && (mMousePressPos-event->pos()).manhattanLength() > 3)\n    mMouseHasMoved = true; // moved too far from mouse press position, don't handle as click on mouse release\n  \n  if (mSelectionRect && mSelectionRect->isActive())\n    mSelectionRect->moveSelection(event);\n  else if (mMouseEventLayerable) // call event of affected layerable:\n    mMouseEventLayerable->mouseMoveEvent(event, mMousePressPos);\n  \n  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.\n}\n\n/*! \\internal\n\n  Event handler for when a mouse button is released. Emits the \\ref mouseRelease signal.\n\n  If the mouse was moved less than a certain threshold in any direction since the \\ref\n  mousePressEvent, it is considered a click which causes the selection mechanism (if activated via\n  \\ref setInteractions) to possibly change selection states accordingly. Further, specialized mouse\n  click signals are emitted (e.g. \\ref plottableClick, \\ref axisClick, etc.)\n\n  If a layerable is the mouse capturer (a \\ref mousePressEvent happened on top of the layerable\n  before), the \\ref mouseReleaseEvent is forwarded to that element.\n\n  \\see mousePressEvent, mouseMoveEvent\n*/\nvoid QCustomPlot::mouseReleaseEvent(QMouseEvent *event)\n{\n  emit mouseRelease(event);\n  \n  if (!mMouseHasMoved) // mouse hasn't moved (much) between press and release, so handle as click\n  {\n    if (mSelectionRect && mSelectionRect->isActive()) // a simple click shouldn't successfully finish a selection rect, so cancel it here\n      mSelectionRect->cancel();\n    if (event->button() == Qt::LeftButton)\n      processPointSelection(event);\n    \n    // emit specialized click signals of QCustomPlot instance:\n    if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(mMouseSignalLayerable))\n    {\n      int dataIndex = 0;\n      if (!mMouseSignalLayerableDetails.value<QCPDataSelection>().isEmpty())\n        dataIndex = mMouseSignalLayerableDetails.value<QCPDataSelection>().dataRange().begin();\n      emit plottableClick(ap, dataIndex, event);\n    } else if (QCPAxis *ax = qobject_cast<QCPAxis*>(mMouseSignalLayerable))\n      emit axisClick(ax, mMouseSignalLayerableDetails.value<QCPAxis::SelectablePart>(), event);\n    else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(mMouseSignalLayerable))\n      emit itemClick(ai, event);\n    else if (QCPLegend *lg = qobject_cast<QCPLegend*>(mMouseSignalLayerable))\n      emit legendClick(lg, nullptr, event);\n    else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(mMouseSignalLayerable))\n      emit legendClick(li->parentLegend(), li, event);\n    mMouseSignalLayerable = nullptr;\n  }\n  \n  if (mSelectionRect && mSelectionRect->isActive()) // Note: if a click was detected above, the selection rect is canceled there\n  {\n    // finish selection rect, the appropriate action will be taken via signal-slot connection:\n    mSelectionRect->endSelection(event);\n  } else\n  {\n    // call event of affected layerable:\n    if (mMouseEventLayerable)\n    {\n      mMouseEventLayerable->mouseReleaseEvent(event, mMousePressPos);\n      mMouseEventLayerable = nullptr;\n    }\n  }\n  \n  if (noAntialiasingOnDrag())\n    replot(rpQueuedReplot);\n  \n  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.\n}\n\n/*! \\internal\n\n  Event handler for mouse wheel events. First, the \\ref mouseWheel signal is emitted. Then\n  determines the affected layerable and forwards the event to it.\n*/\nvoid QCustomPlot::wheelEvent(QWheelEvent *event)\n{\n  emit mouseWheel(event);\n  \n#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)\n  const QPointF pos = event->pos();\n#else\n  const QPointF pos = event->position();\n#endif\n  \n  // forward event to layerable under cursor:\n  foreach (QCPLayerable *candidate, layerableListAt(pos, false))\n  {\n    event->accept(); // default impl of QCPLayerable's mouse events ignore the event, in that case propagate to next candidate in list\n    candidate->wheelEvent(event);\n    if (event->isAccepted())\n      break;\n  }\n  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.\n}\n\n/*! \\internal\n  \n  This function draws the entire plot, including background pixmap, with the specified \\a painter.\n  It does not make use of the paint buffers like \\ref replot, so this is the function typically\n  used by saving/exporting methods such as \\ref savePdf or \\ref toPainter.\n\n  Note that it does not fill the background with the background brush (as the user may specify with\n  \\ref setBackground(const QBrush &brush)), this is up to the respective functions calling this\n  method.\n*/\nvoid QCustomPlot::draw(QCPPainter *painter)\n{\n  updateLayout();\n  \n  // draw viewport background pixmap:\n  drawBackground(painter);\n\n  // draw all layered objects (grid, axes, plottables, items, legend,...):\n  foreach (QCPLayer *layer, mLayers)\n    layer->draw(painter);\n  \n  /* Debug code to draw all layout element rects\n  foreach (QCPLayoutElement *el, findChildren<QCPLayoutElement*>())\n  {\n    painter->setBrush(Qt::NoBrush);\n    painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));\n    painter->drawRect(el->rect());\n    painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));\n    painter->drawRect(el->outerRect());\n  }\n  */\n}\n\n/*! \\internal\n\n  Performs the layout update steps defined by \\ref QCPLayoutElement::UpdatePhase, by calling \\ref\n  QCPLayoutElement::update on the main plot layout.\n\n  Here, the layout elements calculate their positions and margins, and prepare for the following\n  draw call.\n*/\nvoid QCustomPlot::updateLayout()\n{\n  // run through layout phases:\n  mPlotLayout->update(QCPLayoutElement::upPreparation);\n  mPlotLayout->update(QCPLayoutElement::upMargins);\n  mPlotLayout->update(QCPLayoutElement::upLayout);\n\n  emit afterLayout();\n}\n\n/*! \\internal\n  \n  Draws the viewport background pixmap of the plot.\n  \n  If a pixmap was provided via \\ref setBackground, this function buffers the scaled version\n  depending on \\ref setBackgroundScaled and \\ref setBackgroundScaledMode and then draws it inside\n  the viewport with the provided \\a painter. The scaled version is buffered in\n  mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when\n  the axis rect has changed in a way that requires a rescale of the background pixmap (this is\n  dependent on the \\ref setBackgroundScaledMode), or when a differend axis background pixmap was\n  set.\n  \n  Note that this function does not draw a fill with the background brush\n  (\\ref setBackground(const QBrush &brush)) beneath the pixmap.\n  \n  \\see setBackground, setBackgroundScaled, setBackgroundScaledMode\n*/\nvoid QCustomPlot::drawBackground(QCPPainter *painter)\n{\n  // Note: background color is handled in individual replot/save functions\n\n  // draw background pixmap (on top of fill, if brush specified):\n  if (!mBackgroundPixmap.isNull())\n  {\n    if (mBackgroundScaled)\n    {\n      // check whether mScaledBackground needs to be updated:\n      QSize scaledSize(mBackgroundPixmap.size());\n      scaledSize.scale(mViewport.size(), mBackgroundScaledMode);\n      if (mScaledBackgroundPixmap.size() != scaledSize)\n        mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation);\n      painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()) & mScaledBackgroundPixmap.rect());\n    } else\n    {\n      painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()));\n    }\n  }\n}\n\n/*! \\internal\n\n  Goes through the layers and makes sure this QCustomPlot instance holds the correct number of\n  paint buffers and that they have the correct configuration (size, pixel ratio, etc.).\n  Allocations, reallocations and deletions of paint buffers are performed as necessary. It also\n  associates the paint buffers with the layers, so they draw themselves into the right buffer when\n  \\ref QCPLayer::drawToPaintBuffer is called. This means it associates adjacent \\ref\n  QCPLayer::lmLogical layers to a mutual paint buffer and creates dedicated paint buffers for\n  layers in \\ref QCPLayer::lmBuffered mode.\n\n  This method uses \\ref createPaintBuffer to create new paint buffers.\n\n  After this method, the paint buffers are empty (filled with \\c Qt::transparent) and invalidated\n  (so an attempt to replot only a single buffered layer causes a full replot).\n\n  This method is called in every \\ref replot call, prior to actually drawing the layers (into their\n  associated paint buffer). If the paint buffers don't need changing/reallocating, this method\n  basically leaves them alone and thus finishes very fast.\n*/\nvoid QCustomPlot::setupPaintBuffers()\n{\n  int bufferIndex = 0;\n  if (mPaintBuffers.isEmpty())\n    mPaintBuffers.append(QSharedPointer<QCPAbstractPaintBuffer>(createPaintBuffer()));\n  \n  for (int layerIndex = 0; layerIndex < mLayers.size(); ++layerIndex)\n  {\n    QCPLayer *layer = mLayers.at(layerIndex);\n    if (layer->mode() == QCPLayer::lmLogical)\n    {\n      layer->mPaintBuffer = mPaintBuffers.at(bufferIndex).toWeakRef();\n    } else if (layer->mode() == QCPLayer::lmBuffered)\n    {\n      ++bufferIndex;\n      if (bufferIndex >= mPaintBuffers.size())\n        mPaintBuffers.append(QSharedPointer<QCPAbstractPaintBuffer>(createPaintBuffer()));\n      layer->mPaintBuffer = mPaintBuffers.at(bufferIndex).toWeakRef();\n      if (layerIndex < mLayers.size()-1 && mLayers.at(layerIndex+1)->mode() == QCPLayer::lmLogical) // not last layer, and next one is logical, so prepare another buffer for next layerables\n      {\n        ++bufferIndex;\n        if (bufferIndex >= mPaintBuffers.size())\n          mPaintBuffers.append(QSharedPointer<QCPAbstractPaintBuffer>(createPaintBuffer()));\n      }\n    }\n  }\n  // remove unneeded buffers:\n  while (mPaintBuffers.size()-1 > bufferIndex)\n    mPaintBuffers.removeLast();\n  // resize buffers to viewport size and clear contents:\n  foreach (QSharedPointer<QCPAbstractPaintBuffer> buffer, mPaintBuffers)\n  {\n    buffer->setSize(viewport().size()); // won't do anything if already correct size\n    buffer->clear(Qt::transparent);\n    buffer->setInvalidated();\n  }\n}\n\n/*! \\internal\n\n  This method is used by \\ref setupPaintBuffers when it needs to create new paint buffers.\n\n  Depending on the current setting of \\ref setOpenGl, and the current Qt version, different\n  backends (subclasses of \\ref QCPAbstractPaintBuffer) are created, initialized with the proper\n  size and device pixel ratio, and returned.\n*/\nQCPAbstractPaintBuffer *QCustomPlot::createPaintBuffer()\n{\n  if (mOpenGl)\n  {\n#if defined(QCP_OPENGL_FBO)\n    return new QCPPaintBufferGlFbo(viewport().size(), mBufferDevicePixelRatio, mGlContext, mGlPaintDevice);\n#elif defined(QCP_OPENGL_PBUFFER)\n    return new QCPPaintBufferGlPbuffer(viewport().size(), mBufferDevicePixelRatio, mOpenGlMultisamples);\n#else\n    qDebug() << Q_FUNC_INFO << \"OpenGL enabled even though no support for it compiled in, this shouldn't have happened. Falling back to pixmap paint buffer.\";\n    return new QCPPaintBufferPixmap(viewport().size(), mBufferDevicePixelRatio);\n#endif\n  } else\n    return new QCPPaintBufferPixmap(viewport().size(), mBufferDevicePixelRatio);\n}\n\n/*!\n  This method returns whether any of the paint buffers held by this QCustomPlot instance are\n  invalidated.\n\n  If any buffer is invalidated, a partial replot (\\ref QCPLayer::replot) is not allowed and always\n  causes a full replot (\\ref QCustomPlot::replot) of all layers. This is the case when for example\n  the layer order has changed, new layers were added or removed, layer modes were changed (\\ref\n  QCPLayer::setMode), or layerables were added or removed.\n\n  \\see QCPAbstractPaintBuffer::setInvalidated\n*/\nbool QCustomPlot::hasInvalidatedPaintBuffers()\n{\n  foreach (QSharedPointer<QCPAbstractPaintBuffer> buffer, mPaintBuffers)\n  {\n    if (buffer->invalidated())\n      return true;\n  }\n  return false;\n}\n\n/*! \\internal\n\n  When \\ref setOpenGl is set to true, this method is used to initialize OpenGL (create a context,\n  surface, paint device).\n\n  Returns true on success.\n\n  If this method is successful, all paint buffers should be deleted and then reallocated by calling\n  \\ref setupPaintBuffers, so the OpenGL-based paint buffer subclasses (\\ref\n  QCPPaintBufferGlPbuffer, \\ref QCPPaintBufferGlFbo) are used for subsequent replots.\n\n  \\see freeOpenGl\n*/\nbool QCustomPlot::setupOpenGl()\n{\n#ifdef QCP_OPENGL_FBO\n  freeOpenGl();\n  QSurfaceFormat proposedSurfaceFormat;\n  proposedSurfaceFormat.setSamples(mOpenGlMultisamples);\n#ifdef QCP_OPENGL_OFFSCREENSURFACE\n  QOffscreenSurface *surface = new QOffscreenSurface;\n#else\n  QWindow *surface = new QWindow;\n  surface->setSurfaceType(QSurface::OpenGLSurface);\n#endif\n  surface->setFormat(proposedSurfaceFormat);\n  surface->create();\n  mGlSurface = QSharedPointer<QSurface>(surface);\n  mGlContext = QSharedPointer<QOpenGLContext>(new QOpenGLContext);\n  mGlContext->setFormat(mGlSurface->format());\n  if (!mGlContext->create())\n  {\n    qDebug() << Q_FUNC_INFO << \"Failed to create OpenGL context\";\n    mGlContext.clear();\n    mGlSurface.clear();\n    return false;\n  }\n  if (!mGlContext->makeCurrent(mGlSurface.data())) // context needs to be current to create paint device\n  {\n    qDebug() << Q_FUNC_INFO << \"Failed to make opengl context current\";\n    mGlContext.clear();\n    mGlSurface.clear();\n    return false;\n  }\n  if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())\n  {\n    qDebug() << Q_FUNC_INFO << \"OpenGL of this system doesn't support frame buffer objects\";\n    mGlContext.clear();\n    mGlSurface.clear();\n    return false;\n  }\n  mGlPaintDevice = QSharedPointer<QOpenGLPaintDevice>(new QOpenGLPaintDevice);\n  return true;\n#elif defined(QCP_OPENGL_PBUFFER)\n  return QGLFormat::hasOpenGL();\n#else\n  return false;\n#endif\n}\n\n/*! \\internal\n\n  When \\ref setOpenGl is set to false, this method is used to deinitialize OpenGL (releases the\n  context and frees resources).\n\n  After OpenGL is disabled, all paint buffers should be deleted and then reallocated by calling\n  \\ref setupPaintBuffers, so the standard software rendering paint buffer subclass (\\ref\n  QCPPaintBufferPixmap) is used for subsequent replots.\n\n  \\see setupOpenGl\n*/\nvoid QCustomPlot::freeOpenGl()\n{\n#ifdef QCP_OPENGL_FBO\n  mGlPaintDevice.clear();\n  mGlContext.clear();\n  mGlSurface.clear();\n#endif\n}\n\n/*! \\internal\n  \n  This method is used by \\ref QCPAxisRect::removeAxis to report removed axes to the QCustomPlot\n  so it may clear its QCustomPlot::xAxis, yAxis, xAxis2 and yAxis2 members accordingly.\n*/\nvoid QCustomPlot::axisRemoved(QCPAxis *axis)\n{\n  if (xAxis == axis)\n    xAxis = nullptr;\n  if (xAxis2 == axis)\n    xAxis2 = nullptr;\n  if (yAxis == axis)\n    yAxis = nullptr;\n  if (yAxis2 == axis)\n    yAxis2 = nullptr;\n  \n  // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers\n}\n\n/*! \\internal\n  \n  This method is used by the QCPLegend destructor to report legend removal to the QCustomPlot so\n  it may clear its QCustomPlot::legend member accordingly.\n*/\nvoid QCustomPlot::legendRemoved(QCPLegend *legend)\n{\n  if (this->legend == legend)\n    this->legend = nullptr;\n}\n\n/*! \\internal\n  \n  This slot is connected to the selection rect's \\ref QCPSelectionRect::accepted signal when \\ref\n  setSelectionRectMode is set to \\ref QCP::srmSelect.\n\n  First, it determines which axis rect was the origin of the selection rect judging by the starting\n  point of the selection. Then it goes through the plottables (\\ref QCPAbstractPlottable1D to be\n  precise) associated with that axis rect and finds the data points that are in \\a rect. It does\n  this by querying their \\ref QCPAbstractPlottable1D::selectTestRect method.\n  \n  Then, the actual selection is done by calling the plottables' \\ref\n  QCPAbstractPlottable::selectEvent, placing the found selected data points in the \\a details\n  parameter as <tt>QVariant(\\ref QCPDataSelection)</tt>. All plottables that weren't touched by \\a\n  rect receive a \\ref QCPAbstractPlottable::deselectEvent.\n  \n  \\see processRectZoom\n*/\nvoid QCustomPlot::processRectSelection(QRect rect, QMouseEvent *event)\n{\n  typedef QPair<QCPAbstractPlottable*, QCPDataSelection> SelectionCandidate;\n  typedef QMultiMap<int, SelectionCandidate> SelectionCandidates; // map key is number of selected data points, so we have selections sorted by size\n  \n  bool selectionStateChanged = false;\n  \n  if (mInteractions.testFlag(QCP::iSelectPlottables))\n  {\n    SelectionCandidates potentialSelections;\n    QRectF rectF(rect.normalized());\n    if (QCPAxisRect *affectedAxisRect = axisRectAt(rectF.topLeft()))\n    {\n      // determine plottables that were hit by the rect and thus are candidates for selection:\n      foreach (QCPAbstractPlottable *plottable, affectedAxisRect->plottables())\n      {\n        if (QCPPlottableInterface1D *plottableInterface = plottable->interface1D())\n        {\n          QCPDataSelection dataSel = plottableInterface->selectTestRect(rectF, true);\n          if (!dataSel.isEmpty())\n            potentialSelections.insert(dataSel.dataPointCount(), SelectionCandidate(plottable, dataSel));\n        }\n      }\n      \n      if (!mInteractions.testFlag(QCP::iMultiSelect))\n      {\n        // only leave plottable with most selected points in map, since we will only select a single plottable:\n        if (!potentialSelections.isEmpty())\n        {\n          SelectionCandidates::iterator it = potentialSelections.begin();\n          while (it != std::prev(potentialSelections.end())) // erase all except last element\n            it = potentialSelections.erase(it);\n        }\n      }\n      \n      bool additive = event->modifiers().testFlag(mMultiSelectModifier);\n      // deselect all other layerables if not additive selection:\n      if (!additive)\n      {\n        // emit deselection except to those plottables who will be selected afterwards:\n        foreach (QCPLayer *layer, mLayers)\n        {\n          foreach (QCPLayerable *layerable, layer->children())\n          {\n            if ((potentialSelections.isEmpty() || potentialSelections.constBegin()->first != layerable) && mInteractions.testFlag(layerable->selectionCategory()))\n            {\n              bool selChanged = false;\n              layerable->deselectEvent(&selChanged);\n              selectionStateChanged |= selChanged;\n            }\n          }\n        }\n      }\n      \n      // go through selections in reverse (largest selection first) and emit select events:\n      SelectionCandidates::const_iterator it = potentialSelections.constEnd();\n      while (it != potentialSelections.constBegin())\n      {\n        --it;\n        if (mInteractions.testFlag(it.value().first->selectionCategory()))\n        {\n          bool selChanged = false;\n          it.value().first->selectEvent(event, additive, QVariant::fromValue(it.value().second), &selChanged);\n          selectionStateChanged |= selChanged;\n        }\n      }\n    }\n  }\n  \n  if (selectionStateChanged)\n  {\n    emit selectionChangedByUser();\n    replot(rpQueuedReplot);\n  } else if (mSelectionRect)\n    mSelectionRect->layer()->replot();\n}\n\n/*! \\internal\n  \n  This slot is connected to the selection rect's \\ref QCPSelectionRect::accepted signal when \\ref\n  setSelectionRectMode is set to \\ref QCP::srmZoom.\n\n  It determines which axis rect was the origin of the selection rect judging by the starting point\n  of the selection, and then zooms the axes defined via \\ref QCPAxisRect::setRangeZoomAxes to the\n  provided \\a rect (see \\ref QCPAxisRect::zoom).\n  \n  \\see processRectSelection\n*/\nvoid QCustomPlot::processRectZoom(QRect rect, QMouseEvent *event)\n{\n  Q_UNUSED(event)\n  if (QCPAxisRect *axisRect = axisRectAt(rect.topLeft()))\n  {\n    QList<QCPAxis*> affectedAxes = QList<QCPAxis*>() << axisRect->rangeZoomAxes(Qt::Horizontal) << axisRect->rangeZoomAxes(Qt::Vertical);\n    affectedAxes.removeAll(static_cast<QCPAxis*>(nullptr));\n    axisRect->zoom(QRectF(rect), affectedAxes);\n  }\n  replot(rpQueuedReplot); // always replot to make selection rect disappear\n}\n\n/*! \\internal\n\n  This method is called when a simple left mouse click was detected on the QCustomPlot surface.\n\n  It first determines the layerable that was hit by the click, and then calls its \\ref\n  QCPLayerable::selectEvent. All other layerables receive a QCPLayerable::deselectEvent (unless the\n  multi-select modifier was pressed, see \\ref setMultiSelectModifier).\n\n  In this method the hit layerable is determined a second time using \\ref layerableAt (after the\n  one in \\ref mousePressEvent), because we want \\a onlySelectable set to true this time. This\n  implies that the mouse event grabber (mMouseEventLayerable) may be a different one from the\n  clicked layerable determined here. For example, if a non-selectable layerable is in front of a\n  selectable layerable at the click position, the front layerable will receive mouse events but the\n  selectable one in the back will receive the \\ref QCPLayerable::selectEvent.\n\n  \\see processRectSelection, QCPLayerable::selectTest\n*/\nvoid QCustomPlot::processPointSelection(QMouseEvent *event)\n{\n  QVariant details;\n  QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);\n  bool selectionStateChanged = false;\n  bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);\n  // deselect all other layerables if not additive selection:\n  if (!additive)\n  {\n    foreach (QCPLayer *layer, mLayers)\n    {\n      foreach (QCPLayerable *layerable, layer->children())\n      {\n        if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory()))\n        {\n          bool selChanged = false;\n          layerable->deselectEvent(&selChanged);\n          selectionStateChanged |= selChanged;\n        }\n      }\n    }\n  }\n  if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))\n  {\n    // a layerable was actually clicked, call its selectEvent:\n    bool selChanged = false;\n    clickedLayerable->selectEvent(event, additive, details, &selChanged);\n    selectionStateChanged |= selChanged;\n  }\n  if (selectionStateChanged)\n  {\n    emit selectionChangedByUser();\n    replot(rpQueuedReplot);\n  }\n}\n\n/*! \\internal\n  \n  Registers the specified plottable with this QCustomPlot and, if \\ref setAutoAddPlottableToLegend\n  is enabled, adds it to the legend (QCustomPlot::legend). QCustomPlot takes ownership of the\n  plottable.\n  \n  Returns true on success, i.e. when \\a plottable isn't already in this plot and the parent plot of\n  \\a plottable is this QCustomPlot.\n  \n  This method is called automatically in the QCPAbstractPlottable base class constructor.\n*/\nbool QCustomPlot::registerPlottable(QCPAbstractPlottable *plottable)\n{\n  if (mPlottables.contains(plottable))\n  {\n    qDebug() << Q_FUNC_INFO << \"plottable already added to this QCustomPlot:\" << reinterpret_cast<quintptr>(plottable);\n    return false;\n  }\n  if (plottable->parentPlot() != this)\n  {\n    qDebug() << Q_FUNC_INFO << \"plottable not created with this QCustomPlot as parent:\" << reinterpret_cast<quintptr>(plottable);\n    return false;\n  }\n  \n  mPlottables.append(plottable);\n  // possibly add plottable to legend:\n  if (mAutoAddPlottableToLegend)\n    plottable->addToLegend();\n  if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)\n    plottable->setLayer(currentLayer());\n  return true;\n}\n\n/*! \\internal\n  \n  In order to maintain the simplified graph interface of QCustomPlot, this method is called by the\n  QCPGraph constructor to register itself with this QCustomPlot's internal graph list. Returns true\n  on success, i.e. if \\a graph is valid and wasn't already registered with this QCustomPlot.\n  \n  This graph specific registration happens in addition to the call to \\ref registerPlottable by the\n  QCPAbstractPlottable base class.\n*/\nbool QCustomPlot::registerGraph(QCPGraph *graph)\n{\n  if (!graph)\n  {\n    qDebug() << Q_FUNC_INFO << \"passed graph is zero\";\n    return false;\n  }\n  if (mGraphs.contains(graph))\n  {\n    qDebug() << Q_FUNC_INFO << \"graph already registered with this QCustomPlot\";\n    return false;\n  }\n  \n  mGraphs.append(graph);\n  return true;\n}\n\n\n/*! \\internal\n\n  Registers the specified item with this QCustomPlot. QCustomPlot takes ownership of the item.\n  \n  Returns true on success, i.e. when \\a item wasn't already in the plot and the parent plot of \\a\n  item is this QCustomPlot.\n  \n  This method is called automatically in the QCPAbstractItem base class constructor.\n*/\nbool QCustomPlot::registerItem(QCPAbstractItem *item)\n{\n  if (mItems.contains(item))\n  {\n    qDebug() << Q_FUNC_INFO << \"item already added to this QCustomPlot:\" << reinterpret_cast<quintptr>(item);\n    return false;\n  }\n  if (item->parentPlot() != this)\n  {\n    qDebug() << Q_FUNC_INFO << \"item not created with this QCustomPlot as parent:\" << reinterpret_cast<quintptr>(item);\n    return false;\n  }\n  \n  mItems.append(item);\n  if (!item->layer()) // usually the layer is already set in the constructor of the item (via QCPLayerable constructor)\n    item->setLayer(currentLayer());\n  return true;\n}\n\n/*! \\internal\n  \n  Assigns all layers their index (QCPLayer::mIndex) in the mLayers list. This method is thus called\n  after every operation that changes the layer indices, like layer removal, layer creation, layer\n  moving.\n*/\nvoid QCustomPlot::updateLayerIndices() const\n{\n  for (int i=0; i<mLayers.size(); ++i)\n    mLayers.at(i)->mIndex = i;\n}\n\n/*! \\internal\n\n  Returns the top-most layerable at pixel position \\a pos. If \\a onlySelectable is set to true,\n  only those layerables that are selectable will be considered. (Layerable subclasses communicate\n  their selectability via the QCPLayerable::selectTest method, by returning -1.)\n\n  \\a selectionDetails is an output parameter that contains selection specifics of the affected\n  layerable. This is useful if the respective layerable shall be given a subsequent\n  QCPLayerable::selectEvent (like in \\ref mouseReleaseEvent). \\a selectionDetails usually contains\n  information about which part of the layerable was hit, in multi-part layerables (e.g.\n  QCPAxis::SelectablePart). If the layerable is a plottable, \\a selectionDetails contains a \\ref\n  QCPDataSelection instance with the single data point which is closest to \\a pos.\n  \n  \\see layerableListAt, layoutElementAt, axisRectAt\n*/\nQCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const\n{\n  QList<QVariant> details;\n  QList<QCPLayerable*> candidates = layerableListAt(pos, onlySelectable, selectionDetails ? &details : nullptr);\n  if (selectionDetails && !details.isEmpty())\n    *selectionDetails = details.first();\n  if (!candidates.isEmpty())\n    return candidates.first();\n  else\n    return nullptr;\n}\n\n/*! \\internal\n\n  Returns the layerables at pixel position \\a pos. If \\a onlySelectable is set to true, only those\n  layerables that are selectable will be considered. (Layerable subclasses communicate their\n  selectability via the QCPLayerable::selectTest method, by returning -1.)\n\n  The returned list is sorted by the layerable/drawing order such that the layerable that appears\n  on top in the plot is at index 0 of the returned list. If you only need to know the top\n  layerable, rather use \\ref layerableAt.\n\n  \\a selectionDetails is an output parameter that contains selection specifics of the affected\n  layerable. This is useful if the respective layerable shall be given a subsequent\n  QCPLayerable::selectEvent (like in \\ref mouseReleaseEvent). \\a selectionDetails usually contains\n  information about which part of the layerable was hit, in multi-part layerables (e.g.\n  QCPAxis::SelectablePart). If the layerable is a plottable, \\a selectionDetails contains a \\ref\n  QCPDataSelection instance with the single data point which is closest to \\a pos.\n  \n  \\see layerableAt, layoutElementAt, axisRectAt\n*/\nQList<QCPLayerable*> QCustomPlot::layerableListAt(const QPointF &pos, bool onlySelectable, QList<QVariant> *selectionDetails) const\n{\n  QList<QCPLayerable*> result;\n  for (int layerIndex=mLayers.size()-1; layerIndex>=0; --layerIndex)\n  {\n    const QList<QCPLayerable*> layerables = mLayers.at(layerIndex)->children();\n    for (int i=layerables.size()-1; i>=0; --i)\n    {\n      if (!layerables.at(i)->realVisibility())\n        continue;\n      QVariant details;\n      double dist = layerables.at(i)->selectTest(pos, onlySelectable, selectionDetails ? &details : nullptr);\n      if (dist >= 0 && dist < selectionTolerance())\n      {\n        result.append(layerables.at(i));\n        if (selectionDetails)\n          selectionDetails->append(details);\n      }\n    }\n  }\n  return result;\n}\n\n/*!\n  Saves the plot to a rastered image file \\a fileName in the image format \\a format. The plot is\n  sized to \\a width and \\a height in pixels and scaled with \\a scale. (width 100 and scale 2.0 lead\n  to a full resolution file with width 200.) If the \\a format supports compression, \\a quality may\n  be between 0 and 100 to control it.\n\n  Returns true on success. If this function fails, most likely the given \\a format isn't supported\n  by the system, see Qt docs about QImageWriter::supportedImageFormats().\n\n  The \\a resolution will be written to the image file header (if the file format supports this) and\n  has no direct consequence for the quality or the pixel size. However, if opening the image with a\n  tool which respects the metadata, it will be able to scale the image to match either a given size\n  in real units of length (inch, centimeters, etc.), or the target display DPI. You can specify in\n  which units \\a resolution is given, by setting \\a resolutionUnit. The \\a resolution is converted\n  to the format's expected resolution unit internally.\n\n  \\see saveBmp, saveJpg, savePng, savePdf\n*/\nbool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality, int resolution, QCP::ResolutionUnit resolutionUnit)\n{\n  QImage buffer = toPixmap(width, height, scale).toImage();\n  \n  int dotsPerMeter = 0;\n  switch (resolutionUnit)\n  {\n    case QCP::ruDotsPerMeter: dotsPerMeter = resolution; break;\n    case QCP::ruDotsPerCentimeter: dotsPerMeter = resolution*100; break;\n    case QCP::ruDotsPerInch: dotsPerMeter = int(resolution/0.0254); break;\n  }\n  buffer.setDotsPerMeterX(dotsPerMeter); // this is saved together with some image formats, e.g. PNG, and is relevant when opening image in other tools\n  buffer.setDotsPerMeterY(dotsPerMeter); // this is saved together with some image formats, e.g. PNG, and is relevant when opening image in other tools\n  if (!buffer.isNull())\n    return buffer.save(fileName, format, quality);\n  else\n    return false;\n}\n\n/*!\n  Renders the plot to a pixmap and returns it.\n  \n  The plot is sized to \\a width and \\a height in pixels and scaled with \\a scale. (width 100 and\n  scale 2.0 lead to a full resolution pixmap with width 200.)\n  \n  \\see toPainter, saveRastered, saveBmp, savePng, saveJpg, savePdf\n*/\nQPixmap QCustomPlot::toPixmap(int width, int height, double scale)\n{\n  // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too.\n  int newWidth, newHeight;\n  if (width == 0 || height == 0)\n  {\n    newWidth = this->width();\n    newHeight = this->height();\n  } else\n  {\n    newWidth = width;\n    newHeight = height;\n  }\n  int scaledWidth = qRound(scale*newWidth);\n  int scaledHeight = qRound(scale*newHeight);\n\n  QPixmap result(scaledWidth, scaledHeight);\n  result.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent); // if using non-solid pattern, make transparent now and draw brush pattern later\n  QCPPainter painter;\n  painter.begin(&result);\n  if (painter.isActive())\n  {\n    QRect oldViewport = viewport();\n    setViewport(QRect(0, 0, newWidth, newHeight));\n    painter.setMode(QCPPainter::pmNoCaching);\n    if (!qFuzzyCompare(scale, 1.0))\n    {\n      if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines might disappear for very small scales\n        painter.setMode(QCPPainter::pmNonCosmetic);\n      painter.scale(scale, scale);\n    }\n    if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush) // solid fills were done a few lines above with QPixmap::fill\n      painter.fillRect(mViewport, mBackgroundBrush);\n    draw(&painter);\n    setViewport(oldViewport);\n    painter.end();\n  } else // might happen if pixmap has width or height zero\n  {\n    qDebug() << Q_FUNC_INFO << \"Couldn't activate painter on pixmap\";\n    return QPixmap();\n  }\n  return result;\n}\n\n/*!\n  Renders the plot using the passed \\a painter.\n  \n  The plot is sized to \\a width and \\a height in pixels. If the \\a painter's scale is not 1.0, the resulting plot will\n  appear scaled accordingly.\n  \n  \\note If you are restricted to using a QPainter (instead of QCPPainter), create a temporary QPicture and open a QCPPainter\n  on it. Then call \\ref toPainter with this QCPPainter. After ending the paint operation on the picture, draw it with\n  the QPainter. This will reproduce the painter actions the QCPPainter took, with a QPainter.\n  \n  \\see toPixmap\n*/\nvoid QCustomPlot::toPainter(QCPPainter *painter, int width, int height)\n{\n  // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too.\n  int newWidth, newHeight;\n  if (width == 0 || height == 0)\n  {\n    newWidth = this->width();\n    newHeight = this->height();\n  } else\n  {\n    newWidth = width;\n    newHeight = height;\n  }\n\n  if (painter->isActive())\n  {\n    QRect oldViewport = viewport();\n    setViewport(QRect(0, 0, newWidth, newHeight));\n    painter->setMode(QCPPainter::pmNoCaching);\n    if (mBackgroundBrush.style() != Qt::NoBrush) // unlike in toPixmap, we can't do QPixmap::fill for Qt::SolidPattern brush style, so we also draw solid fills with fillRect here\n      painter->fillRect(mViewport, mBackgroundBrush);\n    draw(painter);\n    setViewport(oldViewport);\n  } else\n    qDebug() << Q_FUNC_INFO << \"Passed painter is not active\";\n}\n/* end of 'src/core.cpp' */\n\n\n/* including file 'src/colorgradient.cpp'   */\n/* modified 2021-03-29T02:30:44, size 25278 */\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPColorGradient\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPColorGradient\n  \\brief Defines a color gradient for use with e.g. \\ref QCPColorMap\n  \n  This class describes a color gradient which can be used to encode data with color. For example,\n  QCPColorMap and QCPColorScale have \\ref QCPColorMap::setGradient \"setGradient\" methods which\n  take an instance of this class. Colors are set with \\ref setColorStopAt(double position, const QColor &color)\n  with a \\a position from 0 to 1. In between these defined color positions, the\n  color will be interpolated linearly either in RGB or HSV space, see \\ref setColorInterpolation.\n\n  Alternatively, load one of the preset color gradients shown in the image below, with \\ref\n  loadPreset, or by directly specifying the preset in the constructor.\n  \n  Apart from red, green and blue components, the gradient also interpolates the alpha values of the\n  configured color stops. This allows to display some portions of the data range as transparent in\n  the plot.\n  \n  How NaN values are interpreted can be configured with \\ref setNanHandling.\n  \n  \\image html QCPColorGradient.png\n  \n  The constructor \\ref QCPColorGradient(GradientPreset preset) allows directly converting a \\ref\n  GradientPreset to a QCPColorGradient. This means that you can directly pass \\ref GradientPreset\n  to all the \\a setGradient methods, e.g.:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorgradient-setgradient\n  \n  The total number of levels used in the gradient can be set with \\ref setLevelCount. Whether the\n  color gradient shall be applied periodically (wrapping around) to data values that lie outside\n  the data range specified on the plottable instance can be controlled with \\ref setPeriodic.\n*/\n\n/*!\n  Constructs a new, empty QCPColorGradient with no predefined color stops. You can add own color\n  stops with \\ref setColorStopAt.\n\n  The color level count is initialized to 350.\n*/\nQCPColorGradient::QCPColorGradient() :\n  mLevelCount(350),\n  mColorInterpolation(ciRGB),\n  mNanHandling(nhNone),\n  mNanColor(Qt::black),\n  mPeriodic(false),\n  mColorBufferInvalidated(true)\n{\n  mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);\n}\n\n/*!\n  Constructs a new QCPColorGradient initialized with the colors and color interpolation according\n  to \\a preset.\n\n  The color level count is initialized to 350.\n*/\nQCPColorGradient::QCPColorGradient(GradientPreset preset) :\n  mLevelCount(350),\n  mColorInterpolation(ciRGB),\n  mNanHandling(nhNone),\n  mNanColor(Qt::black),\n  mPeriodic(false),\n  mColorBufferInvalidated(true)\n{\n  mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);\n  loadPreset(preset);\n}\n\n/* undocumented operator */\nbool QCPColorGradient::operator==(const QCPColorGradient &other) const\n{\n  return ((other.mLevelCount == this->mLevelCount) &&\n          (other.mColorInterpolation == this->mColorInterpolation) &&\n          (other.mNanHandling == this ->mNanHandling) &&\n          (other.mNanColor == this->mNanColor) &&\n          (other.mPeriodic == this->mPeriodic) &&\n          (other.mColorStops == this->mColorStops));\n}\n\n/*!\n  Sets the number of discretization levels of the color gradient to \\a n. The default is 350 which\n  is typically enough to create a smooth appearance. The minimum number of levels is 2.\n\n  \\image html QCPColorGradient-levelcount.png\n*/\nvoid QCPColorGradient::setLevelCount(int n)\n{\n  if (n < 2)\n  {\n    qDebug() << Q_FUNC_INFO << \"n must be greater or equal 2 but was\" << n;\n    n = 2;\n  }\n  if (n != mLevelCount)\n  {\n    mLevelCount = n;\n    mColorBufferInvalidated = true;\n  }\n}\n\n/*!\n  Sets at which positions from 0 to 1 which color shall occur. The positions are the keys, the\n  colors are the values of the passed QMap \\a colorStops. In between these color stops, the color\n  is interpolated according to \\ref setColorInterpolation.\n  \n  A more convenient way to create a custom gradient may be to clear all color stops with \\ref\n  clearColorStops (or creating a new, empty QCPColorGradient) and then adding them one by one with\n  \\ref setColorStopAt.\n  \n  \\see clearColorStops\n*/\nvoid QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops)\n{\n  mColorStops = colorStops;\n  mColorBufferInvalidated = true;\n}\n\n/*!\n  Sets the \\a color the gradient will have at the specified \\a position (from 0 to 1). In between\n  these color stops, the color is interpolated according to \\ref setColorInterpolation.\n  \n  \\see setColorStops, clearColorStops\n*/\nvoid QCPColorGradient::setColorStopAt(double position, const QColor &color)\n{\n  mColorStops.insert(position, color);\n  mColorBufferInvalidated = true;\n}\n\n/*!\n  Sets whether the colors in between the configured color stops (see \\ref setColorStopAt) shall be\n  interpolated linearly in RGB or in HSV color space.\n  \n  For example, a sweep in RGB space from red to green will have a muddy brown intermediate color,\n  whereas in HSV space the intermediate color is yellow.\n*/\nvoid QCPColorGradient::setColorInterpolation(QCPColorGradient::ColorInterpolation interpolation)\n{\n  if (interpolation != mColorInterpolation)\n  {\n    mColorInterpolation = interpolation;\n    mColorBufferInvalidated = true;\n  }\n}\n\n/*!\n  Sets how NaNs in the data are displayed in the plot.\n  \n  \\see setNanColor\n*/\nvoid QCPColorGradient::setNanHandling(QCPColorGradient::NanHandling handling)\n{\n  mNanHandling = handling;\n}\n\n/*!\n  Sets the color that NaN data is represented by, if \\ref setNanHandling is set\n  to ref nhNanColor.\n  \n  \\see setNanHandling\n*/\nvoid QCPColorGradient::setNanColor(const QColor &color)\n{\n  mNanColor = color;\n}\n\n/*!\n  Sets whether data points that are outside the configured data range (e.g. \\ref\n  QCPColorMap::setDataRange) are colored by periodically repeating the color gradient or whether\n  they all have the same color, corresponding to the respective gradient boundary color.\n  \n  \\image html QCPColorGradient-periodic.png\n  \n  As shown in the image above, gradients that have the same start and end color are especially\n  suitable for a periodic gradient mapping, since they produce smooth color transitions throughout\n  the color map. A preset that has this property is \\ref gpHues.\n  \n  In practice, using periodic color gradients makes sense when the data corresponds to a periodic\n  dimension, such as an angle or a phase. If this is not the case, the color encoding might become\n  ambiguous, because multiple different data values are shown as the same color.\n*/\nvoid QCPColorGradient::setPeriodic(bool enabled)\n{\n  mPeriodic = enabled;\n}\n\n/*! \\overload\n  \n  This method is used to quickly convert a \\a data array to colors. The colors will be output in\n  the array \\a scanLine. Both \\a data and \\a scanLine must have the length \\a n when passed to this\n  function. The data range that shall be used for mapping the data value to the gradient is passed\n  in \\a range. \\a logarithmic indicates whether the data values shall be mapped to colors\n  logarithmically.\n\n  if \\a data actually contains 2D-data linearized via <tt>[row*columnCount + column]</tt>, you can\n  set \\a dataIndexFactor to <tt>columnCount</tt> to convert a column instead of a row of the data\n  array, in \\a scanLine. \\a scanLine will remain a regular (1D) array. This works because \\a data\n  is addressed <tt>data[i*dataIndexFactor]</tt>.\n  \n  Use the overloaded method to additionally provide alpha map data.\n\n  The QRgb values that are placed in \\a scanLine have their r, g, and b components premultiplied\n  with alpha (see QImage::Format_ARGB32_Premultiplied).\n*/\nvoid QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)\n{\n  // If you change something here, make sure to also adapt color() and the other colorize() overload\n  if (!data)\n  {\n    qDebug() << Q_FUNC_INFO << \"null pointer given as data\";\n    return;\n  }\n  if (!scanLine)\n  {\n    qDebug() << Q_FUNC_INFO << \"null pointer given as scanLine\";\n    return;\n  }\n  if (mColorBufferInvalidated)\n    updateColorBuffer();\n  \n  const bool skipNanCheck = mNanHandling == nhNone;\n  const double posToIndexFactor = !logarithmic ? (mLevelCount-1)/range.size() : (mLevelCount-1)/qLn(range.upper/range.lower);\n  for (int i=0; i<n; ++i)\n  {\n    const double value = data[dataIndexFactor*i];\n    if (skipNanCheck || !std::isnan(value))\n    {\n      int index = int((!logarithmic ? value-range.lower : qLn(value/range.lower)) * posToIndexFactor);\n      if (!mPeriodic)\n      {\n        index = qBound(0, index, mLevelCount-1);\n      } else\n      {\n        index %= mLevelCount;\n        if (index < 0)\n          index += mLevelCount;\n      }\n      scanLine[i] = mColorBuffer.at(index);\n    } else\n    {\n      switch(mNanHandling)\n      {\n      case nhLowestColor: scanLine[i] = mColorBuffer.first(); break;\n      case nhHighestColor: scanLine[i] = mColorBuffer.last(); break;\n      case nhTransparent: scanLine[i] = qRgba(0, 0, 0, 0); break;\n      case nhNanColor: scanLine[i] = mNanColor.rgba(); break;\n      case nhNone: break; // shouldn't happen\n      }\n    }\n  }\n}\n\n/*! \\overload\n\n  Additionally to the other overload of \\ref colorize, this method takes the array \\a alpha, which\n  has the same size and structure as \\a data and encodes the alpha information per data point.\n\n  The QRgb values that are placed in \\a scanLine have their r, g and b components premultiplied\n  with alpha (see QImage::Format_ARGB32_Premultiplied).\n*/\nvoid QCPColorGradient::colorize(const double *data, const unsigned char *alpha, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)\n{\n  // If you change something here, make sure to also adapt color() and the other colorize() overload\n  if (!data)\n  {\n    qDebug() << Q_FUNC_INFO << \"null pointer given as data\";\n    return;\n  }\n  if (!alpha)\n  {\n    qDebug() << Q_FUNC_INFO << \"null pointer given as alpha\";\n    return;\n  }\n  if (!scanLine)\n  {\n    qDebug() << Q_FUNC_INFO << \"null pointer given as scanLine\";\n    return;\n  }\n  if (mColorBufferInvalidated)\n    updateColorBuffer();\n  \n  const bool skipNanCheck = mNanHandling == nhNone;\n  const double posToIndexFactor = !logarithmic ? (mLevelCount-1)/range.size() : (mLevelCount-1)/qLn(range.upper/range.lower);\n  for (int i=0; i<n; ++i)\n  {\n    const double value = data[dataIndexFactor*i];\n    if (skipNanCheck || !std::isnan(value))\n    {\n      int index = int((!logarithmic ? value-range.lower : qLn(value/range.lower)) * posToIndexFactor);\n      if (!mPeriodic)\n      {\n        index = qBound(0, index, mLevelCount-1);\n      } else\n      {\n        index %= mLevelCount;\n        if (index < 0)\n          index += mLevelCount;\n      }\n      if (alpha[dataIndexFactor*i] == 255)\n      {\n        scanLine[i] = mColorBuffer.at(index);\n      } else\n      {\n        const QRgb rgb = mColorBuffer.at(index);\n        const float alphaF = alpha[dataIndexFactor*i]/255.0f;\n        scanLine[i] = qRgba(int(qRed(rgb)*alphaF), int(qGreen(rgb)*alphaF), int(qBlue(rgb)*alphaF), int(qAlpha(rgb)*alphaF)); // also multiply r,g,b with alpha, to conform to Format_ARGB32_Premultiplied\n      }\n    } else\n    {\n      switch(mNanHandling)\n      {\n      case nhLowestColor: scanLine[i] = mColorBuffer.first(); break;\n      case nhHighestColor: scanLine[i] = mColorBuffer.last(); break;\n      case nhTransparent: scanLine[i] = qRgba(0, 0, 0, 0); break;\n      case nhNanColor: scanLine[i] = mNanColor.rgba(); break;\n      case nhNone: break; // shouldn't happen\n      }\n    }\n  }\n}\n\n/*! \\internal\n\n  This method is used to colorize a single data value given in \\a position, to colors. The data\n  range that shall be used for mapping the data value to the gradient is passed in \\a range. \\a\n  logarithmic indicates whether the data value shall be mapped to a color logarithmically.\n\n  If an entire array of data values shall be converted, rather use \\ref colorize, for better\n  performance.\n\n  The returned QRgb has its r, g and b components premultiplied with alpha (see\n  QImage::Format_ARGB32_Premultiplied).\n*/\nQRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic)\n{\n  // If you change something here, make sure to also adapt ::colorize()\n  if (mColorBufferInvalidated)\n    updateColorBuffer();\n  \n  const bool skipNanCheck = mNanHandling == nhNone;\n  if (!skipNanCheck && std::isnan(position))\n  {\n    switch(mNanHandling)\n    {\n    case nhLowestColor: return mColorBuffer.first();\n    case nhHighestColor: return mColorBuffer.last();\n    case nhTransparent: return qRgba(0, 0, 0, 0);\n    case nhNanColor: return mNanColor.rgba();\n    case nhNone: return qRgba(0, 0, 0, 0); // shouldn't happen\n    }\n  }\n  \n  const double posToIndexFactor = !logarithmic ? (mLevelCount-1)/range.size() : (mLevelCount-1)/qLn(range.upper/range.lower);\n  int index = int((!logarithmic ? position-range.lower : qLn(position/range.lower)) * posToIndexFactor);\n  if (!mPeriodic)\n  {\n    index = qBound(0, index, mLevelCount-1);\n  } else\n  {\n    index %= mLevelCount;\n    if (index < 0)\n      index += mLevelCount;\n  }\n  return mColorBuffer.at(index);\n}\n\n/*!\n  Clears the current color stops and loads the specified \\a preset. A preset consists of predefined\n  color stops and the corresponding color interpolation method.\n  \n  The available presets are:\n  \\image html QCPColorGradient.png\n*/\nvoid QCPColorGradient::loadPreset(GradientPreset preset)\n{\n  clearColorStops();\n  switch (preset)\n  {\n    case gpGrayscale:\n      setColorInterpolation(ciRGB);\n      setColorStopAt(0, Qt::black);\n      setColorStopAt(1, Qt::white);\n      break;\n    case gpHot:\n      setColorInterpolation(ciRGB);\n      setColorStopAt(0, QColor(50, 0, 0));\n      setColorStopAt(0.2, QColor(180, 10, 0));\n      setColorStopAt(0.4, QColor(245, 50, 0));\n      setColorStopAt(0.6, QColor(255, 150, 10));\n      setColorStopAt(0.8, QColor(255, 255, 50));\n      setColorStopAt(1, QColor(255, 255, 255));\n      break;\n    case gpCold:\n      setColorInterpolation(ciRGB);\n      setColorStopAt(0, QColor(0, 0, 50));\n      setColorStopAt(0.2, QColor(0, 10, 180));\n      setColorStopAt(0.4, QColor(0, 50, 245));\n      setColorStopAt(0.6, QColor(10, 150, 255));\n      setColorStopAt(0.8, QColor(50, 255, 255));\n      setColorStopAt(1, QColor(255, 255, 255));\n      break;\n    case gpNight:\n      setColorInterpolation(ciHSV);\n      setColorStopAt(0, QColor(10, 20, 30));\n      setColorStopAt(1, QColor(250, 255, 250));\n      break;\n    case gpCandy:\n      setColorInterpolation(ciHSV);\n      setColorStopAt(0, QColor(0, 0, 255));\n      setColorStopAt(1, QColor(255, 250, 250));\n      break;\n    case gpGeography:\n      setColorInterpolation(ciRGB);\n      setColorStopAt(0, QColor(70, 170, 210));\n      setColorStopAt(0.20, QColor(90, 160, 180));\n      setColorStopAt(0.25, QColor(45, 130, 175));\n      setColorStopAt(0.30, QColor(100, 140, 125));\n      setColorStopAt(0.5, QColor(100, 140, 100));\n      setColorStopAt(0.6, QColor(130, 145, 120));\n      setColorStopAt(0.7, QColor(140, 130, 120));\n      setColorStopAt(0.9, QColor(180, 190, 190));\n      setColorStopAt(1, QColor(210, 210, 230));\n      break;\n    case gpIon:\n      setColorInterpolation(ciHSV);\n      setColorStopAt(0, QColor(50, 10, 10));\n      setColorStopAt(0.45, QColor(0, 0, 255));\n      setColorStopAt(0.8, QColor(0, 255, 255));\n      setColorStopAt(1, QColor(0, 255, 0));\n      break;\n    case gpThermal:\n      setColorInterpolation(ciRGB);\n      setColorStopAt(0, QColor(0, 0, 50));\n      setColorStopAt(0.15, QColor(20, 0, 120));\n      setColorStopAt(0.33, QColor(200, 30, 140));\n      setColorStopAt(0.6, QColor(255, 100, 0));\n      setColorStopAt(0.85, QColor(255, 255, 40));\n      setColorStopAt(1, QColor(255, 255, 255));\n      break;\n    case gpPolar:\n      setColorInterpolation(ciRGB);\n      setColorStopAt(0, QColor(50, 255, 255));\n      setColorStopAt(0.18, QColor(10, 70, 255));\n      setColorStopAt(0.28, QColor(10, 10, 190));\n      setColorStopAt(0.5, QColor(0, 0, 0));\n      setColorStopAt(0.72, QColor(190, 10, 10));\n      setColorStopAt(0.82, QColor(255, 70, 10));\n      setColorStopAt(1, QColor(255, 255, 50));\n      break;\n    case gpSpectrum:\n      setColorInterpolation(ciHSV);\n      setColorStopAt(0, QColor(50, 0, 50));\n      setColorStopAt(0.15, QColor(0, 0, 255));\n      setColorStopAt(0.35, QColor(0, 255, 255));\n      setColorStopAt(0.6, QColor(255, 255, 0));\n      setColorStopAt(0.75, QColor(255, 30, 0));\n      setColorStopAt(1, QColor(50, 0, 0));\n      break;\n    case gpJet:\n      setColorInterpolation(ciRGB);\n      setColorStopAt(0, QColor(0, 0, 100));\n      setColorStopAt(0.15, QColor(0, 50, 255));\n      setColorStopAt(0.35, QColor(0, 255, 255));\n      setColorStopAt(0.65, QColor(255, 255, 0));\n      setColorStopAt(0.85, QColor(255, 30, 0));\n      setColorStopAt(1, QColor(100, 0, 0));\n      break;\n    case gpHues:\n      setColorInterpolation(ciHSV);\n      setColorStopAt(0, QColor(255, 0, 0));\n      setColorStopAt(1.0/3.0, QColor(0, 0, 255));\n      setColorStopAt(2.0/3.0, QColor(0, 255, 0));\n      setColorStopAt(1, QColor(255, 0, 0));\n      break;\n  }\n}\n\n/*!\n  Clears all color stops.\n  \n  \\see setColorStops, setColorStopAt\n*/\nvoid QCPColorGradient::clearColorStops()\n{\n  mColorStops.clear();\n  mColorBufferInvalidated = true;\n}\n\n/*!\n  Returns an inverted gradient. The inverted gradient has all properties as this \\ref\n  QCPColorGradient, but the order of the color stops is inverted.\n  \n  \\see setColorStops, setColorStopAt\n*/\nQCPColorGradient QCPColorGradient::inverted() const\n{\n  QCPColorGradient result(*this);\n  result.clearColorStops();\n  for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)\n    result.setColorStopAt(1.0-it.key(), it.value());\n  return result;\n}\n\n/*! \\internal\n  \n  Returns true if the color gradient uses transparency, i.e. if any of the configured color stops\n  has an alpha value below 255.\n*/\nbool QCPColorGradient::stopsUseAlpha() const\n{\n  for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)\n  {\n    if (it.value().alpha() < 255)\n      return true;\n  }\n  return false;\n}\n\n/*! \\internal\n  \n  Updates the internal color buffer which will be used by \\ref colorize and \\ref color, to quickly\n  convert positions to colors. This is where the interpolation between color stops is calculated.\n*/\nvoid QCPColorGradient::updateColorBuffer()\n{\n  if (mColorBuffer.size() != mLevelCount)\n    mColorBuffer.resize(mLevelCount);\n  if (mColorStops.size() > 1)\n  {\n    double indexToPosFactor = 1.0/double(mLevelCount-1);\n    const bool useAlpha = stopsUseAlpha();\n    for (int i=0; i<mLevelCount; ++i)\n    {\n      double position = i*indexToPosFactor;\n      QMap<double, QColor>::const_iterator it = mColorStops.lowerBound(position);\n      if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop\n      {\n        if (useAlpha)\n        {\n          const QColor col = std::prev(it).value();\n          const double alphaPremultiplier = col.alpha()/255.0; // since we use QImage::Format_ARGB32_Premultiplied\n          mColorBuffer[i] = qRgba(int(col.red()*alphaPremultiplier),\n                                  int(col.green()*alphaPremultiplier),\n                                  int(col.blue()*alphaPremultiplier),\n                                  col.alpha());\n        } else\n          mColorBuffer[i] = std::prev(it).value().rgba();\n      } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop\n      {\n        if (useAlpha)\n        {\n          const QColor &col = it.value();\n          const double alphaPremultiplier = col.alpha()/255.0; // since we use QImage::Format_ARGB32_Premultiplied\n          mColorBuffer[i] = qRgba(int(col.red()*alphaPremultiplier),\n                                  int(col.green()*alphaPremultiplier),\n                                  int(col.blue()*alphaPremultiplier),\n                                  col.alpha());\n        } else\n          mColorBuffer[i] = it.value().rgba();\n      } else // position is in between stops (or on an intermediate stop), interpolate color\n      {\n        QMap<double, QColor>::const_iterator high = it;\n        QMap<double, QColor>::const_iterator low = std::prev(it);\n        double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1\n        switch (mColorInterpolation)\n        {\n          case ciRGB:\n          {\n            if (useAlpha)\n            {\n              const int alpha = int((1-t)*low.value().alpha() + t*high.value().alpha());\n              const double alphaPremultiplier = alpha/255.0; // since we use QImage::Format_ARGB32_Premultiplied\n              mColorBuffer[i] = qRgba(int( ((1-t)*low.value().red() + t*high.value().red())*alphaPremultiplier ),\n                                      int( ((1-t)*low.value().green() + t*high.value().green())*alphaPremultiplier ),\n                                      int( ((1-t)*low.value().blue() + t*high.value().blue())*alphaPremultiplier ),\n                                      alpha);\n            } else\n            {\n              mColorBuffer[i] = qRgb(int( ((1-t)*low.value().red() + t*high.value().red()) ),\n                                     int( ((1-t)*low.value().green() + t*high.value().green()) ),\n                                     int( ((1-t)*low.value().blue() + t*high.value().blue())) );\n            }\n            break;\n          }\n          case ciHSV:\n          {\n            QColor lowHsv = low.value().toHsv();\n            QColor highHsv = high.value().toHsv();\n            double hue = 0;\n            double hueDiff = highHsv.hueF()-lowHsv.hueF();\n            if (hueDiff > 0.5)\n              hue = lowHsv.hueF() - t*(1.0-hueDiff);\n            else if (hueDiff < -0.5)\n              hue = lowHsv.hueF() + t*(1.0+hueDiff);\n            else\n              hue = lowHsv.hueF() + t*hueDiff;\n            if (hue < 0) hue += 1.0;\n            else if (hue >= 1.0) hue -= 1.0;\n            if (useAlpha)\n            {\n              const QRgb rgb = QColor::fromHsvF(hue,\n                                                (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(),\n                                                (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();\n              const double alpha = (1-t)*lowHsv.alphaF() + t*highHsv.alphaF();\n              mColorBuffer[i] = qRgba(int(qRed(rgb)*alpha), int(qGreen(rgb)*alpha), int(qBlue(rgb)*alpha), int(255*alpha));\n            }\n            else\n            {\n              mColorBuffer[i] = QColor::fromHsvF(hue,\n                                                 (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(),\n                                                 (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();\n            }\n            break;\n          }\n        }\n      }\n    }\n  } else if (mColorStops.size() == 1)\n  {\n    const QRgb rgb = mColorStops.constBegin().value().rgb();\n    const double alpha = mColorStops.constBegin().value().alphaF();\n    mColorBuffer.fill(qRgba(int(qRed(rgb)*alpha), int(qGreen(rgb)*alpha), int(qBlue(rgb)*alpha), int(255*alpha)));\n  } else // mColorStops is empty, fill color buffer with black\n  {\n    mColorBuffer.fill(qRgb(0, 0, 0));\n  }\n  mColorBufferInvalidated = false;\n}\n/* end of 'src/colorgradient.cpp' */\n\n\n/* including file 'src/selectiondecorator-bracket.cpp' */\n/* modified 2021-03-29T02:30:44, size 12308            */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPSelectionDecoratorBracket\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPSelectionDecoratorBracket\n  \\brief A selection decorator which draws brackets around each selected data segment\n  \n  Additionally to the regular highlighting of selected segments via color, fill and scatter style,\n  this \\ref QCPSelectionDecorator subclass draws markers at the begin and end of each selected data\n  segment of the plottable.\n  \n  The shape of the markers can be controlled with \\ref setBracketStyle, \\ref setBracketWidth and\n  \\ref setBracketHeight. The color/fill can be controlled with \\ref setBracketPen and \\ref\n  setBracketBrush.\n  \n  To introduce custom bracket styles, it is only necessary to sublcass \\ref\n  QCPSelectionDecoratorBracket and reimplement \\ref drawBracket. The rest will be managed by the\n  base class.\n*/\n\n/*!\n  Creates a new QCPSelectionDecoratorBracket instance with default values.\n*/\nQCPSelectionDecoratorBracket::QCPSelectionDecoratorBracket() :\n  mBracketPen(QPen(Qt::black)),\n  mBracketBrush(Qt::NoBrush),\n  mBracketWidth(5),\n  mBracketHeight(50),\n  mBracketStyle(bsSquareBracket),\n  mTangentToData(false),\n  mTangentAverage(2)\n{\n  \n}\n\nQCPSelectionDecoratorBracket::~QCPSelectionDecoratorBracket()\n{\n}\n\n/*!\n  Sets the pen that will be used to draw the brackets at the beginning and end of each selected\n  data segment.\n*/\nvoid QCPSelectionDecoratorBracket::setBracketPen(const QPen &pen)\n{\n  mBracketPen = pen;\n}\n\n/*!\n  Sets the brush that will be used to draw the brackets at the beginning and end of each selected\n  data segment.\n*/\nvoid QCPSelectionDecoratorBracket::setBracketBrush(const QBrush &brush)\n{\n  mBracketBrush = brush;\n}\n\n/*!\n  Sets the width of the drawn bracket. The width dimension is always parallel to the key axis of\n  the data, or the tangent direction of the current data slope, if \\ref setTangentToData is\n  enabled.\n*/\nvoid QCPSelectionDecoratorBracket::setBracketWidth(int width)\n{\n  mBracketWidth = width;\n}\n\n/*!\n  Sets the height of the drawn bracket. The height dimension is always perpendicular to the key axis\n  of the data, or the tangent direction of the current data slope, if \\ref setTangentToData is\n  enabled.\n*/\nvoid QCPSelectionDecoratorBracket::setBracketHeight(int height)\n{\n  mBracketHeight = height;\n}\n\n/*!\n  Sets the shape that the bracket/marker will have.\n  \n  \\see setBracketWidth, setBracketHeight\n*/\nvoid QCPSelectionDecoratorBracket::setBracketStyle(QCPSelectionDecoratorBracket::BracketStyle style)\n{\n  mBracketStyle = style;\n}\n\n/*!\n  Sets whether the brackets will be rotated such that they align with the slope of the data at the\n  position that they appear in.\n  \n  For noisy data, it might be more visually appealing to average the slope over multiple data\n  points. This can be configured via \\ref setTangentAverage.\n*/\nvoid QCPSelectionDecoratorBracket::setTangentToData(bool enabled)\n{\n  mTangentToData = enabled;\n}\n\n/*!\n  Controls over how many data points the slope shall be averaged, when brackets shall be aligned\n  with the data (if \\ref setTangentToData is true).\n  \n  From the position of the bracket, \\a pointCount points towards the selected data range will be\n  taken into account. The smallest value of \\a pointCount is 1, which is effectively equivalent to\n  disabling \\ref setTangentToData.\n*/\nvoid QCPSelectionDecoratorBracket::setTangentAverage(int pointCount)\n{\n  mTangentAverage = pointCount;\n  if (mTangentAverage < 1)\n    mTangentAverage = 1;\n}\n\n/*!\n  Draws the bracket shape with \\a painter. The parameter \\a direction is either -1 or 1 and\n  indicates whether the bracket shall point to the left or the right (i.e. is a closing or opening\n  bracket, respectively).\n  \n  The passed \\a painter already contains all transformations that are necessary to position and\n  rotate the bracket appropriately. Painting operations can be performed as if drawing upright\n  brackets on flat data with horizontal key axis, with (0, 0) being the center of the bracket.\n  \n  If you wish to sublcass \\ref QCPSelectionDecoratorBracket in order to provide custom bracket\n  shapes (see \\ref QCPSelectionDecoratorBracket::bsUserStyle), this is the method you should\n  reimplement.\n*/\nvoid QCPSelectionDecoratorBracket::drawBracket(QCPPainter *painter, int direction) const\n{\n  switch (mBracketStyle)\n  {\n    case bsSquareBracket:\n    {\n      painter->drawLine(QLineF(mBracketWidth*direction, -mBracketHeight*0.5, 0, -mBracketHeight*0.5));\n      painter->drawLine(QLineF(mBracketWidth*direction, mBracketHeight*0.5, 0, mBracketHeight*0.5));\n      painter->drawLine(QLineF(0, -mBracketHeight*0.5, 0, mBracketHeight*0.5));\n      break;\n    }\n    case bsHalfEllipse:\n    {\n      painter->drawArc(QRectF(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight), -90*16, -180*16*direction);\n      break;\n    }\n    case bsEllipse:\n    {\n      painter->drawEllipse(QRectF(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight));\n      break;\n    }\n    case bsPlus:\n    {\n      painter->drawLine(QLineF(0, -mBracketHeight*0.5, 0, mBracketHeight*0.5));\n      painter->drawLine(QLineF(-mBracketWidth*0.5, 0, mBracketWidth*0.5, 0));\n      break;\n    }\n    default:\n    {\n      qDebug() << Q_FUNC_INFO << \"unknown/custom bracket style can't be handeld by default implementation:\" << static_cast<int>(mBracketStyle);\n      break;\n    }\n  }\n}\n\n/*!\n  Draws the bracket decoration on the data points at the begin and end of each selected data\n  segment given in \\a seletion.\n  \n  It uses the method \\ref drawBracket to actually draw the shapes.\n  \n  \\seebaseclassmethod\n*/\nvoid QCPSelectionDecoratorBracket::drawDecoration(QCPPainter *painter, QCPDataSelection selection)\n{\n  if (!mPlottable || selection.isEmpty()) return;\n  \n  if (QCPPlottableInterface1D *interface1d = mPlottable->interface1D())\n  {\n    foreach (const QCPDataRange &dataRange, selection.dataRanges())\n    {\n      // determine position and (if tangent mode is enabled) angle of brackets:\n      int openBracketDir = (mPlottable->keyAxis() && !mPlottable->keyAxis()->rangeReversed()) ? 1 : -1;\n      int closeBracketDir = -openBracketDir;\n      QPointF openBracketPos = getPixelCoordinates(interface1d, dataRange.begin());\n      QPointF closeBracketPos = getPixelCoordinates(interface1d, dataRange.end()-1);\n      double openBracketAngle = 0;\n      double closeBracketAngle = 0;\n      if (mTangentToData)\n      {\n        openBracketAngle = getTangentAngle(interface1d, dataRange.begin(), openBracketDir);\n        closeBracketAngle = getTangentAngle(interface1d, dataRange.end()-1, closeBracketDir);\n      }\n      // draw opening bracket:\n      QTransform oldTransform = painter->transform();\n      painter->setPen(mBracketPen);\n      painter->setBrush(mBracketBrush);\n      painter->translate(openBracketPos);\n      painter->rotate(openBracketAngle/M_PI*180.0);\n      drawBracket(painter, openBracketDir);\n      painter->setTransform(oldTransform);\n      // draw closing bracket:\n      painter->setPen(mBracketPen);\n      painter->setBrush(mBracketBrush);\n      painter->translate(closeBracketPos);\n      painter->rotate(closeBracketAngle/M_PI*180.0);\n      drawBracket(painter, closeBracketDir);\n      painter->setTransform(oldTransform);\n    }\n  }\n}\n\n/*! \\internal\n  \n  If \\ref setTangentToData is enabled, brackets need to be rotated according to the data slope.\n  This method returns the angle in radians by which a bracket at the given \\a dataIndex must be\n  rotated.\n  \n  The parameter \\a direction must be set to either -1 or 1, representing whether it is an opening\n  or closing bracket. Since for slope calculation multiple data points are required, this defines\n  the direction in which the algorithm walks, starting at \\a dataIndex, to average those data\n  points. (see \\ref setTangentToData and \\ref setTangentAverage)\n  \n  \\a interface1d is the interface to the plottable's data which is used to query data coordinates.\n*/\ndouble QCPSelectionDecoratorBracket::getTangentAngle(const QCPPlottableInterface1D *interface1d, int dataIndex, int direction) const\n{\n  if (!interface1d || dataIndex < 0 || dataIndex >= interface1d->dataCount())\n    return 0;\n  direction = direction < 0 ? -1 : 1; // enforce direction is either -1 or 1\n  \n  // how many steps we can actually go from index in the given direction without exceeding data bounds:\n  int averageCount;\n  if (direction < 0)\n    averageCount = qMin(mTangentAverage, dataIndex);\n  else\n    averageCount = qMin(mTangentAverage, interface1d->dataCount()-1-dataIndex);\n  qDebug() << averageCount;\n  // calculate point average of averageCount points:\n  QVector<QPointF> points(averageCount);\n  QPointF pointsAverage;\n  int currentIndex = dataIndex;\n  for (int i=0; i<averageCount; ++i)\n  {\n    points[i] = getPixelCoordinates(interface1d, currentIndex);\n    pointsAverage += points[i];\n    currentIndex += direction;\n  }\n  pointsAverage /= double(averageCount);\n  \n  // calculate slope of linear regression through points:\n  double numSum = 0;\n  double denomSum = 0;\n  for (int i=0; i<averageCount; ++i)\n  {\n    const double dx = points.at(i).x()-pointsAverage.x();\n    const double dy = points.at(i).y()-pointsAverage.y();\n    numSum += dx*dy;\n    denomSum += dx*dx;\n  }\n  if (!qFuzzyIsNull(denomSum) && !qFuzzyIsNull(numSum))\n  {\n    return qAtan2(numSum, denomSum);\n  } else // undetermined angle, probably mTangentAverage == 1, so using only one data point\n    return 0;\n}\n\n/*! \\internal\n  \n  Returns the pixel coordinates of the data point at \\a dataIndex, using \\a interface1d to access\n  the data points.\n*/\nQPointF QCPSelectionDecoratorBracket::getPixelCoordinates(const QCPPlottableInterface1D *interface1d, int dataIndex) const\n{\n  QCPAxis *keyAxis = mPlottable->keyAxis();\n  QCPAxis *valueAxis = mPlottable->valueAxis();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return {0, 0}; }\n  \n  if (keyAxis->orientation() == Qt::Horizontal)\n    return {keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex)), valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex))};\n  else\n    return {valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex)), keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex))};\n}\n/* end of 'src/selectiondecorator-bracket.cpp' */\n\n\n/* including file 'src/layoutelements/layoutelement-axisrect.cpp' */\n/* modified 2021-03-29T02:30:44, size 47193                       */\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPAxisRect\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPAxisRect\n  \\brief Holds multiple axes and arranges them in a rectangular shape.\n  \n  This class represents an axis rect, a rectangular area that is bounded on all sides with an\n  arbitrary number of axes.\n  \n  Initially QCustomPlot has one axis rect, accessible via QCustomPlot::axisRect(). However, the\n  layout system allows to have multiple axis rects, e.g. arranged in a grid layout\n  (QCustomPlot::plotLayout).\n  \n  By default, QCPAxisRect comes with four axes, at bottom, top, left and right. They can be\n  accessed via \\ref axis by providing the respective axis type (\\ref QCPAxis::AxisType) and index.\n  If you need all axes in the axis rect, use \\ref axes. The top and right axes are set to be\n  invisible initially (QCPAxis::setVisible). To add more axes to a side, use \\ref addAxis or \\ref\n  addAxes. To remove an axis, use \\ref removeAxis.\n  \n  The axis rect layerable itself only draws a background pixmap or color, if specified (\\ref\n  setBackground). It is placed on the \"background\" layer initially (see \\ref QCPLayer for an\n  explanation of the QCustomPlot layer system). The axes that are held by the axis rect can be\n  placed on other layers, independently of the axis rect.\n  \n  Every axis rect has a child layout of type \\ref QCPLayoutInset. It is accessible via \\ref\n  insetLayout and can be used to have other layout elements (or even other layouts with multiple\n  elements) hovering inside the axis rect.\n  \n  If an axis rect is clicked and dragged, it processes this by moving certain axis ranges. The\n  behaviour can be controlled with \\ref setRangeDrag and \\ref setRangeDragAxes. If the mouse wheel\n  is scrolled while the cursor is on the axis rect, certain axes are scaled. This is controllable\n  via \\ref setRangeZoom, \\ref setRangeZoomAxes and \\ref setRangeZoomFactor. These interactions are\n  only enabled if \\ref QCustomPlot::setInteractions contains \\ref QCP::iRangeDrag and \\ref\n  QCP::iRangeZoom.\n  \n  \\image html AxisRectSpacingOverview.png\n  <center>Overview of the spacings and paddings that define the geometry of an axis. The dashed\n  line on the far left indicates the viewport/widget border.</center>\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn QCPLayoutInset *QCPAxisRect::insetLayout() const\n  \n  Returns the inset layout of this axis rect. It can be used to place other layout elements (or\n  even layouts with multiple other elements) inside/on top of an axis rect.\n  \n  \\see QCPLayoutInset\n*/\n\n/*! \\fn int QCPAxisRect::left() const\n  \n  Returns the pixel position of the left border of this axis rect. Margins are not taken into\n  account here, so the returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn int QCPAxisRect::right() const\n  \n  Returns the pixel position of the right border of this axis rect. Margins are not taken into\n  account here, so the returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn int QCPAxisRect::top() const\n  \n  Returns the pixel position of the top border of this axis rect. Margins are not taken into\n  account here, so the returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn int QCPAxisRect::bottom() const\n  \n  Returns the pixel position of the bottom border of this axis rect. Margins are not taken into\n  account here, so the returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn int QCPAxisRect::width() const\n  \n  Returns the pixel width of this axis rect. Margins are not taken into account here, so the\n  returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn int QCPAxisRect::height() const\n  \n  Returns the pixel height of this axis rect. Margins are not taken into account here, so the\n  returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn QSize QCPAxisRect::size() const\n  \n  Returns the pixel size of this axis rect. Margins are not taken into account here, so the\n  returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn QPoint QCPAxisRect::topLeft() const\n  \n  Returns the top left corner of this axis rect in pixels. Margins are not taken into account here,\n  so the returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn QPoint QCPAxisRect::topRight() const\n  \n  Returns the top right corner of this axis rect in pixels. Margins are not taken into account\n  here, so the returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn QPoint QCPAxisRect::bottomLeft() const\n  \n  Returns the bottom left corner of this axis rect in pixels. Margins are not taken into account\n  here, so the returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn QPoint QCPAxisRect::bottomRight() const\n  \n  Returns the bottom right corner of this axis rect in pixels. Margins are not taken into account\n  here, so the returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn QPoint QCPAxisRect::center() const\n  \n  Returns the center of this axis rect in pixels. Margins are not taken into account here, so the\n  returned value is with respect to the inner \\ref rect.\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Creates a QCPAxisRect instance and sets default values. An axis is added for each of the four\n  sides, the top and right axes are set invisible initially.\n*/\nQCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :\n  QCPLayoutElement(parentPlot),\n  mBackgroundBrush(Qt::NoBrush),\n  mBackgroundScaled(true),\n  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),\n  mInsetLayout(new QCPLayoutInset),\n  mRangeDrag(Qt::Horizontal|Qt::Vertical),\n  mRangeZoom(Qt::Horizontal|Qt::Vertical),\n  mRangeZoomFactorHorz(0.85),\n  mRangeZoomFactorVert(0.85),\n  mDragging(false)\n{\n  mInsetLayout->initializeParentPlot(mParentPlot);\n  mInsetLayout->setParentLayerable(this);\n  mInsetLayout->setParent(this);\n  \n  setMinimumSize(50, 50);\n  setMinimumMargins(QMargins(15, 15, 15, 15));\n  mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());\n  mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());\n  mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());\n  mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());\n  \n  if (setupDefaultAxes)\n  {\n    QCPAxis *xAxis = addAxis(QCPAxis::atBottom);\n    QCPAxis *yAxis = addAxis(QCPAxis::atLeft);\n    QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);\n    QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);\n    setRangeDragAxes(xAxis, yAxis);\n    setRangeZoomAxes(xAxis, yAxis);\n    xAxis2->setVisible(false);\n    yAxis2->setVisible(false);\n    xAxis->grid()->setVisible(true);\n    yAxis->grid()->setVisible(true);\n    xAxis2->grid()->setVisible(false);\n    yAxis2->grid()->setVisible(false);\n    xAxis2->grid()->setZeroLinePen(Qt::NoPen);\n    yAxis2->grid()->setZeroLinePen(Qt::NoPen);\n    xAxis2->grid()->setVisible(false);\n    yAxis2->grid()->setVisible(false);\n  }\n}\n\nQCPAxisRect::~QCPAxisRect()\n{\n  delete mInsetLayout;\n  mInsetLayout = nullptr;\n  \n  foreach (QCPAxis *axis, axes())\n    removeAxis(axis);\n}\n\n/*!\n  Returns the number of axes on the axis rect side specified with \\a type.\n  \n  \\see axis\n*/\nint QCPAxisRect::axisCount(QCPAxis::AxisType type) const\n{\n  return mAxes.value(type).size();\n}\n\n/*!\n  Returns the axis with the given \\a index on the axis rect side specified with \\a type.\n  \n  \\see axisCount, axes\n*/\nQCPAxis *QCPAxisRect::axis(QCPAxis::AxisType type, int index) const\n{\n  QList<QCPAxis*> ax(mAxes.value(type));\n  if (index >= 0 && index < ax.size())\n  {\n    return ax.at(index);\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"Axis index out of bounds:\" << index;\n    return nullptr;\n  }\n}\n\n/*!\n  Returns all axes on the axis rect sides specified with \\a types.\n  \n  \\a types may be a single \\ref QCPAxis::AxisType or an <tt>or</tt>-combination, to get the axes of\n  multiple sides.\n  \n  \\see axis\n*/\nQList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const\n{\n  QList<QCPAxis*> result;\n  if (types.testFlag(QCPAxis::atLeft))\n    result << mAxes.value(QCPAxis::atLeft);\n  if (types.testFlag(QCPAxis::atRight))\n    result << mAxes.value(QCPAxis::atRight);\n  if (types.testFlag(QCPAxis::atTop))\n    result << mAxes.value(QCPAxis::atTop);\n  if (types.testFlag(QCPAxis::atBottom))\n    result << mAxes.value(QCPAxis::atBottom);\n  return result;\n}\n\n/*! \\overload\n  \n  Returns all axes of this axis rect.\n*/\nQList<QCPAxis*> QCPAxisRect::axes() const\n{\n  QList<QCPAxis*> result;\n  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);\n  while (it.hasNext())\n  {\n    it.next();\n    result << it.value();\n  }\n  return result;\n}\n\n/*!\n  Adds a new axis to the axis rect side specified with \\a type, and returns it. If \\a axis is 0, a\n  new QCPAxis instance is created internally. QCustomPlot owns the returned axis, so if you want to\n  remove an axis, use \\ref removeAxis instead of deleting it manually.\n\n  You may inject QCPAxis instances (or subclasses of QCPAxis) by setting \\a axis to an axis that was\n  previously created outside QCustomPlot. It is important to note that QCustomPlot takes ownership\n  of the axis, so you may not delete it afterwards. Further, the \\a axis must have been created\n  with this axis rect as parent and with the same axis type as specified in \\a type. If this is not\n  the case, a debug output is generated, the axis is not added, and the method returns \\c nullptr.\n\n  This method can not be used to move \\a axis between axis rects. The same \\a axis instance must\n  not be added multiple times to the same or different axis rects.\n\n  If an axis rect side already contains one or more axes, the lower and upper endings of the new\n  axis (\\ref QCPAxis::setLowerEnding, \\ref QCPAxis::setUpperEnding) are set to \\ref\n  QCPLineEnding::esHalfBar.\n\n  \\see addAxes, setupFullAxesBox\n*/\nQCPAxis *QCPAxisRect::addAxis(QCPAxis::AxisType type, QCPAxis *axis)\n{\n  QCPAxis *newAxis = axis;\n  if (!newAxis)\n  {\n    newAxis = new QCPAxis(this, type);\n  } else // user provided existing axis instance, do some sanity checks\n  {\n    if (newAxis->axisType() != type)\n    {\n      qDebug() << Q_FUNC_INFO << \"passed axis has different axis type than specified in type parameter\";\n      return nullptr;\n    }\n    if (newAxis->axisRect() != this)\n    {\n      qDebug() << Q_FUNC_INFO << \"passed axis doesn't have this axis rect as parent axis rect\";\n      return nullptr;\n    }\n    if (axes().contains(newAxis))\n    {\n      qDebug() << Q_FUNC_INFO << \"passed axis is already owned by this axis rect\";\n      return nullptr;\n    }\n  }\n  if (!mAxes[type].isEmpty()) // multiple axes on one side, add half-bar axis ending to additional axes with offset\n  {\n    bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);\n    newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));\n    newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));\n  }\n  mAxes[type].append(newAxis);\n  \n  // reset convenience axis pointers on parent QCustomPlot if they are unset:\n  if (mParentPlot && mParentPlot->axisRectCount() > 0 && mParentPlot->axisRect(0) == this)\n  {\n    switch (type)\n    {\n      case QCPAxis::atBottom: { if (!mParentPlot->xAxis) mParentPlot->xAxis = newAxis; break; }\n      case QCPAxis::atLeft: { if (!mParentPlot->yAxis) mParentPlot->yAxis = newAxis; break; }\n      case QCPAxis::atTop: { if (!mParentPlot->xAxis2) mParentPlot->xAxis2 = newAxis; break; }\n      case QCPAxis::atRight: { if (!mParentPlot->yAxis2) mParentPlot->yAxis2 = newAxis; break; }\n    }\n  }\n  \n  return newAxis;\n}\n\n/*!\n  Adds a new axis with \\ref addAxis to each axis rect side specified in \\a types. This may be an\n  <tt>or</tt>-combination of QCPAxis::AxisType, so axes can be added to multiple sides at once.\n  \n  Returns a list of the added axes.\n  \n  \\see addAxis, setupFullAxesBox\n*/\nQList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)\n{\n  QList<QCPAxis*> result;\n  if (types.testFlag(QCPAxis::atLeft))\n    result << addAxis(QCPAxis::atLeft);\n  if (types.testFlag(QCPAxis::atRight))\n    result << addAxis(QCPAxis::atRight);\n  if (types.testFlag(QCPAxis::atTop))\n    result << addAxis(QCPAxis::atTop);\n  if (types.testFlag(QCPAxis::atBottom))\n    result << addAxis(QCPAxis::atBottom);\n  return result;\n}\n\n/*!\n  Removes the specified \\a axis from the axis rect and deletes it.\n  \n  Returns true on success, i.e. if \\a axis was a valid axis in this axis rect.\n  \n  \\see addAxis\n*/\nbool QCPAxisRect::removeAxis(QCPAxis *axis)\n{\n  // don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers:\n  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);\n  while (it.hasNext())\n  {\n    it.next();\n    if (it.value().contains(axis))\n    {\n      if (it.value().first() == axis && it.value().size() > 1) // if removing first axis, transfer axis offset to the new first axis (which at this point is the second axis, if it exists)\n        it.value()[1]->setOffset(axis->offset());\n      mAxes[it.key()].removeOne(axis);\n      if (qobject_cast<QCustomPlot*>(parentPlot())) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the axis rect is not in any layout and thus QObject-child of QCustomPlot)\n        parentPlot()->axisRemoved(axis);\n      delete axis;\n      return true;\n    }\n  }\n  qDebug() << Q_FUNC_INFO << \"Axis isn't in axis rect:\" << reinterpret_cast<quintptr>(axis);\n  return false;\n}\n\n/*!\n  Zooms in (or out) to the passed rectangular region \\a pixelRect, given in pixel coordinates.\n\n  All axes of this axis rect will have their range zoomed accordingly. If you only wish to zoom\n  specific axes, use the overloaded version of this method.\n  \n  \\see QCustomPlot::setSelectionRectMode\n*/\nvoid QCPAxisRect::zoom(const QRectF &pixelRect)\n{\n  zoom(pixelRect, axes());\n}\n\n/*! \\overload\n  \n  Zooms in (or out) to the passed rectangular region \\a pixelRect, given in pixel coordinates.\n  \n  Only the axes passed in \\a affectedAxes will have their ranges zoomed accordingly.\n  \n  \\see QCustomPlot::setSelectionRectMode\n*/\nvoid QCPAxisRect::zoom(const QRectF &pixelRect, const QList<QCPAxis*> &affectedAxes)\n{\n  foreach (QCPAxis *axis, affectedAxes)\n  {\n    if (!axis)\n    {\n      qDebug() << Q_FUNC_INFO << \"a passed axis was zero\";\n      continue;\n    }\n    QCPRange pixelRange;\n    if (axis->orientation() == Qt::Horizontal)\n      pixelRange = QCPRange(pixelRect.left(), pixelRect.right());\n    else\n      pixelRange = QCPRange(pixelRect.top(), pixelRect.bottom());\n    axis->setRange(axis->pixelToCoord(pixelRange.lower), axis->pixelToCoord(pixelRange.upper));\n  }\n}\n\n/*!\n  Convenience function to create an axis on each side that doesn't have any axes yet and set their\n  visibility to true. Further, the top/right axes are assigned the following properties of the\n  bottom/left axes:\n\n  \\li range (\\ref QCPAxis::setRange)\n  \\li range reversed (\\ref QCPAxis::setRangeReversed)\n  \\li scale type (\\ref QCPAxis::setScaleType)\n  \\li tick visibility (\\ref QCPAxis::setTicks)\n  \\li number format (\\ref QCPAxis::setNumberFormat)\n  \\li number precision (\\ref QCPAxis::setNumberPrecision)\n  \\li tick count of ticker (\\ref QCPAxisTicker::setTickCount)\n  \\li tick origin of ticker (\\ref QCPAxisTicker::setTickOrigin)\n\n  Tick label visibility (\\ref QCPAxis::setTickLabels) of the right and top axes are set to false.\n\n  If \\a connectRanges is true, the \\ref QCPAxis::rangeChanged \"rangeChanged\" signals of the bottom\n  and left axes are connected to the \\ref QCPAxis::setRange slots of the top and right axes.\n*/\nvoid QCPAxisRect::setupFullAxesBox(bool connectRanges)\n{\n  QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;\n  if (axisCount(QCPAxis::atBottom) == 0)\n    xAxis = addAxis(QCPAxis::atBottom);\n  else\n    xAxis = axis(QCPAxis::atBottom);\n  \n  if (axisCount(QCPAxis::atLeft) == 0)\n    yAxis = addAxis(QCPAxis::atLeft);\n  else\n    yAxis = axis(QCPAxis::atLeft);\n  \n  if (axisCount(QCPAxis::atTop) == 0)\n    xAxis2 = addAxis(QCPAxis::atTop);\n  else\n    xAxis2 = axis(QCPAxis::atTop);\n  \n  if (axisCount(QCPAxis::atRight) == 0)\n    yAxis2 = addAxis(QCPAxis::atRight);\n  else\n    yAxis2 = axis(QCPAxis::atRight);\n  \n  xAxis->setVisible(true);\n  yAxis->setVisible(true);\n  xAxis2->setVisible(true);\n  yAxis2->setVisible(true);\n  xAxis2->setTickLabels(false);\n  yAxis2->setTickLabels(false);\n  \n  xAxis2->setRange(xAxis->range());\n  xAxis2->setRangeReversed(xAxis->rangeReversed());\n  xAxis2->setScaleType(xAxis->scaleType());\n  xAxis2->setTicks(xAxis->ticks());\n  xAxis2->setNumberFormat(xAxis->numberFormat());\n  xAxis2->setNumberPrecision(xAxis->numberPrecision());\n  xAxis2->ticker()->setTickCount(xAxis->ticker()->tickCount());\n  xAxis2->ticker()->setTickOrigin(xAxis->ticker()->tickOrigin());\n  \n  yAxis2->setRange(yAxis->range());\n  yAxis2->setRangeReversed(yAxis->rangeReversed());\n  yAxis2->setScaleType(yAxis->scaleType());\n  yAxis2->setTicks(yAxis->ticks());\n  yAxis2->setNumberFormat(yAxis->numberFormat());\n  yAxis2->setNumberPrecision(yAxis->numberPrecision());\n  yAxis2->ticker()->setTickCount(yAxis->ticker()->tickCount());\n  yAxis2->ticker()->setTickOrigin(yAxis->ticker()->tickOrigin());\n  \n  if (connectRanges)\n  {\n    connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));\n    connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));\n  }\n}\n\n/*!\n  Returns a list of all the plottables that are associated with this axis rect.\n  \n  A plottable is considered associated with an axis rect if its key or value axis (or both) is in\n  this axis rect.\n  \n  \\see graphs, items\n*/\nQList<QCPAbstractPlottable*> QCPAxisRect::plottables() const\n{\n  // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries\n  QList<QCPAbstractPlottable*> result;\n  foreach (QCPAbstractPlottable *plottable, mParentPlot->mPlottables)\n  {\n    if (plottable->keyAxis()->axisRect() == this || plottable->valueAxis()->axisRect() == this)\n      result.append(plottable);\n  }\n  return result;\n}\n\n/*!\n  Returns a list of all the graphs that are associated with this axis rect.\n  \n  A graph is considered associated with an axis rect if its key or value axis (or both) is in\n  this axis rect.\n  \n  \\see plottables, items\n*/\nQList<QCPGraph*> QCPAxisRect::graphs() const\n{\n  // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries\n  QList<QCPGraph*> result;\n  foreach (QCPGraph *graph, mParentPlot->mGraphs)\n  {\n    if (graph->keyAxis()->axisRect() == this || graph->valueAxis()->axisRect() == this)\n      result.append(graph);\n  }\n  return result;\n}\n\n/*!\n  Returns a list of all the items that are associated with this axis rect.\n  \n  An item is considered associated with an axis rect if any of its positions has key or value axis\n  set to an axis that is in this axis rect, or if any of its positions has \\ref\n  QCPItemPosition::setAxisRect set to the axis rect, or if the clip axis rect (\\ref\n  QCPAbstractItem::setClipAxisRect) is set to this axis rect.\n  \n  \\see plottables, graphs\n*/\nQList<QCPAbstractItem *> QCPAxisRect::items() const\n{\n  // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries\n  //       and miss those items that have this axis rect as clipAxisRect.\n  QList<QCPAbstractItem*> result;\n  foreach (QCPAbstractItem *item, mParentPlot->mItems)\n  {\n    if (item->clipAxisRect() == this)\n    {\n      result.append(item);\n      continue;\n    }\n    foreach (QCPItemPosition *position, item->positions())\n    {\n      if (position->axisRect() == this ||\n          position->keyAxis()->axisRect() == this ||\n          position->valueAxis()->axisRect() == this)\n      {\n        result.append(item);\n        break;\n      }\n    }\n  }\n  return result;\n}\n\n/*!\n  This method is called automatically upon replot and doesn't need to be called by users of\n  QCPAxisRect.\n  \n  Calls the base class implementation to update the margins (see \\ref QCPLayoutElement::update),\n  and finally passes the \\ref rect to the inset layout (\\ref insetLayout) and calls its\n  QCPInsetLayout::update function.\n  \n  \\seebaseclassmethod\n*/\nvoid QCPAxisRect::update(UpdatePhase phase)\n{\n  QCPLayoutElement::update(phase);\n  \n  switch (phase)\n  {\n    case upPreparation:\n    {\n      foreach (QCPAxis *axis, axes())\n        axis->setupTickVectors();\n      break;\n    }\n    case upLayout:\n    {\n      mInsetLayout->setOuterRect(rect());\n      break;\n    }\n    default: break;\n  }\n  \n  // pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout):\n  mInsetLayout->update(phase);\n}\n\n/* inherits documentation from base class */\nQList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const\n{\n  QList<QCPLayoutElement*> result;\n  if (mInsetLayout)\n  {\n    result << mInsetLayout;\n    if (recursive)\n      result << mInsetLayout->elements(recursive);\n  }\n  return result;\n}\n\n/* inherits documentation from base class */\nvoid QCPAxisRect::applyDefaultAntialiasingHint(QCPPainter *painter) const\n{\n  painter->setAntialiasing(false);\n}\n\n/* inherits documentation from base class */\nvoid QCPAxisRect::draw(QCPPainter *painter)\n{\n  drawBackground(painter);\n}\n\n/*!\n  Sets \\a pm as the axis background pixmap. The axis background pixmap will be drawn inside the\n  axis rect. Since axis rects place themselves on the \"background\" layer by default, the axis rect\n  backgrounds are usually drawn below everything else.\n\n  For cases where the provided pixmap doesn't have the same size as the axis rect, scaling can be\n  enabled with \\ref setBackgroundScaled and the scaling mode (i.e. whether and how the aspect ratio\n  is preserved) can be set with \\ref setBackgroundScaledMode. To set all these options in one call,\n  consider using the overloaded version of this function.\n\n  Below the pixmap, the axis rect may be optionally filled with a brush, if specified with \\ref\n  setBackground(const QBrush &brush).\n  \n  \\see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush &brush)\n*/\nvoid QCPAxisRect::setBackground(const QPixmap &pm)\n{\n  mBackgroundPixmap = pm;\n  mScaledBackgroundPixmap = QPixmap();\n}\n\n/*! \\overload\n  \n  Sets \\a brush as the background brush. The axis rect background will be filled with this brush.\n  Since axis rects place themselves on the \"background\" layer by default, the axis rect backgrounds\n  are usually drawn below everything else.\n\n  The brush will be drawn before (under) any background pixmap, which may be specified with \\ref\n  setBackground(const QPixmap &pm).\n\n  To disable drawing of a background brush, set \\a brush to Qt::NoBrush.\n  \n  \\see setBackground(const QPixmap &pm)\n*/\nvoid QCPAxisRect::setBackground(const QBrush &brush)\n{\n  mBackgroundBrush = brush;\n}\n\n/*! \\overload\n  \n  Allows setting the background pixmap of the axis rect, whether it shall be scaled and how it\n  shall be scaled in one call.\n\n  \\see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode\n*/\nvoid QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)\n{\n  mBackgroundPixmap = pm;\n  mScaledBackgroundPixmap = QPixmap();\n  mBackgroundScaled = scaled;\n  mBackgroundScaledMode = mode;\n}\n\n/*!\n  Sets whether the axis background pixmap shall be scaled to fit the axis rect or not. If \\a scaled\n  is set to true, you may control whether and how the aspect ratio of the original pixmap is\n  preserved with \\ref setBackgroundScaledMode.\n  \n  Note that the scaled version of the original pixmap is buffered, so there is no performance\n  penalty on replots. (Except when the axis rect dimensions are changed continuously.)\n  \n  \\see setBackground, setBackgroundScaledMode\n*/\nvoid QCPAxisRect::setBackgroundScaled(bool scaled)\n{\n  mBackgroundScaled = scaled;\n}\n\n/*!\n  If scaling of the axis background pixmap is enabled (\\ref setBackgroundScaled), use this function to\n  define whether and how the aspect ratio of the original pixmap passed to \\ref setBackground is preserved.\n  \\see setBackground, setBackgroundScaled\n*/\nvoid QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)\n{\n  mBackgroundScaledMode = mode;\n}\n\n/*!\n  Returns the range drag axis of the \\a orientation provided. If multiple axes were set, returns\n  the first one (use \\ref rangeDragAxes to retrieve a list with all set axes).\n\n  \\see setRangeDragAxes\n*/\nQCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)\n{\n  if (orientation == Qt::Horizontal)\n    return mRangeDragHorzAxis.isEmpty() ? nullptr : mRangeDragHorzAxis.first().data();\n  else\n    return mRangeDragVertAxis.isEmpty() ? nullptr : mRangeDragVertAxis.first().data();\n}\n\n/*!\n  Returns the range zoom axis of the \\a orientation provided. If multiple axes were set, returns\n  the first one (use \\ref rangeZoomAxes to retrieve a list with all set axes).\n\n  \\see setRangeZoomAxes\n*/\nQCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)\n{\n  if (orientation == Qt::Horizontal)\n    return mRangeZoomHorzAxis.isEmpty() ? nullptr : mRangeZoomHorzAxis.first().data();\n  else\n    return mRangeZoomVertAxis.isEmpty() ? nullptr : mRangeZoomVertAxis.first().data();\n}\n\n/*!\n  Returns all range drag axes of the \\a orientation provided.\n\n  \\see rangeZoomAxis, setRangeZoomAxes\n*/\nQList<QCPAxis*> QCPAxisRect::rangeDragAxes(Qt::Orientation orientation)\n{\n  QList<QCPAxis*> result;\n  if (orientation == Qt::Horizontal)\n  {\n    foreach (QPointer<QCPAxis> axis, mRangeDragHorzAxis)\n    {\n      if (!axis.isNull())\n        result.append(axis.data());\n    }\n  } else\n  {\n    foreach (QPointer<QCPAxis> axis, mRangeDragVertAxis)\n    {\n      if (!axis.isNull())\n        result.append(axis.data());\n    }\n  }\n  return result;\n}\n\n/*!\n  Returns all range zoom axes of the \\a orientation provided.\n\n  \\see rangeDragAxis, setRangeDragAxes\n*/\nQList<QCPAxis*> QCPAxisRect::rangeZoomAxes(Qt::Orientation orientation)\n{\n  QList<QCPAxis*> result;\n  if (orientation == Qt::Horizontal)\n  {\n    foreach (QPointer<QCPAxis> axis, mRangeZoomHorzAxis)\n    {\n      if (!axis.isNull())\n        result.append(axis.data());\n    }\n  } else\n  {\n    foreach (QPointer<QCPAxis> axis, mRangeZoomVertAxis)\n    {\n      if (!axis.isNull())\n        result.append(axis.data());\n    }\n  }\n  return result;\n}\n\n/*!\n  Returns the range zoom factor of the \\a orientation provided.\n  \n  \\see setRangeZoomFactor\n*/\ndouble QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)\n{\n  return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);\n}\n\n/*!\n  Sets which axis orientation may be range dragged by the user with mouse interaction.\n  What orientation corresponds to which specific axis can be set with\n  \\ref setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical). By\n  default, the horizontal axis is the bottom axis (xAxis) and the vertical axis\n  is the left axis (yAxis).\n  \n  To disable range dragging entirely, pass \\c nullptr as \\a orientations or remove \\ref\n  QCP::iRangeDrag from \\ref QCustomPlot::setInteractions. To enable range dragging for both\n  directions, pass <tt>Qt::Horizontal | Qt::Vertical</tt> as \\a orientations.\n  \n  In addition to setting \\a orientations to a non-zero value, make sure \\ref QCustomPlot::setInteractions\n  contains \\ref QCP::iRangeDrag to enable the range dragging interaction.\n  \n  \\see setRangeZoom, setRangeDragAxes, QCustomPlot::setNoAntialiasingOnDrag\n*/\nvoid QCPAxisRect::setRangeDrag(Qt::Orientations orientations)\n{\n  mRangeDrag = orientations;\n}\n\n/*!\n  Sets which axis orientation may be zoomed by the user with the mouse wheel. What orientation\n  corresponds to which specific axis can be set with \\ref setRangeZoomAxes(QCPAxis *horizontal,\n  QCPAxis *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the vertical\n  axis is the left axis (yAxis).\n\n  To disable range zooming entirely, pass \\c nullptr as \\a orientations or remove \\ref\n  QCP::iRangeZoom from \\ref QCustomPlot::setInteractions. To enable range zooming for both\n  directions, pass <tt>Qt::Horizontal | Qt::Vertical</tt> as \\a orientations.\n  \n  In addition to setting \\a orientations to a non-zero value, make sure \\ref QCustomPlot::setInteractions\n  contains \\ref QCP::iRangeZoom to enable the range zooming interaction.\n  \n  \\see setRangeZoomFactor, setRangeZoomAxes, setRangeDrag\n*/\nvoid QCPAxisRect::setRangeZoom(Qt::Orientations orientations)\n{\n  mRangeZoom = orientations;\n}\n\n/*! \\overload\n  \n  Sets the axes whose range will be dragged when \\ref setRangeDrag enables mouse range dragging on\n  the QCustomPlot widget. Pass \\c nullptr if no axis shall be dragged in the respective\n  orientation.\n\n  Use the overload taking a list of axes, if multiple axes (more than one per orientation) shall\n  react to dragging interactions.\n\n  \\see setRangeZoomAxes\n*/\nvoid QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)\n{\n  QList<QCPAxis*> horz, vert;\n  if (horizontal)\n    horz.append(horizontal);\n  if (vertical)\n    vert.append(vertical);\n  setRangeDragAxes(horz, vert);\n}\n\n/*! \\overload\n\n  This method allows to set up multiple axes to react to horizontal and vertical dragging. The drag\n  orientation that the respective axis will react to is deduced from its orientation (\\ref\n  QCPAxis::orientation).\n\n  In the unusual case that you wish to e.g. drag a vertically oriented axis with a horizontal drag\n  motion, use the overload taking two separate lists for horizontal and vertical dragging.\n*/\nvoid QCPAxisRect::setRangeDragAxes(QList<QCPAxis*> axes)\n{\n  QList<QCPAxis*> horz, vert;\n  foreach (QCPAxis *ax, axes)\n  {\n    if (ax->orientation() == Qt::Horizontal)\n      horz.append(ax);\n    else\n      vert.append(ax);\n  }\n  setRangeDragAxes(horz, vert);\n}\n\n/*! \\overload\n\n  This method allows to set multiple axes up to react to horizontal and vertical dragging, and\n  define specifically which axis reacts to which drag orientation (irrespective of the axis\n  orientation).\n*/\nvoid QCPAxisRect::setRangeDragAxes(QList<QCPAxis*> horizontal, QList<QCPAxis*> vertical)\n{\n  mRangeDragHorzAxis.clear();\n  foreach (QCPAxis *ax, horizontal)\n  {\n    QPointer<QCPAxis> axPointer(ax);\n    if (!axPointer.isNull())\n      mRangeDragHorzAxis.append(axPointer);\n    else\n      qDebug() << Q_FUNC_INFO << \"invalid axis passed in horizontal list:\" << reinterpret_cast<quintptr>(ax);\n  }\n  mRangeDragVertAxis.clear();\n  foreach (QCPAxis *ax, vertical)\n  {\n    QPointer<QCPAxis> axPointer(ax);\n    if (!axPointer.isNull())\n      mRangeDragVertAxis.append(axPointer);\n    else\n      qDebug() << Q_FUNC_INFO << \"invalid axis passed in vertical list:\" << reinterpret_cast<quintptr>(ax);\n  }\n}\n\n/*!\n  Sets the axes whose range will be zoomed when \\ref setRangeZoom enables mouse wheel zooming on\n  the QCustomPlot widget. Pass \\c nullptr if no axis shall be zoomed in the respective orientation.\n\n  The two axes can be zoomed with different strengths, when different factors are passed to \\ref\n  setRangeZoomFactor(double horizontalFactor, double verticalFactor).\n\n  Use the overload taking a list of axes, if multiple axes (more than one per orientation) shall\n  react to zooming interactions.\n\n  \\see setRangeDragAxes\n*/\nvoid QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)\n{\n  QList<QCPAxis*> horz, vert;\n  if (horizontal)\n    horz.append(horizontal);\n  if (vertical)\n    vert.append(vertical);\n  setRangeZoomAxes(horz, vert);\n}\n\n/*! \\overload\n\n  This method allows to set up multiple axes to react to horizontal and vertical range zooming. The\n  zoom orientation that the respective axis will react to is deduced from its orientation (\\ref\n  QCPAxis::orientation).\n\n  In the unusual case that you wish to e.g. zoom a vertically oriented axis with a horizontal zoom\n  interaction, use the overload taking two separate lists for horizontal and vertical zooming.\n*/\nvoid QCPAxisRect::setRangeZoomAxes(QList<QCPAxis*> axes)\n{\n  QList<QCPAxis*> horz, vert;\n  foreach (QCPAxis *ax, axes)\n  {\n    if (ax->orientation() == Qt::Horizontal)\n      horz.append(ax);\n    else\n      vert.append(ax);\n  }\n  setRangeZoomAxes(horz, vert);\n}\n\n/*! \\overload\n\n  This method allows to set multiple axes up to react to horizontal and vertical zooming, and\n  define specifically which axis reacts to which zoom orientation (irrespective of the axis\n  orientation).\n*/\nvoid QCPAxisRect::setRangeZoomAxes(QList<QCPAxis*> horizontal, QList<QCPAxis*> vertical)\n{\n  mRangeZoomHorzAxis.clear();\n  foreach (QCPAxis *ax, horizontal)\n  {\n    QPointer<QCPAxis> axPointer(ax);\n    if (!axPointer.isNull())\n      mRangeZoomHorzAxis.append(axPointer);\n    else\n      qDebug() << Q_FUNC_INFO << \"invalid axis passed in horizontal list:\" << reinterpret_cast<quintptr>(ax);\n  }\n  mRangeZoomVertAxis.clear();\n  foreach (QCPAxis *ax, vertical)\n  {\n    QPointer<QCPAxis> axPointer(ax);\n    if (!axPointer.isNull())\n      mRangeZoomVertAxis.append(axPointer);\n    else\n      qDebug() << Q_FUNC_INFO << \"invalid axis passed in vertical list:\" << reinterpret_cast<quintptr>(ax);\n  }\n}\n\n/*!\n  Sets how strong one rotation step of the mouse wheel zooms, when range zoom was activated with\n  \\ref setRangeZoom. The two parameters \\a horizontalFactor and \\a verticalFactor provide a way to\n  let the horizontal axis zoom at different rates than the vertical axis. Which axis is horizontal\n  and which is vertical, can be set with \\ref setRangeZoomAxes.\n\n  When the zoom factor is greater than one, scrolling the mouse wheel backwards (towards the user)\n  will zoom in (make the currently visible range smaller). For zoom factors smaller than one, the\n  same scrolling direction will zoom out.\n*/\nvoid QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)\n{\n  mRangeZoomFactorHorz = horizontalFactor;\n  mRangeZoomFactorVert = verticalFactor;\n}\n\n/*! \\overload\n  \n  Sets both the horizontal and vertical zoom \\a factor.\n*/\nvoid QCPAxisRect::setRangeZoomFactor(double factor)\n{\n  mRangeZoomFactorHorz = factor;\n  mRangeZoomFactorVert = factor;\n}\n\n/*! \\internal\n  \n  Draws the background of this axis rect. It may consist of a background fill (a QBrush) and a\n  pixmap.\n  \n  If a brush was given via \\ref setBackground(const QBrush &brush), this function first draws an\n  according filling inside the axis rect with the provided \\a painter.\n  \n  Then, if a pixmap was provided via \\ref setBackground, this function buffers the scaled version\n  depending on \\ref setBackgroundScaled and \\ref setBackgroundScaledMode and then draws it inside\n  the axis rect with the provided \\a painter. The scaled version is buffered in\n  mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when\n  the axis rect has changed in a way that requires a rescale of the background pixmap (this is\n  dependent on the \\ref setBackgroundScaledMode), or when a differend axis background pixmap was\n  set.\n  \n  \\see setBackground, setBackgroundScaled, setBackgroundScaledMode\n*/\nvoid QCPAxisRect::drawBackground(QCPPainter *painter)\n{\n  // draw background fill:\n  if (mBackgroundBrush != Qt::NoBrush)\n    painter->fillRect(mRect, mBackgroundBrush);\n  \n  // draw background pixmap (on top of fill, if brush specified):\n  if (!mBackgroundPixmap.isNull())\n  {\n    if (mBackgroundScaled)\n    {\n      // check whether mScaledBackground needs to be updated:\n      QSize scaledSize(mBackgroundPixmap.size());\n      scaledSize.scale(mRect.size(), mBackgroundScaledMode);\n      if (mScaledBackgroundPixmap.size() != scaledSize)\n        mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);\n      painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());\n    } else\n    {\n      painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));\n    }\n  }\n}\n\n/*! \\internal\n  \n  This function makes sure multiple axes on the side specified with \\a type don't collide, but are\n  distributed according to their respective space requirement (QCPAxis::calculateMargin).\n  \n  It does this by setting an appropriate offset (\\ref QCPAxis::setOffset) on all axes except the\n  one with index zero.\n  \n  This function is called by \\ref calculateAutoMargin.\n*/\nvoid QCPAxisRect::updateAxesOffset(QCPAxis::AxisType type)\n{\n  const QList<QCPAxis*> axesList = mAxes.value(type);\n  if (axesList.isEmpty())\n    return;\n  \n  bool isFirstVisible = !axesList.first()->visible(); // if the first axis is visible, the second axis (which is where the loop starts) isn't the first visible axis, so initialize with false\n  for (int i=1; i<axesList.size(); ++i)\n  {\n    int offset = axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin();\n    if (axesList.at(i)->visible()) // only add inner tick length to offset if this axis is visible and it's not the first visible one (might happen if true first axis is invisible)\n    {\n      if (!isFirstVisible)\n        offset += axesList.at(i)->tickLengthIn();\n      isFirstVisible = false;\n    }\n    axesList.at(i)->setOffset(offset);\n  }\n}\n\n/* inherits documentation from base class */\nint QCPAxisRect::calculateAutoMargin(QCP::MarginSide side)\n{\n  if (!mAutoMargins.testFlag(side))\n    qDebug() << Q_FUNC_INFO << \"Called with side that isn't specified as auto margin\";\n  \n  updateAxesOffset(QCPAxis::marginSideToAxisType(side));\n  \n  // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call\n  const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));\n  if (!axesList.isEmpty())\n    return axesList.last()->offset() + axesList.last()->calculateMargin();\n  else\n    return 0;\n}\n\n/*! \\internal\n  \n  Reacts to a change in layout to potentially set the convenience axis pointers \\ref\n  QCustomPlot::xAxis, \\ref QCustomPlot::yAxis, etc. of the parent QCustomPlot to the respective\n  axes of this axis rect. This is only done if the respective convenience pointer is currently zero\n  and if there is no QCPAxisRect at position (0, 0) of the plot layout.\n  \n  This automation makes it simpler to replace the main axis rect with a newly created one, without\n  the need to manually reset the convenience pointers.\n*/\nvoid QCPAxisRect::layoutChanged()\n{\n  if (mParentPlot && mParentPlot->axisRectCount() > 0 && mParentPlot->axisRect(0) == this)\n  {\n    if (axisCount(QCPAxis::atBottom) > 0 && !mParentPlot->xAxis)\n      mParentPlot->xAxis = axis(QCPAxis::atBottom);\n    if (axisCount(QCPAxis::atLeft) > 0 && !mParentPlot->yAxis)\n      mParentPlot->yAxis = axis(QCPAxis::atLeft);\n    if (axisCount(QCPAxis::atTop) > 0 && !mParentPlot->xAxis2)\n      mParentPlot->xAxis2 = axis(QCPAxis::atTop);\n    if (axisCount(QCPAxis::atRight) > 0 && !mParentPlot->yAxis2)\n      mParentPlot->yAxis2 = axis(QCPAxis::atRight);\n  }\n}\n\n/*! \\internal\n  \n  Event handler for when a mouse button is pressed on the axis rect. If the left mouse button is\n  pressed, the range dragging interaction is initialized (the actual range manipulation happens in\n  the \\ref mouseMoveEvent).\n\n  The mDragging flag is set to true and some anchor points are set that are needed to determine the\n  distance the mouse was dragged in the mouse move/release events later.\n  \n  \\see mouseMoveEvent, mouseReleaseEvent\n*/\nvoid QCPAxisRect::mousePressEvent(QMouseEvent *event, const QVariant &details)\n{\n  Q_UNUSED(details)\n  if (event->buttons() & Qt::LeftButton)\n  {\n    mDragging = true;\n    // initialize antialiasing backup in case we start dragging:\n    if (mParentPlot->noAntialiasingOnDrag())\n    {\n      mAADragBackup = mParentPlot->antialiasedElements();\n      mNotAADragBackup = mParentPlot->notAntialiasedElements();\n    }\n    // Mouse range dragging interaction:\n    if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))\n    {\n      mDragStartHorzRange.clear();\n      foreach (QPointer<QCPAxis> axis, mRangeDragHorzAxis)\n        mDragStartHorzRange.append(axis.isNull() ? QCPRange() : axis->range());\n      mDragStartVertRange.clear();\n      foreach (QPointer<QCPAxis> axis, mRangeDragVertAxis)\n        mDragStartVertRange.append(axis.isNull() ? QCPRange() : axis->range());\n    }\n  }\n}\n\n/*! \\internal\n  \n  Event handler for when the mouse is moved on the axis rect. If range dragging was activated in a\n  preceding \\ref mousePressEvent, the range is moved accordingly.\n  \n  \\see mousePressEvent, mouseReleaseEvent\n*/\nvoid QCPAxisRect::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)\n{\n  Q_UNUSED(startPos)\n  // Mouse range dragging interaction:\n  if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))\n  {\n    \n    if (mRangeDrag.testFlag(Qt::Horizontal))\n    {\n      for (int i=0; i<mRangeDragHorzAxis.size(); ++i)\n      {\n        QCPAxis *ax = mRangeDragHorzAxis.at(i).data();\n        if (!ax)\n          continue;\n        if (i >= mDragStartHorzRange.size())\n          break;\n        if (ax->mScaleType == QCPAxis::stLinear)\n        {\n          double diff = ax->pixelToCoord(startPos.x()) - ax->pixelToCoord(event->pos().x());\n          ax->setRange(mDragStartHorzRange.at(i).lower+diff, mDragStartHorzRange.at(i).upper+diff);\n        } else if (ax->mScaleType == QCPAxis::stLogarithmic)\n        {\n          double diff = ax->pixelToCoord(startPos.x()) / ax->pixelToCoord(event->pos().x());\n          ax->setRange(mDragStartHorzRange.at(i).lower*diff, mDragStartHorzRange.at(i).upper*diff);\n        }\n      }\n    }\n    \n    if (mRangeDrag.testFlag(Qt::Vertical))\n    {\n      for (int i=0; i<mRangeDragVertAxis.size(); ++i)\n      {\n        QCPAxis *ax = mRangeDragVertAxis.at(i).data();\n        if (!ax)\n          continue;\n        if (i >= mDragStartVertRange.size())\n          break;\n        if (ax->mScaleType == QCPAxis::stLinear)\n        {\n          double diff = ax->pixelToCoord(startPos.y()) - ax->pixelToCoord(event->pos().y());\n          ax->setRange(mDragStartVertRange.at(i).lower+diff, mDragStartVertRange.at(i).upper+diff);\n        } else if (ax->mScaleType == QCPAxis::stLogarithmic)\n        {\n          double diff = ax->pixelToCoord(startPos.y()) / ax->pixelToCoord(event->pos().y());\n          ax->setRange(mDragStartVertRange.at(i).lower*diff, mDragStartVertRange.at(i).upper*diff);\n        }\n      }\n    }\n    \n    if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot\n    {\n      if (mParentPlot->noAntialiasingOnDrag())\n        mParentPlot->setNotAntialiasedElements(QCP::aeAll);\n      mParentPlot->replot(QCustomPlot::rpQueuedReplot);\n    }\n    \n  }\n}\n\n/* inherits documentation from base class */\nvoid QCPAxisRect::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)\n{\n  Q_UNUSED(event)\n  Q_UNUSED(startPos)\n  mDragging = false;\n  if (mParentPlot->noAntialiasingOnDrag())\n  {\n    mParentPlot->setAntialiasedElements(mAADragBackup);\n    mParentPlot->setNotAntialiasedElements(mNotAADragBackup);\n  }\n}\n\n/*! \\internal\n  \n  Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the\n  ranges of the axes defined as rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of\n  the scaling operation is the current cursor position inside the axis rect. The scaling factor is\n  dependent on the mouse wheel delta (which direction the wheel was rotated) to provide a natural\n  zooming feel. The Strength of the zoom can be controlled via \\ref setRangeZoomFactor.\n  \n  Note, that event->angleDelta() is usually +/-120 for single rotation steps. However, if the mouse\n  wheel is turned rapidly, many steps may bunch up to one event, so the delta may then be multiples\n  of 120. This is taken into account here, by calculating \\a wheelSteps and using it as exponent of\n  the range zoom factor. This takes care of the wheel direction automatically, by inverting the\n  factor, when the wheel step is negative (f^-1 = 1/f).\n*/\nvoid QCPAxisRect::wheelEvent(QWheelEvent *event)\n{\n#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)\n  const double delta = event->delta();\n#else\n  const double delta = event->angleDelta().y();\n#endif\n  \n#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)\n  const QPointF pos = event->pos();\n#else\n  const QPointF pos = event->position();\n#endif\n  \n  // Mouse range zooming interaction:\n  if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))\n  {\n    if (mRangeZoom != 0)\n    {\n      double factor;\n      double wheelSteps = delta/120.0; // a single step delta is +/-120 usually\n      if (mRangeZoom.testFlag(Qt::Horizontal))\n      {\n        factor = qPow(mRangeZoomFactorHorz, wheelSteps);\n        foreach (QPointer<QCPAxis> axis, mRangeZoomHorzAxis)\n        {\n          if (!axis.isNull())\n            axis->scaleRange(factor, axis->pixelToCoord(pos.x()));\n        }\n      }\n      if (mRangeZoom.testFlag(Qt::Vertical))\n      {\n        factor = qPow(mRangeZoomFactorVert, wheelSteps);\n        foreach (QPointer<QCPAxis> axis, mRangeZoomVertAxis)\n        {\n          if (!axis.isNull())\n            axis->scaleRange(factor, axis->pixelToCoord(pos.y()));\n        }\n      }\n      mParentPlot->replot();\n    }\n  }\n}\n/* end of 'src/layoutelements/layoutelement-axisrect.cpp' */\n\n\n/* including file 'src/layoutelements/layoutelement-legend.cpp' */\n/* modified 2021-03-29T02:30:44, size 31762                     */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPAbstractLegendItem\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPAbstractLegendItem\n  \\brief The abstract base class for all entries in a QCPLegend.\n  \n  It defines a very basic interface for entries in a QCPLegend. For representing plottables in the\n  legend, the subclass \\ref QCPPlottableLegendItem is more suitable.\n  \n  Only derive directly from this class when you need absolute freedom (e.g. a custom legend entry\n  that's not even associated with a plottable).\n\n  You must implement the following pure virtual functions:\n  \\li \\ref draw (from QCPLayerable)\n  \n  You inherit the following members you may use:\n  <table>\n    <tr>\n      <td>QCPLegend *\\b mParentLegend</td>\n      <td>A pointer to the parent QCPLegend.</td>\n    </tr><tr>\n      <td>QFont \\b mFont</td>\n      <td>The generic font of the item. You should use this font for all or at least the most prominent text of the item.</td>\n    </tr>\n  </table>\n*/\n\n/* start of documentation of signals */\n\n/*! \\fn void QCPAbstractLegendItem::selectionChanged(bool selected)\n  \n  This signal is emitted when the selection state of this legend item has changed, either by user\n  interaction or by a direct call to \\ref setSelected.\n*/\n\n/* end of documentation of signals */\n\n/*!\n  Constructs a QCPAbstractLegendItem and associates it with the QCPLegend \\a parent. This does not\n  cause the item to be added to \\a parent, so \\ref QCPLegend::addItem must be called separately.\n*/\nQCPAbstractLegendItem::QCPAbstractLegendItem(QCPLegend *parent) :\n  QCPLayoutElement(parent->parentPlot()),\n  mParentLegend(parent),\n  mFont(parent->font()),\n  mTextColor(parent->textColor()),\n  mSelectedFont(parent->selectedFont()),\n  mSelectedTextColor(parent->selectedTextColor()),\n  mSelectable(true),\n  mSelected(false)\n{\n  setLayer(QLatin1String(\"legend\"));\n  setMargins(QMargins(0, 0, 0, 0));\n}\n\n/*!\n  Sets the default font of this specific legend item to \\a font.\n  \n  \\see setTextColor, QCPLegend::setFont\n*/\nvoid QCPAbstractLegendItem::setFont(const QFont &font)\n{\n  mFont = font;\n}\n\n/*!\n  Sets the default text color of this specific legend item to \\a color.\n  \n  \\see setFont, QCPLegend::setTextColor\n*/\nvoid QCPAbstractLegendItem::setTextColor(const QColor &color)\n{\n  mTextColor = color;\n}\n\n/*!\n  When this legend item is selected, \\a font is used to draw generic text, instead of the normal\n  font set with \\ref setFont.\n  \n  \\see setFont, QCPLegend::setSelectedFont\n*/\nvoid QCPAbstractLegendItem::setSelectedFont(const QFont &font)\n{\n  mSelectedFont = font;\n}\n\n/*!\n  When this legend item is selected, \\a color is used to draw generic text, instead of the normal\n  color set with \\ref setTextColor.\n  \n  \\see setTextColor, QCPLegend::setSelectedTextColor\n*/\nvoid QCPAbstractLegendItem::setSelectedTextColor(const QColor &color)\n{\n  mSelectedTextColor = color;\n}\n\n/*!\n  Sets whether this specific legend item is selectable.\n  \n  \\see setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPAbstractLegendItem::setSelectable(bool selectable)\n{\n  if (mSelectable != selectable)\n  {\n    mSelectable = selectable;\n    emit selectableChanged(mSelectable);\n  }\n}\n\n/*!\n  Sets whether this specific legend item is selected.\n  \n  It is possible to set the selection state of this item by calling this function directly, even if\n  setSelectable is set to false.\n  \n  \\see setSelectableParts, QCustomPlot::setInteractions\n*/\nvoid QCPAbstractLegendItem::setSelected(bool selected)\n{\n  if (mSelected != selected)\n  {\n    mSelected = selected;\n    emit selectionChanged(mSelected);\n  }\n}\n\n/* inherits documentation from base class */\ndouble QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(details)\n  if (!mParentPlot) return -1;\n  if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))\n    return -1;\n  \n  if (mRect.contains(pos.toPoint()))\n    return mParentPlot->selectionTolerance()*0.99;\n  else\n    return -1;\n}\n\n/* inherits documentation from base class */\nvoid QCPAbstractLegendItem::applyDefaultAntialiasingHint(QCPPainter *painter) const\n{\n  applyAntialiasingHint(painter, mAntialiased, QCP::aeLegendItems);\n}\n\n/* inherits documentation from base class */\nQRect QCPAbstractLegendItem::clipRect() const\n{\n  return mOuterRect;\n}\n\n/* inherits documentation from base class */\nvoid QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)\n{\n  Q_UNUSED(event)\n  Q_UNUSED(details)\n  if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))\n  {\n    bool selBefore = mSelected;\n    setSelected(additive ? !mSelected : true);\n    if (selectionStateChanged)\n      *selectionStateChanged = mSelected != selBefore;\n  }\n}\n\n/* inherits documentation from base class */\nvoid QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)\n{\n  if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))\n  {\n    bool selBefore = mSelected;\n    setSelected(false);\n    if (selectionStateChanged)\n      *selectionStateChanged = mSelected != selBefore;\n  }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPPlottableLegendItem\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPPlottableLegendItem\n  \\brief A legend item representing a plottable with an icon and the plottable name.\n  \n  This is the standard legend item for plottables. It displays an icon of the plottable next to the\n  plottable name. The icon is drawn by the respective plottable itself (\\ref\n  QCPAbstractPlottable::drawLegendIcon), and tries to give an intuitive symbol for the plottable.\n  For example, the QCPGraph draws a centered horizontal line and/or a single scatter point in the\n  middle.\n  \n  Legend items of this type are always associated with one plottable (retrievable via the\n  plottable() function and settable with the constructor). You may change the font of the plottable\n  name with \\ref setFont. Icon padding and border pen is taken from the parent QCPLegend, see \\ref\n  QCPLegend::setIconBorderPen and \\ref QCPLegend::setIconTextPadding.\n\n  The function \\ref QCPAbstractPlottable::addToLegend/\\ref QCPAbstractPlottable::removeFromLegend\n  creates/removes legend items of this type.\n  \n  Since QCPLegend is based on QCPLayoutGrid, a legend item itself is just a subclass of\n  QCPLayoutElement. While it could be added to a legend (or any other layout) via the normal layout\n  interface, QCPLegend has specialized functions for handling legend items conveniently, see the\n  documentation of \\ref QCPLegend.\n*/\n\n/*!\n  Creates a new legend item associated with \\a plottable.\n  \n  Once it's created, it can be added to the legend via \\ref QCPLegend::addItem.\n  \n  A more convenient way of adding/removing a plottable to/from the legend is via the functions \\ref\n  QCPAbstractPlottable::addToLegend and \\ref QCPAbstractPlottable::removeFromLegend.\n*/\nQCPPlottableLegendItem::QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable) :\n  QCPAbstractLegendItem(parent),\n  mPlottable(plottable)\n{\n  setAntialiased(false);\n}\n\n/*! \\internal\n  \n  Returns the pen that shall be used to draw the icon border, taking into account the selection\n  state of this item.\n*/\nQPen QCPPlottableLegendItem::getIconBorderPen() const\n{\n  return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen();\n}\n\n/*! \\internal\n  \n  Returns the text color that shall be used to draw text, taking into account the selection state\n  of this item.\n*/\nQColor QCPPlottableLegendItem::getTextColor() const\n{\n  return mSelected ? mSelectedTextColor : mTextColor;\n}\n\n/*! \\internal\n  \n  Returns the font that shall be used to draw text, taking into account the selection state of this\n  item.\n*/\nQFont QCPPlottableLegendItem::getFont() const\n{\n  return mSelected ? mSelectedFont : mFont;\n}\n\n/*! \\internal\n  \n  Draws the item with \\a painter. The size and position of the drawn legend item is defined by the\n  parent layout (typically a \\ref QCPLegend) and the \\ref minimumOuterSizeHint and \\ref\n  maximumOuterSizeHint of this legend item.\n*/\nvoid QCPPlottableLegendItem::draw(QCPPainter *painter)\n{\n  if (!mPlottable) return;\n  painter->setFont(getFont());\n  painter->setPen(QPen(getTextColor()));\n  QSize iconSize = mParentLegend->iconSize();\n  QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());\n  QRect iconRect(mRect.topLeft(), iconSize);\n  int textHeight = qMax(textRect.height(), iconSize.height());  // if text has smaller height than icon, center text vertically in icon height, else align tops\n  painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());\n  // draw icon:\n  painter->save();\n  painter->setClipRect(iconRect, Qt::IntersectClip);\n  mPlottable->drawLegendIcon(painter, iconRect);\n  painter->restore();\n  // draw icon border:\n  if (getIconBorderPen().style() != Qt::NoPen)\n  {\n    painter->setPen(getIconBorderPen());\n    painter->setBrush(Qt::NoBrush);\n    int halfPen = qCeil(painter->pen().widthF()*0.5)+1;\n    painter->setClipRect(mOuterRect.adjusted(-halfPen, -halfPen, halfPen, halfPen)); // extend default clip rect so thicker pens (especially during selection) are not clipped\n    painter->drawRect(iconRect);\n  }\n}\n\n/*! \\internal\n  \n  Calculates and returns the size of this item. This includes the icon, the text and the padding in\n  between.\n  \n  \\seebaseclassmethod\n*/\nQSize QCPPlottableLegendItem::minimumOuterSizeHint() const\n{\n  if (!mPlottable) return {};\n  QSize result(0, 0);\n  QRect textRect;\n  QFontMetrics fontMetrics(getFont());\n  QSize iconSize = mParentLegend->iconSize();\n  textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());\n  result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width());\n  result.setHeight(qMax(textRect.height(), iconSize.height()));\n  result.rwidth() += mMargins.left()+mMargins.right();\n  result.rheight() += mMargins.top()+mMargins.bottom();\n  return result;\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPLegend\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPLegend\n  \\brief Manages a legend inside a QCustomPlot.\n\n  A legend is a small box somewhere in the plot which lists plottables with their name and icon.\n\n  A legend is populated with legend items by calling \\ref QCPAbstractPlottable::addToLegend on the\n  plottable, for which a legend item shall be created. In the case of the main legend (\\ref\n  QCustomPlot::legend), simply adding plottables to the plot while \\ref\n  QCustomPlot::setAutoAddPlottableToLegend is set to true (the default) creates corresponding\n  legend items. The legend item associated with a certain plottable can be removed with \\ref\n  QCPAbstractPlottable::removeFromLegend. However, QCPLegend also offers an interface to add and\n  manipulate legend items directly: \\ref item, \\ref itemWithPlottable, \\ref itemCount, \\ref\n  addItem, \\ref removeItem, etc.\n\n  Since \\ref QCPLegend derives from \\ref QCPLayoutGrid, it can be placed in any position a \\ref\n  QCPLayoutElement may be positioned. The legend items are themselves \\ref QCPLayoutElement\n  \"QCPLayoutElements\" which are placed in the grid layout of the legend. \\ref QCPLegend only adds\n  an interface specialized for handling child elements of type \\ref QCPAbstractLegendItem, as\n  mentioned above. In principle, any other layout elements may also be added to a legend via the\n  normal \\ref QCPLayoutGrid interface. See the special page about \\link thelayoutsystem The Layout\n  System\\endlink for examples on how to add other elements to the legend and move it outside the axis\n  rect.\n\n  Use the methods \\ref setFillOrder and \\ref setWrap inherited from \\ref QCPLayoutGrid to control\n  in which order (column first or row first) the legend is filled up when calling \\ref addItem, and\n  at which column or row wrapping occurs. The default fill order for legends is \\ref foRowsFirst.\n\n  By default, every QCustomPlot has one legend (\\ref QCustomPlot::legend) which is placed in the\n  inset layout of the main axis rect (\\ref QCPAxisRect::insetLayout). To move the legend to another\n  position inside the axis rect, use the methods of the \\ref QCPLayoutInset. To move the legend\n  outside of the axis rect, place it anywhere else with the \\ref QCPLayout/\\ref QCPLayoutElement\n  interface.\n*/\n\n/* start of documentation of signals */\n\n/*! \\fn void QCPLegend::selectionChanged(QCPLegend::SelectableParts selection);\n\n  This signal is emitted when the selection state of this legend has changed.\n  \n  \\see setSelectedParts, setSelectableParts\n*/\n\n/* end of documentation of signals */\n\n/*!\n  Constructs a new QCPLegend instance with default values.\n  \n  Note that by default, QCustomPlot already contains a legend ready to be used as \\ref\n  QCustomPlot::legend\n*/\nQCPLegend::QCPLegend() :\n  mIconTextPadding{}\n{\n  setFillOrder(QCPLayoutGrid::foRowsFirst);\n  setWrap(0);\n  \n  setRowSpacing(3);\n  setColumnSpacing(8);\n  setMargins(QMargins(7, 5, 7, 4));\n  setAntialiased(false);\n  setIconSize(32, 18);\n  \n  setIconTextPadding(7);\n  \n  setSelectableParts(spLegendBox | spItems);\n  setSelectedParts(spNone);\n  \n  setBorderPen(QPen(Qt::black, 0));\n  setSelectedBorderPen(QPen(Qt::blue, 2));\n  setIconBorderPen(Qt::NoPen);\n  setSelectedIconBorderPen(QPen(Qt::blue, 2));\n  setBrush(Qt::white);\n  setSelectedBrush(Qt::white);\n  setTextColor(Qt::black);\n  setSelectedTextColor(Qt::blue);\n}\n\nQCPLegend::~QCPLegend()\n{\n  clearItems();\n  if (qobject_cast<QCustomPlot*>(mParentPlot)) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the legend is not in any layout and thus QObject-child of QCustomPlot)\n    mParentPlot->legendRemoved(this);\n}\n\n/* no doc for getter, see setSelectedParts */\nQCPLegend::SelectableParts QCPLegend::selectedParts() const\n{\n  // check whether any legend elements selected, if yes, add spItems to return value\n  bool hasSelectedItems = false;\n  for (int i=0; i<itemCount(); ++i)\n  {\n    if (item(i) && item(i)->selected())\n    {\n      hasSelectedItems = true;\n      break;\n    }\n  }\n  if (hasSelectedItems)\n    return mSelectedParts | spItems;\n  else\n    return mSelectedParts & ~spItems;\n}\n\n/*!\n  Sets the pen, the border of the entire legend is drawn with.\n*/\nvoid QCPLegend::setBorderPen(const QPen &pen)\n{\n  mBorderPen = pen;\n}\n\n/*!\n  Sets the brush of the legend background.\n*/\nvoid QCPLegend::setBrush(const QBrush &brush)\n{\n  mBrush = brush;\n}\n\n/*!\n  Sets the default font of legend text. Legend items that draw text (e.g. the name of a graph) will\n  use this font by default. However, a different font can be specified on a per-item-basis by\n  accessing the specific legend item.\n  \n  This function will also set \\a font on all already existing legend items.\n  \n  \\see QCPAbstractLegendItem::setFont\n*/\nvoid QCPLegend::setFont(const QFont &font)\n{\n  mFont = font;\n  for (int i=0; i<itemCount(); ++i)\n  {\n    if (item(i))\n      item(i)->setFont(mFont);\n  }\n}\n\n/*!\n  Sets the default color of legend text. Legend items that draw text (e.g. the name of a graph)\n  will use this color by default. However, a different colors can be specified on a per-item-basis\n  by accessing the specific legend item.\n  \n  This function will also set \\a color on all already existing legend items.\n  \n  \\see QCPAbstractLegendItem::setTextColor\n*/\nvoid QCPLegend::setTextColor(const QColor &color)\n{\n  mTextColor = color;\n  for (int i=0; i<itemCount(); ++i)\n  {\n    if (item(i))\n      item(i)->setTextColor(color);\n  }\n}\n\n/*!\n  Sets the size of legend icons. Legend items that draw an icon (e.g. a visual\n  representation of the graph) will use this size by default.\n*/\nvoid QCPLegend::setIconSize(const QSize &size)\n{\n  mIconSize = size;\n}\n\n/*! \\overload\n*/\nvoid QCPLegend::setIconSize(int width, int height)\n{\n  mIconSize.setWidth(width);\n  mIconSize.setHeight(height);\n}\n\n/*!\n  Sets the horizontal space in pixels between the legend icon and the text next to it.\n  Legend items that draw an icon (e.g. a visual representation of the graph) and text (e.g. the\n  name of the graph) will use this space by default.\n*/\nvoid QCPLegend::setIconTextPadding(int padding)\n{\n  mIconTextPadding = padding;\n}\n\n/*!\n  Sets the pen used to draw a border around each legend icon. Legend items that draw an\n  icon (e.g. a visual representation of the graph) will use this pen by default.\n  \n  If no border is wanted, set this to \\a Qt::NoPen.\n*/\nvoid QCPLegend::setIconBorderPen(const QPen &pen)\n{\n  mIconBorderPen = pen;\n}\n\n/*!\n  Sets whether the user can (de-)select the parts in \\a selectable by clicking on the QCustomPlot surface.\n  (When \\ref QCustomPlot::setInteractions contains \\ref QCP::iSelectLegend.)\n  \n  However, even when \\a selectable is set to a value not allowing the selection of a specific part,\n  it is still possible to set the selection of this part manually, by calling \\ref setSelectedParts\n  directly.\n  \n  \\see SelectablePart, setSelectedParts\n*/\nvoid QCPLegend::setSelectableParts(const SelectableParts &selectable)\n{\n  if (mSelectableParts != selectable)\n  {\n    mSelectableParts = selectable;\n    emit selectableChanged(mSelectableParts);\n  }\n}\n\n/*!\n  Sets the selected state of the respective legend parts described by \\ref SelectablePart. When a part\n  is selected, it uses a different pen/font and brush. If some legend items are selected and \\a selected\n  doesn't contain \\ref spItems, those items become deselected.\n  \n  The entire selection mechanism is handled automatically when \\ref QCustomPlot::setInteractions\n  contains iSelectLegend. You only need to call this function when you wish to change the selection\n  state manually.\n  \n  This function can change the selection state of a part even when \\ref setSelectableParts was set to a\n  value that actually excludes the part.\n  \n  emits the \\ref selectionChanged signal when \\a selected is different from the previous selection state.\n  \n  Note that it doesn't make sense to set the selected state \\ref spItems here when it wasn't set\n  before, because there's no way to specify which exact items to newly select. Do this by calling\n  \\ref QCPAbstractLegendItem::setSelected directly on the legend item you wish to select.\n  \n  \\see SelectablePart, setSelectableParts, selectTest, setSelectedBorderPen, setSelectedIconBorderPen, setSelectedBrush,\n  setSelectedFont\n*/\nvoid QCPLegend::setSelectedParts(const SelectableParts &selected)\n{\n  SelectableParts newSelected = selected;\n  mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed\n\n  if (mSelectedParts != newSelected)\n  {\n    if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that)\n    {\n      qDebug() << Q_FUNC_INFO << \"spItems flag can not be set, it can only be unset with this function\";\n      newSelected &= ~spItems;\n    }\n    if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection\n    {\n      for (int i=0; i<itemCount(); ++i)\n      {\n        if (item(i))\n          item(i)->setSelected(false);\n      }\n    }\n    mSelectedParts = newSelected;\n    emit selectionChanged(mSelectedParts);\n  }\n}\n\n/*!\n  When the legend box is selected, this pen is used to draw the border instead of the normal pen\n  set via \\ref setBorderPen.\n\n  \\see setSelectedParts, setSelectableParts, setSelectedBrush\n*/\nvoid QCPLegend::setSelectedBorderPen(const QPen &pen)\n{\n  mSelectedBorderPen = pen;\n}\n\n/*!\n  Sets the pen legend items will use to draw their icon borders, when they are selected.\n\n  \\see setSelectedParts, setSelectableParts, setSelectedFont\n*/\nvoid QCPLegend::setSelectedIconBorderPen(const QPen &pen)\n{\n  mSelectedIconBorderPen = pen;\n}\n\n/*!\n  When the legend box is selected, this brush is used to draw the legend background instead of the normal brush\n  set via \\ref setBrush.\n\n  \\see setSelectedParts, setSelectableParts, setSelectedBorderPen\n*/\nvoid QCPLegend::setSelectedBrush(const QBrush &brush)\n{\n  mSelectedBrush = brush;\n}\n\n/*!\n  Sets the default font that is used by legend items when they are selected.\n  \n  This function will also set \\a font on all already existing legend items.\n\n  \\see setFont, QCPAbstractLegendItem::setSelectedFont\n*/\nvoid QCPLegend::setSelectedFont(const QFont &font)\n{\n  mSelectedFont = font;\n  for (int i=0; i<itemCount(); ++i)\n  {\n    if (item(i))\n      item(i)->setSelectedFont(font);\n  }\n}\n\n/*!\n  Sets the default text color that is used by legend items when they are selected.\n  \n  This function will also set \\a color on all already existing legend items.\n\n  \\see setTextColor, QCPAbstractLegendItem::setSelectedTextColor\n*/\nvoid QCPLegend::setSelectedTextColor(const QColor &color)\n{\n  mSelectedTextColor = color;\n  for (int i=0; i<itemCount(); ++i)\n  {\n    if (item(i))\n      item(i)->setSelectedTextColor(color);\n  }\n}\n\n/*!\n  Returns the item with index \\a i. If non-legend items were added to the legend, and the element\n  at the specified cell index is not a QCPAbstractLegendItem, returns \\c nullptr.\n\n  Note that the linear index depends on the current fill order (\\ref setFillOrder).\n\n  \\see itemCount, addItem, itemWithPlottable\n*/\nQCPAbstractLegendItem *QCPLegend::item(int index) const\n{\n  return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));\n}\n\n/*!\n  Returns the QCPPlottableLegendItem which is associated with \\a plottable (e.g. a \\ref QCPGraph*).\n  If such an item isn't in the legend, returns \\c nullptr.\n  \n  \\see hasItemWithPlottable\n*/\nQCPPlottableLegendItem *QCPLegend::itemWithPlottable(const QCPAbstractPlottable *plottable) const\n{\n  for (int i=0; i<itemCount(); ++i)\n  {\n    if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))\n    {\n      if (pli->plottable() == plottable)\n        return pli;\n    }\n  }\n  return nullptr;\n}\n\n/*!\n  Returns the number of items currently in the legend. It is identical to the base class\n  QCPLayoutGrid::elementCount(), and unlike the other \"item\" interface methods of QCPLegend,\n  doesn't only address elements which can be cast to QCPAbstractLegendItem.\n\n  Note that if empty cells are in the legend (e.g. by calling methods of the \\ref QCPLayoutGrid\n  base class which allows creating empty cells), they are included in the returned count.\n\n  \\see item\n*/\nint QCPLegend::itemCount() const\n{\n  return elementCount();\n}\n\n/*!\n  Returns whether the legend contains \\a item.\n  \n  \\see hasItemWithPlottable\n*/\nbool QCPLegend::hasItem(QCPAbstractLegendItem *item) const\n{\n  for (int i=0; i<itemCount(); ++i)\n  {\n    if (item == this->item(i))\n        return true;\n  }\n  return false;\n}\n\n/*!\n  Returns whether the legend contains a QCPPlottableLegendItem which is associated with \\a plottable (e.g. a \\ref QCPGraph*).\n  If such an item isn't in the legend, returns false.\n  \n  \\see itemWithPlottable\n*/\nbool QCPLegend::hasItemWithPlottable(const QCPAbstractPlottable *plottable) const\n{\n  return itemWithPlottable(plottable);\n}\n\n/*!\n  Adds \\a item to the legend, if it's not present already. The element is arranged according to the\n  current fill order (\\ref setFillOrder) and wrapping (\\ref setWrap).\n\n  Returns true on sucess, i.e. if the item wasn't in the list already and has been successfuly added.\n\n  The legend takes ownership of the item.\n\n  \\see removeItem, item, hasItem\n*/\nbool QCPLegend::addItem(QCPAbstractLegendItem *item)\n{\n  return addElement(item);\n}\n\n/*! \\overload\n\n  Removes the item with the specified \\a index from the legend and deletes it.\n\n  After successful removal, the legend is reordered according to the current fill order (\\ref\n  setFillOrder) and wrapping (\\ref setWrap), so no empty cell remains where the removed \\a item\n  was. If you don't want this, rather use the raw element interface of \\ref QCPLayoutGrid.\n\n  Returns true, if successful. Unlike \\ref QCPLayoutGrid::removeAt, this method only removes\n  elements derived from \\ref QCPAbstractLegendItem.\n\n  \\see itemCount, clearItems\n*/\nbool QCPLegend::removeItem(int index)\n{\n  if (QCPAbstractLegendItem *ali = item(index))\n  {\n    bool success = remove(ali);\n    if (success)\n      setFillOrder(fillOrder(), true); // gets rid of empty cell by reordering\n    return success;\n  } else\n    return false;\n}\n\n/*! \\overload\n\n  Removes \\a item from the legend and deletes it.\n\n  After successful removal, the legend is reordered according to the current fill order (\\ref\n  setFillOrder) and wrapping (\\ref setWrap), so no empty cell remains where the removed \\a item\n  was. If you don't want this, rather use the raw element interface of \\ref QCPLayoutGrid.\n\n  Returns true, if successful.\n\n  \\see clearItems\n*/\nbool QCPLegend::removeItem(QCPAbstractLegendItem *item)\n{\n  bool success = remove(item);\n  if (success)\n    setFillOrder(fillOrder(), true); // gets rid of empty cell by reordering\n  return success;\n}\n\n/*!\n  Removes all items from the legend.\n*/\nvoid QCPLegend::clearItems()\n{\n  for (int i=elementCount()-1; i>=0; --i)\n  {\n    if (item(i))\n      removeAt(i); // don't use removeItem() because it would unnecessarily reorder the whole legend for each item\n  }\n  setFillOrder(fillOrder(), true); // get rid of empty cells by reordering once after all items are removed\n}\n\n/*!\n  Returns the legend items that are currently selected. If no items are selected,\n  the list is empty.\n  \n  \\see QCPAbstractLegendItem::setSelected, setSelectable\n*/\nQList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const\n{\n  QList<QCPAbstractLegendItem*> result;\n  for (int i=0; i<itemCount(); ++i)\n  {\n    if (QCPAbstractLegendItem *ali = item(i))\n    {\n      if (ali->selected())\n        result.append(ali);\n    }\n  }\n  return result;\n}\n\n/*! \\internal\n\n  A convenience function to easily set the QPainter::Antialiased hint on the provided \\a painter\n  before drawing main legend elements.\n\n  This is the antialiasing state the painter passed to the \\ref draw method is in by default.\n  \n  This function takes into account the local setting of the antialiasing flag as well as the\n  overrides set with \\ref QCustomPlot::setAntialiasedElements and \\ref\n  QCustomPlot::setNotAntialiasedElements.\n  \n  \\seebaseclassmethod\n  \n  \\see setAntialiased\n*/\nvoid QCPLegend::applyDefaultAntialiasingHint(QCPPainter *painter) const\n{\n  applyAntialiasingHint(painter, mAntialiased, QCP::aeLegend);\n}\n\n/*! \\internal\n  \n  Returns the pen used to paint the border of the legend, taking into account the selection state\n  of the legend box.\n*/\nQPen QCPLegend::getBorderPen() const\n{\n  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;\n}\n\n/*! \\internal\n  \n  Returns the brush used to paint the background of the legend, taking into account the selection\n  state of the legend box.\n*/\nQBrush QCPLegend::getBrush() const\n{\n  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;\n}\n\n/*! \\internal\n  \n  Draws the legend box with the provided \\a painter. The individual legend items are layerables\n  themselves, thus are drawn independently.\n*/\nvoid QCPLegend::draw(QCPPainter *painter)\n{\n  // draw background rect:\n  painter->setBrush(getBrush());\n  painter->setPen(getBorderPen());\n  painter->drawRect(mOuterRect);\n}\n\n/* inherits documentation from base class */\ndouble QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  if (!mParentPlot) return -1;\n  if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))\n    return -1;\n  \n  if (mOuterRect.contains(pos.toPoint()))\n  {\n    if (details) details->setValue(spLegendBox);\n    return mParentPlot->selectionTolerance()*0.99;\n  }\n  return -1;\n}\n\n/* inherits documentation from base class */\nvoid QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)\n{\n  Q_UNUSED(event)\n  mSelectedParts = selectedParts(); // in case item selection has changed\n  if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))\n  {\n    SelectableParts selBefore = mSelectedParts;\n    setSelectedParts(additive ? mSelectedParts^spLegendBox : mSelectedParts|spLegendBox); // no need to unset spItems in !additive case, because they will be deselected by QCustomPlot (they're normal QCPLayerables with own deselectEvent)\n    if (selectionStateChanged)\n      *selectionStateChanged = mSelectedParts != selBefore;\n  }\n}\n\n/* inherits documentation from base class */\nvoid QCPLegend::deselectEvent(bool *selectionStateChanged)\n{\n  mSelectedParts = selectedParts(); // in case item selection has changed\n  if (mSelectableParts.testFlag(spLegendBox))\n  {\n    SelectableParts selBefore = mSelectedParts;\n    setSelectedParts(selectedParts() & ~spLegendBox);\n    if (selectionStateChanged)\n      *selectionStateChanged = mSelectedParts != selBefore;\n  }\n}\n\n/* inherits documentation from base class */\nQCP::Interaction QCPLegend::selectionCategory() const\n{\n  return QCP::iSelectLegend;\n}\n\n/* inherits documentation from base class */\nQCP::Interaction QCPAbstractLegendItem::selectionCategory() const\n{\n  return QCP::iSelectLegend;\n}\n\n/* inherits documentation from base class */\nvoid QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot)\n{\n  if (parentPlot && !parentPlot->legend)\n    parentPlot->legend = this;\n}\n/* end of 'src/layoutelements/layoutelement-legend.cpp' */\n\n\n/* including file 'src/layoutelements/layoutelement-textelement.cpp' */\n/* modified 2021-03-29T02:30:44, size 12925                          */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPTextElement\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPTextElement\n  \\brief A layout element displaying a text\n\n  The text may be specified with \\ref setText, the formatting can be controlled with \\ref setFont,\n  \\ref setTextColor, and \\ref setTextFlags.\n\n  A text element can be added as follows:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcptextelement-creation\n*/\n\n/* start documentation of signals */\n\n/*! \\fn void QCPTextElement::selectionChanged(bool selected)\n  \n  This signal is emitted when the selection state has changed to \\a selected, either by user\n  interaction or by a direct call to \\ref setSelected.\n  \n  \\see setSelected, setSelectable\n*/\n\n/*! \\fn void QCPTextElement::clicked(QMouseEvent *event)\n\n  This signal is emitted when the text element is clicked.\n\n  \\see doubleClicked, selectTest\n*/\n\n/*! \\fn void QCPTextElement::doubleClicked(QMouseEvent *event)\n\n  This signal is emitted when the text element is double clicked.\n\n  \\see clicked, selectTest\n*/\n\n/* end documentation of signals */\n\n/*! \\overload\n  \n  Creates a new QCPTextElement instance and sets default values. The initial text is empty (\\ref\n  setText).\n*/\nQCPTextElement::QCPTextElement(QCustomPlot *parentPlot) :\n  QCPLayoutElement(parentPlot),\n  mText(),\n  mTextFlags(Qt::AlignCenter),\n  mFont(QFont(QLatin1String(\"sans serif\"), 12)), // will be taken from parentPlot if available, see below\n  mTextColor(Qt::black),\n  mSelectedFont(QFont(QLatin1String(\"sans serif\"), 12)), // will be taken from parentPlot if available, see below\n  mSelectedTextColor(Qt::blue),\n  mSelectable(false),\n  mSelected(false)\n{\n  if (parentPlot)\n  {\n    mFont = parentPlot->font();\n    mSelectedFont = parentPlot->font();\n  }\n  setMargins(QMargins(2, 2, 2, 2));\n}\n\n/*! \\overload\n  \n  Creates a new QCPTextElement instance and sets default values.\n\n  The initial text is set to \\a text.\n*/\nQCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text) :\n  QCPLayoutElement(parentPlot),\n  mText(text),\n  mTextFlags(Qt::AlignCenter),\n  mFont(QFont(QLatin1String(\"sans serif\"), 12)), // will be taken from parentPlot if available, see below\n  mTextColor(Qt::black),\n  mSelectedFont(QFont(QLatin1String(\"sans serif\"), 12)), // will be taken from parentPlot if available, see below\n  mSelectedTextColor(Qt::blue),\n  mSelectable(false),\n  mSelected(false)\n{\n  if (parentPlot)\n  {\n    mFont = parentPlot->font();\n    mSelectedFont = parentPlot->font();\n  }\n  setMargins(QMargins(2, 2, 2, 2));\n}\n\n/*! \\overload\n  \n  Creates a new QCPTextElement instance and sets default values.\n\n  The initial text is set to \\a text with \\a pointSize.\n*/\nQCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, double pointSize) :\n  QCPLayoutElement(parentPlot),\n  mText(text),\n  mTextFlags(Qt::AlignCenter),\n  mFont(QFont(QLatin1String(\"sans serif\"), int(pointSize))), // will be taken from parentPlot if available, see below\n  mTextColor(Qt::black),\n  mSelectedFont(QFont(QLatin1String(\"sans serif\"), int(pointSize))), // will be taken from parentPlot if available, see below\n  mSelectedTextColor(Qt::blue),\n  mSelectable(false),\n  mSelected(false)\n{\n  mFont.setPointSizeF(pointSize); // set here again as floating point, because constructor above only takes integer\n  if (parentPlot)\n  {\n    mFont = parentPlot->font();\n    mFont.setPointSizeF(pointSize);\n    mSelectedFont = parentPlot->font();\n    mSelectedFont.setPointSizeF(pointSize);\n  }\n  setMargins(QMargins(2, 2, 2, 2));\n}\n\n/*! \\overload\n  \n  Creates a new QCPTextElement instance and sets default values.\n\n  The initial text is set to \\a text with \\a pointSize and the specified \\a fontFamily.\n*/\nQCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, const QString &fontFamily, double pointSize) :\n  QCPLayoutElement(parentPlot),\n  mText(text),\n  mTextFlags(Qt::AlignCenter),\n  mFont(QFont(fontFamily, int(pointSize))),\n  mTextColor(Qt::black),\n  mSelectedFont(QFont(fontFamily, int(pointSize))),\n  mSelectedTextColor(Qt::blue),\n  mSelectable(false),\n  mSelected(false)\n{\n  mFont.setPointSizeF(pointSize); // set here again as floating point, because constructor above only takes integer\n  setMargins(QMargins(2, 2, 2, 2));\n}\n\n/*! \\overload\n  \n  Creates a new QCPTextElement instance and sets default values.\n\n  The initial text is set to \\a text with the specified \\a font.\n*/\nQCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, const QFont &font) :\n  QCPLayoutElement(parentPlot),\n  mText(text),\n  mTextFlags(Qt::AlignCenter),\n  mFont(font),\n  mTextColor(Qt::black),\n  mSelectedFont(font),\n  mSelectedTextColor(Qt::blue),\n  mSelectable(false),\n  mSelected(false)\n{\n  setMargins(QMargins(2, 2, 2, 2));\n}\n\n/*!\n  Sets the text that will be displayed to \\a text. Multiple lines can be created by insertion of \"\\n\".\n  \n  \\see setFont, setTextColor, setTextFlags\n*/\nvoid QCPTextElement::setText(const QString &text)\n{\n  mText = text;\n}\n\n/*!\n  Sets options for text alignment and wrapping behaviour. \\a flags is a bitwise OR-combination of\n  \\c Qt::AlignmentFlag and \\c Qt::TextFlag enums.\n  \n  Possible enums are:\n  - Qt::AlignLeft\n  - Qt::AlignRight\n  - Qt::AlignHCenter\n  - Qt::AlignJustify\n  - Qt::AlignTop\n  - Qt::AlignBottom\n  - Qt::AlignVCenter\n  - Qt::AlignCenter\n  - Qt::TextDontClip\n  - Qt::TextSingleLine\n  - Qt::TextExpandTabs\n  - Qt::TextShowMnemonic\n  - Qt::TextWordWrap\n  - Qt::TextIncludeTrailingSpaces\n*/\nvoid QCPTextElement::setTextFlags(int flags)\n{\n  mTextFlags = flags;\n}\n\n/*!\n  Sets the \\a font of the text.\n  \n  \\see setTextColor, setSelectedFont\n*/\nvoid QCPTextElement::setFont(const QFont &font)\n{\n  mFont = font;\n}\n\n/*!\n  Sets the \\a color of the text.\n  \n  \\see setFont, setSelectedTextColor\n*/\nvoid QCPTextElement::setTextColor(const QColor &color)\n{\n  mTextColor = color;\n}\n\n/*!\n  Sets the \\a font of the text that will be used if the text element is selected (\\ref setSelected).\n  \n  \\see setFont\n*/\nvoid QCPTextElement::setSelectedFont(const QFont &font)\n{\n  mSelectedFont = font;\n}\n\n/*!\n  Sets the \\a color of the text that will be used if the text element is selected (\\ref setSelected).\n  \n  \\see setTextColor\n*/\nvoid QCPTextElement::setSelectedTextColor(const QColor &color)\n{\n  mSelectedTextColor = color;\n}\n\n/*!\n  Sets whether the user may select this text element.\n\n  Note that even when \\a selectable is set to <tt>false</tt>, the selection state may be changed\n  programmatically via \\ref setSelected.\n*/\nvoid QCPTextElement::setSelectable(bool selectable)\n{\n  if (mSelectable != selectable)\n  {\n    mSelectable = selectable;\n    emit selectableChanged(mSelectable);\n  }\n}\n\n/*!\n  Sets the selection state of this text element to \\a selected. If the selection has changed, \\ref\n  selectionChanged is emitted.\n  \n  Note that this function can change the selection state independently of the current \\ref\n  setSelectable state.\n*/\nvoid QCPTextElement::setSelected(bool selected)\n{\n  if (mSelected != selected)\n  {\n    mSelected = selected;\n    emit selectionChanged(mSelected);\n  }\n}\n\n/* inherits documentation from base class */\nvoid QCPTextElement::applyDefaultAntialiasingHint(QCPPainter *painter) const\n{\n  applyAntialiasingHint(painter, mAntialiased, QCP::aeOther);\n}\n\n/* inherits documentation from base class */\nvoid QCPTextElement::draw(QCPPainter *painter)\n{\n  painter->setFont(mainFont());\n  painter->setPen(QPen(mainTextColor()));\n  painter->drawText(mRect, mTextFlags, mText, &mTextBoundingRect);\n}\n\n/* inherits documentation from base class */\nQSize QCPTextElement::minimumOuterSizeHint() const\n{\n  QFontMetrics metrics(mFont);\n  QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, mText).size());\n  result.rwidth() += mMargins.left()+mMargins.right();\n  result.rheight() += mMargins.top()+mMargins.bottom();\n  return result;\n}\n\n/* inherits documentation from base class */\nQSize QCPTextElement::maximumOuterSizeHint() const\n{\n  QFontMetrics metrics(mFont);\n  QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, mText).size());\n  result.setWidth(QWIDGETSIZE_MAX);\n  result.rheight() += mMargins.top()+mMargins.bottom();\n  return result;\n}\n\n/* inherits documentation from base class */\nvoid QCPTextElement::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)\n{\n  Q_UNUSED(event)\n  Q_UNUSED(details)\n  if (mSelectable)\n  {\n    bool selBefore = mSelected;\n    setSelected(additive ? !mSelected : true);\n    if (selectionStateChanged)\n      *selectionStateChanged = mSelected != selBefore;\n  }\n}\n\n/* inherits documentation from base class */\nvoid QCPTextElement::deselectEvent(bool *selectionStateChanged)\n{\n  if (mSelectable)\n  {\n    bool selBefore = mSelected;\n    setSelected(false);\n    if (selectionStateChanged)\n      *selectionStateChanged = mSelected != selBefore;\n  }\n}\n\n/*!\n  Returns 0.99*selectionTolerance (see \\ref QCustomPlot::setSelectionTolerance) when \\a pos is\n  within the bounding box of the text element's text. Note that this bounding box is updated in the\n  draw call.\n\n  If \\a pos is outside the text's bounding box or if \\a onlySelectable is true and this text\n  element is not selectable (\\ref setSelectable), returns -1.\n\n  \\seebaseclassmethod\n*/\ndouble QCPTextElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(details)\n  if (onlySelectable && !mSelectable)\n    return -1;\n  \n  if (mTextBoundingRect.contains(pos.toPoint()))\n    return mParentPlot->selectionTolerance()*0.99;\n  else\n    return -1;\n}\n\n/*!\n  Accepts the mouse event in order to emit the according click signal in the \\ref\n  mouseReleaseEvent.\n\n  \\seebaseclassmethod\n*/\nvoid QCPTextElement::mousePressEvent(QMouseEvent *event, const QVariant &details)\n{\n  Q_UNUSED(details)\n  event->accept();\n}\n\n/*!\n  Emits the \\ref clicked signal if the cursor hasn't moved by more than a few pixels since the \\ref\n  mousePressEvent.\n\n  \\seebaseclassmethod\n*/\nvoid QCPTextElement::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)\n{\n  if ((QPointF(event->pos())-startPos).manhattanLength() <= 3)\n    emit clicked(event);\n}\n\n/*!\n  Emits the \\ref doubleClicked signal.\n\n  \\seebaseclassmethod\n*/\nvoid QCPTextElement::mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details)\n{\n  Q_UNUSED(details)\n  emit doubleClicked(event);\n}\n\n/*! \\internal\n  \n  Returns the main font to be used. This is mSelectedFont if \\ref setSelected is set to\n  <tt>true</tt>, else mFont is returned.\n*/\nQFont QCPTextElement::mainFont() const\n{\n  return mSelected ? mSelectedFont : mFont;\n}\n\n/*! \\internal\n  \n  Returns the main color to be used. This is mSelectedTextColor if \\ref setSelected is set to\n  <tt>true</tt>, else mTextColor is returned.\n*/\nQColor QCPTextElement::mainTextColor() const\n{\n  return mSelected ? mSelectedTextColor : mTextColor;\n}\n/* end of 'src/layoutelements/layoutelement-textelement.cpp' */\n\n\n/* including file 'src/layoutelements/layoutelement-colorscale.cpp' */\n/* modified 2021-03-29T02:30:44, size 26531                         */\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPColorScale\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPColorScale\n  \\brief A color scale for use with color coding data such as QCPColorMap\n  \n  This layout element can be placed on the plot to correlate a color gradient with data values. It\n  is usually used in combination with one or multiple \\ref QCPColorMap \"QCPColorMaps\".\n\n  \\image html QCPColorScale.png\n  \n  The color scale can be either horizontal or vertical, as shown in the image above. The\n  orientation and the side where the numbers appear is controlled with \\ref setType.\n  \n  Use \\ref QCPColorMap::setColorScale to connect a color map with a color scale. Once they are\n  connected, they share their gradient, data range and data scale type (\\ref setGradient, \\ref\n  setDataRange, \\ref setDataScaleType). Multiple color maps may be associated with a single color\n  scale, to make them all synchronize these properties.\n  \n  To have finer control over the number display and axis behaviour, you can directly access the\n  \\ref axis. See the documentation of QCPAxis for details about configuring axes. For example, if\n  you want to change the number of automatically generated ticks, call\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-tickcount\n  \n  Placing a color scale next to the main axis rect works like with any other layout element:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-creation\n  In this case we have placed it to the right of the default axis rect, so it wasn't necessary to\n  call \\ref setType, since \\ref QCPAxis::atRight is already the default. The text next to the color\n  scale can be set with \\ref setLabel.\n  \n  For optimum appearance (like in the image above), it may be desirable to line up the axis rect and\n  the borders of the color scale. Use a \\ref QCPMarginGroup to achieve this:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-margingroup\n  \n  Color scales are initialized with a non-zero minimum top and bottom margin (\\ref\n  setMinimumMargins), because vertical color scales are most common and the minimum top/bottom\n  margin makes sure it keeps some distance to the top/bottom widget border. So if you change to a\n  horizontal color scale by setting \\ref setType to \\ref QCPAxis::atBottom or \\ref QCPAxis::atTop, you\n  might want to also change the minimum margins accordingly, e.g. <tt>setMinimumMargins(QMargins(6, 0, 6, 0))</tt>.\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn QCPAxis *QCPColorScale::axis() const\n  \n  Returns the internal \\ref QCPAxis instance of this color scale. You can access it to alter the\n  appearance and behaviour of the axis. \\ref QCPColorScale duplicates some properties in its\n  interface for convenience. Those are \\ref setDataRange (\\ref QCPAxis::setRange), \\ref\n  setDataScaleType (\\ref QCPAxis::setScaleType), and the method \\ref setLabel (\\ref\n  QCPAxis::setLabel). As they each are connected, it does not matter whether you use the method on\n  the QCPColorScale or on its QCPAxis.\n  \n  If the type of the color scale is changed with \\ref setType, the axis returned by this method\n  will change, too, to either the left, right, bottom or top axis, depending on which type was set.\n*/\n\n/* end documentation of signals */\n/* start documentation of signals */\n\n/*! \\fn void QCPColorScale::dataRangeChanged(const QCPRange &newRange);\n  \n  This signal is emitted when the data range changes.\n  \n  \\see setDataRange\n*/\n\n/*! \\fn void QCPColorScale::dataScaleTypeChanged(QCPAxis::ScaleType scaleType);\n  \n  This signal is emitted when the data scale type changes.\n  \n  \\see setDataScaleType\n*/\n\n/*! \\fn void QCPColorScale::gradientChanged(const QCPColorGradient &newGradient);\n  \n  This signal is emitted when the gradient changes.\n  \n  \\see setGradient\n*/\n\n/* end documentation of signals */\n\n/*!\n  Constructs a new QCPColorScale.\n*/\nQCPColorScale::QCPColorScale(QCustomPlot *parentPlot) :\n  QCPLayoutElement(parentPlot),\n  mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight\n  mDataScaleType(QCPAxis::stLinear),\n  mGradient(QCPColorGradient::gpCold),\n  mBarWidth(20),\n  mAxisRect(new QCPColorScaleAxisRectPrivate(this))\n{\n  setMinimumMargins(QMargins(0, 6, 0, 6)); // for default right color scale types, keep some room at bottom and top (important if no margin group is used)\n  setType(QCPAxis::atRight);\n  setDataRange(QCPRange(0, 6));\n}\n\nQCPColorScale::~QCPColorScale()\n{\n  delete mAxisRect;\n}\n\n/* undocumented getter */\nQString QCPColorScale::label() const\n{\n  if (!mColorAxis)\n  {\n    qDebug() << Q_FUNC_INFO << \"internal color axis undefined\";\n    return QString();\n  }\n  \n  return mColorAxis.data()->label();\n}\n\n/* undocumented getter */\nbool QCPColorScale::rangeDrag() const\n{\n  if (!mAxisRect)\n  {\n    qDebug() << Q_FUNC_INFO << \"internal axis rect was deleted\";\n    return false;\n  }\n  \n  return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) &&\n      mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&\n      mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);\n}\n\n/* undocumented getter */\nbool QCPColorScale::rangeZoom() const\n{\n  if (!mAxisRect)\n  {\n    qDebug() << Q_FUNC_INFO << \"internal axis rect was deleted\";\n    return false;\n  }\n  \n  return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) &&\n      mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&\n      mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);\n}\n\n/*!\n  Sets at which side of the color scale the axis is placed, and thus also its orientation.\n  \n  Note that after setting \\a type to a different value, the axis returned by \\ref axis() will\n  be a different one. The new axis will adopt the following properties from the previous axis: The\n  range, scale type, label and ticker (the latter will be shared and not copied).\n*/\nvoid QCPColorScale::setType(QCPAxis::AxisType type)\n{\n  if (!mAxisRect)\n  {\n    qDebug() << Q_FUNC_INFO << \"internal axis rect was deleted\";\n    return;\n  }\n  if (mType != type)\n  {\n    mType = type;\n    QCPRange rangeTransfer(0, 6);\n    QString labelTransfer;\n    QSharedPointer<QCPAxisTicker> tickerTransfer;\n    // transfer/revert some settings on old axis if it exists:\n    bool doTransfer = !mColorAxis.isNull();\n    if (doTransfer)\n    {\n      rangeTransfer = mColorAxis.data()->range();\n      labelTransfer = mColorAxis.data()->label();\n      tickerTransfer = mColorAxis.data()->ticker();\n      mColorAxis.data()->setLabel(QString());\n      disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));\n      disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));\n    }\n    const QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop;\n    foreach (QCPAxis::AxisType atype, allAxisTypes)\n    {\n      mAxisRect.data()->axis(atype)->setTicks(atype == mType);\n      mAxisRect.data()->axis(atype)->setTickLabels(atype== mType);\n    }\n    // set new mColorAxis pointer:\n    mColorAxis = mAxisRect.data()->axis(mType);\n    // transfer settings to new axis:\n    if (doTransfer)\n    {\n      mColorAxis.data()->setRange(rangeTransfer); // range transfer necessary if axis changes from vertical to horizontal or vice versa (axes with same orientation are synchronized via signals)\n      mColorAxis.data()->setLabel(labelTransfer);\n      mColorAxis.data()->setTicker(tickerTransfer);\n    }\n    connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));\n    connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));\n    mAxisRect.data()->setRangeDragAxes(QList<QCPAxis*>() << mColorAxis.data());\n  }\n}\n\n/*!\n  Sets the range spanned by the color gradient and that is shown by the axis in the color scale.\n  \n  It is equivalent to calling QCPColorMap::setDataRange on any of the connected color maps. It is\n  also equivalent to directly accessing the \\ref axis and setting its range with \\ref\n  QCPAxis::setRange.\n  \n  \\see setDataScaleType, setGradient, rescaleDataRange\n*/\nvoid QCPColorScale::setDataRange(const QCPRange &dataRange)\n{\n  if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)\n  {\n    mDataRange = dataRange;\n    if (mColorAxis)\n      mColorAxis.data()->setRange(mDataRange);\n    emit dataRangeChanged(mDataRange);\n  }\n}\n\n/*!\n  Sets the scale type of the color scale, i.e. whether values are associated with colors linearly\n  or logarithmically.\n  \n  It is equivalent to calling QCPColorMap::setDataScaleType on any of the connected color maps. It is\n  also equivalent to directly accessing the \\ref axis and setting its scale type with \\ref\n  QCPAxis::setScaleType.\n  \n  Note that this method controls the coordinate transformation. For logarithmic scales, you will\n  likely also want to use a logarithmic tick spacing and labeling, which can be achieved by setting\n  the color scale's \\ref axis ticker to an instance of \\ref QCPAxisTickerLog :\n  \n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpaxisticker-log-colorscale\n  \n  See the documentation of \\ref QCPAxisTickerLog about the details of logarithmic axis tick\n  creation.\n  \n  \\see setDataRange, setGradient\n*/\nvoid QCPColorScale::setDataScaleType(QCPAxis::ScaleType scaleType)\n{\n  if (mDataScaleType != scaleType)\n  {\n    mDataScaleType = scaleType;\n    if (mColorAxis)\n      mColorAxis.data()->setScaleType(mDataScaleType);\n    if (mDataScaleType == QCPAxis::stLogarithmic)\n      setDataRange(mDataRange.sanitizedForLogScale());\n    emit dataScaleTypeChanged(mDataScaleType);\n  }\n}\n\n/*!\n  Sets the color gradient that will be used to represent data values.\n  \n  It is equivalent to calling QCPColorMap::setGradient on any of the connected color maps.\n  \n  \\see setDataRange, setDataScaleType\n*/\nvoid QCPColorScale::setGradient(const QCPColorGradient &gradient)\n{\n  if (mGradient != gradient)\n  {\n    mGradient = gradient;\n    if (mAxisRect)\n      mAxisRect.data()->mGradientImageInvalidated = true;\n    emit gradientChanged(mGradient);\n  }\n}\n\n/*!\n  Sets the axis label of the color scale. This is equivalent to calling \\ref QCPAxis::setLabel on\n  the internal \\ref axis.\n*/\nvoid QCPColorScale::setLabel(const QString &str)\n{\n  if (!mColorAxis)\n  {\n    qDebug() << Q_FUNC_INFO << \"internal color axis undefined\";\n    return;\n  }\n  \n  mColorAxis.data()->setLabel(str);\n}\n\n/*!\n  Sets the width (or height, for horizontal color scales) the bar where the gradient is displayed\n  will have.\n*/\nvoid QCPColorScale::setBarWidth(int width)\n{\n  mBarWidth = width;\n}\n\n/*!\n  Sets whether the user can drag the data range (\\ref setDataRange).\n  \n  Note that \\ref QCP::iRangeDrag must be in the QCustomPlot's interactions (\\ref\n  QCustomPlot::setInteractions) to allow range dragging.\n*/\nvoid QCPColorScale::setRangeDrag(bool enabled)\n{\n  if (!mAxisRect)\n  {\n    qDebug() << Q_FUNC_INFO << \"internal axis rect was deleted\";\n    return;\n  }\n  \n  if (enabled)\n  {\n    mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));\n  } else\n  {\n#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)\n    mAxisRect.data()->setRangeDrag(nullptr);\n#else\n    mAxisRect.data()->setRangeDrag({});\n#endif\n  }\n}\n\n/*!\n  Sets whether the user can zoom the data range (\\ref setDataRange) by scrolling the mouse wheel.\n  \n  Note that \\ref QCP::iRangeZoom must be in the QCustomPlot's interactions (\\ref\n  QCustomPlot::setInteractions) to allow range dragging.\n*/\nvoid QCPColorScale::setRangeZoom(bool enabled)\n{\n  if (!mAxisRect)\n  {\n    qDebug() << Q_FUNC_INFO << \"internal axis rect was deleted\";\n    return;\n  }\n  \n  if (enabled)\n  {\n    mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));\n  } else\n  {\n#if QT_VERSION < QT_VERSION_CHECK(5, 2, 0)\n    mAxisRect.data()->setRangeDrag(nullptr);\n#else\n    mAxisRect.data()->setRangeZoom({});\n#endif\n  }\n}\n\n/*!\n  Returns a list of all the color maps associated with this color scale.\n*/\nQList<QCPColorMap*> QCPColorScale::colorMaps() const\n{\n  QList<QCPColorMap*> result;\n  for (int i=0; i<mParentPlot->plottableCount(); ++i)\n  {\n    if (QCPColorMap *cm = qobject_cast<QCPColorMap*>(mParentPlot->plottable(i)))\n      if (cm->colorScale() == this)\n        result.append(cm);\n  }\n  return result;\n}\n\n/*!\n  Changes the data range such that all color maps associated with this color scale are fully mapped\n  to the gradient in the data dimension.\n  \n  \\see setDataRange\n*/\nvoid QCPColorScale::rescaleDataRange(bool onlyVisibleMaps)\n{\n  QList<QCPColorMap*> maps = colorMaps();\n  QCPRange newRange;\n  bool haveRange = false;\n  QCP::SignDomain sign = QCP::sdBoth;\n  if (mDataScaleType == QCPAxis::stLogarithmic)\n    sign = (mDataRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive);\n  foreach (QCPColorMap *map, maps)\n  {\n    if (!map->realVisibility() && onlyVisibleMaps)\n      continue;\n    QCPRange mapRange;\n    if (map->colorScale() == this)\n    {\n      bool currentFoundRange = true;\n      mapRange = map->data()->dataBounds();\n      if (sign == QCP::sdPositive)\n      {\n        if (mapRange.lower <= 0 && mapRange.upper > 0)\n          mapRange.lower = mapRange.upper*1e-3;\n        else if (mapRange.lower <= 0 && mapRange.upper <= 0)\n          currentFoundRange = false;\n      } else if (sign == QCP::sdNegative)\n      {\n        if (mapRange.upper >= 0 && mapRange.lower < 0)\n          mapRange.upper = mapRange.lower*1e-3;\n        else if (mapRange.upper >= 0 && mapRange.lower >= 0)\n          currentFoundRange = false;\n      }\n      if (currentFoundRange)\n      {\n        if (!haveRange)\n          newRange = mapRange;\n        else\n          newRange.expand(mapRange);\n        haveRange = true;\n      }\n    }\n  }\n  if (haveRange)\n  {\n    if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this dimension), shift current range to at least center the data\n    {\n      double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason\n      if (mDataScaleType == QCPAxis::stLinear)\n      {\n        newRange.lower = center-mDataRange.size()/2.0;\n        newRange.upper = center+mDataRange.size()/2.0;\n      } else // mScaleType == stLogarithmic\n      {\n        newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower);\n        newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower);\n      }\n    }\n    setDataRange(newRange);\n  }\n}\n\n/* inherits documentation from base class */\nvoid QCPColorScale::update(UpdatePhase phase)\n{\n  QCPLayoutElement::update(phase);\n  if (!mAxisRect)\n  {\n    qDebug() << Q_FUNC_INFO << \"internal axis rect was deleted\";\n    return;\n  }\n  \n  mAxisRect.data()->update(phase);\n  \n  switch (phase)\n  {\n    case upMargins:\n    {\n      if (mType == QCPAxis::atBottom || mType == QCPAxis::atTop)\n      {\n        setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom());\n        setMinimumSize(0,               mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom());\n      } else\n      {\n        setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right(), QWIDGETSIZE_MAX);\n        setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right(), 0);\n      }\n      break;\n    }\n    case upLayout:\n    {\n      mAxisRect.data()->setOuterRect(rect());\n      break;\n    }\n    default: break;\n  }\n}\n\n/* inherits documentation from base class */\nvoid QCPColorScale::applyDefaultAntialiasingHint(QCPPainter *painter) const\n{\n  painter->setAntialiasing(false);\n}\n\n/* inherits documentation from base class */\nvoid QCPColorScale::mousePressEvent(QMouseEvent *event, const QVariant &details)\n{\n  if (!mAxisRect)\n  {\n    qDebug() << Q_FUNC_INFO << \"internal axis rect was deleted\";\n    return;\n  }\n  mAxisRect.data()->mousePressEvent(event, details);\n}\n\n/* inherits documentation from base class */\nvoid QCPColorScale::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)\n{\n  if (!mAxisRect)\n  {\n    qDebug() << Q_FUNC_INFO << \"internal axis rect was deleted\";\n    return;\n  }\n  mAxisRect.data()->mouseMoveEvent(event, startPos);\n}\n\n/* inherits documentation from base class */\nvoid QCPColorScale::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)\n{\n  if (!mAxisRect)\n  {\n    qDebug() << Q_FUNC_INFO << \"internal axis rect was deleted\";\n    return;\n  }\n  mAxisRect.data()->mouseReleaseEvent(event, startPos);\n}\n\n/* inherits documentation from base class */\nvoid QCPColorScale::wheelEvent(QWheelEvent *event)\n{\n  if (!mAxisRect)\n  {\n    qDebug() << Q_FUNC_INFO << \"internal axis rect was deleted\";\n    return;\n  }\n  mAxisRect.data()->wheelEvent(event);\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPColorScaleAxisRectPrivate\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPColorScaleAxisRectPrivate\n\n  \\internal\n  \\brief An axis rect subclass for use in a QCPColorScale\n  \n  This is a private class and not part of the public QCustomPlot interface.\n  \n  It provides the axis rect functionality for the QCPColorScale class.\n*/\n\n\n/*!\n  Creates a new instance, as a child of \\a parentColorScale.\n*/\nQCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale) :\n  QCPAxisRect(parentColorScale->parentPlot(), true),\n  mParentColorScale(parentColorScale),\n  mGradientImageInvalidated(true)\n{\n  setParentLayerable(parentColorScale);\n  setMinimumMargins(QMargins(0, 0, 0, 0));\n  const QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;\n  foreach (QCPAxis::AxisType type, allAxisTypes)\n  {\n    axis(type)->setVisible(true);\n    axis(type)->grid()->setVisible(false);\n    axis(type)->setPadding(0);\n    connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));\n    connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));\n  }\n\n  connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));\n  connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));\n  connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));\n  connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));\n  connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));\n  connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType)));\n  connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType)));\n  connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));\n  \n  // make layer transfers of color scale transfer to axis rect and axes\n  // the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect:\n  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*)));\n  foreach (QCPAxis::AxisType type, allAxisTypes)\n    connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*)));\n}\n\n/*! \\internal\n  \n  Updates the color gradient image if necessary, by calling \\ref updateGradientImage, then draws\n  it. Then the axes are drawn by calling the \\ref QCPAxisRect::draw base class implementation.\n  \n  \\seebaseclassmethod\n*/\nvoid QCPColorScaleAxisRectPrivate::draw(QCPPainter *painter)\n{\n  if (mGradientImageInvalidated)\n    updateGradientImage();\n  \n  bool mirrorHorz = false;\n  bool mirrorVert = false;\n  if (mParentColorScale->mColorAxis)\n  {\n    mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop);\n    mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight);\n  }\n  \n  painter->drawImage(rect().adjusted(0, -1, 0, -1), mGradientImage.mirrored(mirrorHorz, mirrorVert));\n  QCPAxisRect::draw(painter);\n}\n\n/*! \\internal\n\n  Uses the current gradient of the parent \\ref QCPColorScale (specified in the constructor) to\n  generate a gradient image. This gradient image will be used in the \\ref draw method.\n*/\nvoid QCPColorScaleAxisRectPrivate::updateGradientImage()\n{\n  if (rect().isEmpty())\n    return;\n  \n  const QImage::Format format = QImage::Format_ARGB32_Premultiplied;\n  int n = mParentColorScale->mGradient.levelCount();\n  int w, h;\n  QVector<double> data(n);\n  for (int i=0; i<n; ++i)\n    data[i] = i;\n  if (mParentColorScale->mType == QCPAxis::atBottom || mParentColorScale->mType == QCPAxis::atTop)\n  {\n    w = n;\n    h = rect().height();\n    mGradientImage = QImage(w, h, format);\n    QVector<QRgb*> pixels;\n    for (int y=0; y<h; ++y)\n      pixels.append(reinterpret_cast<QRgb*>(mGradientImage.scanLine(y)));\n    mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n);\n    for (int y=1; y<h; ++y)\n      memcpy(pixels.at(y), pixels.first(), size_t(n)*sizeof(QRgb));\n  } else\n  {\n    w = rect().width();\n    h = n;\n    mGradientImage = QImage(w, h, format);\n    for (int y=0; y<h; ++y)\n    {\n      QRgb *pixels = reinterpret_cast<QRgb*>(mGradientImage.scanLine(y));\n      const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1));\n      for (int x=0; x<w; ++x)\n        pixels[x] = lineColor;\n    }\n  }\n  mGradientImageInvalidated = false;\n}\n\n/*! \\internal\n\n  This slot is connected to the selectionChanged signals of the four axes in the constructor. It\n  synchronizes the selection state of the axes.\n*/\nvoid QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts)\n{\n  // axis bases of four axes shall always (de-)selected synchronously:\n  const QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;\n  foreach (QCPAxis::AxisType type, allAxisTypes)\n  {\n    if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))\n      if (senderAxis->axisType() == type)\n        continue;\n    \n    if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))\n    {\n      if (selectedParts.testFlag(QCPAxis::spAxis))\n        axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis);\n      else\n        axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis);\n    }\n  }\n}\n\n/*! \\internal\n\n  This slot is connected to the selectableChanged signals of the four axes in the constructor. It\n  synchronizes the selectability of the axes.\n*/\nvoid QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts)\n{\n  // synchronize axis base selectability:\n  const QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;\n  foreach (QCPAxis::AxisType type, allAxisTypes)\n  {\n    if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))\n      if (senderAxis->axisType() == type)\n        continue;\n    \n    if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))\n    {\n      if (selectableParts.testFlag(QCPAxis::spAxis))\n        axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis);\n      else\n        axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis);\n    }\n  }\n}\n/* end of 'src/layoutelements/layoutelement-colorscale.cpp' */\n\n\n/* including file 'src/plottables/plottable-graph.cpp' */\n/* modified 2021-03-29T02:30:44, size 74518            */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPGraphData\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPGraphData\n  \\brief Holds the data of one single data point for QCPGraph.\n  \n  The stored data is:\n  \\li \\a key: coordinate on the key axis of this data point (this is the \\a mainKey and the \\a sortKey)\n  \\li \\a value: coordinate on the value axis of this data point (this is the \\a mainValue)\n  \n  The container for storing multiple data points is \\ref QCPGraphDataContainer. It is a typedef for\n  \\ref QCPDataContainer with \\ref QCPGraphData as the DataType template parameter. See the\n  documentation there for an explanation regarding the data type's generic methods.\n  \n  \\see QCPGraphDataContainer\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn double QCPGraphData::sortKey() const\n  \n  Returns the \\a key member of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn static QCPGraphData QCPGraphData::fromSortKey(double sortKey)\n  \n  Returns a data point with the specified \\a sortKey. All other members are set to zero.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn static static bool QCPGraphData::sortKeyIsMainKey()\n  \n  Since the member \\a key is both the data point key coordinate and the data ordering parameter,\n  this method returns true.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn double QCPGraphData::mainKey() const\n  \n  Returns the \\a key member of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn double QCPGraphData::mainValue() const\n  \n  Returns the \\a value member of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn QCPRange QCPGraphData::valueRange() const\n  \n  Returns a QCPRange with both lower and upper boundary set to \\a value of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Constructs a data point with key and value set to zero.\n*/\nQCPGraphData::QCPGraphData() :\n  key(0),\n  value(0)\n{\n}\n\n/*!\n  Constructs a data point with the specified \\a key and \\a value.\n*/\nQCPGraphData::QCPGraphData(double key, double value) :\n  key(key),\n  value(value)\n{\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPGraph\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPGraph\n  \\brief A plottable representing a graph in a plot.\n\n  \\image html QCPGraph.png\n  \n  Usually you create new graphs by calling QCustomPlot::addGraph. The resulting instance can be\n  accessed via QCustomPlot::graph.\n\n  To plot data, assign it with the \\ref setData or \\ref addData functions. Alternatively, you can\n  also access and modify the data via the \\ref data method, which returns a pointer to the internal\n  \\ref QCPGraphDataContainer.\n  \n  Graphs are used to display single-valued data. Single-valued means that there should only be one\n  data point per unique key coordinate. In other words, the graph can't have \\a loops. If you do\n  want to plot non-single-valued curves, rather use the QCPCurve plottable.\n  \n  Gaps in the graph line can be created by adding data points with NaN as value\n  (<tt>qQNaN()</tt> or <tt>std::numeric_limits<double>::quiet_NaN()</tt>) in between the two data points that shall be\n  separated.\n  \n  \\section qcpgraph-appearance Changing the appearance\n  \n  The appearance of the graph is mainly determined by the line style, scatter style, brush and pen\n  of the graph (\\ref setLineStyle, \\ref setScatterStyle, \\ref setBrush, \\ref setPen).\n  \n  \\subsection filling Filling under or between graphs\n  \n  QCPGraph knows two types of fills: Normal graph fills towards the zero-value-line parallel to\n  the key axis of the graph, and fills between two graphs, called channel fills. To enable a fill,\n  just set a brush with \\ref setBrush which is neither Qt::NoBrush nor fully transparent.\n  \n  By default, a normal fill towards the zero-value-line will be drawn. To set up a channel fill\n  between this graph and another one, call \\ref setChannelFillGraph with the other graph as\n  parameter.\n\n  \\see QCustomPlot::addGraph, QCustomPlot::graph\n*/\n\n/* start of documentation of inline functions */\n\n/*! \\fn QSharedPointer<QCPGraphDataContainer> QCPGraph::data() const\n  \n  Returns a shared pointer to the internal data storage of type \\ref QCPGraphDataContainer. You may\n  use it to directly manipulate the data, which may be more convenient and faster than using the\n  regular \\ref setData or \\ref addData methods.\n*/\n\n/* end of documentation of inline functions */\n\n/*!\n  Constructs a graph which uses \\a keyAxis as its key axis (\"x\") and \\a valueAxis as its value\n  axis (\"y\"). \\a keyAxis and \\a valueAxis must reside in the same QCustomPlot instance and not have\n  the same orientation. If either of these restrictions is violated, a corresponding message is\n  printed to the debug output (qDebug), the construction is not aborted, though.\n  \n  The created QCPGraph is automatically registered with the QCustomPlot instance inferred from \\a\n  keyAxis. This QCustomPlot instance takes ownership of the QCPGraph, so do not delete it manually\n  but use QCustomPlot::removePlottable() instead.\n  \n  To directly create a graph inside a plot, you can also use the simpler QCustomPlot::addGraph function.\n*/\nQCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :\n  QCPAbstractPlottable1D<QCPGraphData>(keyAxis, valueAxis),\n  mLineStyle{},\n  mScatterSkip{},\n  mAdaptiveSampling{}\n{\n  // special handling for QCPGraphs to maintain the simple graph interface:\n  mParentPlot->registerGraph(this);\n\n  setPen(QPen(Qt::blue, 0));\n  setBrush(Qt::NoBrush);\n  \n  setLineStyle(lsLine);\n  setScatterSkip(0);\n  setChannelFillGraph(nullptr);\n  setAdaptiveSampling(true);\n}\n\nQCPGraph::~QCPGraph()\n{\n}\n\n/*! \\overload\n  \n  Replaces the current data container with the provided \\a data container.\n  \n  Since a QSharedPointer is used, multiple QCPGraphs may share the same data container safely.\n  Modifying the data in the container will then affect all graphs that share the container. Sharing\n  can be achieved by simply exchanging the data containers wrapped in shared pointers:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpgraph-datasharing-1\n  \n  If you do not wish to share containers, but create a copy from an existing container, rather use\n  the \\ref QCPDataContainer<DataType>::set method on the graph's data container directly:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpgraph-datasharing-2\n  \n  \\see addData\n*/\nvoid QCPGraph::setData(QSharedPointer<QCPGraphDataContainer> data)\n{\n  mDataContainer = data;\n}\n\n/*! \\overload\n  \n  Replaces the current data with the provided points in \\a keys and \\a values. The provided\n  vectors should have equal length. Else, the number of added points will be the size of the\n  smallest vector.\n  \n  If you can guarantee that the passed data points are sorted by \\a keys in ascending order, you\n  can set \\a alreadySorted to true, to improve performance by saving a sorting run.\n  \n  \\see addData\n*/\nvoid QCPGraph::setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)\n{\n  mDataContainer->clear();\n  addData(keys, values, alreadySorted);\n}\n\n/*!\n  Sets how the single data points are connected in the plot. For scatter-only plots, set \\a ls to\n  \\ref lsNone and \\ref setScatterStyle to the desired scatter style.\n  \n  \\see setScatterStyle\n*/\nvoid QCPGraph::setLineStyle(LineStyle ls)\n{\n  mLineStyle = ls;\n}\n\n/*!\n  Sets the visual appearance of single data points in the plot. If set to \\ref QCPScatterStyle::ssNone, no scatter points\n  are drawn (e.g. for line-only-plots with appropriate line style).\n  \n  \\see QCPScatterStyle, setLineStyle\n*/\nvoid QCPGraph::setScatterStyle(const QCPScatterStyle &style)\n{\n  mScatterStyle = style;\n}\n\n/*!\n  If scatters are displayed (scatter style not \\ref QCPScatterStyle::ssNone), \\a skip number of\n  scatter points are skipped/not drawn after every drawn scatter point.\n\n  This can be used to make the data appear sparser while for example still having a smooth line,\n  and to improve performance for very high density plots.\n\n  If \\a skip is set to 0 (default), all scatter points are drawn.\n\n  \\see setScatterStyle\n*/\nvoid QCPGraph::setScatterSkip(int skip)\n{\n  mScatterSkip = qMax(0, skip);\n}\n\n/*!\n  Sets the target graph for filling the area between this graph and \\a targetGraph with the current\n  brush (\\ref setBrush).\n  \n  When \\a targetGraph is set to 0, a normal graph fill to the zero-value-line will be shown. To\n  disable any filling, set the brush to Qt::NoBrush.\n\n  \\see setBrush\n*/\nvoid QCPGraph::setChannelFillGraph(QCPGraph *targetGraph)\n{\n  // prevent setting channel target to this graph itself:\n  if (targetGraph == this)\n  {\n    qDebug() << Q_FUNC_INFO << \"targetGraph is this graph itself\";\n    mChannelFillGraph = nullptr;\n    return;\n  }\n  // prevent setting channel target to a graph not in the plot:\n  if (targetGraph && targetGraph->mParentPlot != mParentPlot)\n  {\n    qDebug() << Q_FUNC_INFO << \"targetGraph not in same plot\";\n    mChannelFillGraph = nullptr;\n    return;\n  }\n  \n  mChannelFillGraph = targetGraph;\n}\n\n/*!\n  Sets whether adaptive sampling shall be used when plotting this graph. QCustomPlot's adaptive\n  sampling technique can drastically improve the replot performance for graphs with a larger number\n  of points (e.g. above 10,000), without notably changing the appearance of the graph.\n  \n  By default, adaptive sampling is enabled. Even if enabled, QCustomPlot decides whether adaptive\n  sampling shall actually be used on a per-graph basis. So leaving adaptive sampling enabled has no\n  disadvantage in almost all cases.\n  \n  \\image html adaptive-sampling-line.png \"A line plot of 500,000 points without and with adaptive sampling\"\n  \n  As can be seen, line plots experience no visual degradation from adaptive sampling. Outliers are\n  reproduced reliably, as well as the overall shape of the data set. The replot time reduces\n  dramatically though. This allows QCustomPlot to display large amounts of data in realtime.\n  \n  \\image html adaptive-sampling-scatter.png \"A scatter plot of 100,000 points without and with adaptive sampling\"\n  \n  Care must be taken when using high-density scatter plots in combination with adaptive sampling.\n  The adaptive sampling algorithm treats scatter plots more carefully than line plots which still\n  gives a significant reduction of replot times, but not quite as much as for line plots. This is\n  because scatter plots inherently need more data points to be preserved in order to still resemble\n  the original, non-adaptive-sampling plot. As shown above, the results still aren't quite\n  identical, as banding occurs for the outer data points. This is in fact intentional, such that\n  the boundaries of the data cloud stay visible to the viewer. How strong the banding appears,\n  depends on the point density, i.e. the number of points in the plot.\n  \n  For some situations with scatter plots it might thus be desirable to manually turn adaptive\n  sampling off. For example, when saving the plot to disk. This can be achieved by setting \\a\n  enabled to false before issuing a command like \\ref QCustomPlot::savePng, and setting \\a enabled\n  back to true afterwards.\n*/\nvoid QCPGraph::setAdaptiveSampling(bool enabled)\n{\n  mAdaptiveSampling = enabled;\n}\n\n/*! \\overload\n  \n  Adds the provided points in \\a keys and \\a values to the current data. The provided vectors\n  should have equal length. Else, the number of added points will be the size of the smallest\n  vector.\n  \n  If you can guarantee that the passed data points are sorted by \\a keys in ascending order, you\n  can set \\a alreadySorted to true, to improve performance by saving a sorting run.\n  \n  Alternatively, you can also access and modify the data directly via the \\ref data method, which\n  returns a pointer to the internal data container.\n*/\nvoid QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)\n{\n  if (keys.size() != values.size())\n    qDebug() << Q_FUNC_INFO << \"keys and values have different sizes:\" << keys.size() << values.size();\n  const int n = qMin(keys.size(), values.size());\n  QVector<QCPGraphData> tempData(n);\n  QVector<QCPGraphData>::iterator it = tempData.begin();\n  const QVector<QCPGraphData>::iterator itEnd = tempData.end();\n  int i = 0;\n  while (it != itEnd)\n  {\n    it->key = keys[i];\n    it->value = values[i];\n    ++it;\n    ++i;\n  }\n  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write\n}\n\n/*! \\overload\n  \n  Adds the provided data point as \\a key and \\a value to the current data.\n  \n  Alternatively, you can also access and modify the data directly via the \\ref data method, which\n  returns a pointer to the internal data container.\n*/\nvoid QCPGraph::addData(double key, double value)\n{\n  mDataContainer->add(QCPGraphData(key, value));\n}\n\n/*!\n  Implements a selectTest specific to this plottable's point geometry.\n\n  If \\a details is not 0, it will be set to a \\ref QCPDataSelection, describing the closest data\n  point to \\a pos.\n  \n  \\seebaseclassmethod \\ref QCPAbstractPlottable::selectTest\n*/\ndouble QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())\n    return -1;\n  if (!mKeyAxis || !mValueAxis)\n    return -1;\n  \n  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))\n  {\n    QCPGraphDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();\n    double result = pointDistance(pos, closestDataPoint);\n    if (details)\n    {\n      int pointIndex = int(closestDataPoint-mDataContainer->constBegin());\n      details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));\n    }\n    return result;\n  } else\n    return -1;\n}\n\n/* inherits documentation from base class */\nQCPRange QCPGraph::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const\n{\n  return mDataContainer->keyRange(foundRange, inSignDomain);\n}\n\n/* inherits documentation from base class */\nQCPRange QCPGraph::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const\n{\n  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);\n}\n\n/* inherits documentation from base class */\nvoid QCPGraph::draw(QCPPainter *painter)\n{\n  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n  if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return;\n  if (mLineStyle == lsNone && mScatterStyle.isNone()) return;\n  \n  QVector<QPointF> lines, scatters; // line and (if necessary) scatter pixel coordinates will be stored here while iterating over segments\n  \n  // loop over and draw segments of unselected/selected data:\n  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;\n  getDataSegments(selectedSegments, unselectedSegments);\n  allSegments << unselectedSegments << selectedSegments;\n  for (int i=0; i<allSegments.size(); ++i)\n  {\n    bool isSelectedSegment = i >= unselectedSegments.size();\n    // get line pixel points appropriate to line style:\n    QCPDataRange lineDataRange = isSelectedSegment ? allSegments.at(i) : allSegments.at(i).adjusted(-1, 1); // unselected segments extend lines to bordering selected data point (safe to exceed total data bounds in first/last segment, getLines takes care)\n    getLines(&lines, lineDataRange);\n    \n    // check data validity if flag set:\n#ifdef QCUSTOMPLOT_CHECK_DATA\n    QCPGraphDataContainer::const_iterator it;\n    for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)\n    {\n      if (QCP::isInvalidData(it->key, it->value))\n        qDebug() << Q_FUNC_INFO << \"Data point at\" << it->key << \"invalid.\" << \"Plottable name:\" << name();\n    }\n#endif\n    \n    // draw fill of graph:\n    if (isSelectedSegment && mSelectionDecorator)\n      mSelectionDecorator->applyBrush(painter);\n    else\n      painter->setBrush(mBrush);\n    painter->setPen(Qt::NoPen);\n    drawFill(painter, &lines);\n    \n    // draw line:\n    if (mLineStyle != lsNone)\n    {\n      if (isSelectedSegment && mSelectionDecorator)\n        mSelectionDecorator->applyPen(painter);\n      else\n        painter->setPen(mPen);\n      painter->setBrush(Qt::NoBrush);\n      if (mLineStyle == lsImpulse)\n        drawImpulsePlot(painter, lines);\n      else\n        drawLinePlot(painter, lines); // also step plots can be drawn as a line plot\n    }\n    \n    // draw scatters:\n    QCPScatterStyle finalScatterStyle = mScatterStyle;\n    if (isSelectedSegment && mSelectionDecorator)\n      finalScatterStyle = mSelectionDecorator->getFinalScatterStyle(mScatterStyle);\n    if (!finalScatterStyle.isNone())\n    {\n      getScatters(&scatters, allSegments.at(i));\n      drawScatterPlot(painter, scatters, finalScatterStyle);\n    }\n  }\n  \n  // draw other selection decoration that isn't just line/scatter pens and brushes:\n  if (mSelectionDecorator)\n    mSelectionDecorator->drawDecoration(painter, selection());\n}\n\n/* inherits documentation from base class */\nvoid QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const\n{\n  // draw fill:\n  if (mBrush.style() != Qt::NoBrush)\n  {\n    applyFillAntialiasingHint(painter);\n    painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);\n  }\n  // draw line vertically centered:\n  if (mLineStyle != lsNone)\n  {\n    applyDefaultAntialiasingHint(painter);\n    painter->setPen(mPen);\n    painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens\n  }\n  // draw scatter symbol:\n  if (!mScatterStyle.isNone())\n  {\n    applyScattersAntialiasingHint(painter);\n    // scale scatter pixmap if it's too large to fit in legend icon rect:\n    if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))\n    {\n      QCPScatterStyle scaledStyle(mScatterStyle);\n      scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));\n      scaledStyle.applyTo(painter, mPen);\n      scaledStyle.drawShape(painter, QRectF(rect).center());\n    } else\n    {\n      mScatterStyle.applyTo(painter, mPen);\n      mScatterStyle.drawShape(painter, QRectF(rect).center());\n    }\n  }\n}\n\n/*! \\internal\n\n  This method retrieves an optimized set of data points via \\ref getOptimizedLineData, and branches\n  out to the line style specific functions such as \\ref dataToLines, \\ref dataToStepLeftLines, etc.\n  according to the line style of the graph.\n\n  \\a lines will be filled with points in pixel coordinates, that can be drawn with the according\n  draw functions like \\ref drawLinePlot and \\ref drawImpulsePlot. The points returned in \\a lines\n  aren't necessarily the original data points. For example, step line styles require additional\n  points to form the steps when drawn. If the line style of the graph is \\ref lsNone, the \\a\n  lines vector will be empty.\n\n  \\a dataRange specifies the beginning and ending data indices that will be taken into account for\n  conversion. In this function, the specified range may exceed the total data bounds without harm:\n  a correspondingly trimmed data range will be used. This takes the burden off the user of this\n  function to check for valid indices in \\a dataRange, e.g. when extending ranges coming from \\ref\n  getDataSegments.\n\n  \\see getScatters\n*/\nvoid QCPGraph::getLines(QVector<QPointF> *lines, const QCPDataRange &dataRange) const\n{\n  if (!lines) return;\n  QCPGraphDataContainer::const_iterator begin, end;\n  getVisibleDataBounds(begin, end, dataRange);\n  if (begin == end)\n  {\n    lines->clear();\n    return;\n  }\n  \n  QVector<QCPGraphData> lineData;\n  if (mLineStyle != lsNone)\n    getOptimizedLineData(&lineData, begin, end);\n  \n  if (mKeyAxis->rangeReversed() != (mKeyAxis->orientation() == Qt::Vertical)) // make sure key pixels are sorted ascending in lineData (significantly simplifies following processing)\n    std::reverse(lineData.begin(), lineData.end());\n\n  switch (mLineStyle)\n  {\n    case lsNone: lines->clear(); break;\n    case lsLine: *lines = dataToLines(lineData); break;\n    case lsStepLeft: *lines = dataToStepLeftLines(lineData); break;\n    case lsStepRight: *lines = dataToStepRightLines(lineData); break;\n    case lsStepCenter: *lines = dataToStepCenterLines(lineData); break;\n    case lsImpulse: *lines = dataToImpulseLines(lineData); break;\n  }\n}\n\n/*! \\internal\n\n  This method retrieves an optimized set of data points via \\ref getOptimizedScatterData and then\n  converts them to pixel coordinates. The resulting points are returned in \\a scatters, and can be\n  passed to \\ref drawScatterPlot.\n\n  \\a dataRange specifies the beginning and ending data indices that will be taken into account for\n  conversion. In this function, the specified range may exceed the total data bounds without harm:\n  a correspondingly trimmed data range will be used. This takes the burden off the user of this\n  function to check for valid indices in \\a dataRange, e.g. when extending ranges coming from \\ref\n  getDataSegments.\n*/\nvoid QCPGraph::getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange) const\n{\n  if (!scatters) return;\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; scatters->clear(); return; }\n  \n  QCPGraphDataContainer::const_iterator begin, end;\n  getVisibleDataBounds(begin, end, dataRange);\n  if (begin == end)\n  {\n    scatters->clear();\n    return;\n  }\n  \n  QVector<QCPGraphData> data;\n  getOptimizedScatterData(&data, begin, end);\n  \n  if (mKeyAxis->rangeReversed() != (mKeyAxis->orientation() == Qt::Vertical)) // make sure key pixels are sorted ascending in data (significantly simplifies following processing)\n    std::reverse(data.begin(), data.end());\n  \n  scatters->resize(data.size());\n  if (keyAxis->orientation() == Qt::Vertical)\n  {\n    for (int i=0; i<data.size(); ++i)\n    {\n      if (!qIsNaN(data.at(i).value))\n      {\n        (*scatters)[i].setX(valueAxis->coordToPixel(data.at(i).value));\n        (*scatters)[i].setY(keyAxis->coordToPixel(data.at(i).key));\n      }\n    }\n  } else\n  {\n    for (int i=0; i<data.size(); ++i)\n    {\n      if (!qIsNaN(data.at(i).value))\n      {\n        (*scatters)[i].setX(keyAxis->coordToPixel(data.at(i).key));\n        (*scatters)[i].setY(valueAxis->coordToPixel(data.at(i).value));\n      }\n    }\n  }\n}\n\n/*! \\internal\n\n  Takes raw data points in plot coordinates as \\a data, and returns a vector containing pixel\n  coordinate points which are suitable for drawing the line style \\ref lsLine.\n  \n  The source of \\a data is usually \\ref getOptimizedLineData, and this method is called in \\a\n  getLines if the line style is set accordingly.\n\n  \\see dataToStepLeftLines, dataToStepRightLines, dataToStepCenterLines, dataToImpulseLines, getLines, drawLinePlot\n*/\nQVector<QPointF> QCPGraph::dataToLines(const QVector<QCPGraphData> &data) const\n{\n  QVector<QPointF> result;\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return result; }\n\n  result.resize(data.size());\n  \n  // transform data points to pixels:\n  if (keyAxis->orientation() == Qt::Vertical)\n  {\n    for (int i=0; i<data.size(); ++i)\n    {\n      result[i].setX(valueAxis->coordToPixel(data.at(i).value));\n      result[i].setY(keyAxis->coordToPixel(data.at(i).key));\n    }\n  } else // key axis is horizontal\n  {\n    for (int i=0; i<data.size(); ++i)\n    {\n      result[i].setX(keyAxis->coordToPixel(data.at(i).key));\n      result[i].setY(valueAxis->coordToPixel(data.at(i).value));\n    }\n  }\n  return result;\n}\n\n/*! \\internal\n\n  Takes raw data points in plot coordinates as \\a data, and returns a vector containing pixel\n  coordinate points which are suitable for drawing the line style \\ref lsStepLeft.\n  \n  The source of \\a data is usually \\ref getOptimizedLineData, and this method is called in \\a\n  getLines if the line style is set accordingly.\n\n  \\see dataToLines, dataToStepRightLines, dataToStepCenterLines, dataToImpulseLines, getLines, drawLinePlot\n*/\nQVector<QPointF> QCPGraph::dataToStepLeftLines(const QVector<QCPGraphData> &data) const\n{\n  QVector<QPointF> result;\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return result; }\n  \n  result.resize(data.size()*2);\n  \n  // calculate steps from data and transform to pixel coordinates:\n  if (keyAxis->orientation() == Qt::Vertical)\n  {\n    double lastValue = valueAxis->coordToPixel(data.first().value);\n    for (int i=0; i<data.size(); ++i)\n    {\n      const double key = keyAxis->coordToPixel(data.at(i).key);\n      result[i*2+0].setX(lastValue);\n      result[i*2+0].setY(key);\n      lastValue = valueAxis->coordToPixel(data.at(i).value);\n      result[i*2+1].setX(lastValue);\n      result[i*2+1].setY(key);\n    }\n  } else // key axis is horizontal\n  {\n    double lastValue = valueAxis->coordToPixel(data.first().value);\n    for (int i=0; i<data.size(); ++i)\n    {\n      const double key = keyAxis->coordToPixel(data.at(i).key);\n      result[i*2+0].setX(key);\n      result[i*2+0].setY(lastValue);\n      lastValue = valueAxis->coordToPixel(data.at(i).value);\n      result[i*2+1].setX(key);\n      result[i*2+1].setY(lastValue);\n    }\n  }\n  return result;\n}\n\n/*! \\internal\n\n  Takes raw data points in plot coordinates as \\a data, and returns a vector containing pixel\n  coordinate points which are suitable for drawing the line style \\ref lsStepRight.\n  \n  The source of \\a data is usually \\ref getOptimizedLineData, and this method is called in \\a\n  getLines if the line style is set accordingly.\n\n  \\see dataToLines, dataToStepLeftLines, dataToStepCenterLines, dataToImpulseLines, getLines, drawLinePlot\n*/\nQVector<QPointF> QCPGraph::dataToStepRightLines(const QVector<QCPGraphData> &data) const\n{\n  QVector<QPointF> result;\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return result; }\n  \n  result.resize(data.size()*2);\n  \n  // calculate steps from data and transform to pixel coordinates:\n  if (keyAxis->orientation() == Qt::Vertical)\n  {\n    double lastKey = keyAxis->coordToPixel(data.first().key);\n    for (int i=0; i<data.size(); ++i)\n    {\n      const double value = valueAxis->coordToPixel(data.at(i).value);\n      result[i*2+0].setX(value);\n      result[i*2+0].setY(lastKey);\n      lastKey = keyAxis->coordToPixel(data.at(i).key);\n      result[i*2+1].setX(value);\n      result[i*2+1].setY(lastKey);\n    }\n  } else // key axis is horizontal\n  {\n    double lastKey = keyAxis->coordToPixel(data.first().key);\n    for (int i=0; i<data.size(); ++i)\n    {\n      const double value = valueAxis->coordToPixel(data.at(i).value);\n      result[i*2+0].setX(lastKey);\n      result[i*2+0].setY(value);\n      lastKey = keyAxis->coordToPixel(data.at(i).key);\n      result[i*2+1].setX(lastKey);\n      result[i*2+1].setY(value);\n    }\n  }\n  return result;\n}\n\n/*! \\internal\n\n  Takes raw data points in plot coordinates as \\a data, and returns a vector containing pixel\n  coordinate points which are suitable for drawing the line style \\ref lsStepCenter.\n  \n  The source of \\a data is usually \\ref getOptimizedLineData, and this method is called in \\a\n  getLines if the line style is set accordingly.\n\n  \\see dataToLines, dataToStepLeftLines, dataToStepRightLines, dataToImpulseLines, getLines, drawLinePlot\n*/\nQVector<QPointF> QCPGraph::dataToStepCenterLines(const QVector<QCPGraphData> &data) const\n{\n  QVector<QPointF> result;\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return result; }\n  \n  result.resize(data.size()*2);\n  \n  // calculate steps from data and transform to pixel coordinates:\n  if (keyAxis->orientation() == Qt::Vertical)\n  {\n    double lastKey = keyAxis->coordToPixel(data.first().key);\n    double lastValue = valueAxis->coordToPixel(data.first().value);\n    result[0].setX(lastValue);\n    result[0].setY(lastKey);\n    for (int i=1; i<data.size(); ++i)\n    {\n      const double key = (keyAxis->coordToPixel(data.at(i).key)+lastKey)*0.5;\n      result[i*2-1].setX(lastValue);\n      result[i*2-1].setY(key);\n      lastValue = valueAxis->coordToPixel(data.at(i).value);\n      lastKey = keyAxis->coordToPixel(data.at(i).key);\n      result[i*2+0].setX(lastValue);\n      result[i*2+0].setY(key);\n    }\n    result[data.size()*2-1].setX(lastValue);\n    result[data.size()*2-1].setY(lastKey);\n  } else // key axis is horizontal\n  {\n    double lastKey = keyAxis->coordToPixel(data.first().key);\n    double lastValue = valueAxis->coordToPixel(data.first().value);\n    result[0].setX(lastKey);\n    result[0].setY(lastValue);\n    for (int i=1; i<data.size(); ++i)\n    {\n      const double key = (keyAxis->coordToPixel(data.at(i).key)+lastKey)*0.5;\n      result[i*2-1].setX(key);\n      result[i*2-1].setY(lastValue);\n      lastValue = valueAxis->coordToPixel(data.at(i).value);\n      lastKey = keyAxis->coordToPixel(data.at(i).key);\n      result[i*2+0].setX(key);\n      result[i*2+0].setY(lastValue);\n    }\n    result[data.size()*2-1].setX(lastKey);\n    result[data.size()*2-1].setY(lastValue);\n  }\n  return result;\n}\n\n/*! \\internal\n\n  Takes raw data points in plot coordinates as \\a data, and returns a vector containing pixel\n  coordinate points which are suitable for drawing the line style \\ref lsImpulse.\n  \n  The source of \\a data is usually \\ref getOptimizedLineData, and this method is called in \\a\n  getLines if the line style is set accordingly.\n\n  \\see dataToLines, dataToStepLeftLines, dataToStepRightLines, dataToStepCenterLines, getLines, drawImpulsePlot\n*/\nQVector<QPointF> QCPGraph::dataToImpulseLines(const QVector<QCPGraphData> &data) const\n{\n  QVector<QPointF> result;\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return result; }\n  \n  result.resize(data.size()*2);\n  \n  // transform data points to pixels:\n  if (keyAxis->orientation() == Qt::Vertical)\n  {\n    for (int i=0; i<data.size(); ++i)\n    {\n      const double key = keyAxis->coordToPixel(data.at(i).key);\n      result[i*2+0].setX(valueAxis->coordToPixel(0));\n      result[i*2+0].setY(key);\n      result[i*2+1].setX(valueAxis->coordToPixel(data.at(i).value));\n      result[i*2+1].setY(key);\n    }\n  } else // key axis is horizontal\n  {\n    for (int i=0; i<data.size(); ++i)\n    {\n      const double key = keyAxis->coordToPixel(data.at(i).key);\n      result[i*2+0].setX(key);\n      result[i*2+0].setY(valueAxis->coordToPixel(0));\n      result[i*2+1].setX(key);\n      result[i*2+1].setY(valueAxis->coordToPixel(data.at(i).value));\n    }\n  }\n  return result;\n}\n\n/*! \\internal\n  \n  Draws the fill of the graph using the specified \\a painter, with the currently set brush.\n  \n  Depending on whether a normal fill or a channel fill (\\ref setChannelFillGraph) is needed, \\ref\n  getFillPolygon or \\ref getChannelFillPolygon are used to find the according fill polygons.\n  \n  In order to handle NaN Data points correctly (the fill needs to be split into disjoint areas),\n  this method first determines a list of non-NaN segments with \\ref getNonNanSegments, on which to\n  operate. In the channel fill case, \\ref getOverlappingSegments is used to consolidate the non-NaN\n  segments of the two involved graphs, before passing the overlapping pairs to \\ref\n  getChannelFillPolygon.\n  \n  Pass the points of this graph's line as \\a lines, in pixel coordinates.\n\n  \\see drawLinePlot, drawImpulsePlot, drawScatterPlot\n*/\nvoid QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lines) const\n{\n  if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot\n  if (painter->brush().style() == Qt::NoBrush || painter->brush().color().alpha() == 0) return;\n  \n  applyFillAntialiasingHint(painter);\n  const QVector<QCPDataRange> segments = getNonNanSegments(lines, keyAxis()->orientation());\n  if (!mChannelFillGraph)\n  {\n    // draw base fill under graph, fill goes all the way to the zero-value-line:\n    foreach (QCPDataRange segment, segments)\n      painter->drawPolygon(getFillPolygon(lines, segment));\n  } else\n  {\n    // draw fill between this graph and mChannelFillGraph:\n    QVector<QPointF> otherLines;\n    mChannelFillGraph->getLines(&otherLines, QCPDataRange(0, mChannelFillGraph->dataCount()));\n    if (!otherLines.isEmpty())\n    {\n      QVector<QCPDataRange> otherSegments = getNonNanSegments(&otherLines, mChannelFillGraph->keyAxis()->orientation());\n      QVector<QPair<QCPDataRange, QCPDataRange> > segmentPairs = getOverlappingSegments(segments, lines, otherSegments, &otherLines);\n      for (int i=0; i<segmentPairs.size(); ++i)\n        painter->drawPolygon(getChannelFillPolygon(lines, segmentPairs.at(i).first, &otherLines, segmentPairs.at(i).second));\n    }\n  }\n}\n\n/*! \\internal\n\n  Draws scatter symbols at every point passed in \\a scatters, given in pixel coordinates. The\n  scatters will be drawn with \\a painter and have the appearance as specified in \\a style.\n\n  \\see drawLinePlot, drawImpulsePlot\n*/\nvoid QCPGraph::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &scatters, const QCPScatterStyle &style) const\n{\n  applyScattersAntialiasingHint(painter);\n  style.applyTo(painter, mPen);\n  foreach (const QPointF &scatter, scatters)\n    style.drawShape(painter, scatter.x(), scatter.y());\n}\n\n/*!  \\internal\n  \n  Draws lines between the points in \\a lines, given in pixel coordinates.\n  \n  \\see drawScatterPlot, drawImpulsePlot, QCPAbstractPlottable1D::drawPolyline\n*/\nvoid QCPGraph::drawLinePlot(QCPPainter *painter, const QVector<QPointF> &lines) const\n{\n  if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)\n  {\n    applyDefaultAntialiasingHint(painter);\n    drawPolyline(painter, lines);\n  }\n}\n\n/*! \\internal\n\n  Draws impulses from the provided data, i.e. it connects all line pairs in \\a lines, given in\n  pixel coordinates. The \\a lines necessary for impulses are generated by \\ref dataToImpulseLines\n  from the regular graph data points.\n\n  \\see drawLinePlot, drawScatterPlot\n*/\nvoid QCPGraph::drawImpulsePlot(QCPPainter *painter, const QVector<QPointF> &lines) const\n{\n  if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)\n  {\n    applyDefaultAntialiasingHint(painter);\n    QPen oldPen = painter->pen();\n    QPen newPen = painter->pen();\n    newPen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line\n    painter->setPen(newPen);\n    painter->drawLines(lines);\n    painter->setPen(oldPen);\n  }\n}\n\n/*! \\internal\n\n  Returns via \\a lineData the data points that need to be visualized for this graph when plotting\n  graph lines, taking into consideration the currently visible axis ranges and, if \\ref\n  setAdaptiveSampling is enabled, local point densities. The considered data can be restricted\n  further by \\a begin and \\a end, e.g. to only plot a certain segment of the data (see \\ref\n  getDataSegments).\n\n  This method is used by \\ref getLines to retrieve the basic working set of data.\n\n  \\see getOptimizedScatterData\n*/\nvoid QCPGraph::getOptimizedLineData(QVector<QCPGraphData> *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const\n{\n  if (!lineData) return;\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n  if (begin == end) return;\n  \n  int dataCount = int(end-begin);\n  int maxCount = (std::numeric_limits<int>::max)();\n  if (mAdaptiveSampling)\n  {\n    double keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key));\n    if (2*keyPixelSpan+2 < static_cast<double>((std::numeric_limits<int>::max)()))\n      maxCount = int(2*keyPixelSpan+2);\n  }\n  \n  if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average\n  {\n    QCPGraphDataContainer::const_iterator it = begin;\n    double minValue = it->value;\n    double maxValue = it->value;\n    QCPGraphDataContainer::const_iterator currentIntervalFirstPoint = it;\n    int reversedFactor = keyAxis->pixelOrientation(); // is used to calculate keyEpsilon pixel into the correct direction\n    int reversedRound = reversedFactor==-1 ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey\n    double currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(begin->key)+reversedRound));\n    double lastIntervalEndKey = currentIntervalStartKey;\n    double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates\n    bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)\n    int intervalDataCount = 1;\n    ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect\n    while (it != end)\n    {\n      if (it->key < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary\n      {\n        if (it->value < minValue)\n          minValue = it->value;\n        else if (it->value > maxValue)\n          maxValue = it->value;\n        ++intervalDataCount;\n      } else // new pixel interval started\n      {\n        if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster\n        {\n          if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point\n            lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint->value));\n          lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.25, minValue));\n          lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));\n          if (it->key > currentIntervalStartKey+keyEpsilon*2) // new pixel started further away from previous cluster, so make sure the last point of the cluster is at a real data point\n            lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.8, (it-1)->value));\n        } else\n          lineData->append(QCPGraphData(currentIntervalFirstPoint->key, currentIntervalFirstPoint->value));\n        lastIntervalEndKey = (it-1)->key;\n        minValue = it->value;\n        maxValue = it->value;\n        currentIntervalFirstPoint = it;\n        currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(it->key)+reversedRound));\n        if (keyEpsilonVariable)\n          keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));\n        intervalDataCount = 1;\n      }\n      ++it;\n    }\n    // handle last interval:\n    if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster\n    {\n      if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point\n        lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint->value));\n      lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.25, minValue));\n      lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));\n    } else\n      lineData->append(QCPGraphData(currentIntervalFirstPoint->key, currentIntervalFirstPoint->value));\n    \n  } else // don't use adaptive sampling algorithm, transfer points one-to-one from the data container into the output\n  {\n    lineData->resize(dataCount);\n    std::copy(begin, end, lineData->begin());\n  }\n}\n\n/*! \\internal\n\n  Returns via \\a scatterData the data points that need to be visualized for this graph when\n  plotting scatter points, taking into consideration the currently visible axis ranges and, if \\ref\n  setAdaptiveSampling is enabled, local point densities. The considered data can be restricted\n  further by \\a begin and \\a end, e.g. to only plot a certain segment of the data (see \\ref\n  getDataSegments).\n\n  This method is used by \\ref getScatters to retrieve the basic working set of data.\n\n  \\see getOptimizedLineData\n*/\nvoid QCPGraph::getOptimizedScatterData(QVector<QCPGraphData> *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const\n{\n  if (!scatterData) return;\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n  \n  const int scatterModulo = mScatterSkip+1;\n  const bool doScatterSkip = mScatterSkip > 0;\n  int beginIndex = int(begin-mDataContainer->constBegin());\n  int endIndex = int(end-mDataContainer->constBegin());\n  while (doScatterSkip && begin != end && beginIndex % scatterModulo != 0) // advance begin iterator to first non-skipped scatter\n  {\n    ++beginIndex;\n    ++begin;\n  }\n  if (begin == end) return;\n  int dataCount = int(end-begin);\n  int maxCount = (std::numeric_limits<int>::max)();\n  if (mAdaptiveSampling)\n  {\n    int keyPixelSpan = int(qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key)));\n    maxCount = 2*keyPixelSpan+2;\n  }\n  \n  if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average\n  {\n    double valueMaxRange = valueAxis->range().upper;\n    double valueMinRange = valueAxis->range().lower;\n    QCPGraphDataContainer::const_iterator it = begin;\n    int itIndex = int(beginIndex);\n    double minValue = it->value;\n    double maxValue = it->value;\n    QCPGraphDataContainer::const_iterator minValueIt = it;\n    QCPGraphDataContainer::const_iterator maxValueIt = it;\n    QCPGraphDataContainer::const_iterator currentIntervalStart = it;\n    int reversedFactor = keyAxis->pixelOrientation(); // is used to calculate keyEpsilon pixel into the correct direction\n    int reversedRound = reversedFactor==-1 ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey\n    double currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(begin->key)+reversedRound));\n    double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates\n    bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)\n    int intervalDataCount = 1;\n    // advance iterator to second (non-skipped) data point because adaptive sampling works in 1 point retrospect:\n    if (!doScatterSkip)\n      ++it;\n    else\n    {\n      itIndex += scatterModulo;\n      if (itIndex < endIndex) // make sure we didn't jump over end\n        it += scatterModulo;\n      else\n      {\n        it = end;\n        itIndex = endIndex;\n      }\n    }\n    // main loop over data points:\n    while (it != end)\n    {\n      if (it->key < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary\n      {\n        if (it->value < minValue && it->value > valueMinRange && it->value < valueMaxRange)\n        {\n          minValue = it->value;\n          minValueIt = it;\n        } else if (it->value > maxValue && it->value > valueMinRange && it->value < valueMaxRange)\n        {\n          maxValue = it->value;\n          maxValueIt = it;\n        }\n        ++intervalDataCount;\n      } else // new pixel started\n      {\n        if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them\n        {\n          // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):\n          double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));\n          int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average\n          QCPGraphDataContainer::const_iterator intervalIt = currentIntervalStart;\n          int c = 0;\n          while (intervalIt != it)\n          {\n            if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt->value > valueMinRange && intervalIt->value < valueMaxRange)\n              scatterData->append(*intervalIt);\n            ++c;\n            if (!doScatterSkip)\n              ++intervalIt;\n            else\n              intervalIt += scatterModulo; // since we know indices of \"currentIntervalStart\", \"intervalIt\" and \"it\" are multiples of scatterModulo, we can't accidentally jump over \"it\" here\n          }\n        } else if (currentIntervalStart->value > valueMinRange && currentIntervalStart->value < valueMaxRange)\n          scatterData->append(*currentIntervalStart);\n        minValue = it->value;\n        maxValue = it->value;\n        currentIntervalStart = it;\n        currentIntervalStartKey = keyAxis->pixelToCoord(int(keyAxis->coordToPixel(it->key)+reversedRound));\n        if (keyEpsilonVariable)\n          keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));\n        intervalDataCount = 1;\n      }\n      // advance to next data point:\n      if (!doScatterSkip)\n        ++it;\n      else\n      {\n        itIndex += scatterModulo;\n        if (itIndex < endIndex) // make sure we didn't jump over end\n          it += scatterModulo;\n        else\n        {\n          it = end;\n          itIndex = endIndex;\n        }\n      }\n    }\n    // handle last interval:\n    if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them\n    {\n      // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):\n      double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));\n      int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average\n      QCPGraphDataContainer::const_iterator intervalIt = currentIntervalStart;\n      int intervalItIndex = int(intervalIt-mDataContainer->constBegin());\n      int c = 0;\n      while (intervalIt != it)\n      {\n        if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt->value > valueMinRange && intervalIt->value < valueMaxRange)\n          scatterData->append(*intervalIt);\n        ++c;\n        if (!doScatterSkip)\n          ++intervalIt;\n        else // here we can't guarantee that adding scatterModulo doesn't exceed \"it\" (because \"it\" is equal to \"end\" here, and \"end\" isn't scatterModulo-aligned), so check via index comparison:\n        {\n          intervalItIndex += scatterModulo;\n          if (intervalItIndex < itIndex)\n            intervalIt += scatterModulo;\n          else\n          {\n            intervalIt = it;\n            intervalItIndex = itIndex;\n          }\n        }\n      }\n    } else if (currentIntervalStart->value > valueMinRange && currentIntervalStart->value < valueMaxRange)\n      scatterData->append(*currentIntervalStart);\n    \n  } else // don't use adaptive sampling algorithm, transfer points one-to-one from the data container into the output\n  {\n    QCPGraphDataContainer::const_iterator it = begin;\n    int itIndex = beginIndex;\n    scatterData->reserve(dataCount);\n    while (it != end)\n    {\n      scatterData->append(*it);\n      // advance to next data point:\n      if (!doScatterSkip)\n        ++it;\n      else\n      {\n        itIndex += scatterModulo;\n        if (itIndex < endIndex)\n          it += scatterModulo;\n        else\n        {\n          it = end;\n          itIndex = endIndex;\n        }\n      }\n    }\n  }\n}\n\n/*!\n  This method outputs the currently visible data range via \\a begin and \\a end. The returned range\n  will also never exceed \\a rangeRestriction.\n\n  This method takes into account that the drawing of data lines at the axis rect border always\n  requires the points just outside the visible axis range. So \\a begin and \\a end may actually\n  indicate a range that contains one additional data point to the left and right of the visible\n  axis range.\n*/\nvoid QCPGraph::getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const\n{\n  if (rangeRestriction.isEmpty())\n  {\n    end = mDataContainer->constEnd();\n    begin = end;\n  } else\n  {\n    QCPAxis *keyAxis = mKeyAxis.data();\n    QCPAxis *valueAxis = mValueAxis.data();\n    if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n    // get visible data range:\n    begin = mDataContainer->findBegin(keyAxis->range().lower);\n    end = mDataContainer->findEnd(keyAxis->range().upper);\n    // limit lower/upperEnd to rangeRestriction:\n    mDataContainer->limitIteratorsToDataRange(begin, end, rangeRestriction); // this also ensures rangeRestriction outside data bounds doesn't break anything\n  }\n}\n\n/*!  \\internal\n  \n  This method goes through the passed points in \\a lineData and returns a list of the segments\n  which don't contain NaN data points.\n  \n  \\a keyOrientation defines whether the \\a x or \\a y member of the passed QPointF is used to check\n  for NaN. If \\a keyOrientation is \\c Qt::Horizontal, the \\a y member is checked, if it is \\c\n  Qt::Vertical, the \\a x member is checked.\n  \n  \\see getOverlappingSegments, drawFill\n*/\nQVector<QCPDataRange> QCPGraph::getNonNanSegments(const QVector<QPointF> *lineData, Qt::Orientation keyOrientation) const\n{\n  QVector<QCPDataRange> result;\n  const int n = lineData->size();\n  \n  QCPDataRange currentSegment(-1, -1);\n  int i = 0;\n  \n  if (keyOrientation == Qt::Horizontal)\n  {\n    while (i < n)\n    {\n      while (i < n && qIsNaN(lineData->at(i).y())) // seek next non-NaN data point\n        ++i;\n      if (i == n)\n        break;\n      currentSegment.setBegin(i++);\n      while (i < n && !qIsNaN(lineData->at(i).y())) // seek next NaN data point or end of data\n        ++i;\n      currentSegment.setEnd(i++);\n      result.append(currentSegment);\n    }\n  } else // keyOrientation == Qt::Vertical\n  {\n    while (i < n)\n    {\n      while (i < n && qIsNaN(lineData->at(i).x())) // seek next non-NaN data point\n        ++i;\n      if (i == n)\n        break;\n      currentSegment.setBegin(i++);\n      while (i < n && !qIsNaN(lineData->at(i).x())) // seek next NaN data point or end of data\n        ++i;\n      currentSegment.setEnd(i++);\n      result.append(currentSegment);\n    }\n  }\n  return result;\n}\n\n/*!  \\internal\n  \n  This method takes two segment lists (e.g. created by \\ref getNonNanSegments) \\a thisSegments and\n  \\a otherSegments, and their associated point data \\a thisData and \\a otherData.\n\n  It returns all pairs of segments (the first from \\a thisSegments, the second from \\a\n  otherSegments), which overlap in plot coordinates.\n  \n  This method is useful in the case of a channel fill between two graphs, when only those non-NaN\n  segments which actually overlap in their key coordinate shall be considered for drawing a channel\n  fill polygon.\n  \n  It is assumed that the passed segments in \\a thisSegments are ordered ascending by index, and\n  that the segments don't overlap themselves. The same is assumed for the segments in \\a\n  otherSegments. This is fulfilled when the segments are obtained via \\ref getNonNanSegments.\n  \n  \\see getNonNanSegments, segmentsIntersect, drawFill, getChannelFillPolygon\n*/\nQVector<QPair<QCPDataRange, QCPDataRange> > QCPGraph::getOverlappingSegments(QVector<QCPDataRange> thisSegments, const QVector<QPointF> *thisData, QVector<QCPDataRange> otherSegments, const QVector<QPointF> *otherData) const\n{\n  QVector<QPair<QCPDataRange, QCPDataRange> > result;\n  if (thisData->isEmpty() || otherData->isEmpty() || thisSegments.isEmpty() || otherSegments.isEmpty())\n    return result;\n  \n  int thisIndex = 0;\n  int otherIndex = 0;\n  const bool verticalKey = mKeyAxis->orientation() == Qt::Vertical;\n  while (thisIndex < thisSegments.size() && otherIndex < otherSegments.size())\n  {\n    if (thisSegments.at(thisIndex).size() < 2) // segments with fewer than two points won't have a fill anyhow\n    {\n      ++thisIndex;\n      continue;\n    }\n    if (otherSegments.at(otherIndex).size() < 2) // segments with fewer than two points won't have a fill anyhow\n    {\n      ++otherIndex;\n      continue;\n    }\n    double thisLower, thisUpper, otherLower, otherUpper;\n    if (!verticalKey)\n    {\n      thisLower = thisData->at(thisSegments.at(thisIndex).begin()).x();\n      thisUpper = thisData->at(thisSegments.at(thisIndex).end()-1).x();\n      otherLower = otherData->at(otherSegments.at(otherIndex).begin()).x();\n      otherUpper = otherData->at(otherSegments.at(otherIndex).end()-1).x();\n    } else\n    {\n      thisLower = thisData->at(thisSegments.at(thisIndex).begin()).y();\n      thisUpper = thisData->at(thisSegments.at(thisIndex).end()-1).y();\n      otherLower = otherData->at(otherSegments.at(otherIndex).begin()).y();\n      otherUpper = otherData->at(otherSegments.at(otherIndex).end()-1).y();\n    }\n    \n    int bPrecedence;\n    if (segmentsIntersect(thisLower, thisUpper, otherLower, otherUpper, bPrecedence))\n      result.append(QPair<QCPDataRange, QCPDataRange>(thisSegments.at(thisIndex), otherSegments.at(otherIndex)));\n    \n    if (bPrecedence <= 0) // otherSegment doesn't reach as far as thisSegment, so continue with next otherSegment, keeping current thisSegment\n      ++otherIndex;\n    else // otherSegment reaches further than thisSegment, so continue with next thisSegment, keeping current otherSegment\n      ++thisIndex;\n  }\n  \n  return result;\n}\n\n/*!  \\internal\n  \n  Returns whether the segments defined by the coordinates (aLower, aUpper) and (bLower, bUpper)\n  have overlap.\n  \n  The output parameter \\a bPrecedence indicates whether the \\a b segment reaches farther than the\n  \\a a segment or not. If \\a bPrecedence returns 1, segment \\a b reaches the farthest to higher\n  coordinates (i.e. bUpper > aUpper). If it returns -1, segment \\a a reaches the farthest. Only if\n  both segment's upper bounds are identical, 0 is returned as \\a bPrecedence.\n  \n  It is assumed that the lower bounds always have smaller or equal values than the upper bounds.\n  \n  \\see getOverlappingSegments\n*/\nbool QCPGraph::segmentsIntersect(double aLower, double aUpper, double bLower, double bUpper, int &bPrecedence) const\n{\n  bPrecedence = 0;\n  if (aLower > bUpper)\n  {\n    bPrecedence = -1;\n    return false;\n  } else if (bLower > aUpper)\n  {\n    bPrecedence = 1;\n    return false;\n  } else\n  {\n    if (aUpper > bUpper)\n      bPrecedence = -1;\n    else if (aUpper < bUpper)\n      bPrecedence = 1;\n    \n    return true;\n  }\n}\n\n/*! \\internal\n  \n  Returns the point which closes the fill polygon on the zero-value-line parallel to the key axis.\n  The logarithmic axis scale case is a bit special, since the zero-value-line in pixel coordinates\n  is in positive or negative infinity. So this case is handled separately by just closing the fill\n  polygon on the axis which lies in the direction towards the zero value.\n\n  \\a matchingDataPoint will provide the key (in pixels) of the returned point. Depending on whether\n  the key axis of this graph is horizontal or vertical, \\a matchingDataPoint will provide the x or\n  y value of the returned point, respectively.\n*/\nQPointF QCPGraph::getFillBasePoint(QPointF matchingDataPoint) const\n{\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return {}; }\n  \n  QPointF result;\n  if (valueAxis->scaleType() == QCPAxis::stLinear)\n  {\n    if (keyAxis->orientation() == Qt::Horizontal)\n    {\n      result.setX(matchingDataPoint.x());\n      result.setY(valueAxis->coordToPixel(0));\n    } else // keyAxis->orientation() == Qt::Vertical\n    {\n      result.setX(valueAxis->coordToPixel(0));\n      result.setY(matchingDataPoint.y());\n    }\n  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic\n  {\n    // In logarithmic scaling we can't just draw to value 0 so we just fill all the way\n    // to the axis which is in the direction towards 0\n    if (keyAxis->orientation() == Qt::Vertical)\n    {\n      if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||\n          (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis\n        result.setX(keyAxis->axisRect()->right());\n      else\n        result.setX(keyAxis->axisRect()->left());\n      result.setY(matchingDataPoint.y());\n    } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)\n    {\n      result.setX(matchingDataPoint.x());\n      if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||\n          (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis\n        result.setY(keyAxis->axisRect()->top());\n      else\n        result.setY(keyAxis->axisRect()->bottom());\n    }\n  }\n  return result;\n}\n\n/*! \\internal\n  \n  Returns the polygon needed for drawing normal fills between this graph and the key axis.\n  \n  Pass the graph's data points (in pixel coordinates) as \\a lineData, and specify the \\a segment\n  which shall be used for the fill. The collection of \\a lineData points described by \\a segment\n  must not contain NaN data points (see \\ref getNonNanSegments).\n  \n  The returned fill polygon will be closed at the key axis (the zero-value line) for linear value\n  axes. For logarithmic value axes the polygon will reach just beyond the corresponding axis rect\n  side (see \\ref getFillBasePoint).\n\n  For increased performance (due to implicit sharing), keep the returned QPolygonF const.\n  \n  \\see drawFill, getNonNanSegments\n*/\nconst QPolygonF QCPGraph::getFillPolygon(const QVector<QPointF> *lineData, QCPDataRange segment) const\n{\n  if (segment.size() < 2)\n    return QPolygonF();\n  QPolygonF result(segment.size()+2);\n  \n  result[0] = getFillBasePoint(lineData->at(segment.begin()));\n  std::copy(lineData->constBegin()+segment.begin(), lineData->constBegin()+segment.end(), result.begin()+1);\n  result[result.size()-1] = getFillBasePoint(lineData->at(segment.end()-1));\n  \n  return result;\n}\n\n/*! \\internal\n  \n  Returns the polygon needed for drawing (partial) channel fills between this graph and the graph\n  specified by \\ref setChannelFillGraph.\n  \n  The data points of this graph are passed as pixel coordinates via \\a thisData, the data of the\n  other graph as \\a otherData. The returned polygon will be calculated for the specified data\n  segments \\a thisSegment and \\a otherSegment, pertaining to the respective \\a thisData and \\a\n  otherData, respectively.\n  \n  The passed \\a thisSegment and \\a otherSegment should correspond to the segment pairs returned by\n  \\ref getOverlappingSegments, to make sure only segments that actually have key coordinate overlap\n  need to be processed here.\n  \n  For increased performance due to implicit sharing, keep the returned QPolygonF const.\n  \n  \\see drawFill, getOverlappingSegments, getNonNanSegments\n*/\nconst QPolygonF QCPGraph::getChannelFillPolygon(const QVector<QPointF> *thisData, QCPDataRange thisSegment, const QVector<QPointF> *otherData, QCPDataRange otherSegment) const\n{\n  if (!mChannelFillGraph)\n    return QPolygonF();\n  \n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return QPolygonF(); }\n  if (!mChannelFillGraph.data()->mKeyAxis) { qDebug() << Q_FUNC_INFO << \"channel fill target key axis invalid\"; return QPolygonF(); }\n  \n  if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() != keyAxis->orientation())\n    return QPolygonF(); // don't have same axis orientation, can't fill that (Note: if keyAxis fits, valueAxis will fit too, because it's always orthogonal to keyAxis)\n  \n  if (thisData->isEmpty()) return QPolygonF();\n  QVector<QPointF> thisSegmentData(thisSegment.size());\n  QVector<QPointF> otherSegmentData(otherSegment.size());\n  std::copy(thisData->constBegin()+thisSegment.begin(), thisData->constBegin()+thisSegment.end(), thisSegmentData.begin());\n  std::copy(otherData->constBegin()+otherSegment.begin(), otherData->constBegin()+otherSegment.end(), otherSegmentData.begin());\n  // pointers to be able to swap them, depending which data range needs cropping:\n  QVector<QPointF> *staticData = &thisSegmentData;\n  QVector<QPointF> *croppedData = &otherSegmentData;\n  \n  // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType):\n  if (keyAxis->orientation() == Qt::Horizontal)\n  {\n    // x is key\n    // crop lower bound:\n    if (staticData->first().x() < croppedData->first().x()) // other one must be cropped\n      qSwap(staticData, croppedData);\n    const int lowBound = findIndexBelowX(croppedData, staticData->first().x());\n    if (lowBound == -1) return QPolygonF(); // key ranges have no overlap\n    croppedData->remove(0, lowBound);\n    // set lowest point of cropped data to fit exactly key position of first static data point via linear interpolation:\n    if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation\n    double slope;\n    if (!qFuzzyCompare(croppedData->at(1).x(), croppedData->at(0).x()))\n      slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x());\n    else\n      slope = 0;\n    (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x()));\n    (*croppedData)[0].setX(staticData->first().x());\n    \n    // crop upper bound:\n    if (staticData->last().x() > croppedData->last().x()) // other one must be cropped\n      qSwap(staticData, croppedData);\n    int highBound = findIndexAboveX(croppedData, staticData->last().x());\n    if (highBound == -1) return QPolygonF(); // key ranges have no overlap\n    croppedData->remove(highBound+1, croppedData->size()-(highBound+1));\n    // set highest point of cropped data to fit exactly key position of last static data point via linear interpolation:\n    if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation\n    const int li = croppedData->size()-1; // last index\n    if (!qFuzzyCompare(croppedData->at(li).x(), croppedData->at(li-1).x()))\n      slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x());\n    else\n      slope = 0;\n    (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x()));\n    (*croppedData)[li].setX(staticData->last().x());\n  } else // mKeyAxis->orientation() == Qt::Vertical\n  {\n    // y is key\n    // crop lower bound:\n    if (staticData->first().y() < croppedData->first().y()) // other one must be cropped\n      qSwap(staticData, croppedData);\n    int lowBound = findIndexBelowY(croppedData, staticData->first().y());\n    if (lowBound == -1) return QPolygonF(); // key ranges have no overlap\n    croppedData->remove(0, lowBound);\n    // set lowest point of cropped data to fit exactly key position of first static data point via linear interpolation:\n    if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation\n    double slope;\n    if (!qFuzzyCompare(croppedData->at(1).y(), croppedData->at(0).y())) // avoid division by zero in step plots\n      slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y());\n    else\n      slope = 0;\n    (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y()));\n    (*croppedData)[0].setY(staticData->first().y());\n    \n    // crop upper bound:\n    if (staticData->last().y() > croppedData->last().y()) // other one must be cropped\n      qSwap(staticData, croppedData);\n    int highBound = findIndexAboveY(croppedData, staticData->last().y());\n    if (highBound == -1) return QPolygonF(); // key ranges have no overlap\n    croppedData->remove(highBound+1, croppedData->size()-(highBound+1));\n    // set highest point of cropped data to fit exactly key position of last static data point via linear interpolation:\n    if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation\n    int li = croppedData->size()-1; // last index\n    if (!qFuzzyCompare(croppedData->at(li).y(), croppedData->at(li-1).y())) // avoid division by zero in step plots\n      slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y());\n    else\n      slope = 0;\n    (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y()));\n    (*croppedData)[li].setY(staticData->last().y());\n  }\n  \n  // return joined:\n  for (int i=otherSegmentData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted\n    thisSegmentData << otherSegmentData.at(i);\n  return QPolygonF(thisSegmentData);\n}\n\n/*! \\internal\n  \n  Finds the smallest index of \\a data, whose points x value is just above \\a x. Assumes x values in\n  \\a data points are ordered ascending, as is ensured by \\ref getLines/\\ref getScatters if the key\n  axis is horizontal.\n\n  Used to calculate the channel fill polygon, see \\ref getChannelFillPolygon.\n*/\nint QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const\n{\n  for (int i=data->size()-1; i>=0; --i)\n  {\n    if (data->at(i).x() < x)\n    {\n      if (i<data->size()-1)\n        return i+1;\n      else\n        return data->size()-1;\n    }\n  }\n  return -1;\n}\n\n/*! \\internal\n  \n  Finds the highest index of \\a data, whose points x value is just below \\a x. Assumes x values in\n  \\a data points are ordered ascending, as is ensured by \\ref getLines/\\ref getScatters if the key\n  axis is horizontal.\n  \n  Used to calculate the channel fill polygon, see \\ref getChannelFillPolygon.\n*/\nint QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const\n{\n  for (int i=0; i<data->size(); ++i)\n  {\n    if (data->at(i).x() > x)\n    {\n      if (i>0)\n        return i-1;\n      else\n        return 0;\n    }\n  }\n  return -1;\n}\n\n/*! \\internal\n  \n  Finds the smallest index of \\a data, whose points y value is just above \\a y. Assumes y values in\n  \\a data points are ordered ascending, as is ensured by \\ref getLines/\\ref getScatters if the key\n  axis is vertical.\n  \n  Used to calculate the channel fill polygon, see \\ref getChannelFillPolygon.\n*/\nint QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const\n{\n  for (int i=data->size()-1; i>=0; --i)\n  {\n    if (data->at(i).y() < y)\n    {\n      if (i<data->size()-1)\n        return i+1;\n      else\n        return data->size()-1;\n    }\n  }\n  return -1;\n}\n\n/*! \\internal\n  \n  Calculates the minimum distance in pixels the graph's representation has from the given \\a\n  pixelPoint. This is used to determine whether the graph was clicked or not, e.g. in \\ref\n  selectTest. The closest data point to \\a pixelPoint is returned in \\a closestData. Note that if\n  the graph has a line representation, the returned distance may be smaller than the distance to\n  the \\a closestData point, since the distance to the graph line is also taken into account.\n  \n  If either the graph has no data or if the line style is \\ref lsNone and the scatter style's shape\n  is \\ref QCPScatterStyle::ssNone (i.e. there is no visual representation of the graph), returns -1.0.\n*/\ndouble QCPGraph::pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const\n{\n  closestData = mDataContainer->constEnd();\n  if (mDataContainer->isEmpty())\n    return -1.0;\n  if (mLineStyle == lsNone && mScatterStyle.isNone())\n    return -1.0;\n  \n  // calculate minimum distances to graph data points and find closestData iterator:\n  double minDistSqr = (std::numeric_limits<double>::max)();\n  // determine which key range comes into question, taking selection tolerance around pos into account:\n  double posKeyMin, posKeyMax, dummy;\n  pixelsToCoords(pixelPoint-QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMin, dummy);\n  pixelsToCoords(pixelPoint+QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMax, dummy);\n  if (posKeyMin > posKeyMax)\n    qSwap(posKeyMin, posKeyMax);\n  // iterate over found data points and then choose the one with the shortest distance to pos:\n  QCPGraphDataContainer::const_iterator begin = mDataContainer->findBegin(posKeyMin, true);\n  QCPGraphDataContainer::const_iterator end = mDataContainer->findEnd(posKeyMax, true);\n  for (QCPGraphDataContainer::const_iterator it=begin; it!=end; ++it)\n  {\n    const double currentDistSqr = QCPVector2D(coordsToPixels(it->key, it->value)-pixelPoint).lengthSquared();\n    if (currentDistSqr < minDistSqr)\n    {\n      minDistSqr = currentDistSqr;\n      closestData = it;\n    }\n  }\n    \n  // calculate distance to graph line if there is one (if so, will probably be smaller than distance to closest data point):\n  if (mLineStyle != lsNone)\n  {\n    // line displayed, calculate distance to line segments:\n    QVector<QPointF> lineData;\n    getLines(&lineData, QCPDataRange(0, dataCount())); // don't limit data range further since with sharp data spikes, line segments may be closer to test point than segments with closer key coordinate\n    QCPVector2D p(pixelPoint);\n    const int step = mLineStyle==lsImpulse ? 2 : 1; // impulse plot differs from other line styles in that the lineData points are only pairwise connected\n    for (int i=0; i<lineData.size()-1; i+=step)\n    {\n      const double currentDistSqr = p.distanceSquaredToLine(lineData.at(i), lineData.at(i+1));\n      if (currentDistSqr < minDistSqr)\n        minDistSqr = currentDistSqr;\n    }\n  }\n  \n  return qSqrt(minDistSqr);\n}\n\n/*! \\internal\n  \n  Finds the highest index of \\a data, whose points y value is just below \\a y. Assumes y values in\n  \\a data points are ordered ascending, as is ensured by \\ref getLines/\\ref getScatters if the key\n  axis is vertical.\n\n  Used to calculate the channel fill polygon, see \\ref getChannelFillPolygon.\n*/\nint QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const\n{\n  for (int i=0; i<data->size(); ++i)\n  {\n    if (data->at(i).y() > y)\n    {\n      if (i>0)\n        return i-1;\n      else\n        return 0;\n    }\n  }\n  return -1;\n}\n/* end of 'src/plottables/plottable-graph.cpp' */\n\n\n/* including file 'src/plottables/plottable-curve.cpp' */\n/* modified 2021-03-29T02:30:44, size 63851            */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPCurveData\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPCurveData\n  \\brief Holds the data of one single data point for QCPCurve.\n  \n  The stored data is:\n  \\li \\a t: the free ordering parameter of this curve point, like in the mathematical vector <em>(x(t), y(t))</em>. (This is the \\a sortKey)\n  \\li \\a key: coordinate on the key axis of this curve point (this is the \\a mainKey)\n  \\li \\a value: coordinate on the value axis of this curve point (this is the \\a mainValue)\n  \n  The container for storing multiple data points is \\ref QCPCurveDataContainer. It is a typedef for\n  \\ref QCPDataContainer with \\ref QCPCurveData as the DataType template parameter. See the\n  documentation there for an explanation regarding the data type's generic methods.\n  \n  \\see QCPCurveDataContainer\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn double QCPCurveData::sortKey() const\n  \n  Returns the \\a t member of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn static QCPCurveData QCPCurveData::fromSortKey(double sortKey)\n  \n  Returns a data point with the specified \\a sortKey (assigned to the data point's \\a t member).\n  All other members are set to zero.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn static static bool QCPCurveData::sortKeyIsMainKey()\n  \n  Since the member \\a key is the data point key coordinate and the member \\a t is the data ordering\n  parameter, this method returns false.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn double QCPCurveData::mainKey() const\n  \n  Returns the \\a key member of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn double QCPCurveData::mainValue() const\n  \n  Returns the \\a value member of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn QCPRange QCPCurveData::valueRange() const\n  \n  Returns a QCPRange with both lower and upper boundary set to \\a value of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Constructs a curve data point with t, key and value set to zero.\n*/\nQCPCurveData::QCPCurveData() :\n  t(0),\n  key(0),\n  value(0)\n{\n}\n\n/*!\n  Constructs a curve data point with the specified \\a t, \\a key and \\a value.\n*/\nQCPCurveData::QCPCurveData(double t, double key, double value) :\n  t(t),\n  key(key),\n  value(value)\n{\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPCurve\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPCurve\n  \\brief A plottable representing a parametric curve in a plot.\n  \n  \\image html QCPCurve.png\n  \n  Unlike QCPGraph, plottables of this type may have multiple points with the same key coordinate,\n  so their visual representation can have \\a loops. This is realized by introducing a third\n  coordinate \\a t, which defines the order of the points described by the other two coordinates \\a\n  x and \\a y.\n\n  To plot data, assign it with the \\ref setData or \\ref addData functions. Alternatively, you can\n  also access and modify the curve's data via the \\ref data method, which returns a pointer to the\n  internal \\ref QCPCurveDataContainer.\n  \n  Gaps in the curve can be created by adding data points with NaN as key and value\n  (<tt>qQNaN()</tt> or <tt>std::numeric_limits<double>::quiet_NaN()</tt>) in between the two data points that shall be\n  separated.\n  \n  \\section qcpcurve-appearance Changing the appearance\n  \n  The appearance of the curve is determined by the pen and the brush (\\ref setPen, \\ref setBrush).\n  \n  \\section qcpcurve-usage Usage\n  \n  Like all data representing objects in QCustomPlot, the QCPCurve is a plottable\n  (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies\n  (QCustomPlot::plottable, QCustomPlot::removePlottable, etc.)\n  \n  Usually, you first create an instance:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-1\n  which registers it with the QCustomPlot instance of the passed axes. Note that this QCustomPlot instance takes\n  ownership of the plottable, so do not delete it manually but use QCustomPlot::removePlottable() instead.\n  The newly created plottable can be modified, e.g.:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-2\n*/\n\n/* start of documentation of inline functions */\n\n/*! \\fn QSharedPointer<QCPCurveDataContainer> QCPCurve::data() const\n  \n  Returns a shared pointer to the internal data storage of type \\ref QCPCurveDataContainer. You may\n  use it to directly manipulate the data, which may be more convenient and faster than using the\n  regular \\ref setData or \\ref addData methods.\n*/\n\n/* end of documentation of inline functions */\n\n/*!\n  Constructs a curve which uses \\a keyAxis as its key axis (\"x\") and \\a valueAxis as its value\n  axis (\"y\"). \\a keyAxis and \\a valueAxis must reside in the same QCustomPlot instance and not have\n  the same orientation. If either of these restrictions is violated, a corresponding message is\n  printed to the debug output (qDebug), the construction is not aborted, though.\n  \n  The created QCPCurve is automatically registered with the QCustomPlot instance inferred from \\a\n  keyAxis. This QCustomPlot instance takes ownership of the QCPCurve, so do not delete it manually\n  but use QCustomPlot::removePlottable() instead.\n*/\nQCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :\n  QCPAbstractPlottable1D<QCPCurveData>(keyAxis, valueAxis),\n  mScatterSkip{},\n  mLineStyle{}\n{\n  // modify inherited properties from abstract plottable:\n  setPen(QPen(Qt::blue, 0));\n  setBrush(Qt::NoBrush);\n  \n  setScatterStyle(QCPScatterStyle());\n  setLineStyle(lsLine);\n  setScatterSkip(0);\n}\n\nQCPCurve::~QCPCurve()\n{\n}\n\n/*! \\overload\n  \n  Replaces the current data container with the provided \\a data container.\n  \n  Since a QSharedPointer is used, multiple QCPCurves may share the same data container safely.\n  Modifying the data in the container will then affect all curves that share the container. Sharing\n  can be achieved by simply exchanging the data containers wrapped in shared pointers:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-datasharing-1\n  \n  If you do not wish to share containers, but create a copy from an existing container, rather use\n  the \\ref QCPDataContainer<DataType>::set method on the curve's data container directly:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-datasharing-2\n  \n  \\see addData\n*/\nvoid QCPCurve::setData(QSharedPointer<QCPCurveDataContainer> data)\n{\n  mDataContainer = data;\n}\n\n/*! \\overload\n  \n  Replaces the current data with the provided points in \\a t, \\a keys and \\a values. The provided\n  vectors should have equal length. Else, the number of added points will be the size of the\n  smallest vector.\n  \n  If you can guarantee that the passed data points are sorted by \\a t in ascending order, you can\n  set \\a alreadySorted to true, to improve performance by saving a sorting run.\n  \n  \\see addData\n*/\nvoid QCPCurve::setData(const QVector<double> &t, const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)\n{\n  mDataContainer->clear();\n  addData(t, keys, values, alreadySorted);\n}\n\n\n/*! \\overload\n  \n  Replaces the current data with the provided points in \\a keys and \\a values. The provided vectors\n  should have equal length. Else, the number of added points will be the size of the smallest\n  vector.\n  \n  The t parameter of each data point will be set to the integer index of the respective key/value\n  pair.\n  \n  \\see addData\n*/\nvoid QCPCurve::setData(const QVector<double> &keys, const QVector<double> &values)\n{\n  mDataContainer->clear();\n  addData(keys, values);\n}\n\n/*!\n  Sets the visual appearance of single data points in the plot. If set to \\ref\n  QCPScatterStyle::ssNone, no scatter points are drawn (e.g. for line-only plots with appropriate\n  line style).\n  \n  \\see QCPScatterStyle, setLineStyle\n*/\nvoid QCPCurve::setScatterStyle(const QCPScatterStyle &style)\n{\n  mScatterStyle = style;\n}\n\n/*!\n  If scatters are displayed (scatter style not \\ref QCPScatterStyle::ssNone), \\a skip number of\n  scatter points are skipped/not drawn after every drawn scatter point.\n\n  This can be used to make the data appear sparser while for example still having a smooth line,\n  and to improve performance for very high density plots.\n\n  If \\a skip is set to 0 (default), all scatter points are drawn.\n\n  \\see setScatterStyle\n*/\nvoid QCPCurve::setScatterSkip(int skip)\n{\n  mScatterSkip = qMax(0, skip);\n}\n\n/*!\n  Sets how the single data points are connected in the plot or how they are represented visually\n  apart from the scatter symbol. For scatter-only plots, set \\a style to \\ref lsNone and \\ref\n  setScatterStyle to the desired scatter style.\n  \n  \\see setScatterStyle\n*/\nvoid QCPCurve::setLineStyle(QCPCurve::LineStyle style)\n{\n  mLineStyle = style;\n}\n\n/*! \\overload\n  \n  Adds the provided points in \\a t, \\a keys and \\a values to the current data. The provided vectors\n  should have equal length. Else, the number of added points will be the size of the smallest\n  vector.\n  \n  If you can guarantee that the passed data points are sorted by \\a keys in ascending order, you\n  can set \\a alreadySorted to true, to improve performance by saving a sorting run.\n  \n  Alternatively, you can also access and modify the data directly via the \\ref data method, which\n  returns a pointer to the internal data container.\n*/\nvoid QCPCurve::addData(const QVector<double> &t, const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)\n{\n  if (t.size() != keys.size() || t.size() != values.size())\n    qDebug() << Q_FUNC_INFO << \"ts, keys and values have different sizes:\" << t.size() << keys.size() << values.size();\n  const int n = qMin(qMin(t.size(), keys.size()), values.size());\n  QVector<QCPCurveData> tempData(n);\n  QVector<QCPCurveData>::iterator it = tempData.begin();\n  const QVector<QCPCurveData>::iterator itEnd = tempData.end();\n  int i = 0;\n  while (it != itEnd)\n  {\n    it->t = t[i];\n    it->key = keys[i];\n    it->value = values[i];\n    ++it;\n    ++i;\n  }\n  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write\n}\n\n/*! \\overload\n  \n  Adds the provided points in \\a keys and \\a values to the current data. The provided vectors\n  should have equal length. Else, the number of added points will be the size of the smallest\n  vector.\n  \n  The t parameter of each data point will be set to the integer index of the respective key/value\n  pair.\n  \n  Alternatively, you can also access and modify the data directly via the \\ref data method, which\n  returns a pointer to the internal data container.\n*/\nvoid QCPCurve::addData(const QVector<double> &keys, const QVector<double> &values)\n{\n  if (keys.size() != values.size())\n    qDebug() << Q_FUNC_INFO << \"keys and values have different sizes:\" << keys.size() << values.size();\n  const int n = qMin(keys.size(), values.size());\n  double tStart;\n  if (!mDataContainer->isEmpty())\n    tStart = (mDataContainer->constEnd()-1)->t + 1.0;\n  else\n    tStart = 0;\n  QVector<QCPCurveData> tempData(n);\n  QVector<QCPCurveData>::iterator it = tempData.begin();\n  const QVector<QCPCurveData>::iterator itEnd = tempData.end();\n  int i = 0;\n  while (it != itEnd)\n  {\n    it->t = tStart + i;\n    it->key = keys[i];\n    it->value = values[i];\n    ++it;\n    ++i;\n  }\n  mDataContainer->add(tempData, true); // don't modify tempData beyond this to prevent copy on write\n}\n\n/*! \\overload\n  Adds the provided data point as \\a t, \\a key and \\a value to the current data.\n  \n  Alternatively, you can also access and modify the data directly via the \\ref data method, which\n  returns a pointer to the internal data container.\n*/\nvoid QCPCurve::addData(double t, double key, double value)\n{\n  mDataContainer->add(QCPCurveData(t, key, value));\n}\n\n/*! \\overload\n  \n  Adds the provided data point as \\a key and \\a value to the current data.\n  \n  The t parameter is generated automatically by increments of 1 for each point, starting at the\n  highest t of previously existing data or 0, if the curve data is empty.\n  \n  Alternatively, you can also access and modify the data directly via the \\ref data method, which\n  returns a pointer to the internal data container.\n*/\nvoid QCPCurve::addData(double key, double value)\n{\n  if (!mDataContainer->isEmpty())\n    mDataContainer->add(QCPCurveData((mDataContainer->constEnd()-1)->t + 1.0, key, value));\n  else\n    mDataContainer->add(QCPCurveData(0.0, key, value));\n}\n\n/*!\n  Implements a selectTest specific to this plottable's point geometry.\n\n  If \\a details is not 0, it will be set to a \\ref QCPDataSelection, describing the closest data\n  point to \\a pos.\n  \n  \\seebaseclassmethod \\ref QCPAbstractPlottable::selectTest\n*/\ndouble QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())\n    return -1;\n  if (!mKeyAxis || !mValueAxis)\n    return -1;\n  \n  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))\n  {\n    QCPCurveDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();\n    double result = pointDistance(pos, closestDataPoint);\n    if (details)\n    {\n      int pointIndex = int( closestDataPoint-mDataContainer->constBegin() );\n      details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));\n    }\n    return result;\n  } else\n    return -1;\n}\n\n/* inherits documentation from base class */\nQCPRange QCPCurve::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const\n{\n  return mDataContainer->keyRange(foundRange, inSignDomain);\n}\n\n/* inherits documentation from base class */\nQCPRange QCPCurve::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const\n{\n  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);\n}\n\n/* inherits documentation from base class */\nvoid QCPCurve::draw(QCPPainter *painter)\n{\n  if (mDataContainer->isEmpty()) return;\n  \n  // allocate line vector:\n  QVector<QPointF> lines, scatters;\n  \n  // loop over and draw segments of unselected/selected data:\n  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;\n  getDataSegments(selectedSegments, unselectedSegments);\n  allSegments << unselectedSegments << selectedSegments;\n  for (int i=0; i<allSegments.size(); ++i)\n  {\n    bool isSelectedSegment = i >= unselectedSegments.size();\n    \n    // fill with curve data:\n    QPen finalCurvePen = mPen; // determine the final pen already here, because the line optimization depends on its stroke width\n    if (isSelectedSegment && mSelectionDecorator)\n      finalCurvePen = mSelectionDecorator->pen();\n    \n    QCPDataRange lineDataRange = isSelectedSegment ? allSegments.at(i) : allSegments.at(i).adjusted(-1, 1); // unselected segments extend lines to bordering selected data point (safe to exceed total data bounds in first/last segment, getCurveLines takes care)\n    getCurveLines(&lines, lineDataRange, finalCurvePen.widthF());\n    \n    // check data validity if flag set:\n  #ifdef QCUSTOMPLOT_CHECK_DATA\n    for (QCPCurveDataContainer::const_iterator it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)\n    {\n      if (QCP::isInvalidData(it->t) ||\n          QCP::isInvalidData(it->key, it->value))\n        qDebug() << Q_FUNC_INFO << \"Data point at\" << it->key << \"invalid.\" << \"Plottable name:\" << name();\n    }\n  #endif\n    \n    // draw curve fill:\n    applyFillAntialiasingHint(painter);\n    if (isSelectedSegment && mSelectionDecorator)\n      mSelectionDecorator->applyBrush(painter);\n    else\n      painter->setBrush(mBrush);\n    painter->setPen(Qt::NoPen);\n    if (painter->brush().style() != Qt::NoBrush && painter->brush().color().alpha() != 0)\n      painter->drawPolygon(QPolygonF(lines));\n    \n    // draw curve line:\n    if (mLineStyle != lsNone)\n    {\n      painter->setPen(finalCurvePen);\n      painter->setBrush(Qt::NoBrush);\n      drawCurveLine(painter, lines);\n    }\n    \n    // draw scatters:\n    QCPScatterStyle finalScatterStyle = mScatterStyle;\n    if (isSelectedSegment && mSelectionDecorator)\n      finalScatterStyle = mSelectionDecorator->getFinalScatterStyle(mScatterStyle);\n    if (!finalScatterStyle.isNone())\n    {\n      getScatters(&scatters, allSegments.at(i), finalScatterStyle.size());\n      drawScatterPlot(painter, scatters, finalScatterStyle);\n    }\n  }\n  \n  // draw other selection decoration that isn't just line/scatter pens and brushes:\n  if (mSelectionDecorator)\n    mSelectionDecorator->drawDecoration(painter, selection());\n}\n\n/* inherits documentation from base class */\nvoid QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const\n{\n  // draw fill:\n  if (mBrush.style() != Qt::NoBrush)\n  {\n    applyFillAntialiasingHint(painter);\n    painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);\n  }\n  // draw line vertically centered:\n  if (mLineStyle != lsNone)\n  {\n    applyDefaultAntialiasingHint(painter);\n    painter->setPen(mPen);\n    painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens\n  }\n  // draw scatter symbol:\n  if (!mScatterStyle.isNone())\n  {\n    applyScattersAntialiasingHint(painter);\n    // scale scatter pixmap if it's too large to fit in legend icon rect:\n    if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))\n    {\n      QCPScatterStyle scaledStyle(mScatterStyle);\n      scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));\n      scaledStyle.applyTo(painter, mPen);\n      scaledStyle.drawShape(painter, QRectF(rect).center());\n    } else\n    {\n      mScatterStyle.applyTo(painter, mPen);\n      mScatterStyle.drawShape(painter, QRectF(rect).center());\n    }\n  }\n}\n\n/*!  \\internal\n\n  Draws lines between the points in \\a lines, given in pixel coordinates.\n\n  \\see drawScatterPlot, getCurveLines\n*/\nvoid QCPCurve::drawCurveLine(QCPPainter *painter, const QVector<QPointF> &lines) const\n{\n  if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)\n  {\n    applyDefaultAntialiasingHint(painter);\n    drawPolyline(painter, lines);\n  }\n}\n\n/*! \\internal\n\n  Draws scatter symbols at every point passed in \\a points, given in pixel coordinates. The\n  scatters will be drawn with \\a painter and have the appearance as specified in \\a style.\n\n  \\see drawCurveLine, getCurveLines\n*/\nvoid QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &points, const QCPScatterStyle &style) const\n{\n  // draw scatter point symbols:\n  applyScattersAntialiasingHint(painter);\n  style.applyTo(painter, mPen);\n  foreach (const QPointF &point, points)\n    if (!qIsNaN(point.x()) && !qIsNaN(point.y()))\n      style.drawShape(painter,  point);\n}\n\n/*! \\internal\n\n  Called by \\ref draw to generate points in pixel coordinates which represent the line of the\n  curve.\n\n  Line segments that aren't visible in the current axis rect are handled in an optimized way. They\n  are projected onto a rectangle slightly larger than the visible axis rect and simplified\n  regarding point count. The algorithm makes sure to preserve appearance of lines and fills inside\n  the visible axis rect by generating new temporary points on the outer rect if necessary.\n\n  \\a lines will be filled with points in pixel coordinates, that can be drawn with \\ref\n  drawCurveLine.\n\n  \\a dataRange specifies the beginning and ending data indices that will be taken into account for\n  conversion. In this function, the specified range may exceed the total data bounds without harm:\n  a correspondingly trimmed data range will be used. This takes the burden off the user of this\n  function to check for valid indices in \\a dataRange, e.g. when extending ranges coming from \\ref\n  getDataSegments.\n\n  \\a penWidth specifies the pen width that will be used to later draw the lines generated by this\n  function. This is needed here to calculate an accordingly wider margin around the axis rect when\n  performing the line optimization.\n\n  Methods that are also involved in the algorithm are: \\ref getRegion, \\ref getOptimizedPoint, \\ref\n  getOptimizedCornerPoints \\ref mayTraverse, \\ref getTraverse, \\ref getTraverseCornerPoints.\n\n  \\see drawCurveLine, drawScatterPlot\n*/\nvoid QCPCurve::getCurveLines(QVector<QPointF> *lines, const QCPDataRange &dataRange, double penWidth) const\n{\n  if (!lines) return;\n  lines->clear();\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n  \n  // add margins to rect to compensate for stroke width\n  const double strokeMargin = qMax(qreal(1.0), qreal(penWidth*0.75)); // stroke radius + 50% safety\n  const double keyMin = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().lower)-strokeMargin*keyAxis->pixelOrientation());\n  const double keyMax = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().upper)+strokeMargin*keyAxis->pixelOrientation());\n  const double valueMin = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().lower)-strokeMargin*valueAxis->pixelOrientation());\n  const double valueMax = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().upper)+strokeMargin*valueAxis->pixelOrientation());\n  QCPCurveDataContainer::const_iterator itBegin = mDataContainer->constBegin();\n  QCPCurveDataContainer::const_iterator itEnd = mDataContainer->constEnd();\n  mDataContainer->limitIteratorsToDataRange(itBegin, itEnd, dataRange);\n  if (itBegin == itEnd)\n    return;\n  QCPCurveDataContainer::const_iterator it = itBegin;\n  QCPCurveDataContainer::const_iterator prevIt = itEnd-1;\n  int prevRegion = getRegion(prevIt->key, prevIt->value, keyMin, valueMax, keyMax, valueMin);\n  QVector<QPointF> trailingPoints; // points that must be applied after all other points (are generated only when handling first point to get virtual segment between last and first point right)\n  while (it != itEnd)\n  {\n    const int currentRegion = getRegion(it->key, it->value, keyMin, valueMax, keyMax, valueMin);\n    if (currentRegion != prevRegion) // changed region, possibly need to add some optimized edge points or original points if entering R\n    {\n      if (currentRegion != 5) // segment doesn't end in R, so it's a candidate for removal\n      {\n        QPointF crossA, crossB;\n        if (prevRegion == 5) // we're coming from R, so add this point optimized\n        {\n          lines->append(getOptimizedPoint(currentRegion, it->key, it->value, prevIt->key, prevIt->value, keyMin, valueMax, keyMax, valueMin));\n          // in the situations 5->1/7/9/3 the segment may leave R and directly cross through two outer regions. In these cases we need to add an additional corner point\n          *lines << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin);\n        } else if (mayTraverse(prevRegion, currentRegion) &&\n                   getTraverse(prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin, crossA, crossB))\n        {\n          // add the two cross points optimized if segment crosses R and if segment isn't virtual zeroth segment between last and first curve point:\n          QVector<QPointF> beforeTraverseCornerPoints, afterTraverseCornerPoints;\n          getTraverseCornerPoints(prevRegion, currentRegion, keyMin, valueMax, keyMax, valueMin, beforeTraverseCornerPoints, afterTraverseCornerPoints);\n          if (it != itBegin)\n          {\n            *lines << beforeTraverseCornerPoints;\n            lines->append(crossA);\n            lines->append(crossB);\n            *lines << afterTraverseCornerPoints;\n          } else\n          {\n            lines->append(crossB);\n            *lines << afterTraverseCornerPoints;\n            trailingPoints << beforeTraverseCornerPoints << crossA ;\n          }\n        } else // doesn't cross R, line is just moving around in outside regions, so only need to add optimized point(s) at the boundary corner(s)\n        {\n          *lines << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin);\n        }\n      } else // segment does end in R, so we add previous point optimized and this point at original position\n      {\n        if (it == itBegin) // it is first point in curve and prevIt is last one. So save optimized point for adding it to the lineData in the end\n          trailingPoints << getOptimizedPoint(prevRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin);\n        else\n          lines->append(getOptimizedPoint(prevRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin));\n        lines->append(coordsToPixels(it->key, it->value));\n      }\n    } else // region didn't change\n    {\n      if (currentRegion == 5) // still in R, keep adding original points\n      {\n        lines->append(coordsToPixels(it->key, it->value));\n      } else // still outside R, no need to add anything\n      {\n        // see how this is not doing anything? That's the main optimization...\n      }\n    }\n    prevIt = it;\n    prevRegion = currentRegion;\n    ++it;\n  }\n  *lines << trailingPoints;\n}\n\n/*! \\internal\n\n  Called by \\ref draw to generate points in pixel coordinates which represent the scatters of the\n  curve. If a scatter skip is configured (\\ref setScatterSkip), the returned points are accordingly\n  sparser.\n\n  Scatters that aren't visible in the current axis rect are optimized away.\n\n  \\a scatters will be filled with points in pixel coordinates, that can be drawn with \\ref\n  drawScatterPlot.\n\n  \\a dataRange specifies the beginning and ending data indices that will be taken into account for\n  conversion.\n\n  \\a scatterWidth specifies the scatter width that will be used to later draw the scatters at pixel\n  coordinates generated by this function. This is needed here to calculate an accordingly wider\n  margin around the axis rect when performing the data point reduction.\n\n  \\see draw, drawScatterPlot\n*/\nvoid QCPCurve::getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange, double scatterWidth) const\n{\n  if (!scatters) return;\n  scatters->clear();\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n  \n  QCPCurveDataContainer::const_iterator begin = mDataContainer->constBegin();\n  QCPCurveDataContainer::const_iterator end = mDataContainer->constEnd();\n  mDataContainer->limitIteratorsToDataRange(begin, end, dataRange);\n  if (begin == end)\n    return;\n  const int scatterModulo = mScatterSkip+1;\n  const bool doScatterSkip = mScatterSkip > 0;\n  int endIndex = int( end-mDataContainer->constBegin() );\n  \n  QCPRange keyRange = keyAxis->range();\n  QCPRange valueRange = valueAxis->range();\n  // extend range to include width of scatter symbols:\n  keyRange.lower = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyRange.lower)-scatterWidth*keyAxis->pixelOrientation());\n  keyRange.upper = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyRange.upper)+scatterWidth*keyAxis->pixelOrientation());\n  valueRange.lower = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueRange.lower)-scatterWidth*valueAxis->pixelOrientation());\n  valueRange.upper = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueRange.upper)+scatterWidth*valueAxis->pixelOrientation());\n  \n  QCPCurveDataContainer::const_iterator it = begin;\n  int itIndex = int( begin-mDataContainer->constBegin() );\n  while (doScatterSkip && it != end && itIndex % scatterModulo != 0) // advance begin iterator to first non-skipped scatter\n  {\n    ++itIndex;\n    ++it;\n  }\n  if (keyAxis->orientation() == Qt::Vertical)\n  {\n    while (it != end)\n    {\n      if (!qIsNaN(it->value) && keyRange.contains(it->key) && valueRange.contains(it->value))\n        scatters->append(QPointF(valueAxis->coordToPixel(it->value), keyAxis->coordToPixel(it->key)));\n      \n      // advance iterator to next (non-skipped) data point:\n      if (!doScatterSkip)\n        ++it;\n      else\n      {\n        itIndex += scatterModulo;\n        if (itIndex < endIndex) // make sure we didn't jump over end\n          it += scatterModulo;\n        else\n        {\n          it = end;\n          itIndex = endIndex;\n        }\n      }\n    }\n  } else\n  {\n    while (it != end)\n    {\n      if (!qIsNaN(it->value) && keyRange.contains(it->key) && valueRange.contains(it->value))\n        scatters->append(QPointF(keyAxis->coordToPixel(it->key), valueAxis->coordToPixel(it->value)));\n      \n      // advance iterator to next (non-skipped) data point:\n      if (!doScatterSkip)\n        ++it;\n      else\n      {\n        itIndex += scatterModulo;\n        if (itIndex < endIndex) // make sure we didn't jump over end\n          it += scatterModulo;\n        else\n        {\n          it = end;\n          itIndex = endIndex;\n        }\n      }\n    }\n  }\n}\n\n/*! \\internal\n\n  This function is part of the curve optimization algorithm of \\ref getCurveLines.\n\n  It returns the region of the given point (\\a key, \\a value) with respect to a rectangle defined\n  by \\a keyMin, \\a keyMax, \\a valueMin, and \\a valueMax.\n\n  The regions are enumerated from top to bottom (\\a valueMin to \\a valueMax) and left to right (\\a\n  keyMin to \\a keyMax):\n\n  <table style=\"width:10em; text-align:center\">\n    <tr><td>1</td><td>4</td><td>7</td></tr>\n    <tr><td>2</td><td style=\"border:1px solid black\">5</td><td>8</td></tr>\n    <tr><td>3</td><td>6</td><td>9</td></tr>\n  </table>\n\n  With the rectangle being region 5, and the outer regions extending infinitely outwards. In the\n  curve optimization algorithm, region 5 is considered to be the visible portion of the plot.\n*/\nint QCPCurve::getRegion(double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const\n{\n  if (key < keyMin) // region 123\n  {\n    if (value > valueMax)\n      return 1;\n    else if (value < valueMin)\n      return 3;\n    else\n      return 2;\n  } else if (key > keyMax) // region 789\n  {\n    if (value > valueMax)\n      return 7;\n    else if (value < valueMin)\n      return 9;\n    else\n      return 8;\n  } else // region 456\n  {\n    if (value > valueMax)\n      return 4;\n    else if (value < valueMin)\n      return 6;\n    else\n      return 5;\n  }\n}\n\n/*! \\internal\n  \n  This function is part of the curve optimization algorithm of \\ref getCurveLines.\n  \n  This method is used in case the current segment passes from inside the visible rect (region 5,\n  see \\ref getRegion) to any of the outer regions (\\a otherRegion). The current segment is given by\n  the line connecting (\\a key, \\a value) with (\\a otherKey, \\a otherValue).\n  \n  It returns the intersection point of the segment with the border of region 5.\n  \n  For this function it doesn't matter whether (\\a key, \\a value) is the point inside region 5 or\n  whether it's (\\a otherKey, \\a otherValue), i.e. whether the segment is coming from region 5 or\n  leaving it. It is important though that \\a otherRegion correctly identifies the other region not\n  equal to 5.\n*/\nQPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const\n{\n  // The intersection point interpolation here is done in pixel coordinates, so we don't need to\n  // differentiate between different axis scale types. Note that the nomenclature\n  // top/left/bottom/right/min/max is with respect to the rect in plot coordinates, wich may be\n  // different in pixel coordinates (horz/vert key axes, reversed ranges)\n  \n  const double keyMinPx = mKeyAxis->coordToPixel(keyMin);\n  const double keyMaxPx = mKeyAxis->coordToPixel(keyMax);\n  const double valueMinPx = mValueAxis->coordToPixel(valueMin);\n  const double valueMaxPx = mValueAxis->coordToPixel(valueMax);\n  const double otherValuePx = mValueAxis->coordToPixel(otherValue);\n  const double valuePx = mValueAxis->coordToPixel(value);\n  const double otherKeyPx = mKeyAxis->coordToPixel(otherKey);\n  const double keyPx = mKeyAxis->coordToPixel(key);\n  double intersectKeyPx = keyMinPx; // initial key just a fail-safe\n  double intersectValuePx = valueMinPx; // initial value just a fail-safe\n  switch (otherRegion)\n  {\n    case 1: // top and left edge\n    {\n      intersectValuePx = valueMaxPx;\n      intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);\n      if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether top edge is not intersected, then it must be left edge (qMin/qMax necessary since axes may be reversed)\n      {\n        intersectKeyPx = keyMinPx;\n        intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);\n      }\n      break;\n    }\n    case 2: // left edge\n    {\n      intersectKeyPx = keyMinPx;\n      intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);\n      break;\n    }\n    case 3: // bottom and left edge\n    {\n      intersectValuePx = valueMinPx;\n      intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);\n      if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether bottom edge is not intersected, then it must be left edge (qMin/qMax necessary since axes may be reversed)\n      {\n        intersectKeyPx = keyMinPx;\n        intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);\n      }\n      break;\n    }\n    case 4: // top edge\n    {\n      intersectValuePx = valueMaxPx;\n      intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);\n      break;\n    }\n    case 5:\n    {\n      break; // case 5 shouldn't happen for this function but we add it anyway to prevent potential discontinuity in branch table\n    }\n    case 6: // bottom edge\n    {\n      intersectValuePx = valueMinPx;\n      intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);\n      break;\n    }\n    case 7: // top and right edge\n    {\n      intersectValuePx = valueMaxPx;\n      intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);\n      if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether top edge is not intersected, then it must be right edge (qMin/qMax necessary since axes may be reversed)\n      {\n        intersectKeyPx = keyMaxPx;\n        intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);\n      }\n      break;\n    }\n    case 8: // right edge\n    {\n      intersectKeyPx = keyMaxPx;\n      intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);\n      break;\n    }\n    case 9: // bottom and right edge\n    {\n      intersectValuePx = valueMinPx;\n      intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);\n      if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether bottom edge is not intersected, then it must be right edge (qMin/qMax necessary since axes may be reversed)\n      {\n        intersectKeyPx = keyMaxPx;\n        intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);\n      }\n      break;\n    }\n  }\n  if (mKeyAxis->orientation() == Qt::Horizontal)\n    return {intersectKeyPx, intersectValuePx};\n  else\n    return {intersectValuePx, intersectKeyPx};\n}\n\n/*! \\internal\n  \n  This function is part of the curve optimization algorithm of \\ref getCurveLines.\n  \n  In situations where a single segment skips over multiple regions it might become necessary to add\n  extra points at the corners of region 5 (see \\ref getRegion) such that the optimized segment\n  doesn't unintentionally cut through the visible area of the axis rect and create plot artifacts.\n  This method provides these points that must be added, assuming the original segment doesn't\n  start, end, or traverse region 5. (Corner points where region 5 is traversed are calculated by\n  \\ref getTraverseCornerPoints.)\n  \n  For example, consider a segment which directly goes from region 4 to 2 but originally is far out\n  to the top left such that it doesn't cross region 5. Naively optimizing these points by\n  projecting them on the top and left borders of region 5 will create a segment that surely crosses\n  5, creating a visual artifact in the plot. This method prevents this by providing extra points at\n  the top left corner, making the optimized curve correctly pass from region 4 to 1 to 2 without\n  traversing 5.\n*/\nQVector<QPointF> QCPCurve::getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const\n{\n  QVector<QPointF> result;\n  switch (prevRegion)\n  {\n    case 1:\n    {\n      switch (currentRegion)\n      {\n        case 2: { result << coordsToPixels(keyMin, valueMax); break; }\n        case 4: { result << coordsToPixels(keyMin, valueMax); break; }\n        case 3: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); break; }\n        case 7: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); break; }\n        case 6: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }\n        case 8: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }\n        case 9: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points\n          if ((value-prevValue)/(key-prevKey)*(keyMin-key)+value < valueMin) // segment passes below R\n          { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); }\n          else\n          { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); }\n          break;\n        }\n      }\n      break;\n    }\n    case 2:\n    {\n      switch (currentRegion)\n      {\n        case 1: { result << coordsToPixels(keyMin, valueMax); break; }\n        case 3: { result << coordsToPixels(keyMin, valueMin); break; }\n        case 4: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }\n        case 6: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }\n        case 7: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); break; }\n        case 9: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); break; }\n      }\n      break;\n    }\n    case 3:\n    {\n      switch (currentRegion)\n      {\n        case 2: { result << coordsToPixels(keyMin, valueMin); break; }\n        case 6: { result << coordsToPixels(keyMin, valueMin); break; }\n        case 1: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); break; }\n        case 9: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); break; }\n        case 4: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }\n        case 8: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }\n        case 7: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points\n          if ((value-prevValue)/(key-prevKey)*(keyMax-key)+value < valueMin) // segment passes below R\n          { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); }\n          else\n          { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); }\n          break;\n        }\n      }\n      break;\n    }\n    case 4:\n    {\n      switch (currentRegion)\n      {\n        case 1: { result << coordsToPixels(keyMin, valueMax); break; }\n        case 7: { result << coordsToPixels(keyMax, valueMax); break; }\n        case 2: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }\n        case 8: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }\n        case 3: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); break; }\n        case 9: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); break; }\n      }\n      break;\n    }\n    case 5:\n    {\n      switch (currentRegion)\n      {\n        case 1: { result << coordsToPixels(keyMin, valueMax); break; }\n        case 7: { result << coordsToPixels(keyMax, valueMax); break; }\n        case 9: { result << coordsToPixels(keyMax, valueMin); break; }\n        case 3: { result << coordsToPixels(keyMin, valueMin); break; }\n      }\n      break;\n    }\n    case 6:\n    {\n      switch (currentRegion)\n      {\n        case 3: { result << coordsToPixels(keyMin, valueMin); break; }\n        case 9: { result << coordsToPixels(keyMax, valueMin); break; }\n        case 2: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }\n        case 8: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }\n        case 1: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); break; }\n        case 7: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); break; }\n      }\n      break;\n    }\n    case 7:\n    {\n      switch (currentRegion)\n      {\n        case 4: { result << coordsToPixels(keyMax, valueMax); break; }\n        case 8: { result << coordsToPixels(keyMax, valueMax); break; }\n        case 1: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); break; }\n        case 9: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); break; }\n        case 2: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }\n        case 6: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }\n        case 3: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points\n          if ((value-prevValue)/(key-prevKey)*(keyMax-key)+value < valueMin) // segment passes below R\n          { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); }\n          else\n          { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); }\n          break;\n        }\n      }\n      break;\n    }\n    case 8:\n    {\n      switch (currentRegion)\n      {\n        case 7: { result << coordsToPixels(keyMax, valueMax); break; }\n        case 9: { result << coordsToPixels(keyMax, valueMin); break; }\n        case 4: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }\n        case 6: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }\n        case 1: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); break; }\n        case 3: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); break; }\n      }\n      break;\n    }\n    case 9:\n    {\n      switch (currentRegion)\n      {\n        case 6: { result << coordsToPixels(keyMax, valueMin); break; }\n        case 8: { result << coordsToPixels(keyMax, valueMin); break; }\n        case 3: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); break; }\n        case 7: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); break; }\n        case 2: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }\n        case 4: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }\n        case 1: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points\n          if ((value-prevValue)/(key-prevKey)*(keyMin-key)+value < valueMin) // segment passes below R\n          { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); }\n          else\n          { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); }\n          break;\n        }\n      }\n      break;\n    }\n  }\n  return result;\n}\n\n/*! \\internal\n  \n  This function is part of the curve optimization algorithm of \\ref getCurveLines.\n  \n  This method returns whether a segment going from \\a prevRegion to \\a currentRegion (see \\ref\n  getRegion) may traverse the visible region 5. This function assumes that neither \\a prevRegion\n  nor \\a currentRegion is 5 itself.\n  \n  If this method returns false, the segment for sure doesn't pass region 5. If it returns true, the\n  segment may or may not pass region 5 and a more fine-grained calculation must be used (\\ref\n  getTraverse).\n*/\nbool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const\n{\n  switch (prevRegion)\n  {\n    case 1:\n    {\n      switch (currentRegion)\n      {\n        case 4:\n        case 7:\n        case 2:\n        case 3: return false;\n        default: return true;\n      }\n    }\n    case 2:\n    {\n      switch (currentRegion)\n      {\n        case 1:\n        case 3: return false;\n        default: return true;\n      }\n    }\n    case 3:\n    {\n      switch (currentRegion)\n      {\n        case 1:\n        case 2:\n        case 6:\n        case 9: return false;\n        default: return true;\n      }\n    }\n    case 4:\n    {\n      switch (currentRegion)\n      {\n        case 1:\n        case 7: return false;\n        default: return true;\n      }\n    }\n    case 5: return false; // should never occur\n    case 6:\n    {\n      switch (currentRegion)\n      {\n        case 3:\n        case 9: return false;\n        default: return true;\n      }\n    }\n    case 7:\n    {\n      switch (currentRegion)\n      {\n        case 1:\n        case 4:\n        case 8:\n        case 9: return false;\n        default: return true;\n      }\n    }\n    case 8:\n    {\n      switch (currentRegion)\n      {\n        case 7:\n        case 9: return false;\n        default: return true;\n      }\n    }\n    case 9:\n    {\n      switch (currentRegion)\n      {\n        case 3:\n        case 6:\n        case 8:\n        case 7: return false;\n        default: return true;\n      }\n    }\n    default: return true;\n  }\n}\n\n\n/*! \\internal\n  \n  This function is part of the curve optimization algorithm of \\ref getCurveLines.\n  \n  This method assumes that the \\ref mayTraverse test has returned true, so there is a chance the\n  segment defined by (\\a prevKey, \\a prevValue) and (\\a key, \\a value) goes through the visible\n  region 5.\n  \n  The return value of this method indicates whether the segment actually traverses region 5 or not.\n  \n  If the segment traverses 5, the output parameters \\a crossA and \\a crossB indicate the entry and\n  exit points of region 5. They will become the optimized points for that segment.\n*/\nbool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin, QPointF &crossA, QPointF &crossB) const\n{\n  // The intersection point interpolation here is done in pixel coordinates, so we don't need to\n  // differentiate between different axis scale types. Note that the nomenclature\n  // top/left/bottom/right/min/max is with respect to the rect in plot coordinates, wich may be\n  // different in pixel coordinates (horz/vert key axes, reversed ranges)\n  \n  QList<QPointF> intersections;\n  const double valueMinPx = mValueAxis->coordToPixel(valueMin);\n  const double valueMaxPx = mValueAxis->coordToPixel(valueMax);\n  const double keyMinPx = mKeyAxis->coordToPixel(keyMin);\n  const double keyMaxPx = mKeyAxis->coordToPixel(keyMax);\n  const double keyPx = mKeyAxis->coordToPixel(key);\n  const double valuePx = mValueAxis->coordToPixel(value);\n  const double prevKeyPx = mKeyAxis->coordToPixel(prevKey);\n  const double prevValuePx = mValueAxis->coordToPixel(prevValue);\n  if (qFuzzyIsNull(keyPx-prevKeyPx)) // line is parallel to value axis\n  {\n    // due to region filter in mayTraverse(), if line is parallel to value or key axis, region 5 is traversed here\n    intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyPx, valueMinPx) : QPointF(valueMinPx, keyPx)); // direction will be taken care of at end of method\n    intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyPx, valueMaxPx) : QPointF(valueMaxPx, keyPx));\n  } else if (qFuzzyIsNull(valuePx-prevValuePx)) // line is parallel to key axis\n  {\n    // due to region filter in mayTraverse(), if line is parallel to value or key axis, region 5 is traversed here\n    intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMinPx, valuePx) : QPointF(valuePx, keyMinPx)); // direction will be taken care of at end of method\n    intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMaxPx, valuePx) : QPointF(valuePx, keyMaxPx));\n  } else // line is skewed\n  {\n    double gamma;\n    double keyPerValuePx = (keyPx-prevKeyPx)/(valuePx-prevValuePx);\n    // check top of rect:\n    gamma = prevKeyPx + (valueMaxPx-prevValuePx)*keyPerValuePx;\n    if (gamma >= qMin(keyMinPx, keyMaxPx) && gamma <= qMax(keyMinPx, keyMaxPx)) // qMin/qMax necessary since axes may be reversed\n      intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(gamma, valueMaxPx) : QPointF(valueMaxPx, gamma));\n    // check bottom of rect:\n    gamma = prevKeyPx + (valueMinPx-prevValuePx)*keyPerValuePx;\n    if (gamma >= qMin(keyMinPx, keyMaxPx) && gamma <= qMax(keyMinPx, keyMaxPx)) // qMin/qMax necessary since axes may be reversed\n      intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(gamma, valueMinPx) : QPointF(valueMinPx, gamma));\n    const double valuePerKeyPx = 1.0/keyPerValuePx;\n    // check left of rect:\n    gamma = prevValuePx + (keyMinPx-prevKeyPx)*valuePerKeyPx;\n    if (gamma >= qMin(valueMinPx, valueMaxPx) && gamma <= qMax(valueMinPx, valueMaxPx)) // qMin/qMax necessary since axes may be reversed\n      intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMinPx, gamma) : QPointF(gamma, keyMinPx));\n    // check right of rect:\n    gamma = prevValuePx + (keyMaxPx-prevKeyPx)*valuePerKeyPx;\n    if (gamma >= qMin(valueMinPx, valueMaxPx) && gamma <= qMax(valueMinPx, valueMaxPx)) // qMin/qMax necessary since axes may be reversed\n      intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMaxPx, gamma) : QPointF(gamma, keyMaxPx));\n  }\n  \n  // handle cases where found points isn't exactly 2:\n  if (intersections.size() > 2)\n  {\n    // line probably goes through corner of rect, and we got duplicate points there. single out the point pair with greatest distance in between:\n    double distSqrMax = 0;\n    QPointF pv1, pv2;\n    for (int i=0; i<intersections.size()-1; ++i)\n    {\n      for (int k=i+1; k<intersections.size(); ++k)\n      {\n        QPointF distPoint = intersections.at(i)-intersections.at(k);\n        double distSqr = distPoint.x()*distPoint.x()+distPoint.y()+distPoint.y();\n        if (distSqr > distSqrMax)\n        {\n          pv1 = intersections.at(i);\n          pv2 = intersections.at(k);\n          distSqrMax = distSqr;\n        }\n      }\n    }\n    intersections = QList<QPointF>() << pv1 << pv2;\n  } else if (intersections.size() != 2)\n  {\n    // one or even zero points found (shouldn't happen unless line perfectly tangent to corner), no need to draw segment\n    return false;\n  }\n  \n  // possibly re-sort points so optimized point segment has same direction as original segment:\n  double xDelta = keyPx-prevKeyPx;\n  double yDelta = valuePx-prevValuePx;\n  if (mKeyAxis->orientation() != Qt::Horizontal)\n    qSwap(xDelta, yDelta);\n  if (xDelta*(intersections.at(1).x()-intersections.at(0).x()) + yDelta*(intersections.at(1).y()-intersections.at(0).y()) < 0) // scalar product of both segments < 0 -> opposite direction\n    intersections.move(0, 1);\n  crossA = intersections.at(0);\n  crossB = intersections.at(1);\n  return true;\n}\n\n/*! \\internal\n  \n  This function is part of the curve optimization algorithm of \\ref getCurveLines.\n  \n  This method assumes that the \\ref getTraverse test has returned true, so the segment definitely\n  traverses the visible region 5 when going from \\a prevRegion to \\a currentRegion.\n  \n  In certain situations it is not sufficient to merely generate the entry and exit points of the\n  segment into/out of region 5, as \\ref getTraverse provides. It may happen that a single segment, in\n  addition to traversing region 5, skips another region outside of region 5, which makes it\n  necessary to add an optimized corner point there (very similar to the job \\ref\n  getOptimizedCornerPoints does for segments that are completely in outside regions and don't\n  traverse 5).\n  \n  As an example, consider a segment going from region 1 to region 6, traversing the lower left\n  corner of region 5. In this configuration, the segment additionally crosses the border between\n  region 1 and 2 before entering region 5. This makes it necessary to add an additional point in\n  the top left corner, before adding the optimized traverse points. So in this case, the output\n  parameter \\a beforeTraverse will contain the top left corner point, and \\a afterTraverse will be\n  empty.\n  \n  In some cases, such as when going from region 1 to 9, it may even be necessary to add additional\n  corner points before and after the traverse. Then both \\a beforeTraverse and \\a afterTraverse\n  return the respective corner points.\n*/\nvoid QCPCurve::getTraverseCornerPoints(int prevRegion, int currentRegion, double keyMin, double valueMax, double keyMax, double valueMin, QVector<QPointF> &beforeTraverse, QVector<QPointF> &afterTraverse) const\n{\n  switch (prevRegion)\n  {\n    case 1:\n    {\n      switch (currentRegion)\n      {\n        case 6: { beforeTraverse << coordsToPixels(keyMin, valueMax); break; }\n        case 9: { beforeTraverse << coordsToPixels(keyMin, valueMax); afterTraverse << coordsToPixels(keyMax, valueMin); break; }\n        case 8: { beforeTraverse << coordsToPixels(keyMin, valueMax); break; }\n      }\n      break;\n    }\n    case 2:\n    {\n      switch (currentRegion)\n      {\n        case 7: { afterTraverse << coordsToPixels(keyMax, valueMax); break; }\n        case 9: { afterTraverse << coordsToPixels(keyMax, valueMin); break; }\n      }\n      break;\n    }\n    case 3:\n    {\n      switch (currentRegion)\n      {\n        case 4: { beforeTraverse << coordsToPixels(keyMin, valueMin); break; }\n        case 7: { beforeTraverse << coordsToPixels(keyMin, valueMin); afterTraverse << coordsToPixels(keyMax, valueMax); break; }\n        case 8: { beforeTraverse << coordsToPixels(keyMin, valueMin); break; }\n      }\n      break;\n    }\n    case 4:\n    {\n      switch (currentRegion)\n      {\n        case 3: { afterTraverse << coordsToPixels(keyMin, valueMin); break; }\n        case 9: { afterTraverse << coordsToPixels(keyMax, valueMin); break; }\n      }\n      break;\n    }\n    case 5: { break; } // shouldn't happen because this method only handles full traverses\n    case 6:\n    {\n      switch (currentRegion)\n      {\n        case 1: { afterTraverse << coordsToPixels(keyMin, valueMax); break; }\n        case 7: { afterTraverse << coordsToPixels(keyMax, valueMax); break; }\n      }\n      break;\n    }\n    case 7:\n    {\n      switch (currentRegion)\n      {\n        case 2: { beforeTraverse << coordsToPixels(keyMax, valueMax); break; }\n        case 3: { beforeTraverse << coordsToPixels(keyMax, valueMax); afterTraverse << coordsToPixels(keyMin, valueMin); break; }\n        case 6: { beforeTraverse << coordsToPixels(keyMax, valueMax); break; }\n      }\n      break;\n    }\n    case 8:\n    {\n      switch (currentRegion)\n      {\n        case 1: { afterTraverse << coordsToPixels(keyMin, valueMax); break; }\n        case 3: { afterTraverse << coordsToPixels(keyMin, valueMin); break; }\n      }\n      break;\n    }\n    case 9:\n    {\n      switch (currentRegion)\n      {\n        case 2: { beforeTraverse << coordsToPixels(keyMax, valueMin); break; }\n        case 1: { beforeTraverse << coordsToPixels(keyMax, valueMin); afterTraverse << coordsToPixels(keyMin, valueMax); break; }\n        case 4: { beforeTraverse << coordsToPixels(keyMax, valueMin); break; }\n      }\n      break;\n    }\n  }\n}\n\n/*! \\internal\n  \n  Calculates the (minimum) distance (in pixels) the curve's representation has from the given \\a\n  pixelPoint in pixels. This is used to determine whether the curve was clicked or not, e.g. in\n  \\ref selectTest. The closest data point to \\a pixelPoint is returned in \\a closestData. Note that\n  if the curve has a line representation, the returned distance may be smaller than the distance to\n  the \\a closestData point, since the distance to the curve line is also taken into account.\n  \n  If either the curve has no data or if the line style is \\ref lsNone and the scatter style's shape\n  is \\ref QCPScatterStyle::ssNone (i.e. there is no visual representation of the curve), returns\n  -1.0.\n*/\ndouble QCPCurve::pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer::const_iterator &closestData) const\n{\n  closestData = mDataContainer->constEnd();\n  if (mDataContainer->isEmpty())\n    return -1.0;\n  if (mLineStyle == lsNone && mScatterStyle.isNone())\n    return -1.0;\n  \n  if (mDataContainer->size() == 1)\n  {\n    QPointF dataPoint = coordsToPixels(mDataContainer->constBegin()->key, mDataContainer->constBegin()->value);\n    closestData = mDataContainer->constBegin();\n    return QCPVector2D(dataPoint-pixelPoint).length();\n  }\n  \n  // calculate minimum distances to curve data points and find closestData iterator:\n  double minDistSqr = (std::numeric_limits<double>::max)();\n  // iterate over found data points and then choose the one with the shortest distance to pos:\n  QCPCurveDataContainer::const_iterator begin = mDataContainer->constBegin();\n  QCPCurveDataContainer::const_iterator end = mDataContainer->constEnd();\n  for (QCPCurveDataContainer::const_iterator it=begin; it!=end; ++it)\n  {\n    const double currentDistSqr = QCPVector2D(coordsToPixels(it->key, it->value)-pixelPoint).lengthSquared();\n    if (currentDistSqr < minDistSqr)\n    {\n      minDistSqr = currentDistSqr;\n      closestData = it;\n    }\n  }\n  \n  // calculate distance to line if there is one (if so, will probably be smaller than distance to closest data point):\n  if (mLineStyle != lsNone)\n  {\n    QVector<QPointF> lines;\n    getCurveLines(&lines, QCPDataRange(0, dataCount()), mParentPlot->selectionTolerance()*1.2); // optimized lines outside axis rect shouldn't respond to clicks at the edge, so use 1.2*tolerance as pen width\n    for (int i=0; i<lines.size()-1; ++i)\n    {\n      double currentDistSqr = QCPVector2D(pixelPoint).distanceSquaredToLine(lines.at(i), lines.at(i+1));\n      if (currentDistSqr < minDistSqr)\n        minDistSqr = currentDistSqr;\n    }\n  }\n  \n  return qSqrt(minDistSqr);\n}\n/* end of 'src/plottables/plottable-curve.cpp' */\n\n\n/* including file 'src/plottables/plottable-bars.cpp' */\n/* modified 2021-03-29T02:30:44, size 43907           */\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPBarsGroup\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPBarsGroup\n  \\brief Groups multiple QCPBars together so they appear side by side\n  \n  \\image html QCPBarsGroup.png\n  \n  When showing multiple QCPBars in one plot which have bars at identical keys, it may be desirable\n  to have them appearing next to each other at each key. This is what adding the respective QCPBars\n  plottables to a QCPBarsGroup achieves. (An alternative approach is to stack them on top of each\n  other, see \\ref QCPBars::moveAbove.)\n  \n  \\section qcpbarsgroup-usage Usage\n  \n  To add a QCPBars plottable to the group, create a new group and then add the respective bars\n  intances:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpbarsgroup-creation\n  Alternatively to appending to the group like shown above, you can also set the group on the\n  QCPBars plottable via \\ref QCPBars::setBarsGroup.\n  \n  The spacing between the bars can be configured via \\ref setSpacingType and \\ref setSpacing. The\n  bars in this group appear in the plot in the order they were appended. To insert a bars plottable\n  at a certain index position, or to reposition a bars plottable which is already in the group, use\n  \\ref insert.\n  \n  To remove specific bars from the group, use either \\ref remove or call \\ref\n  QCPBars::setBarsGroup \"QCPBars::setBarsGroup(0)\" on the respective bars plottable.\n  \n  To clear the entire group, call \\ref clear, or simply delete the group.\n  \n  \\section qcpbarsgroup-example Example\n  \n  The image above is generated with the following code:\n  \\snippet documentation/doc-image-generator/mainwindow.cpp qcpbarsgroup-example\n*/\n\n/* start of documentation of inline functions */\n\n/*! \\fn QList<QCPBars*> QCPBarsGroup::bars() const\n  \n  Returns all bars currently in this group.\n  \n  \\see bars(int index)\n*/\n\n/*! \\fn int QCPBarsGroup::size() const\n  \n  Returns the number of QCPBars plottables that are part of this group.\n  \n*/\n\n/*! \\fn bool QCPBarsGroup::isEmpty() const\n  \n  Returns whether this bars group is empty.\n  \n  \\see size\n*/\n\n/*! \\fn bool QCPBarsGroup::contains(QCPBars *bars)\n  \n  Returns whether the specified \\a bars plottable is part of this group.\n  \n*/\n\n/* end of documentation of inline functions */\n\n/*!\n  Constructs a new bars group for the specified QCustomPlot instance.\n*/\nQCPBarsGroup::QCPBarsGroup(QCustomPlot *parentPlot) :\n  QObject(parentPlot),\n  mParentPlot(parentPlot),\n  mSpacingType(stAbsolute),\n  mSpacing(4)\n{\n}\n\nQCPBarsGroup::~QCPBarsGroup()\n{\n  clear();\n}\n\n/*!\n  Sets how the spacing between adjacent bars is interpreted. See \\ref SpacingType.\n  \n  The actual spacing can then be specified with \\ref setSpacing.\n\n  \\see setSpacing\n*/\nvoid QCPBarsGroup::setSpacingType(SpacingType spacingType)\n{\n  mSpacingType = spacingType;\n}\n\n/*!\n  Sets the spacing between adjacent bars. What the number passed as \\a spacing actually means, is\n  defined by the current \\ref SpacingType, which can be set with \\ref setSpacingType.\n\n  \\see setSpacingType\n*/\nvoid QCPBarsGroup::setSpacing(double spacing)\n{\n  mSpacing = spacing;\n}\n\n/*!\n  Returns the QCPBars instance with the specified \\a index in this group. If no such QCPBars\n  exists, returns \\c nullptr.\n\n  \\see bars(), size\n*/\nQCPBars *QCPBarsGroup::bars(int index) const\n{\n  if (index >= 0 && index < mBars.size())\n  {\n    return mBars.at(index);\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"index out of bounds:\" << index;\n    return nullptr;\n  }\n}\n\n/*!\n  Removes all QCPBars plottables from this group.\n\n  \\see isEmpty\n*/\nvoid QCPBarsGroup::clear()\n{\n  const QList<QCPBars*> oldBars = mBars;\n  foreach (QCPBars *bars, oldBars)\n    bars->setBarsGroup(nullptr); // removes itself from mBars via removeBars\n}\n\n/*!\n  Adds the specified \\a bars plottable to this group. Alternatively, you can also use \\ref\n  QCPBars::setBarsGroup on the \\a bars instance.\n\n  \\see insert, remove\n*/\nvoid QCPBarsGroup::append(QCPBars *bars)\n{\n  if (!bars)\n  {\n    qDebug() << Q_FUNC_INFO << \"bars is 0\";\n    return;\n  }\n    \n  if (!mBars.contains(bars))\n    bars->setBarsGroup(this);\n  else\n    qDebug() << Q_FUNC_INFO << \"bars plottable is already in this bars group:\" << reinterpret_cast<quintptr>(bars);\n}\n\n/*!\n  Inserts the specified \\a bars plottable into this group at the specified index position \\a i.\n  This gives you full control over the ordering of the bars.\n  \n  \\a bars may already be part of this group. In that case, \\a bars is just moved to the new index\n  position.\n\n  \\see append, remove\n*/\nvoid QCPBarsGroup::insert(int i, QCPBars *bars)\n{\n  if (!bars)\n  {\n    qDebug() << Q_FUNC_INFO << \"bars is 0\";\n    return;\n  }\n  \n  // first append to bars list normally:\n  if (!mBars.contains(bars))\n    bars->setBarsGroup(this);\n  // then move to according position:\n  mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size()-1));\n}\n\n/*!\n  Removes the specified \\a bars plottable from this group.\n  \n  \\see contains, clear\n*/\nvoid QCPBarsGroup::remove(QCPBars *bars)\n{\n  if (!bars)\n  {\n    qDebug() << Q_FUNC_INFO << \"bars is 0\";\n    return;\n  }\n  \n  if (mBars.contains(bars))\n    bars->setBarsGroup(nullptr);\n  else\n    qDebug() << Q_FUNC_INFO << \"bars plottable is not in this bars group:\" << reinterpret_cast<quintptr>(bars);\n}\n\n/*! \\internal\n  \n  Adds the specified \\a bars to the internal mBars list of bars. This method does not change the\n  barsGroup property on \\a bars.\n  \n  \\see unregisterBars\n*/\nvoid QCPBarsGroup::registerBars(QCPBars *bars)\n{\n  if (!mBars.contains(bars))\n    mBars.append(bars);\n}\n\n/*! \\internal\n  \n  Removes the specified \\a bars from the internal mBars list of bars. This method does not change\n  the barsGroup property on \\a bars.\n  \n  \\see registerBars\n*/\nvoid QCPBarsGroup::unregisterBars(QCPBars *bars)\n{\n  mBars.removeOne(bars);\n}\n\n/*! \\internal\n  \n  Returns the pixel offset in the key dimension the specified \\a bars plottable should have at the\n  given key coordinate \\a keyCoord. The offset is relative to the pixel position of the key\n  coordinate \\a keyCoord.\n*/\ndouble QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord)\n{\n  // find list of all base bars in case some mBars are stacked:\n  QList<const QCPBars*> baseBars;\n  foreach (const QCPBars *b, mBars)\n  {\n    while (b->barBelow())\n      b = b->barBelow();\n    if (!baseBars.contains(b))\n      baseBars.append(b);\n  }\n  // find base bar this \"bars\" is stacked on:\n  const QCPBars *thisBase = bars;\n  while (thisBase->barBelow())\n    thisBase = thisBase->barBelow();\n  \n  // determine key pixel offset of this base bars considering all other base bars in this barsgroup:\n  double result = 0;\n  int index = baseBars.indexOf(thisBase);\n  if (index >= 0)\n  {\n    if (baseBars.size() % 2 == 1 && index == (baseBars.size()-1)/2) // is center bar (int division on purpose)\n    {\n      return result;\n    } else\n    {\n      double lowerPixelWidth, upperPixelWidth;\n      int startIndex;\n      int dir = (index <= (baseBars.size()-1)/2) ? -1 : 1; // if bar is to lower keys of center, dir is negative\n      if (baseBars.size() % 2 == 0) // even number of bars\n      {\n        startIndex = baseBars.size()/2 + (dir < 0 ? -1 : 0);\n        result += getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing\n      } else // uneven number of bars\n      {\n        startIndex = (baseBars.size()-1)/2+dir;\n        baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);\n        result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar\n        result += getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing\n      }\n      for (int i = startIndex; i != index; i += dir) // add widths and spacings of bars in between center and our bars\n      {\n        baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);\n        result += qAbs(upperPixelWidth-lowerPixelWidth);\n        result += getPixelSpacing(baseBars.at(i), keyCoord);\n      }\n      // finally half of our bars width:\n      baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);\n      result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5;\n      // correct sign of result depending on orientation and direction of key axis:\n      result *= dir*thisBase->keyAxis()->pixelOrientation();\n    }\n  }\n  return result;\n}\n\n/*! \\internal\n  \n  Returns the spacing in pixels which is between this \\a bars and the following one, both at the\n  key coordinate \\a keyCoord.\n  \n  \\note Typically the returned value doesn't depend on \\a bars or \\a keyCoord. \\a bars is only\n  needed to get access to the key axis transformation and axis rect for the modes \\ref\n  stAxisRectRatio and \\ref stPlotCoords. The \\a keyCoord is only relevant for spacings given in\n  \\ref stPlotCoords on a logarithmic axis.\n*/\ndouble QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord)\n{\n  switch (mSpacingType)\n  {\n    case stAbsolute:\n    {\n      return mSpacing;\n    }\n    case stAxisRectRatio:\n    {\n      if (bars->keyAxis()->orientation() == Qt::Horizontal)\n        return bars->keyAxis()->axisRect()->width()*mSpacing;\n      else\n        return bars->keyAxis()->axisRect()->height()*mSpacing;\n    }\n    case stPlotCoords:\n    {\n      double keyPixel = bars->keyAxis()->coordToPixel(keyCoord);\n      return qAbs(bars->keyAxis()->coordToPixel(keyCoord+mSpacing)-keyPixel);\n    }\n  }\n  return 0;\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPBarsData\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPBarsData\n  \\brief Holds the data of one single data point (one bar) for QCPBars.\n  \n  The stored data is:\n  \\li \\a key: coordinate on the key axis of this bar (this is the \\a mainKey and the \\a sortKey)\n  \\li \\a value: height coordinate on the value axis of this bar (this is the \\a mainValue)\n  \n  The container for storing multiple data points is \\ref QCPBarsDataContainer. It is a typedef for\n  \\ref QCPDataContainer with \\ref QCPBarsData as the DataType template parameter. See the\n  documentation there for an explanation regarding the data type's generic methods.\n  \n  \\see QCPBarsDataContainer\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn double QCPBarsData::sortKey() const\n  \n  Returns the \\a key member of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn static QCPBarsData QCPBarsData::fromSortKey(double sortKey)\n  \n  Returns a data point with the specified \\a sortKey. All other members are set to zero.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn static static bool QCPBarsData::sortKeyIsMainKey()\n  \n  Since the member \\a key is both the data point key coordinate and the data ordering parameter,\n  this method returns true.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn double QCPBarsData::mainKey() const\n  \n  Returns the \\a key member of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn double QCPBarsData::mainValue() const\n  \n  Returns the \\a value member of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn QCPRange QCPBarsData::valueRange() const\n  \n  Returns a QCPRange with both lower and upper boundary set to \\a value of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Constructs a bar data point with key and value set to zero.\n*/\nQCPBarsData::QCPBarsData() :\n  key(0),\n  value(0)\n{\n}\n\n/*!\n  Constructs a bar data point with the specified \\a key and \\a value.\n*/\nQCPBarsData::QCPBarsData(double key, double value) :\n  key(key),\n  value(value)\n{\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPBars\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPBars\n  \\brief A plottable representing a bar chart in a plot.\n\n  \\image html QCPBars.png\n  \n  To plot data, assign it with the \\ref setData or \\ref addData functions.\n  \n  \\section qcpbars-appearance Changing the appearance\n  \n  The appearance of the bars is determined by the pen and the brush (\\ref setPen, \\ref setBrush).\n  The width of the individual bars can be controlled with \\ref setWidthType and \\ref setWidth.\n  \n  Bar charts are stackable. This means, two QCPBars plottables can be placed on top of each other\n  (see \\ref QCPBars::moveAbove). So when two bars are at the same key position, they will appear\n  stacked.\n  \n  If you would like to group multiple QCPBars plottables together so they appear side by side as\n  shown below, use QCPBarsGroup.\n  \n  \\image html QCPBarsGroup.png\n  \n  \\section qcpbars-usage Usage\n  \n  Like all data representing objects in QCustomPlot, the QCPBars is a plottable\n  (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies\n  (QCustomPlot::plottable, QCustomPlot::removePlottable, etc.)\n  \n  Usually, you first create an instance:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-1\n  which registers it with the QCustomPlot instance of the passed axes. Note that this QCustomPlot instance takes\n  ownership of the plottable, so do not delete it manually but use QCustomPlot::removePlottable() instead.\n  The newly created plottable can be modified, e.g.:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-2\n*/\n\n/* start of documentation of inline functions */\n\n/*! \\fn QSharedPointer<QCPBarsDataContainer> QCPBars::data() const\n  \n  Returns a shared pointer to the internal data storage of type \\ref QCPBarsDataContainer. You may\n  use it to directly manipulate the data, which may be more convenient and faster than using the\n  regular \\ref setData or \\ref addData methods.\n*/\n\n/*! \\fn QCPBars *QCPBars::barBelow() const\n  Returns the bars plottable that is directly below this bars plottable.\n  If there is no such plottable, returns \\c nullptr.\n  \n  \\see barAbove, moveBelow, moveAbove\n*/\n\n/*! \\fn QCPBars *QCPBars::barAbove() const\n  Returns the bars plottable that is directly above this bars plottable.\n  If there is no such plottable, returns \\c nullptr.\n  \n  \\see barBelow, moveBelow, moveAbove\n*/\n\n/* end of documentation of inline functions */\n\n/*!\n  Constructs a bar chart which uses \\a keyAxis as its key axis (\"x\") and \\a valueAxis as its value\n  axis (\"y\"). \\a keyAxis and \\a valueAxis must reside in the same QCustomPlot instance and not have\n  the same orientation. If either of these restrictions is violated, a corresponding message is\n  printed to the debug output (qDebug), the construction is not aborted, though.\n  \n  The created QCPBars is automatically registered with the QCustomPlot instance inferred from \\a\n  keyAxis. This QCustomPlot instance takes ownership of the QCPBars, so do not delete it manually\n  but use QCustomPlot::removePlottable() instead.\n*/\nQCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :\n  QCPAbstractPlottable1D<QCPBarsData>(keyAxis, valueAxis),\n  mWidth(0.75),\n  mWidthType(wtPlotCoords),\n  mBarsGroup(nullptr),\n  mBaseValue(0),\n  mStackingGap(1)\n{\n  // modify inherited properties from abstract plottable:\n  mPen.setColor(Qt::blue);\n  mPen.setStyle(Qt::SolidLine);\n  mBrush.setColor(QColor(40, 50, 255, 30));\n  mBrush.setStyle(Qt::SolidPattern);\n  mSelectionDecorator->setBrush(QBrush(QColor(160, 160, 255)));\n}\n\nQCPBars::~QCPBars()\n{\n  setBarsGroup(nullptr);\n  if (mBarBelow || mBarAbove)\n    connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking\n}\n\n/*! \\overload\n  \n  Replaces the current data container with the provided \\a data container.\n  \n  Since a QSharedPointer is used, multiple QCPBars may share the same data container safely.\n  Modifying the data in the container will then affect all bars that share the container. Sharing\n  can be achieved by simply exchanging the data containers wrapped in shared pointers:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-datasharing-1\n  \n  If you do not wish to share containers, but create a copy from an existing container, rather use\n  the \\ref QCPDataContainer<DataType>::set method on the bar's data container directly:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-datasharing-2\n  \n  \\see addData\n*/\nvoid QCPBars::setData(QSharedPointer<QCPBarsDataContainer> data)\n{\n  mDataContainer = data;\n}\n\n/*! \\overload\n  \n  Replaces the current data with the provided points in \\a keys and \\a values. The provided\n  vectors should have equal length. Else, the number of added points will be the size of the\n  smallest vector.\n  \n  If you can guarantee that the passed data points are sorted by \\a keys in ascending order, you\n  can set \\a alreadySorted to true, to improve performance by saving a sorting run.\n  \n  \\see addData\n*/\nvoid QCPBars::setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)\n{\n  mDataContainer->clear();\n  addData(keys, values, alreadySorted);\n}\n\n/*!\n  Sets the width of the bars.\n\n  How the number passed as \\a width is interpreted (e.g. screen pixels, plot coordinates,...),\n  depends on the currently set width type, see \\ref setWidthType and \\ref WidthType.\n*/\nvoid QCPBars::setWidth(double width)\n{\n  mWidth = width;\n}\n\n/*!\n  Sets how the width of the bars is defined. See the documentation of \\ref WidthType for an\n  explanation of the possible values for \\a widthType.\n  \n  The default value is \\ref wtPlotCoords.\n  \n  \\see setWidth\n*/\nvoid QCPBars::setWidthType(QCPBars::WidthType widthType)\n{\n  mWidthType = widthType;\n}\n\n/*!\n  Sets to which QCPBarsGroup this QCPBars instance belongs to. Alternatively, you can also use \\ref\n  QCPBarsGroup::append.\n  \n  To remove this QCPBars from any group, set \\a barsGroup to \\c nullptr.\n*/\nvoid QCPBars::setBarsGroup(QCPBarsGroup *barsGroup)\n{\n  // deregister at old group:\n  if (mBarsGroup)\n    mBarsGroup->unregisterBars(this);\n  mBarsGroup = barsGroup;\n  // register at new group:\n  if (mBarsGroup)\n    mBarsGroup->registerBars(this);\n}\n\n/*!\n  Sets the base value of this bars plottable.\n\n  The base value defines where on the value coordinate the bars start. How far the bars extend from\n  the base value is given by their individual value data. For example, if the base value is set to\n  1, a bar with data value 2 will have its lowest point at value coordinate 1 and highest point at\n  3.\n  \n  For stacked bars, only the base value of the bottom-most QCPBars has meaning.\n  \n  The default base value is 0.\n*/\nvoid QCPBars::setBaseValue(double baseValue)\n{\n  mBaseValue = baseValue;\n}\n\n/*!\n  If this bars plottable is stacked on top of another bars plottable (\\ref moveAbove), this method\n  allows specifying a distance in \\a pixels, by which the drawn bar rectangles will be separated by\n  the bars below it.\n*/\nvoid QCPBars::setStackingGap(double pixels)\n{\n  mStackingGap = pixels;\n}\n\n/*! \\overload\n  \n  Adds the provided points in \\a keys and \\a values to the current data. The provided vectors\n  should have equal length. Else, the number of added points will be the size of the smallest\n  vector.\n  \n  If you can guarantee that the passed data points are sorted by \\a keys in ascending order, you\n  can set \\a alreadySorted to true, to improve performance by saving a sorting run.\n  \n  Alternatively, you can also access and modify the data directly via the \\ref data method, which\n  returns a pointer to the internal data container.\n*/\nvoid QCPBars::addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)\n{\n  if (keys.size() != values.size())\n    qDebug() << Q_FUNC_INFO << \"keys and values have different sizes:\" << keys.size() << values.size();\n  const int n = qMin(keys.size(), values.size());\n  QVector<QCPBarsData> tempData(n);\n  QVector<QCPBarsData>::iterator it = tempData.begin();\n  const QVector<QCPBarsData>::iterator itEnd = tempData.end();\n  int i = 0;\n  while (it != itEnd)\n  {\n    it->key = keys[i];\n    it->value = values[i];\n    ++it;\n    ++i;\n  }\n  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write\n}\n\n/*! \\overload\n  Adds the provided data point as \\a key and \\a value to the current data.\n  \n  Alternatively, you can also access and modify the data directly via the \\ref data method, which\n  returns a pointer to the internal data container.\n*/\nvoid QCPBars::addData(double key, double value)\n{\n  mDataContainer->add(QCPBarsData(key, value));\n}\n\n/*!\n  Moves this bars plottable below \\a bars. In other words, the bars of this plottable will appear\n  below the bars of \\a bars. The move target \\a bars must use the same key and value axis as this\n  plottable.\n  \n  Inserting into and removing from existing bar stacking is handled gracefully. If \\a bars already\n  has a bars object below itself, this bars object is inserted between the two. If this bars object\n  is already between two other bars, the two other bars will be stacked on top of each other after\n  the operation.\n  \n  To remove this bars plottable from any stacking, set \\a bars to \\c nullptr.\n  \n  \\see moveBelow, barAbove, barBelow\n*/\nvoid QCPBars::moveBelow(QCPBars *bars)\n{\n  if (bars == this) return;\n  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))\n  {\n    qDebug() << Q_FUNC_INFO << \"passed QCPBars* doesn't have same key and value axis as this QCPBars\";\n    return;\n  }\n  // remove from stacking:\n  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0\n  // if new bar given, insert this bar below it:\n  if (bars)\n  {\n    if (bars->mBarBelow)\n      connectBars(bars->mBarBelow.data(), this);\n    connectBars(this, bars);\n  }\n}\n\n/*!\n  Moves this bars plottable above \\a bars. In other words, the bars of this plottable will appear\n  above the bars of \\a bars. The move target \\a bars must use the same key and value axis as this\n  plottable.\n  \n  Inserting into and removing from existing bar stacking is handled gracefully. If \\a bars already\n  has a bars object above itself, this bars object is inserted between the two. If this bars object\n  is already between two other bars, the two other bars will be stacked on top of each other after\n  the operation.\n  \n  To remove this bars plottable from any stacking, set \\a bars to \\c nullptr.\n  \n  \\see moveBelow, barBelow, barAbove\n*/\nvoid QCPBars::moveAbove(QCPBars *bars)\n{\n  if (bars == this) return;\n  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))\n  {\n    qDebug() << Q_FUNC_INFO << \"passed QCPBars* doesn't have same key and value axis as this QCPBars\";\n    return;\n  }\n  // remove from stacking:\n  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0\n  // if new bar given, insert this bar above it:\n  if (bars)\n  {\n    if (bars->mBarAbove)\n      connectBars(this, bars->mBarAbove.data());\n    connectBars(bars, this);\n  }\n}\n\n/*!\n  \\copydoc QCPPlottableInterface1D::selectTestRect\n*/\nQCPDataSelection QCPBars::selectTestRect(const QRectF &rect, bool onlySelectable) const\n{\n  QCPDataSelection result;\n  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())\n    return result;\n  if (!mKeyAxis || !mValueAxis)\n    return result;\n  \n  QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;\n  getVisibleDataBounds(visibleBegin, visibleEnd);\n  \n  for (QCPBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)\n  {\n    if (rect.intersects(getBarRect(it->key, it->value)))\n      result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false);\n  }\n  result.simplify();\n  return result;\n}\n\n/*!\n  Implements a selectTest specific to this plottable's point geometry.\n\n  If \\a details is not 0, it will be set to a \\ref QCPDataSelection, describing the closest data\n  point to \\a pos.\n  \n  \\seebaseclassmethod \\ref QCPAbstractPlottable::selectTest\n*/\ndouble QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(details)\n  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())\n    return -1;\n  if (!mKeyAxis || !mValueAxis)\n    return -1;\n  \n  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))\n  {\n    // get visible data range:\n    QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;\n    getVisibleDataBounds(visibleBegin, visibleEnd);\n    for (QCPBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)\n    {\n      if (getBarRect(it->key, it->value).contains(pos))\n      {\n        if (details)\n        {\n          int pointIndex = int(it-mDataContainer->constBegin());\n          details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));\n        }\n        return mParentPlot->selectionTolerance()*0.99;\n      }\n    }\n  }\n  return -1;\n}\n\n/* inherits documentation from base class */\nQCPRange QCPBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const\n{\n  /* Note: If this QCPBars uses absolute pixels as width (or is in a QCPBarsGroup with spacing in\n  absolute pixels), using this method to adapt the key axis range to fit the bars into the\n  currently visible axis range will not work perfectly. Because in the moment the axis range is\n  changed to the new range, the fixed pixel widths/spacings will represent different coordinate\n  spans than before, which in turn would require a different key range to perfectly fit, and so on.\n  The only solution would be to iteratively approach the perfect fitting axis range, but the\n  mismatch isn't large enough in most applications, to warrant this here. If a user does need a\n  better fit, he should call the corresponding axis rescale multiple times in a row.\n  */\n  QCPRange range;\n  range = mDataContainer->keyRange(foundRange, inSignDomain);\n  \n  // determine exact range of bars by including bar width and barsgroup offset:\n  if (foundRange && mKeyAxis)\n  {\n    double lowerPixelWidth, upperPixelWidth, keyPixel;\n    // lower range bound:\n    getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth);\n    keyPixel = mKeyAxis.data()->coordToPixel(range.lower) + lowerPixelWidth;\n    if (mBarsGroup)\n      keyPixel += mBarsGroup->keyPixelOffset(this, range.lower);\n    const double lowerCorrected = mKeyAxis.data()->pixelToCoord(keyPixel);\n    if (!qIsNaN(lowerCorrected) && qIsFinite(lowerCorrected) && range.lower > lowerCorrected)\n      range.lower = lowerCorrected;\n    // upper range bound:\n    getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth);\n    keyPixel = mKeyAxis.data()->coordToPixel(range.upper) + upperPixelWidth;\n    if (mBarsGroup)\n      keyPixel += mBarsGroup->keyPixelOffset(this, range.upper);\n    const double upperCorrected = mKeyAxis.data()->pixelToCoord(keyPixel);\n    if (!qIsNaN(upperCorrected) && qIsFinite(upperCorrected) && range.upper < upperCorrected)\n      range.upper = upperCorrected;\n  }\n  return range;\n}\n\n/* inherits documentation from base class */\nQCPRange QCPBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const\n{\n  // Note: can't simply use mDataContainer->valueRange here because we need to\n  // take into account bar base value and possible stacking of multiple bars\n  QCPRange range;\n  range.lower = mBaseValue;\n  range.upper = mBaseValue;\n  bool haveLower = true; // set to true, because baseValue should always be visible in bar charts\n  bool haveUpper = true; // set to true, because baseValue should always be visible in bar charts\n  QCPBarsDataContainer::const_iterator itBegin = mDataContainer->constBegin();\n  QCPBarsDataContainer::const_iterator itEnd = mDataContainer->constEnd();\n  if (inKeyRange != QCPRange())\n  {\n    itBegin = mDataContainer->findBegin(inKeyRange.lower, false);\n    itEnd = mDataContainer->findEnd(inKeyRange.upper, false);\n  }\n  for (QCPBarsDataContainer::const_iterator it = itBegin; it != itEnd; ++it)\n  {\n    const double current = it->value + getStackedBaseValue(it->key, it->value >= 0);\n    if (qIsNaN(current)) continue;\n    if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))\n    {\n      if (current < range.lower || !haveLower)\n      {\n        range.lower = current;\n        haveLower = true;\n      }\n      if (current > range.upper || !haveUpper)\n      {\n        range.upper = current;\n        haveUpper = true;\n      }\n    }\n  }\n  \n  foundRange = true; // return true because bar charts always have the 0-line visible\n  return range;\n}\n\n/* inherits documentation from base class */\nQPointF QCPBars::dataPixelPosition(int index) const\n{\n  if (index >= 0 && index < mDataContainer->size())\n  {\n    QCPAxis *keyAxis = mKeyAxis.data();\n    QCPAxis *valueAxis = mValueAxis.data();\n    if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return {}; }\n    \n    const QCPDataContainer<QCPBarsData>::const_iterator it = mDataContainer->constBegin()+index;\n    const double valuePixel = valueAxis->coordToPixel(getStackedBaseValue(it->key, it->value >= 0) + it->value);\n    const double keyPixel = keyAxis->coordToPixel(it->key) + (mBarsGroup ? mBarsGroup->keyPixelOffset(this, it->key) : 0);\n    if (keyAxis->orientation() == Qt::Horizontal)\n      return {keyPixel, valuePixel};\n    else\n      return {valuePixel, keyPixel};\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"Index out of bounds\" << index;\n    return {};\n  }\n}\n\n/* inherits documentation from base class */\nvoid QCPBars::draw(QCPPainter *painter)\n{\n  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n  if (mDataContainer->isEmpty()) return;\n  \n  QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;\n  getVisibleDataBounds(visibleBegin, visibleEnd);\n  \n  // loop over and draw segments of unselected/selected data:\n  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;\n  getDataSegments(selectedSegments, unselectedSegments);\n  allSegments << unselectedSegments << selectedSegments;\n  for (int i=0; i<allSegments.size(); ++i)\n  {\n    bool isSelectedSegment = i >= unselectedSegments.size();\n    QCPBarsDataContainer::const_iterator begin = visibleBegin;\n    QCPBarsDataContainer::const_iterator end = visibleEnd;\n    mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));\n    if (begin == end)\n      continue;\n    \n    for (QCPBarsDataContainer::const_iterator it=begin; it!=end; ++it)\n    {\n      // check data validity if flag set:\n#ifdef QCUSTOMPLOT_CHECK_DATA\n      if (QCP::isInvalidData(it->key, it->value))\n        qDebug() << Q_FUNC_INFO << \"Data point at\" << it->key << \"of drawn range invalid.\" << \"Plottable name:\" << name();\n#endif\n      // draw bar:\n      if (isSelectedSegment && mSelectionDecorator)\n      {\n        mSelectionDecorator->applyBrush(painter);\n        mSelectionDecorator->applyPen(painter);\n      } else\n      {\n        painter->setBrush(mBrush);\n        painter->setPen(mPen);\n      }\n      applyDefaultAntialiasingHint(painter);\n      painter->drawPolygon(getBarRect(it->key, it->value));\n    }\n  }\n  \n  // draw other selection decoration that isn't just line/scatter pens and brushes:\n  if (mSelectionDecorator)\n    mSelectionDecorator->drawDecoration(painter, selection());\n}\n\n/* inherits documentation from base class */\nvoid QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const\n{\n  // draw filled rect:\n  applyDefaultAntialiasingHint(painter);\n  painter->setBrush(mBrush);\n  painter->setPen(mPen);\n  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);\n  r.moveCenter(rect.center());\n  painter->drawRect(r);\n}\n\n/*!  \\internal\n  \n  called by \\ref draw to determine which data (key) range is visible at the current key axis range\n  setting, so only that needs to be processed. It also takes into account the bar width.\n  \n  \\a begin returns an iterator to the lowest data point that needs to be taken into account when\n  plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \\a\n  lower may still be just outside the visible range.\n  \n  \\a end returns an iterator one higher than the highest visible data point. Same as before, \\a end\n  may also lie just outside of the visible range.\n  \n  if the plottable contains no data, both \\a begin and \\a end point to constEnd.\n*/\nvoid QCPBars::getVisibleDataBounds(QCPBarsDataContainer::const_iterator &begin, QCPBarsDataContainer::const_iterator &end) const\n{\n  if (!mKeyAxis)\n  {\n    qDebug() << Q_FUNC_INFO << \"invalid key axis\";\n    begin = mDataContainer->constEnd();\n    end = mDataContainer->constEnd();\n    return;\n  }\n  if (mDataContainer->isEmpty())\n  {\n    begin = mDataContainer->constEnd();\n    end = mDataContainer->constEnd();\n    return;\n  }\n  \n  // get visible data range as QMap iterators\n  begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower);\n  end = mDataContainer->findEnd(mKeyAxis.data()->range().upper);\n  double lowerPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().lower);\n  double upperPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().upper);\n  bool isVisible = false;\n  // walk left from begin to find lower bar that actually is completely outside visible pixel range:\n  QCPBarsDataContainer::const_iterator it = begin;\n  while (it != mDataContainer->constBegin())\n  {\n    --it;\n    const QRectF barRect = getBarRect(it->key, it->value);\n    if (mKeyAxis.data()->orientation() == Qt::Horizontal)\n      isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.right() >= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.left() <= lowerPixelBound));\n    else // keyaxis is vertical\n      isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.top() <= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.bottom() >= lowerPixelBound));\n    if (isVisible)\n      begin = it;\n    else\n      break;\n  }\n  // walk right from ubound to find upper bar that actually is completely outside visible pixel range:\n  it = end;\n  while (it != mDataContainer->constEnd())\n  {\n    const QRectF barRect = getBarRect(it->key, it->value);\n    if (mKeyAxis.data()->orientation() == Qt::Horizontal)\n      isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.left() <= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.right() >= upperPixelBound));\n    else // keyaxis is vertical\n      isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.bottom() >= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.top() <= upperPixelBound));\n    if (isVisible)\n      end = it+1;\n    else\n      break;\n    ++it;\n  }\n}\n\n/*! \\internal\n  \n  Returns the rect in pixel coordinates of a single bar with the specified \\a key and \\a value. The\n  rect is shifted according to the bar stacking (see \\ref moveAbove) and base value (see \\ref\n  setBaseValue), and to have non-overlapping border lines with the bars stacked below.\n*/\nQRectF QCPBars::getBarRect(double key, double value) const\n{\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return {}; }\n  \n  double lowerPixelWidth, upperPixelWidth;\n  getPixelWidth(key, lowerPixelWidth, upperPixelWidth);\n  double base = getStackedBaseValue(key, value >= 0);\n  double basePixel = valueAxis->coordToPixel(base);\n  double valuePixel = valueAxis->coordToPixel(base+value);\n  double keyPixel = keyAxis->coordToPixel(key);\n  if (mBarsGroup)\n    keyPixel += mBarsGroup->keyPixelOffset(this, key);\n  double bottomOffset = (mBarBelow && mPen != Qt::NoPen ? 1 : 0)*(mPen.isCosmetic() ? 1 : mPen.widthF());\n  bottomOffset += mBarBelow ? mStackingGap : 0;\n  bottomOffset *= (value<0 ? -1 : 1)*valueAxis->pixelOrientation();\n  if (qAbs(valuePixel-basePixel) <= qAbs(bottomOffset))\n    bottomOffset = valuePixel-basePixel;\n  if (keyAxis->orientation() == Qt::Horizontal)\n  {\n    return QRectF(QPointF(keyPixel+lowerPixelWidth, valuePixel), QPointF(keyPixel+upperPixelWidth, basePixel+bottomOffset)).normalized();\n  } else\n  {\n    return QRectF(QPointF(basePixel+bottomOffset, keyPixel+lowerPixelWidth), QPointF(valuePixel, keyPixel+upperPixelWidth)).normalized();\n  }\n}\n\n/*! \\internal\n  \n  This function is used to determine the width of the bar at coordinate \\a key, according to the\n  specified width (\\ref setWidth) and width type (\\ref setWidthType).\n  \n  The output parameters \\a lower and \\a upper return the number of pixels the bar extends to lower\n  and higher keys, relative to the \\a key coordinate (so with a non-reversed horizontal axis, \\a\n  lower is negative and \\a upper positive).\n*/\nvoid QCPBars::getPixelWidth(double key, double &lower, double &upper) const\n{\n  lower = 0;\n  upper = 0;\n  switch (mWidthType)\n  {\n    case wtAbsolute:\n    {\n      upper = mWidth*0.5*mKeyAxis.data()->pixelOrientation();\n      lower = -upper;\n      break;\n    }\n    case wtAxisRectRatio:\n    {\n      if (mKeyAxis && mKeyAxis.data()->axisRect())\n      {\n        if (mKeyAxis.data()->orientation() == Qt::Horizontal)\n          upper = mKeyAxis.data()->axisRect()->width()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();\n        else\n          upper = mKeyAxis.data()->axisRect()->height()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();\n        lower = -upper;\n      } else\n        qDebug() << Q_FUNC_INFO << \"No key axis or axis rect defined\";\n      break;\n    }\n    case wtPlotCoords:\n    {\n      if (mKeyAxis)\n      {\n        double keyPixel = mKeyAxis.data()->coordToPixel(key);\n        upper = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel;\n        lower = mKeyAxis.data()->coordToPixel(key-mWidth*0.5)-keyPixel;\n        // no need to qSwap(lower, higher) when range reversed, because higher/lower are gained by\n        // coordinate transform which includes range direction\n      } else\n        qDebug() << Q_FUNC_INFO << \"No key axis defined\";\n      break;\n    }\n  }\n}\n\n/*! \\internal\n  \n  This function is called to find at which value to start drawing the base of a bar at \\a key, when\n  it is stacked on top of another QCPBars (e.g. with \\ref moveAbove).\n  \n  positive and negative bars are separated per stack (positive are stacked above baseValue upwards,\n  negative are stacked below baseValue downwards). This can be indicated with \\a positive. So if the\n  bar for which we need the base value is negative, set \\a positive to false.\n*/\ndouble QCPBars::getStackedBaseValue(double key, bool positive) const\n{\n  if (mBarBelow)\n  {\n    double max = 0; // don't initialize with mBaseValue here because only base value of bottom-most bar has meaning in a bar stack\n    // find bars of mBarBelow that are approximately at key and find largest one:\n    double epsilon = qAbs(key)*(sizeof(key)==4 ? 1e-6 : 1e-14); // should be safe even when changed to use float at some point\n    if (key == 0)\n      epsilon = (sizeof(key)==4 ? 1e-6 : 1e-14);\n    QCPBarsDataContainer::const_iterator it = mBarBelow.data()->mDataContainer->findBegin(key-epsilon);\n    QCPBarsDataContainer::const_iterator itEnd = mBarBelow.data()->mDataContainer->findEnd(key+epsilon);\n    while (it != itEnd)\n    {\n      if (it->key > key-epsilon && it->key < key+epsilon)\n      {\n        if ((positive && it->value > max) ||\n            (!positive && it->value < max))\n          max = it->value;\n      }\n      ++it;\n    }\n    // recurse down the bar-stack to find the total height:\n    return max + mBarBelow.data()->getStackedBaseValue(key, positive);\n  } else\n    return mBaseValue;\n}\n\n/*! \\internal\n\n  Connects \\a below and \\a above to each other via their mBarAbove/mBarBelow properties. The bar(s)\n  currently above lower and below upper will become disconnected to lower/upper.\n  \n  If lower is zero, upper will be disconnected at the bottom.\n  If upper is zero, lower will be disconnected at the top.\n*/\nvoid QCPBars::connectBars(QCPBars *lower, QCPBars *upper)\n{\n  if (!lower && !upper) return;\n  \n  if (!lower) // disconnect upper at bottom\n  {\n    // disconnect old bar below upper:\n    if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)\n      upper->mBarBelow.data()->mBarAbove = nullptr;\n    upper->mBarBelow = nullptr;\n  } else if (!upper) // disconnect lower at top\n  {\n    // disconnect old bar above lower:\n    if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)\n      lower->mBarAbove.data()->mBarBelow = nullptr;\n    lower->mBarAbove = nullptr;\n  } else // connect lower and upper\n  {\n    // disconnect old bar above lower:\n    if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)\n      lower->mBarAbove.data()->mBarBelow = nullptr;\n    // disconnect old bar below upper:\n    if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)\n      upper->mBarBelow.data()->mBarAbove = nullptr;\n    lower->mBarAbove = upper;\n    upper->mBarBelow = lower;\n  }\n}\n/* end of 'src/plottables/plottable-bars.cpp' */\n\n\n/* including file 'src/plottables/plottable-statisticalbox.cpp' */\n/* modified 2021-03-29T02:30:44, size 28951                     */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPStatisticalBoxData\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPStatisticalBoxData\n  \\brief Holds the data of one single data point for QCPStatisticalBox.\n  \n  The stored data is:\n  \n  \\li \\a key: coordinate on the key axis of this data point (this is the \\a mainKey and the \\a sortKey)\n  \n  \\li \\a minimum: the position of the lower whisker, typically the minimum measurement of the\n  sample that's not considered an outlier.\n  \n  \\li \\a lowerQuartile: the lower end of the box. The lower and the upper quartiles are the two\n  statistical quartiles around the median of the sample, they should contain 50% of the sample\n  data.\n  \n  \\li \\a median: the value of the median mark inside the quartile box. The median separates the\n  sample data in half (50% of the sample data is below/above the median). (This is the \\a mainValue)\n  \n  \\li \\a upperQuartile: the upper end of the box. The lower and the upper quartiles are the two\n  statistical quartiles around the median of the sample, they should contain 50% of the sample\n  data.\n  \n  \\li \\a maximum: the position of the upper whisker, typically the maximum measurement of the\n  sample that's not considered an outlier.\n  \n  \\li \\a outliers: a QVector of outlier values that will be drawn as scatter points at the \\a key\n  coordinate of this data point (see \\ref QCPStatisticalBox::setOutlierStyle)\n  \n  The container for storing multiple data points is \\ref QCPStatisticalBoxDataContainer. It is a\n  typedef for \\ref QCPDataContainer with \\ref QCPStatisticalBoxData as the DataType template\n  parameter. See the documentation there for an explanation regarding the data type's generic\n  methods.\n  \n  \\see QCPStatisticalBoxDataContainer\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn double QCPStatisticalBoxData::sortKey() const\n  \n  Returns the \\a key member of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn static QCPStatisticalBoxData QCPStatisticalBoxData::fromSortKey(double sortKey)\n  \n  Returns a data point with the specified \\a sortKey. All other members are set to zero.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn static static bool QCPStatisticalBoxData::sortKeyIsMainKey()\n  \n  Since the member \\a key is both the data point key coordinate and the data ordering parameter,\n  this method returns true.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn double QCPStatisticalBoxData::mainKey() const\n  \n  Returns the \\a key member of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn double QCPStatisticalBoxData::mainValue() const\n  \n  Returns the \\a median member of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn QCPRange QCPStatisticalBoxData::valueRange() const\n  \n  Returns a QCPRange spanning from the \\a minimum to the \\a maximum member of this statistical box\n  data point, possibly further expanded by outliers.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Constructs a data point with key and all values set to zero.\n*/\nQCPStatisticalBoxData::QCPStatisticalBoxData() :\n  key(0),\n  minimum(0),\n  lowerQuartile(0),\n  median(0),\n  upperQuartile(0),\n  maximum(0)\n{\n}\n\n/*!\n  Constructs a data point with the specified \\a key, \\a minimum, \\a lowerQuartile, \\a median, \\a\n  upperQuartile, \\a maximum and optionally a number of \\a outliers.\n*/\nQCPStatisticalBoxData::QCPStatisticalBoxData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector<double> &outliers) :\n  key(key),\n  minimum(minimum),\n  lowerQuartile(lowerQuartile),\n  median(median),\n  upperQuartile(upperQuartile),\n  maximum(maximum),\n  outliers(outliers)\n{\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPStatisticalBox\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPStatisticalBox\n  \\brief A plottable representing a single statistical box in a plot.\n\n  \\image html QCPStatisticalBox.png\n  \n  To plot data, assign it with the \\ref setData or \\ref addData functions. Alternatively, you can\n  also access and modify the data via the \\ref data method, which returns a pointer to the internal\n  \\ref QCPStatisticalBoxDataContainer.\n  \n  Additionally each data point can itself have a list of outliers, drawn as scatter points at the\n  key coordinate of the respective statistical box data point. They can either be set by using the\n  respective \\ref addData(double,double,double,double,double,double,const QVector<double>&)\n  \"addData\" method or accessing the individual data points through \\ref data, and setting the\n  <tt>QVector<double> outliers</tt> of the data points directly.\n  \n  \\section qcpstatisticalbox-appearance Changing the appearance\n  \n  The appearance of each data point box, ranging from the lower to the upper quartile, is\n  controlled via \\ref setPen and \\ref setBrush. You may change the width of the boxes with \\ref\n  setWidth in plot coordinates.\n\n  Each data point's visual representation also consists of two whiskers. Whiskers are the lines\n  which reach from the upper quartile to the maximum, and from the lower quartile to the minimum.\n  The appearance of the whiskers can be modified with: \\ref setWhiskerPen, \\ref setWhiskerBarPen,\n  \\ref setWhiskerWidth. The whisker width is the width of the bar perpendicular to the whisker at\n  the top (for maximum) and bottom (for minimum). If the whisker pen is changed, make sure to set\n  the \\c capStyle to \\c Qt::FlatCap. Otherwise the backbone line might exceed the whisker bars by a\n  few pixels due to the pen cap being not perfectly flat.\n  \n  The median indicator line inside the box has its own pen, \\ref setMedianPen.\n  \n  The outlier data points are drawn as normal scatter points. Their look can be controlled with\n  \\ref setOutlierStyle\n  \n  \\section qcpstatisticalbox-usage Usage\n  \n  Like all data representing objects in QCustomPlot, the QCPStatisticalBox is a plottable\n  (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies\n  (QCustomPlot::plottable, QCustomPlot::removePlottable, etc.)\n  \n  Usually, you first create an instance:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-creation-1\n  which registers it with the QCustomPlot instance of the passed axes. Note that this QCustomPlot instance takes\n  ownership of the plottable, so do not delete it manually but use QCustomPlot::removePlottable() instead.\n  The newly created plottable can be modified, e.g.:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-creation-2\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn QSharedPointer<QCPStatisticalBoxDataContainer> QCPStatisticalBox::data() const\n  \n  Returns a shared pointer to the internal data storage of type \\ref\n  QCPStatisticalBoxDataContainer. You may use it to directly manipulate the data, which may be more\n  convenient and faster than using the regular \\ref setData or \\ref addData methods.\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Constructs a statistical box which uses \\a keyAxis as its key axis (\"x\") and \\a valueAxis as its\n  value axis (\"y\"). \\a keyAxis and \\a valueAxis must reside in the same QCustomPlot instance and\n  not have the same orientation. If either of these restrictions is violated, a corresponding\n  message is printed to the debug output (qDebug), the construction is not aborted, though.\n  \n  The created QCPStatisticalBox is automatically registered with the QCustomPlot instance inferred\n  from \\a keyAxis. This QCustomPlot instance takes ownership of the QCPStatisticalBox, so do not\n  delete it manually but use QCustomPlot::removePlottable() instead.\n*/\nQCPStatisticalBox::QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis) :\n  QCPAbstractPlottable1D<QCPStatisticalBoxData>(keyAxis, valueAxis),\n  mWidth(0.5),\n  mWhiskerWidth(0.2),\n  mWhiskerPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap),\n  mWhiskerBarPen(Qt::black),\n  mWhiskerAntialiased(false),\n  mMedianPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap),\n  mOutlierStyle(QCPScatterStyle::ssCircle, Qt::blue, 6)\n{\n  setPen(QPen(Qt::black));\n  setBrush(Qt::NoBrush);\n}\n\n/*! \\overload\n  \n  Replaces the current data container with the provided \\a data container.\n  \n  Since a QSharedPointer is used, multiple QCPStatisticalBoxes may share the same data container\n  safely. Modifying the data in the container will then affect all statistical boxes that share the\n  container. Sharing can be achieved by simply exchanging the data containers wrapped in shared\n  pointers:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-datasharing-1\n  \n  If you do not wish to share containers, but create a copy from an existing container, rather use\n  the \\ref QCPDataContainer<DataType>::set method on the statistical box data container directly:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-datasharing-2\n  \n  \\see addData\n*/\nvoid QCPStatisticalBox::setData(QSharedPointer<QCPStatisticalBoxDataContainer> data)\n{\n  mDataContainer = data;\n}\n/*! \\overload\n  \n  Replaces the current data with the provided points in \\a keys, \\a minimum, \\a lowerQuartile, \\a\n  median, \\a upperQuartile and \\a maximum. The provided vectors should have equal length. Else, the\n  number of added points will be the size of the smallest vector.\n  \n  If you can guarantee that the passed data points are sorted by \\a keys in ascending order, you\n  can set \\a alreadySorted to true, to improve performance by saving a sorting run.\n  \n  \\see addData\n*/\nvoid QCPStatisticalBox::setData(const QVector<double> &keys, const QVector<double> &minimum, const QVector<double> &lowerQuartile, const QVector<double> &median, const QVector<double> &upperQuartile, const QVector<double> &maximum, bool alreadySorted)\n{\n  mDataContainer->clear();\n  addData(keys, minimum, lowerQuartile, median, upperQuartile, maximum, alreadySorted);\n}\n\n/*!\n  Sets the width of the boxes in key coordinates.\n  \n  \\see setWhiskerWidth\n*/\nvoid QCPStatisticalBox::setWidth(double width)\n{\n  mWidth = width;\n}\n\n/*!\n  Sets the width of the whiskers in key coordinates.\n  \n  Whiskers are the lines which reach from the upper quartile to the maximum, and from the lower\n  quartile to the minimum.\n  \n  \\see setWidth\n*/\nvoid QCPStatisticalBox::setWhiskerWidth(double width)\n{\n  mWhiskerWidth = width;\n}\n\n/*!\n  Sets the pen used for drawing the whisker backbone.\n  \n  Whiskers are the lines which reach from the upper quartile to the maximum, and from the lower\n  quartile to the minimum.\n  \n  Make sure to set the \\c capStyle of the passed \\a pen to \\c Qt::FlatCap. Otherwise the backbone\n  line might exceed the whisker bars by a few pixels due to the pen cap being not perfectly flat.\n  \n  \\see setWhiskerBarPen\n*/\nvoid QCPStatisticalBox::setWhiskerPen(const QPen &pen)\n{\n  mWhiskerPen = pen;\n}\n\n/*!\n  Sets the pen used for drawing the whisker bars. Those are the lines parallel to the key axis at\n  each end of the whisker backbone.\n  \n  Whiskers are the lines which reach from the upper quartile to the maximum, and from the lower\n  quartile to the minimum.\n  \n  \\see setWhiskerPen\n*/\nvoid QCPStatisticalBox::setWhiskerBarPen(const QPen &pen)\n{\n  mWhiskerBarPen = pen;\n}\n\n/*!\n  Sets whether the statistical boxes whiskers are drawn with antialiasing or not.\n\n  Note that antialiasing settings may be overridden by QCustomPlot::setAntialiasedElements and\n  QCustomPlot::setNotAntialiasedElements.\n*/\nvoid QCPStatisticalBox::setWhiskerAntialiased(bool enabled)\n{\n  mWhiskerAntialiased = enabled;\n}\n\n/*!\n  Sets the pen used for drawing the median indicator line inside the statistical boxes.\n*/\nvoid QCPStatisticalBox::setMedianPen(const QPen &pen)\n{\n  mMedianPen = pen;\n}\n\n/*!\n  Sets the appearance of the outlier data points.\n\n  Outliers can be specified with the method\n  \\ref addData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector<double> &outliers)\n*/\nvoid QCPStatisticalBox::setOutlierStyle(const QCPScatterStyle &style)\n{\n  mOutlierStyle = style;\n}\n\n/*! \\overload\n   \n  Adds the provided points in \\a keys, \\a minimum, \\a lowerQuartile, \\a median, \\a upperQuartile and\n  \\a maximum to the current data. The provided vectors should have equal length. Else, the number\n  of added points will be the size of the smallest vector.\n   \n  If you can guarantee that the passed data points are sorted by \\a keys in ascending order, you\n  can set \\a alreadySorted to true, to improve performance by saving a sorting run.\n   \n  Alternatively, you can also access and modify the data directly via the \\ref data method, which\n  returns a pointer to the internal data container.\n*/\nvoid QCPStatisticalBox::addData(const QVector<double> &keys, const QVector<double> &minimum, const QVector<double> &lowerQuartile, const QVector<double> &median, const QVector<double> &upperQuartile, const QVector<double> &maximum, bool alreadySorted)\n{\n  if (keys.size() != minimum.size() || minimum.size() != lowerQuartile.size() || lowerQuartile.size() != median.size() ||\n      median.size() != upperQuartile.size() || upperQuartile.size() != maximum.size() || maximum.size() != keys.size())\n    qDebug() << Q_FUNC_INFO << \"keys, minimum, lowerQuartile, median, upperQuartile, maximum have different sizes:\"\n             << keys.size() << minimum.size() << lowerQuartile.size() << median.size() << upperQuartile.size() << maximum.size();\n  const int n = qMin(keys.size(), qMin(minimum.size(), qMin(lowerQuartile.size(), qMin(median.size(), qMin(upperQuartile.size(), maximum.size())))));\n  QVector<QCPStatisticalBoxData> tempData(n);\n  QVector<QCPStatisticalBoxData>::iterator it = tempData.begin();\n  const QVector<QCPStatisticalBoxData>::iterator itEnd = tempData.end();\n  int i = 0;\n  while (it != itEnd)\n  {\n    it->key = keys[i];\n    it->minimum = minimum[i];\n    it->lowerQuartile = lowerQuartile[i];\n    it->median = median[i];\n    it->upperQuartile = upperQuartile[i];\n    it->maximum = maximum[i];\n    ++it;\n    ++i;\n  }\n  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write\n}\n\n/*! \\overload\n  \n  Adds the provided data point as \\a key, \\a minimum, \\a lowerQuartile, \\a median, \\a upperQuartile\n  and \\a maximum to the current data.\n  \n  Alternatively, you can also access and modify the data directly via the \\ref data method, which\n  returns a pointer to the internal data container.\n*/\nvoid QCPStatisticalBox::addData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector<double> &outliers)\n{\n  mDataContainer->add(QCPStatisticalBoxData(key, minimum, lowerQuartile, median, upperQuartile, maximum, outliers));\n}\n\n/*!\n  \\copydoc QCPPlottableInterface1D::selectTestRect\n*/\nQCPDataSelection QCPStatisticalBox::selectTestRect(const QRectF &rect, bool onlySelectable) const\n{\n  QCPDataSelection result;\n  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())\n    return result;\n  if (!mKeyAxis || !mValueAxis)\n    return result;\n  \n  QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;\n  getVisibleDataBounds(visibleBegin, visibleEnd);\n  \n  for (QCPStatisticalBoxDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)\n  {\n    if (rect.intersects(getQuartileBox(it)))\n      result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false);\n  }\n  result.simplify();\n  return result;\n}\n\n/*!\n  Implements a selectTest specific to this plottable's point geometry.\n\n  If \\a details is not 0, it will be set to a \\ref QCPDataSelection, describing the closest data\n  point to \\a pos.\n  \n  \\seebaseclassmethod \\ref QCPAbstractPlottable::selectTest\n*/\ndouble QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(details)\n  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())\n    return -1;\n  if (!mKeyAxis || !mValueAxis)\n    return -1;\n  \n  if (mKeyAxis->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))\n  {\n    // get visible data range:\n    QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;\n    QCPStatisticalBoxDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();\n    getVisibleDataBounds(visibleBegin, visibleEnd);\n    double minDistSqr = (std::numeric_limits<double>::max)();\n    for (QCPStatisticalBoxDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)\n    {\n      if (getQuartileBox(it).contains(pos)) // quartile box\n      {\n        double currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;\n        if (currentDistSqr < minDistSqr)\n        {\n          minDistSqr = currentDistSqr;\n          closestDataPoint = it;\n        }\n      } else // whiskers\n      {\n        const QVector<QLineF> whiskerBackbones = getWhiskerBackboneLines(it);\n        const QCPVector2D posVec(pos);\n        foreach (const QLineF &backbone, whiskerBackbones)\n        {\n          double currentDistSqr = posVec.distanceSquaredToLine(backbone);\n          if (currentDistSqr < minDistSqr)\n          {\n            minDistSqr = currentDistSqr;\n            closestDataPoint = it;\n          }\n        }\n      }\n    }\n    if (details)\n    {\n      int pointIndex = int(closestDataPoint-mDataContainer->constBegin());\n      details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));\n    }\n    return qSqrt(minDistSqr);\n  }\n  return -1;\n}\n\n/* inherits documentation from base class */\nQCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const\n{\n  QCPRange range = mDataContainer->keyRange(foundRange, inSignDomain);\n  // determine exact range by including width of bars/flags:\n  if (foundRange)\n  {\n    if (inSignDomain != QCP::sdPositive || range.lower-mWidth*0.5 > 0)\n      range.lower -= mWidth*0.5;\n    if (inSignDomain != QCP::sdNegative || range.upper+mWidth*0.5 < 0)\n      range.upper += mWidth*0.5;\n  }\n  return range;\n}\n\n/* inherits documentation from base class */\nQCPRange QCPStatisticalBox::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const\n{\n  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);\n}\n\n/* inherits documentation from base class */\nvoid QCPStatisticalBox::draw(QCPPainter *painter)\n{\n  if (mDataContainer->isEmpty()) return;\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n  \n  QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;\n  getVisibleDataBounds(visibleBegin, visibleEnd);\n  \n  // loop over and draw segments of unselected/selected data:\n  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;\n  getDataSegments(selectedSegments, unselectedSegments);\n  allSegments << unselectedSegments << selectedSegments;\n  for (int i=0; i<allSegments.size(); ++i)\n  {\n    bool isSelectedSegment = i >= unselectedSegments.size();\n    QCPStatisticalBoxDataContainer::const_iterator begin = visibleBegin;\n    QCPStatisticalBoxDataContainer::const_iterator end = visibleEnd;\n    mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));\n    if (begin == end)\n      continue;\n    \n    for (QCPStatisticalBoxDataContainer::const_iterator it=begin; it!=end; ++it)\n    {\n      // check data validity if flag set:\n# ifdef QCUSTOMPLOT_CHECK_DATA\n      if (QCP::isInvalidData(it->key, it->minimum) ||\n          QCP::isInvalidData(it->lowerQuartile, it->median) ||\n          QCP::isInvalidData(it->upperQuartile, it->maximum))\n        qDebug() << Q_FUNC_INFO << \"Data point at\" << it->key << \"of drawn range has invalid data.\" << \"Plottable name:\" << name();\n      for (int i=0; i<it->outliers.size(); ++i)\n        if (QCP::isInvalidData(it->outliers.at(i)))\n          qDebug() << Q_FUNC_INFO << \"Data point outlier at\" << it->key << \"of drawn range invalid.\" << \"Plottable name:\" << name();\n# endif\n      \n      if (isSelectedSegment && mSelectionDecorator)\n      {\n        mSelectionDecorator->applyPen(painter);\n        mSelectionDecorator->applyBrush(painter);\n      } else\n      {\n        painter->setPen(mPen);\n        painter->setBrush(mBrush);\n      }\n      QCPScatterStyle finalOutlierStyle = mOutlierStyle;\n      if (isSelectedSegment && mSelectionDecorator)\n        finalOutlierStyle = mSelectionDecorator->getFinalScatterStyle(mOutlierStyle);\n      drawStatisticalBox(painter, it, finalOutlierStyle);\n    }\n  }\n  \n  // draw other selection decoration that isn't just line/scatter pens and brushes:\n  if (mSelectionDecorator)\n    mSelectionDecorator->drawDecoration(painter, selection());\n}\n\n/* inherits documentation from base class */\nvoid QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const\n{\n  // draw filled rect:\n  applyDefaultAntialiasingHint(painter);\n  painter->setPen(mPen);\n  painter->setBrush(mBrush);\n  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);\n  r.moveCenter(rect.center());\n  painter->drawRect(r);\n}\n\n/*!\n  Draws the graphical representation of a single statistical box with the data given by the\n  iterator \\a it with the provided \\a painter.\n\n  If the statistical box has a set of outlier data points, they are drawn with \\a outlierStyle.\n\n  \\see getQuartileBox, getWhiskerBackboneLines, getWhiskerBarLines\n*/\nvoid QCPStatisticalBox::drawStatisticalBox(QCPPainter *painter, QCPStatisticalBoxDataContainer::const_iterator it, const QCPScatterStyle &outlierStyle) const\n{\n  // draw quartile box:\n  applyDefaultAntialiasingHint(painter);\n  const QRectF quartileBox = getQuartileBox(it);\n  painter->drawRect(quartileBox);\n  // draw median line with cliprect set to quartile box:\n  painter->save();\n  painter->setClipRect(quartileBox, Qt::IntersectClip);\n  painter->setPen(mMedianPen);\n  painter->drawLine(QLineF(coordsToPixels(it->key-mWidth*0.5, it->median), coordsToPixels(it->key+mWidth*0.5, it->median)));\n  painter->restore();\n  // draw whisker lines:\n  applyAntialiasingHint(painter, mWhiskerAntialiased, QCP::aePlottables);\n  painter->setPen(mWhiskerPen);\n  painter->drawLines(getWhiskerBackboneLines(it));\n  painter->setPen(mWhiskerBarPen);\n  painter->drawLines(getWhiskerBarLines(it));\n  // draw outliers:\n  applyScattersAntialiasingHint(painter);\n  outlierStyle.applyTo(painter, mPen);\n  for (int i=0; i<it->outliers.size(); ++i)\n    outlierStyle.drawShape(painter, coordsToPixels(it->key, it->outliers.at(i)));\n}\n\n/*!  \\internal\n  \n  called by \\ref draw to determine which data (key) range is visible at the current key axis range\n  setting, so only that needs to be processed. It also takes into account the bar width.\n  \n  \\a begin returns an iterator to the lowest data point that needs to be taken into account when\n  plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \\a\n  lower may still be just outside the visible range.\n  \n  \\a end returns an iterator one higher than the highest visible data point. Same as before, \\a end\n  may also lie just outside of the visible range.\n  \n  if the plottable contains no data, both \\a begin and \\a end point to constEnd.\n*/\nvoid QCPStatisticalBox::getVisibleDataBounds(QCPStatisticalBoxDataContainer::const_iterator &begin, QCPStatisticalBoxDataContainer::const_iterator &end) const\n{\n  if (!mKeyAxis)\n  {\n    qDebug() << Q_FUNC_INFO << \"invalid key axis\";\n    begin = mDataContainer->constEnd();\n    end = mDataContainer->constEnd();\n    return;\n  }\n  begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower-mWidth*0.5); // subtract half width of box to include partially visible data points\n  end = mDataContainer->findEnd(mKeyAxis.data()->range().upper+mWidth*0.5); // add half width of box to include partially visible data points\n}\n\n/*!  \\internal\n\n  Returns the box in plot coordinates (keys in x, values in y of the returned rect) that covers the\n  value range from the lower to the upper quartile, of the data given by \\a it.\n\n  \\see drawStatisticalBox, getWhiskerBackboneLines, getWhiskerBarLines\n*/\nQRectF QCPStatisticalBox::getQuartileBox(QCPStatisticalBoxDataContainer::const_iterator it) const\n{\n  QRectF result;\n  result.setTopLeft(coordsToPixels(it->key-mWidth*0.5, it->upperQuartile));\n  result.setBottomRight(coordsToPixels(it->key+mWidth*0.5, it->lowerQuartile));\n  return result;\n}\n\n/*!  \\internal\n\n  Returns the whisker backbones (keys in x, values in y of the returned lines) that cover the value\n  range from the minimum to the lower quartile, and from the upper quartile to the maximum of the\n  data given by \\a it.\n\n  \\see drawStatisticalBox, getQuartileBox, getWhiskerBarLines\n*/\nQVector<QLineF> QCPStatisticalBox::getWhiskerBackboneLines(QCPStatisticalBoxDataContainer::const_iterator it) const\n{\n  QVector<QLineF> result(2);\n  result[0].setPoints(coordsToPixels(it->key, it->lowerQuartile), coordsToPixels(it->key, it->minimum)); // min backbone\n  result[1].setPoints(coordsToPixels(it->key, it->upperQuartile), coordsToPixels(it->key, it->maximum)); // max backbone\n  return result;\n}\n\n/*!  \\internal\n\n  Returns the whisker bars (keys in x, values in y of the returned lines) that are placed at the\n  end of the whisker backbones, at the minimum and maximum of the data given by \\a it.\n\n  \\see drawStatisticalBox, getQuartileBox, getWhiskerBackboneLines\n*/\nQVector<QLineF> QCPStatisticalBox::getWhiskerBarLines(QCPStatisticalBoxDataContainer::const_iterator it) const\n{\n  QVector<QLineF> result(2);\n  result[0].setPoints(coordsToPixels(it->key-mWhiskerWidth*0.5, it->minimum), coordsToPixels(it->key+mWhiskerWidth*0.5, it->minimum)); // min bar\n  result[1].setPoints(coordsToPixels(it->key-mWhiskerWidth*0.5, it->maximum), coordsToPixels(it->key+mWhiskerWidth*0.5, it->maximum)); // max bar\n  return result;\n}\n/* end of 'src/plottables/plottable-statisticalbox.cpp' */\n\n\n/* including file 'src/plottables/plottable-colormap.cpp' */\n/* modified 2021-03-29T02:30:44, size 48149               */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPColorMapData\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPColorMapData\n  \\brief Holds the two-dimensional data of a QCPColorMap plottable.\n  \n  This class is a data storage for \\ref QCPColorMap. It holds a two-dimensional array, which \\ref\n  QCPColorMap then displays as a 2D image in the plot, where the array values are represented by a\n  color, depending on the value.\n  \n  The size of the array can be controlled via \\ref setSize (or \\ref setKeySize, \\ref setValueSize).\n  Which plot coordinates these cells correspond to can be configured with \\ref setRange (or \\ref\n  setKeyRange, \\ref setValueRange).\n  \n  The data cells can be accessed in two ways: They can be directly addressed by an integer index\n  with \\ref setCell. This is the fastest method. Alternatively, they can be addressed by their plot\n  coordinate with \\ref setData. plot coordinate to cell index transformations and vice versa are\n  provided by the functions \\ref coordToCell and \\ref cellToCoord.\n  \n  A \\ref QCPColorMapData also holds an on-demand two-dimensional array of alpha values which (if\n  allocated) has the same size as the data map. It can be accessed via \\ref setAlpha, \\ref\n  fillAlpha and \\ref clearAlpha. The memory for the alpha map is only allocated if needed, i.e. on\n  the first call of \\ref setAlpha. \\ref clearAlpha restores full opacity and frees the alpha map.\n  \n  This class also buffers the minimum and maximum values that are in the data set, to provide\n  QCPColorMap::rescaleDataRange with the necessary information quickly. Setting a cell to a value\n  that is greater than the current maximum increases this maximum to the new value. However,\n  setting the cell that currently holds the maximum value to a smaller value doesn't decrease the\n  maximum again, because finding the true new maximum would require going through the entire data\n  array, which might be time consuming. The same holds for the data minimum. This functionality is\n  given by \\ref recalculateDataBounds, such that you can decide when it is sensible to find the\n  true current minimum and maximum. The method QCPColorMap::rescaleDataRange offers a convenience\n  parameter \\a recalculateDataBounds which may be set to true to automatically call \\ref\n  recalculateDataBounds internally.\n*/\n\n/* start of documentation of inline functions */\n\n/*! \\fn bool QCPColorMapData::isEmpty() const\n  \n  Returns whether this instance carries no data. This is equivalent to having a size where at least\n  one of the dimensions is 0 (see \\ref setSize).\n*/\n\n/* end of documentation of inline functions */\n\n/*!\n  Constructs a new QCPColorMapData instance. The instance has \\a keySize cells in the key direction\n  and \\a valueSize cells in the value direction. These cells will be displayed by the \\ref QCPColorMap\n  at the coordinates \\a keyRange and \\a valueRange.\n  \n  \\see setSize, setKeySize, setValueSize, setRange, setKeyRange, setValueRange\n*/\nQCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange) :\n  mKeySize(0),\n  mValueSize(0),\n  mKeyRange(keyRange),\n  mValueRange(valueRange),\n  mIsEmpty(true),\n  mData(nullptr),\n  mAlpha(nullptr),\n  mDataModified(true)\n{\n  setSize(keySize, valueSize);\n  fill(0);\n}\n\nQCPColorMapData::~QCPColorMapData()\n{\n  delete[] mData;\n  delete[] mAlpha;\n}\n\n/*!\n  Constructs a new QCPColorMapData instance copying the data and range of \\a other.\n*/\nQCPColorMapData::QCPColorMapData(const QCPColorMapData &other) :\n  mKeySize(0),\n  mValueSize(0),\n  mIsEmpty(true),\n  mData(nullptr),\n  mAlpha(nullptr),\n  mDataModified(true)\n{\n  *this = other;\n}\n\n/*!\n  Overwrites this color map data instance with the data stored in \\a other. The alpha map state is\n  transferred, too.\n*/\nQCPColorMapData &QCPColorMapData::operator=(const QCPColorMapData &other)\n{\n  if (&other != this)\n  {\n    const int keySize = other.keySize();\n    const int valueSize = other.valueSize();\n    if (!other.mAlpha && mAlpha)\n      clearAlpha();\n    setSize(keySize, valueSize);\n    if (other.mAlpha && !mAlpha)\n      createAlpha(false);\n    setRange(other.keyRange(), other.valueRange());\n    if (!isEmpty())\n    {\n      memcpy(mData, other.mData, sizeof(mData[0])*size_t(keySize*valueSize));\n      if (mAlpha)\n        memcpy(mAlpha, other.mAlpha, sizeof(mAlpha[0])*size_t(keySize*valueSize));\n    }\n    mDataBounds = other.mDataBounds;\n    mDataModified = true;\n  }\n  return *this;\n}\n\n/* undocumented getter */\ndouble QCPColorMapData::data(double key, double value)\n{\n  int keyCell = int( (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5 );\n  int valueCell = int( (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5 );\n  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)\n    return mData[valueCell*mKeySize + keyCell];\n  else\n    return 0;\n}\n\n/* undocumented getter */\ndouble QCPColorMapData::cell(int keyIndex, int valueIndex)\n{\n  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)\n    return mData[valueIndex*mKeySize + keyIndex];\n  else\n    return 0;\n}\n\n/*!\n  Returns the alpha map value of the cell with the indices \\a keyIndex and \\a valueIndex.\n\n  If this color map data doesn't have an alpha map (because \\ref setAlpha was never called after\n  creation or after a call to \\ref clearAlpha), returns 255, which corresponds to full opacity.\n\n  \\see setAlpha\n*/\nunsigned char QCPColorMapData::alpha(int keyIndex, int valueIndex)\n{\n  if (mAlpha && keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)\n    return mAlpha[valueIndex*mKeySize + keyIndex];\n  else\n    return 255;\n}\n\n/*!\n  Resizes the data array to have \\a keySize cells in the key dimension and \\a valueSize cells in\n  the value dimension.\n\n  The current data is discarded and the map cells are set to 0, unless the map had already the\n  requested size.\n  \n  Setting at least one of \\a keySize or \\a valueSize to zero frees the internal data array and \\ref\n  isEmpty returns true.\n\n  \\see setRange, setKeySize, setValueSize\n*/\nvoid QCPColorMapData::setSize(int keySize, int valueSize)\n{\n  if (keySize != mKeySize || valueSize != mValueSize)\n  {\n    mKeySize = keySize;\n    mValueSize = valueSize;\n    delete[] mData;\n    mIsEmpty = mKeySize == 0 || mValueSize == 0;\n    if (!mIsEmpty)\n    {\n#ifdef __EXCEPTIONS\n      try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message\n#endif\n      mData = new double[size_t(mKeySize*mValueSize)];\n#ifdef __EXCEPTIONS\n      } catch (...) { mData = nullptr; }\n#endif\n      if (mData)\n        fill(0);\n      else\n        qDebug() << Q_FUNC_INFO << \"out of memory for data dimensions \"<< mKeySize << \"*\" << mValueSize;\n    } else\n      mData = nullptr;\n    \n    if (mAlpha) // if we had an alpha map, recreate it with new size\n      createAlpha();\n    \n    mDataModified = true;\n  }\n}\n\n/*!\n  Resizes the data array to have \\a keySize cells in the key dimension.\n\n  The current data is discarded and the map cells are set to 0, unless the map had already the\n  requested size.\n  \n  Setting \\a keySize to zero frees the internal data array and \\ref isEmpty returns true.\n\n  \\see setKeyRange, setSize, setValueSize\n*/\nvoid QCPColorMapData::setKeySize(int keySize)\n{\n  setSize(keySize, mValueSize);\n}\n\n/*!\n  Resizes the data array to have \\a valueSize cells in the value dimension.\n\n  The current data is discarded and the map cells are set to 0, unless the map had already the\n  requested size.\n  \n  Setting \\a valueSize to zero frees the internal data array and \\ref isEmpty returns true.\n\n  \\see setValueRange, setSize, setKeySize\n*/\nvoid QCPColorMapData::setValueSize(int valueSize)\n{\n  setSize(mKeySize, valueSize);\n}\n\n/*!\n  Sets the coordinate ranges the data shall be distributed over. This defines the rectangular area\n  covered by the color map in plot coordinates.\n  \n  The outer cells will be centered on the range boundaries given to this function. For example, if\n  the key size (\\ref setKeySize) is 3 and \\a keyRange is set to <tt>QCPRange(2, 3)</tt> there will\n  be cells centered on the key coordinates 2, 2.5 and 3.\n \n  \\see setSize\n*/\nvoid QCPColorMapData::setRange(const QCPRange &keyRange, const QCPRange &valueRange)\n{\n  setKeyRange(keyRange);\n  setValueRange(valueRange);\n}\n\n/*!\n  Sets the coordinate range the data shall be distributed over in the key dimension. Together with\n  the value range, This defines the rectangular area covered by the color map in plot coordinates.\n  \n  The outer cells will be centered on the range boundaries given to this function. For example, if\n  the key size (\\ref setKeySize) is 3 and \\a keyRange is set to <tt>QCPRange(2, 3)</tt> there will\n  be cells centered on the key coordinates 2, 2.5 and 3.\n \n  \\see setRange, setValueRange, setSize\n*/\nvoid QCPColorMapData::setKeyRange(const QCPRange &keyRange)\n{\n  mKeyRange = keyRange;\n}\n\n/*!\n  Sets the coordinate range the data shall be distributed over in the value dimension. Together with\n  the key range, This defines the rectangular area covered by the color map in plot coordinates.\n  \n  The outer cells will be centered on the range boundaries given to this function. For example, if\n  the value size (\\ref setValueSize) is 3 and \\a valueRange is set to <tt>QCPRange(2, 3)</tt> there\n  will be cells centered on the value coordinates 2, 2.5 and 3.\n \n  \\see setRange, setKeyRange, setSize\n*/\nvoid QCPColorMapData::setValueRange(const QCPRange &valueRange)\n{\n  mValueRange = valueRange;\n}\n\n/*!\n  Sets the data of the cell, which lies at the plot coordinates given by \\a key and \\a value, to \\a\n  z.\n  \n  \\note The QCPColorMap always displays the data at equal key/value intervals, even if the key or\n  value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,\n  you shouldn't use the \\ref QCPColorMapData::setData method as it uses a linear transformation to\n  determine the cell index. Rather directly access the cell index with \\ref\n  QCPColorMapData::setCell.\n \n  \\see setCell, setRange\n*/\nvoid QCPColorMapData::setData(double key, double value, double z)\n{\n  int keyCell = int( (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5 );\n  int valueCell = int( (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5 );\n  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)\n  {\n    mData[valueCell*mKeySize + keyCell] = z;\n    if (z < mDataBounds.lower)\n      mDataBounds.lower = z;\n    if (z > mDataBounds.upper)\n      mDataBounds.upper = z;\n     mDataModified = true;\n  }\n}\n\n/*!\n  Sets the data of the cell with indices \\a keyIndex and \\a valueIndex to \\a z. The indices\n  enumerate the cells starting from zero, up to the map's size-1 in the respective dimension (see\n  \\ref setSize).\n  \n  In the standard plot configuration (horizontal key axis and vertical value axis, both not\n  range-reversed), the cell with indices (0, 0) is in the bottom left corner and the cell with\n  indices (keySize-1, valueSize-1) is in the top right corner of the color map.\n  \n  \\see setData, setSize\n*/\nvoid QCPColorMapData::setCell(int keyIndex, int valueIndex, double z)\n{\n  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)\n  {\n    mData[valueIndex*mKeySize + keyIndex] = z;\n    if (z < mDataBounds.lower)\n      mDataBounds.lower = z;\n    if (z > mDataBounds.upper)\n      mDataBounds.upper = z;\n     mDataModified = true;\n  } else\n    qDebug() << Q_FUNC_INFO << \"index out of bounds:\" << keyIndex << valueIndex;\n}\n\n/*!\n  Sets the alpha of the color map cell given by \\a keyIndex and \\a valueIndex to \\a alpha. A value\n  of 0 for \\a alpha results in a fully transparent cell, and a value of 255 results in a fully\n  opaque cell.\n\n  If an alpha map doesn't exist yet for this color map data, it will be created here. If you wish\n  to restore full opacity and free any allocated memory of the alpha map, call \\ref clearAlpha.\n\n  Note that the cell-wise alpha which can be configured here is independent of any alpha configured\n  in the color map's gradient (\\ref QCPColorGradient). If a cell is affected both by the cell-wise\n  and gradient alpha, the alpha values will be blended accordingly during rendering of the color\n  map.\n\n  \\see fillAlpha, clearAlpha\n*/\nvoid QCPColorMapData::setAlpha(int keyIndex, int valueIndex, unsigned char alpha)\n{\n  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)\n  {\n    if (mAlpha || createAlpha())\n    {\n      mAlpha[valueIndex*mKeySize + keyIndex] = alpha;\n      mDataModified = true;\n    }\n  } else\n    qDebug() << Q_FUNC_INFO << \"index out of bounds:\" << keyIndex << valueIndex;\n}\n\n/*!\n  Goes through the data and updates the buffered minimum and maximum data values.\n  \n  Calling this method is only advised if you are about to call \\ref QCPColorMap::rescaleDataRange\n  and can not guarantee that the cells holding the maximum or minimum data haven't been overwritten\n  with a smaller or larger value respectively, since the buffered maximum/minimum values have been\n  updated the last time. Why this is the case is explained in the class description (\\ref\n  QCPColorMapData).\n  \n  Note that the method \\ref QCPColorMap::rescaleDataRange provides a parameter \\a\n  recalculateDataBounds for convenience. Setting this to true will call this method for you, before\n  doing the rescale.\n*/\nvoid QCPColorMapData::recalculateDataBounds()\n{\n  if (mKeySize > 0 && mValueSize > 0)\n  {\n    double minHeight = mData[0];\n    double maxHeight = mData[0];\n    const int dataCount = mValueSize*mKeySize;\n    for (int i=0; i<dataCount; ++i)\n    {\n      if (mData[i] > maxHeight)\n        maxHeight = mData[i];\n      if (mData[i] < minHeight)\n        minHeight = mData[i];\n    }\n    mDataBounds.lower = minHeight;\n    mDataBounds.upper = maxHeight;\n  }\n}\n\n/*!\n  Frees the internal data memory.\n  \n  This is equivalent to calling \\ref setSize \"setSize(0, 0)\".\n*/\nvoid QCPColorMapData::clear()\n{\n  setSize(0, 0);\n}\n\n/*!\n  Frees the internal alpha map. The color map will have full opacity again.\n*/\nvoid QCPColorMapData::clearAlpha()\n{\n  if (mAlpha)\n  {\n    delete[] mAlpha;\n    mAlpha = nullptr;\n    mDataModified = true;\n  }\n}\n\n/*!\n  Sets all cells to the value \\a z.\n*/\nvoid QCPColorMapData::fill(double z)\n{\n  const int dataCount = mValueSize*mKeySize;\n  for (int i=0; i<dataCount; ++i)\n    mData[i] = z;\n  mDataBounds = QCPRange(z, z);\n  mDataModified = true;\n}\n\n/*!\n  Sets the opacity of all color map cells to \\a alpha. A value of 0 for \\a alpha results in a fully\n  transparent color map, and a value of 255 results in a fully opaque color map.\n\n  If you wish to restore opacity to 100% and free any used memory for the alpha map, rather use\n  \\ref clearAlpha.\n\n  \\see setAlpha\n*/\nvoid QCPColorMapData::fillAlpha(unsigned char alpha)\n{\n  if (mAlpha || createAlpha(false))\n  {\n    const int dataCount = mValueSize*mKeySize;\n    for (int i=0; i<dataCount; ++i)\n      mAlpha[i] = alpha;\n    mDataModified = true;\n  }\n}\n\n/*!\n  Transforms plot coordinates given by \\a key and \\a value to cell indices of this QCPColorMapData\n  instance. The resulting cell indices are returned via the output parameters \\a keyIndex and \\a\n  valueIndex.\n  \n  The retrieved key/value cell indices can then be used for example with \\ref setCell.\n  \n  If you are only interested in a key or value index, you may pass \\c nullptr as \\a valueIndex or\n  \\a keyIndex.\n  \n  \\note The QCPColorMap always displays the data at equal key/value intervals, even if the key or\n  value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,\n  you shouldn't use the \\ref QCPColorMapData::coordToCell method as it uses a linear transformation to\n  determine the cell index.\n  \n  \\see cellToCoord, QCPAxis::coordToPixel\n*/\nvoid QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const\n{\n  if (keyIndex)\n    *keyIndex = int( (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5 );\n  if (valueIndex)\n    *valueIndex = int( (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5 );\n}\n\n/*!\n  Transforms cell indices given by \\a keyIndex and \\a valueIndex to cell indices of this QCPColorMapData\n  instance. The resulting coordinates are returned via the output parameters \\a key and \\a\n  value.\n  \n  If you are only interested in a key or value coordinate, you may pass \\c nullptr as \\a key or \\a\n  value.\n  \n  \\note The QCPColorMap always displays the data at equal key/value intervals, even if the key or\n  value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,\n  you shouldn't use the \\ref QCPColorMapData::cellToCoord method as it uses a linear transformation to\n  determine the cell index.\n  \n  \\see coordToCell, QCPAxis::pixelToCoord\n*/\nvoid QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const\n{\n  if (key)\n    *key = keyIndex/double(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower;\n  if (value)\n    *value = valueIndex/double(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower;\n}\n\n/*! \\internal\n\n  Allocates the internal alpha map with the current data map key/value size and, if \\a\n  initializeOpaque is true, initializes all values to 255. If \\a initializeOpaque is false, the\n  values are not initialized at all. In this case, the alpha map should be initialized manually,\n  e.g. with \\ref fillAlpha.\n\n  If an alpha map exists already, it is deleted first. If this color map is empty (has either key\n  or value size zero, see \\ref isEmpty), the alpha map is cleared.\n\n  The return value indicates the existence of the alpha map after the call. So this method returns\n  true if the data map isn't empty and an alpha map was successfully allocated.\n*/\nbool QCPColorMapData::createAlpha(bool initializeOpaque)\n{\n  clearAlpha();\n  if (isEmpty())\n    return false;\n  \n#ifdef __EXCEPTIONS\n  try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message\n#endif\n    mAlpha = new unsigned char[size_t(mKeySize*mValueSize)];\n#ifdef __EXCEPTIONS\n  } catch (...) { mAlpha = nullptr; }\n#endif\n  if (mAlpha)\n  {\n    if (initializeOpaque)\n      fillAlpha(255);\n    return true;\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"out of memory for data dimensions \"<< mKeySize << \"*\" << mValueSize;\n    return false;\n  }\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPColorMap\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPColorMap\n  \\brief A plottable representing a two-dimensional color map in a plot.\n\n  \\image html QCPColorMap.png\n  \n  The data is stored in the class \\ref QCPColorMapData, which can be accessed via the data()\n  method.\n  \n  A color map has three dimensions to represent a data point: The \\a key dimension, the \\a value\n  dimension and the \\a data dimension. As with other plottables such as graphs, \\a key and \\a value\n  correspond to two orthogonal axes on the QCustomPlot surface that you specify in the QCPColorMap\n  constructor. The \\a data dimension however is encoded as the color of the point at (\\a key, \\a\n  value).\n\n  Set the number of points (or \\a cells) in the key/value dimension via \\ref\n  QCPColorMapData::setSize. The plot coordinate range over which these points will be displayed is\n  specified via \\ref QCPColorMapData::setRange. The first cell will be centered on the lower range\n  boundary and the last cell will be centered on the upper range boundary. The data can be set by\n  either accessing the cells directly with QCPColorMapData::setCell or by addressing the cells via\n  their plot coordinates with \\ref QCPColorMapData::setData. If possible, you should prefer\n  setCell, since it doesn't need to do any coordinate transformation and thus performs a bit\n  better.\n  \n  The cell with index (0, 0) is at the bottom left, if the color map uses normal (i.e. not reversed)\n  key and value axes.\n  \n  To show the user which colors correspond to which \\a data values, a \\ref QCPColorScale is\n  typically placed to the right of the axis rect. See the documentation there for details on how to\n  add and use a color scale.\n  \n  \\section qcpcolormap-appearance Changing the appearance\n  \n  Most important to the appearance is the color gradient, which can be specified via \\ref\n  setGradient. See the documentation of \\ref QCPColorGradient for details on configuring a color\n  gradient.\n  \n  The \\a data range that is mapped to the colors of the gradient can be specified with \\ref\n  setDataRange. To make the data range encompass the whole data set minimum to maximum, call \\ref\n  rescaleDataRange. If your data may contain NaN values, use \\ref QCPColorGradient::setNanHandling\n  to define how they are displayed.\n  \n  \\section qcpcolormap-transparency Transparency\n  \n  Transparency in color maps can be achieved by two mechanisms. On one hand, you can specify alpha\n  values for color stops of the \\ref QCPColorGradient, via the regular QColor interface. This will\n  cause the color map data which gets mapped to colors around those color stops to appear with the\n  accordingly interpolated transparency.\n  \n  On the other hand you can also directly apply an alpha value to each cell independent of its\n  data, by using the alpha map feature of \\ref QCPColorMapData. The relevant methods are \\ref\n  QCPColorMapData::setAlpha, QCPColorMapData::fillAlpha and \\ref QCPColorMapData::clearAlpha().\n  \n  The two transparencies will be joined together in the plot and otherwise not interfere with each\n  other. They are mixed in a multiplicative matter, so an alpha of e.g. 50% (128/255) in both modes\n  simultaneously, will result in a total transparency of 25% (64/255).\n  \n  \\section qcpcolormap-usage Usage\n  \n  Like all data representing objects in QCustomPlot, the QCPColorMap is a plottable\n  (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies\n  (QCustomPlot::plottable, QCustomPlot::removePlottable, etc.)\n  \n  Usually, you first create an instance:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolormap-creation-1\n  which registers it with the QCustomPlot instance of the passed axes. Note that this QCustomPlot instance takes\n  ownership of the plottable, so do not delete it manually but use QCustomPlot::removePlottable() instead.\n  The newly created plottable can be modified, e.g.:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolormap-creation-2\n  \n  \\note The QCPColorMap always displays the data at equal key/value intervals, even if the key or\n  value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,\n  you shouldn't use the \\ref QCPColorMapData::setData method as it uses a linear transformation to\n  determine the cell index. Rather directly access the cell index with \\ref\n  QCPColorMapData::setCell.\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn QCPColorMapData *QCPColorMap::data() const\n  \n  Returns a pointer to the internal data storage of type \\ref QCPColorMapData. Access this to\n  modify data points (cells) and the color map key/value range.\n  \n  \\see setData\n*/\n\n/* end documentation of inline functions */\n\n/* start documentation of signals */\n\n/*! \\fn void QCPColorMap::dataRangeChanged(const QCPRange &newRange);\n  \n  This signal is emitted when the data range changes.\n  \n  \\see setDataRange\n*/\n\n/*! \\fn void QCPColorMap::dataScaleTypeChanged(QCPAxis::ScaleType scaleType);\n  \n  This signal is emitted when the data scale type changes.\n  \n  \\see setDataScaleType\n*/\n\n/*! \\fn void QCPColorMap::gradientChanged(const QCPColorGradient &newGradient);\n  \n  This signal is emitted when the gradient changes.\n  \n  \\see setGradient\n*/\n\n/* end documentation of signals */\n\n/*!\n  Constructs a color map with the specified \\a keyAxis and \\a valueAxis.\n  \n  The created QCPColorMap is automatically registered with the QCustomPlot instance inferred from\n  \\a keyAxis. This QCustomPlot instance takes ownership of the QCPColorMap, so do not delete it\n  manually but use QCustomPlot::removePlottable() instead.\n*/\nQCPColorMap::QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis) :\n  QCPAbstractPlottable(keyAxis, valueAxis),\n  mDataScaleType(QCPAxis::stLinear),\n  mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),\n  mGradient(QCPColorGradient::gpCold),\n  mInterpolate(true),\n  mTightBoundary(false),\n  mMapImageInvalidated(true)\n{\n}\n\nQCPColorMap::~QCPColorMap()\n{\n  delete mMapData;\n}\n\n/*!\n  Replaces the current \\ref data with the provided \\a data.\n  \n  If \\a copy is set to true, the \\a data object will only be copied. if false, the color map\n  takes ownership of the passed data and replaces the internal data pointer with it. This is\n  significantly faster than copying for large datasets.\n*/\nvoid QCPColorMap::setData(QCPColorMapData *data, bool copy)\n{\n  if (mMapData == data)\n  {\n    qDebug() << Q_FUNC_INFO << \"The data pointer is already in (and owned by) this plottable\" << reinterpret_cast<quintptr>(data);\n    return;\n  }\n  if (copy)\n  {\n    *mMapData = *data;\n  } else\n  {\n    delete mMapData;\n    mMapData = data;\n  }\n  mMapImageInvalidated = true;\n}\n\n/*!\n  Sets the data range of this color map to \\a dataRange. The data range defines which data values\n  are mapped to the color gradient.\n  \n  To make the data range span the full range of the data set, use \\ref rescaleDataRange.\n  \n  \\see QCPColorScale::setDataRange\n*/\nvoid QCPColorMap::setDataRange(const QCPRange &dataRange)\n{\n  if (!QCPRange::validRange(dataRange)) return;\n  if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)\n  {\n    if (mDataScaleType == QCPAxis::stLogarithmic)\n      mDataRange = dataRange.sanitizedForLogScale();\n    else\n      mDataRange = dataRange.sanitizedForLinScale();\n    mMapImageInvalidated = true;\n    emit dataRangeChanged(mDataRange);\n  }\n}\n\n/*!\n  Sets whether the data is correlated with the color gradient linearly or logarithmically.\n  \n  \\see QCPColorScale::setDataScaleType\n*/\nvoid QCPColorMap::setDataScaleType(QCPAxis::ScaleType scaleType)\n{\n  if (mDataScaleType != scaleType)\n  {\n    mDataScaleType = scaleType;\n    mMapImageInvalidated = true;\n    emit dataScaleTypeChanged(mDataScaleType);\n    if (mDataScaleType == QCPAxis::stLogarithmic)\n      setDataRange(mDataRange.sanitizedForLogScale());\n  }\n}\n\n/*!\n  Sets the color gradient that is used to represent the data. For more details on how to create an\n  own gradient or use one of the preset gradients, see \\ref QCPColorGradient.\n  \n  The colors defined by the gradient will be used to represent data values in the currently set\n  data range, see \\ref setDataRange. Data points that are outside this data range will either be\n  colored uniformly with the respective gradient boundary color, or the gradient will repeat,\n  depending on \\ref QCPColorGradient::setPeriodic.\n  \n  \\see QCPColorScale::setGradient\n*/\nvoid QCPColorMap::setGradient(const QCPColorGradient &gradient)\n{\n  if (mGradient != gradient)\n  {\n    mGradient = gradient;\n    mMapImageInvalidated = true;\n    emit gradientChanged(mGradient);\n  }\n}\n\n/*!\n  Sets whether the color map image shall use bicubic interpolation when displaying the color map\n  shrinked or expanded, and not at a 1:1 pixel-to-data scale.\n  \n  \\image html QCPColorMap-interpolate.png \"A 10*10 color map, with interpolation and without interpolation enabled\"\n*/\nvoid QCPColorMap::setInterpolate(bool enabled)\n{\n  mInterpolate = enabled;\n  mMapImageInvalidated = true; // because oversampling factors might need to change\n}\n\n/*!\n  Sets whether the outer most data rows and columns are clipped to the specified key and value\n  range (see \\ref QCPColorMapData::setKeyRange, \\ref QCPColorMapData::setValueRange).\n  \n  if \\a enabled is set to false, the data points at the border of the color map are drawn with the\n  same width and height as all other data points. Since the data points are represented by\n  rectangles of one color centered on the data coordinate, this means that the shown color map\n  extends by half a data point over the specified key/value range in each direction.\n  \n  \\image html QCPColorMap-tightboundary.png \"A color map, with tight boundary enabled and disabled\"\n*/\nvoid QCPColorMap::setTightBoundary(bool enabled)\n{\n  mTightBoundary = enabled;\n}\n\n/*!\n  Associates the color scale \\a colorScale with this color map.\n  \n  This means that both the color scale and the color map synchronize their gradient, data range and\n  data scale type (\\ref setGradient, \\ref setDataRange, \\ref setDataScaleType). Multiple color maps\n  can be associated with one single color scale. This causes the color maps to also synchronize\n  those properties, via the mutual color scale.\n  \n  This function causes the color map to adopt the current color gradient, data range and data scale\n  type of \\a colorScale. After this call, you may change these properties at either the color map\n  or the color scale, and the setting will be applied to both.\n  \n  Pass \\c nullptr as \\a colorScale to disconnect the color scale from this color map again.\n*/\nvoid QCPColorMap::setColorScale(QCPColorScale *colorScale)\n{\n  if (mColorScale) // unconnect signals from old color scale\n  {\n    disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));\n    disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));\n    disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));\n    disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));\n    disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));\n    disconnect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));\n  }\n  mColorScale = colorScale;\n  if (mColorScale) // connect signals to new color scale\n  {\n    setGradient(mColorScale.data()->gradient());\n    setDataRange(mColorScale.data()->dataRange());\n    setDataScaleType(mColorScale.data()->dataScaleType());\n    connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));\n    connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));\n    connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));\n    connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));\n    connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));\n    connect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));\n  }\n}\n\n/*!\n  Sets the data range (\\ref setDataRange) to span the minimum and maximum values that occur in the\n  current data set. This corresponds to the \\ref rescaleKeyAxis or \\ref rescaleValueAxis methods,\n  only for the third data dimension of the color map.\n  \n  The minimum and maximum values of the data set are buffered in the internal QCPColorMapData\n  instance (\\ref data). As data is updated via its \\ref QCPColorMapData::setCell or \\ref\n  QCPColorMapData::setData, the buffered minimum and maximum values are updated, too. For\n  performance reasons, however, they are only updated in an expanding fashion. So the buffered\n  maximum can only increase and the buffered minimum can only decrease. In consequence, changes to\n  the data that actually lower the maximum of the data set (by overwriting the cell holding the\n  current maximum with a smaller value), aren't recognized and the buffered maximum overestimates\n  the true maximum of the data set. The same happens for the buffered minimum. To recalculate the\n  true minimum and maximum by explicitly looking at each cell, the method\n  QCPColorMapData::recalculateDataBounds can be used. For convenience, setting the parameter \\a\n  recalculateDataBounds calls this method before setting the data range to the buffered minimum and\n  maximum.\n  \n  \\see setDataRange\n*/\nvoid QCPColorMap::rescaleDataRange(bool recalculateDataBounds)\n{\n  if (recalculateDataBounds)\n    mMapData->recalculateDataBounds();\n  setDataRange(mMapData->dataBounds());\n}\n\n/*!\n  Takes the current appearance of the color map and updates the legend icon, which is used to\n  represent this color map in the legend (see \\ref QCPLegend).\n  \n  The \\a transformMode specifies whether the rescaling is done by a faster, low quality image\n  scaling algorithm (Qt::FastTransformation) or by a slower, higher quality algorithm\n  (Qt::SmoothTransformation).\n  \n  The current color map appearance is scaled down to \\a thumbSize. Ideally, this should be equal to\n  the size of the legend icon (see \\ref QCPLegend::setIconSize). If it isn't exactly the configured\n  legend icon size, the thumb will be rescaled during drawing of the legend item.\n  \n  \\see setDataRange\n*/\nvoid QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode, const QSize &thumbSize)\n{\n  if (mMapImage.isNull() && !data()->isEmpty())\n    updateMapImage(); // try to update map image if it's null (happens if no draw has happened yet)\n  \n  if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so check here again\n  {\n    bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();\n    bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();\n    mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY)).scaled(thumbSize, Qt::KeepAspectRatio, transformMode);\n  }\n}\n\n/* inherits documentation from base class */\ndouble QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(details)\n  if ((onlySelectable && mSelectable == QCP::stNone) || mMapData->isEmpty())\n    return -1;\n  if (!mKeyAxis || !mValueAxis)\n    return -1;\n  \n  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))\n  {\n    double posKey, posValue;\n    pixelsToCoords(pos, posKey, posValue);\n    if (mMapData->keyRange().contains(posKey) && mMapData->valueRange().contains(posValue))\n    {\n      if (details)\n        details->setValue(QCPDataSelection(QCPDataRange(0, 1))); // temporary solution, to facilitate whole-plottable selection. Replace in future version with segmented 2D selection.\n      return mParentPlot->selectionTolerance()*0.99;\n    }\n  }\n  return -1;\n}\n\n/* inherits documentation from base class */\nQCPRange QCPColorMap::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const\n{\n  foundRange = true;\n  QCPRange result = mMapData->keyRange();\n  result.normalize();\n  if (inSignDomain == QCP::sdPositive)\n  {\n    if (result.lower <= 0 && result.upper > 0)\n      result.lower = result.upper*1e-3;\n    else if (result.lower <= 0 && result.upper <= 0)\n      foundRange = false;\n  } else if (inSignDomain == QCP::sdNegative)\n  {\n    if (result.upper >= 0 && result.lower < 0)\n      result.upper = result.lower*1e-3;\n    else if (result.upper >= 0 && result.lower >= 0)\n      foundRange = false;\n  }\n  return result;\n}\n\n/* inherits documentation from base class */\nQCPRange QCPColorMap::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const\n{\n  if (inKeyRange != QCPRange())\n  {\n    if (mMapData->keyRange().upper < inKeyRange.lower || mMapData->keyRange().lower > inKeyRange.upper)\n    {\n      foundRange = false;\n      return {};\n    }\n  }\n  \n  foundRange = true;\n  QCPRange result = mMapData->valueRange();\n  result.normalize();\n  if (inSignDomain == QCP::sdPositive)\n  {\n    if (result.lower <= 0 && result.upper > 0)\n      result.lower = result.upper*1e-3;\n    else if (result.lower <= 0 && result.upper <= 0)\n      foundRange = false;\n  } else if (inSignDomain == QCP::sdNegative)\n  {\n    if (result.upper >= 0 && result.lower < 0)\n      result.upper = result.lower*1e-3;\n    else if (result.upper >= 0 && result.lower >= 0)\n      foundRange = false;\n  }\n  return result;\n}\n\n/*! \\internal\n  \n  Updates the internal map image buffer by going through the internal \\ref QCPColorMapData and\n  turning the data values into color pixels with \\ref QCPColorGradient::colorize.\n  \n  This method is called by \\ref QCPColorMap::draw if either the data has been modified or the map image\n  has been invalidated for a different reason (e.g. a change of the data range with \\ref\n  setDataRange).\n  \n  If the map cell count is low, the image created will be oversampled in order to avoid a\n  QPainter::drawImage bug which makes inner pixel boundaries jitter when stretch-drawing images\n  without smooth transform enabled. Accordingly, oversampling isn't performed if \\ref\n  setInterpolate is true.\n*/\nvoid QCPColorMap::updateMapImage()\n{\n  QCPAxis *keyAxis = mKeyAxis.data();\n  if (!keyAxis) return;\n  if (mMapData->isEmpty()) return;\n  \n  const QImage::Format format = QImage::Format_ARGB32_Premultiplied;\n  const int keySize = mMapData->keySize();\n  const int valueSize = mMapData->valueSize();\n  int keyOversamplingFactor = mInterpolate ? 1 : int(1.0+100.0/double(keySize)); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on\n  int valueOversamplingFactor = mInterpolate ? 1 : int(1.0+100.0/double(valueSize)); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on\n  \n  // resize mMapImage to correct dimensions including possible oversampling factors, according to key/value axes orientation:\n  if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.width() != keySize*keyOversamplingFactor || mMapImage.height() != valueSize*valueOversamplingFactor))\n    mMapImage = QImage(QSize(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor), format);\n  else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.width() != valueSize*valueOversamplingFactor || mMapImage.height() != keySize*keyOversamplingFactor))\n    mMapImage = QImage(QSize(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor), format);\n  \n  if (mMapImage.isNull())\n  {\n    qDebug() << Q_FUNC_INFO << \"Couldn't create map image (possibly too large for memory)\";\n    mMapImage = QImage(QSize(10, 10), format);\n    mMapImage.fill(Qt::black);\n  } else\n  {\n    QImage *localMapImage = &mMapImage; // this is the image on which the colorization operates. Either the final mMapImage, or if we need oversampling, mUndersampledMapImage\n    if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)\n    {\n      // resize undersampled map image to actual key/value cell sizes:\n      if (keyAxis->orientation() == Qt::Horizontal && (mUndersampledMapImage.width() != keySize || mUndersampledMapImage.height() != valueSize))\n        mUndersampledMapImage = QImage(QSize(keySize, valueSize), format);\n      else if (keyAxis->orientation() == Qt::Vertical && (mUndersampledMapImage.width() != valueSize || mUndersampledMapImage.height() != keySize))\n        mUndersampledMapImage = QImage(QSize(valueSize, keySize), format);\n      localMapImage = &mUndersampledMapImage; // make the colorization run on the undersampled image\n    } else if (!mUndersampledMapImage.isNull())\n      mUndersampledMapImage = QImage(); // don't need oversampling mechanism anymore (map size has changed) but mUndersampledMapImage still has nonzero size, free it\n    \n    const double *rawData = mMapData->mData;\n    const unsigned char *rawAlpha = mMapData->mAlpha;\n    if (keyAxis->orientation() == Qt::Horizontal)\n    {\n      const int lineCount = valueSize;\n      const int rowCount = keySize;\n      for (int line=0; line<lineCount; ++line)\n      {\n        QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)\n        if (rawAlpha)\n          mGradient.colorize(rawData+line*rowCount, rawAlpha+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);\n        else\n          mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);\n      }\n    } else // keyAxis->orientation() == Qt::Vertical\n    {\n      const int lineCount = keySize;\n      const int rowCount = valueSize;\n      for (int line=0; line<lineCount; ++line)\n      {\n        QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)\n        if (rawAlpha)\n          mGradient.colorize(rawData+line, rawAlpha+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);\n        else\n          mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);\n      }\n    }\n    \n    if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)\n    {\n      if (keyAxis->orientation() == Qt::Horizontal)\n        mMapImage = mUndersampledMapImage.scaled(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);\n      else\n        mMapImage = mUndersampledMapImage.scaled(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);\n    }\n  }\n  mMapData->mDataModified = false;\n  mMapImageInvalidated = false;\n}\n\n/* inherits documentation from base class */\nvoid QCPColorMap::draw(QCPPainter *painter)\n{\n  if (mMapData->isEmpty()) return;\n  if (!mKeyAxis || !mValueAxis) return;\n  applyDefaultAntialiasingHint(painter);\n  \n  if (mMapData->mDataModified || mMapImageInvalidated)\n    updateMapImage();\n  \n  // use buffer if painting vectorized (PDF):\n  const bool useBuffer = painter->modes().testFlag(QCPPainter::pmVectorized);\n  QCPPainter *localPainter = painter; // will be redirected to paint on mapBuffer if painting vectorized\n  QRectF mapBufferTarget; // the rect in absolute widget coordinates where the visible map portion/buffer will end up in\n  QPixmap mapBuffer;\n  if (useBuffer)\n  {\n    const double mapBufferPixelRatio = 3; // factor by which DPI is increased in embedded bitmaps\n    mapBufferTarget = painter->clipRegion().boundingRect();\n    mapBuffer = QPixmap((mapBufferTarget.size()*mapBufferPixelRatio).toSize());\n    mapBuffer.fill(Qt::transparent);\n    localPainter = new QCPPainter(&mapBuffer);\n    localPainter->scale(mapBufferPixelRatio, mapBufferPixelRatio);\n    localPainter->translate(-mapBufferTarget.topLeft());\n  }\n  \n  QRectF imageRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),\n                            coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized();\n  // extend imageRect to contain outer halves/quarters of bordering/cornering pixels (cells are centered on map range boundary):\n  double halfCellWidth = 0; // in pixels\n  double halfCellHeight = 0; // in pixels\n  if (keyAxis()->orientation() == Qt::Horizontal)\n  {\n    if (mMapData->keySize() > 1)\n      halfCellWidth = 0.5*imageRect.width()/double(mMapData->keySize()-1);\n    if (mMapData->valueSize() > 1)\n      halfCellHeight = 0.5*imageRect.height()/double(mMapData->valueSize()-1);\n  } else // keyAxis orientation is Qt::Vertical\n  {\n    if (mMapData->keySize() > 1)\n      halfCellHeight = 0.5*imageRect.height()/double(mMapData->keySize()-1);\n    if (mMapData->valueSize() > 1)\n      halfCellWidth = 0.5*imageRect.width()/double(mMapData->valueSize()-1);\n  }\n  imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth, halfCellHeight);\n  const bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();\n  const bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();\n  const bool smoothBackup = localPainter->renderHints().testFlag(QPainter::SmoothPixmapTransform);\n  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);\n  QRegion clipBackup;\n  if (mTightBoundary)\n  {\n    clipBackup = localPainter->clipRegion();\n    QRectF tightClipRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),\n                                  coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized();\n    localPainter->setClipRect(tightClipRect, Qt::IntersectClip);\n  }\n  localPainter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));\n  if (mTightBoundary)\n    localPainter->setClipRegion(clipBackup);\n  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);\n  \n  if (useBuffer) // localPainter painted to mapBuffer, so now draw buffer with original painter\n  {\n    delete localPainter;\n    painter->drawPixmap(mapBufferTarget.toRect(), mapBuffer);\n  }\n}\n\n/* inherits documentation from base class */\nvoid QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const\n{\n  applyDefaultAntialiasingHint(painter);\n  // draw map thumbnail:\n  if (!mLegendIcon.isNull())\n  {\n    QPixmap scaledIcon = mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation);\n    QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());\n    iconRect.moveCenter(rect.center());\n    painter->drawPixmap(iconRect.topLeft(), scaledIcon);\n  }\n  /*\n  // draw frame:\n  painter->setBrush(Qt::NoBrush);\n  painter->setPen(Qt::black);\n  painter->drawRect(rect.adjusted(1, 1, 0, 0));\n  */\n}\n/* end of 'src/plottables/plottable-colormap.cpp' */\n\n\n/* including file 'src/plottables/plottable-financial.cpp' */\n/* modified 2021-03-29T02:30:44, size 42914                */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPFinancialData\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPFinancialData\n  \\brief Holds the data of one single data point for QCPFinancial.\n  \n  The stored data is:\n  \\li \\a key: coordinate on the key axis of this data point (this is the \\a mainKey and the \\a sortKey)\n  \\li \\a open: The opening value at the data point (this is the \\a mainValue)\n  \\li \\a high: The high/maximum value at the data point\n  \\li \\a low: The low/minimum value at the data point\n  \\li \\a close: The closing value at the data point\n  \n  The container for storing multiple data points is \\ref QCPFinancialDataContainer. It is a typedef\n  for \\ref QCPDataContainer with \\ref QCPFinancialData as the DataType template parameter. See the\n  documentation there for an explanation regarding the data type's generic methods.\n  \n  \\see QCPFinancialDataContainer\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn double QCPFinancialData::sortKey() const\n  \n  Returns the \\a key member of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn static QCPFinancialData QCPFinancialData::fromSortKey(double sortKey)\n  \n  Returns a data point with the specified \\a sortKey. All other members are set to zero.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn static static bool QCPFinancialData::sortKeyIsMainKey()\n  \n  Since the member \\a key is both the data point key coordinate and the data ordering parameter,\n  this method returns true.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn double QCPFinancialData::mainKey() const\n  \n  Returns the \\a key member of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn double QCPFinancialData::mainValue() const\n  \n  Returns the \\a open member of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/*! \\fn QCPRange QCPFinancialData::valueRange() const\n  \n  Returns a QCPRange spanning from the \\a low to the \\a high value of this data point.\n  \n  For a general explanation of what this method is good for in the context of the data container,\n  see the documentation of \\ref QCPDataContainer.\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Constructs a data point with key and all values set to zero.\n*/\nQCPFinancialData::QCPFinancialData() :\n  key(0),\n  open(0),\n  high(0),\n  low(0),\n  close(0)\n{\n}\n\n/*!\n  Constructs a data point with the specified \\a key and OHLC values.\n*/\nQCPFinancialData::QCPFinancialData(double key, double open, double high, double low, double close) :\n  key(key),\n  open(open),\n  high(high),\n  low(low),\n  close(close)\n{\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPFinancial\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPFinancial\n  \\brief A plottable representing a financial stock chart\n\n  \\image html QCPFinancial.png\n\n  This plottable represents time series data binned to certain intervals, mainly used for stock\n  charts. The two common representations OHLC (Open-High-Low-Close) bars and Candlesticks can be\n  set via \\ref setChartStyle.\n\n  The data is passed via \\ref setData as a set of open/high/low/close values at certain keys\n  (typically times). This means the data must be already binned appropriately. If data is only\n  available as a series of values (e.g. \\a price against \\a time), you can use the static\n  convenience function \\ref timeSeriesToOhlc to generate binned OHLC-data which can then be passed\n  to \\ref setData.\n\n  The width of the OHLC bars/candlesticks can be controlled with \\ref setWidth and \\ref\n  setWidthType. A typical choice is to set the width type to \\ref wtPlotCoords (the default) and\n  the width to (or slightly less than) one time bin interval width.\n\n  \\section qcpfinancial-appearance Changing the appearance\n\n  Charts can be either single- or two-colored (\\ref setTwoColored). If set to be single-colored,\n  lines are drawn with the plottable's pen (\\ref setPen) and fills with the brush (\\ref setBrush).\n\n  If set to two-colored, positive changes of the value during an interval (\\a close >= \\a open) are\n  represented with a different pen and brush than negative changes (\\a close < \\a open). These can\n  be configured with \\ref setPenPositive, \\ref setPenNegative, \\ref setBrushPositive, and \\ref\n  setBrushNegative. In two-colored mode, the normal plottable pen/brush is ignored. Upon selection\n  however, the normal selected pen/brush (provided by the \\ref selectionDecorator) is used,\n  irrespective of whether the chart is single- or two-colored.\n\n  \\section qcpfinancial-usage Usage\n\n  Like all data representing objects in QCustomPlot, the QCPFinancial is a plottable\n  (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies\n  (QCustomPlot::plottable, QCustomPlot::removePlottable, etc.)\n\n  Usually, you first create an instance:\n\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpfinancial-creation-1\n  which registers it with the QCustomPlot instance of the passed axes. Note that this QCustomPlot\n  instance takes ownership of the plottable, so do not delete it manually but use\n  QCustomPlot::removePlottable() instead. The newly created plottable can be modified, e.g.:\n\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpfinancial-creation-2\n  Here we have used the static helper method \\ref timeSeriesToOhlc, to turn a time-price data\n  series into a 24-hour binned open-high-low-close data series as QCPFinancial uses.\n*/\n\n/* start of documentation of inline functions */\n\n/*! \\fn QCPFinancialDataContainer *QCPFinancial::data() const\n  \n  Returns a pointer to the internal data storage of type \\ref QCPFinancialDataContainer. You may\n  use it to directly manipulate the data, which may be more convenient and faster than using the\n  regular \\ref setData or \\ref addData methods, in certain situations.\n*/\n\n/* end of documentation of inline functions */\n\n/*!\n  Constructs a financial chart which uses \\a keyAxis as its key axis (\"x\") and \\a valueAxis as its value\n  axis (\"y\"). \\a keyAxis and \\a valueAxis must reside in the same QCustomPlot instance and not have\n  the same orientation. If either of these restrictions is violated, a corresponding message is\n  printed to the debug output (qDebug), the construction is not aborted, though.\n  \n  The created QCPFinancial is automatically registered with the QCustomPlot instance inferred from \\a\n  keyAxis. This QCustomPlot instance takes ownership of the QCPFinancial, so do not delete it manually\n  but use QCustomPlot::removePlottable() instead.\n*/\nQCPFinancial::QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis) :\n  QCPAbstractPlottable1D<QCPFinancialData>(keyAxis, valueAxis),\n  mChartStyle(csCandlestick),\n  mWidth(0.5),\n  mWidthType(wtPlotCoords),\n  mTwoColored(true),\n  mBrushPositive(QBrush(QColor(50, 160, 0))),\n  mBrushNegative(QBrush(QColor(180, 0, 15))),\n  mPenPositive(QPen(QColor(40, 150, 0))),\n  mPenNegative(QPen(QColor(170, 5, 5)))\n{\n  mSelectionDecorator->setBrush(QBrush(QColor(160, 160, 255)));\n}\n\nQCPFinancial::~QCPFinancial()\n{\n}\n\n/*! \\overload\n  \n  Replaces the current data container with the provided \\a data container.\n  \n  Since a QSharedPointer is used, multiple QCPFinancials may share the same data container safely.\n  Modifying the data in the container will then affect all financials that share the container.\n  Sharing can be achieved by simply exchanging the data containers wrapped in shared pointers:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpfinancial-datasharing-1\n  \n  If you do not wish to share containers, but create a copy from an existing container, rather use\n  the \\ref QCPDataContainer<DataType>::set method on the financial's data container directly:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpfinancial-datasharing-2\n  \n  \\see addData, timeSeriesToOhlc\n*/\nvoid QCPFinancial::setData(QSharedPointer<QCPFinancialDataContainer> data)\n{\n  mDataContainer = data;\n}\n\n/*! \\overload\n  \n  Replaces the current data with the provided points in \\a keys, \\a open, \\a high, \\a low and \\a\n  close. The provided vectors should have equal length. Else, the number of added points will be\n  the size of the smallest vector.\n  \n  If you can guarantee that the passed data points are sorted by \\a keys in ascending order, you\n  can set \\a alreadySorted to true, to improve performance by saving a sorting run.\n  \n  \\see addData, timeSeriesToOhlc\n*/\nvoid QCPFinancial::setData(const QVector<double> &keys, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close, bool alreadySorted)\n{\n  mDataContainer->clear();\n  addData(keys, open, high, low, close, alreadySorted);\n}\n\n/*!\n  Sets which representation style shall be used to display the OHLC data.\n*/\nvoid QCPFinancial::setChartStyle(QCPFinancial::ChartStyle style)\n{\n  mChartStyle = style;\n}\n\n/*!\n  Sets the width of the individual bars/candlesticks to \\a width in plot key coordinates.\n  \n  A typical choice is to set it to (or slightly less than) one bin interval width.\n*/\nvoid QCPFinancial::setWidth(double width)\n{\n  mWidth = width;\n}\n\n/*!\n  Sets how the width of the financial bars is defined. See the documentation of \\ref WidthType for\n  an explanation of the possible values for \\a widthType.\n\n  The default value is \\ref wtPlotCoords.\n\n  \\see setWidth\n*/\nvoid QCPFinancial::setWidthType(QCPFinancial::WidthType widthType)\n{\n  mWidthType = widthType;\n}\n\n/*!\n  Sets whether this chart shall contrast positive from negative trends per data point by using two\n  separate colors to draw the respective bars/candlesticks.\n  \n  If \\a twoColored is false, the normal plottable's pen and brush are used (\\ref setPen, \\ref\n  setBrush).\n  \n  \\see setPenPositive, setPenNegative, setBrushPositive, setBrushNegative\n*/\nvoid QCPFinancial::setTwoColored(bool twoColored)\n{\n  mTwoColored = twoColored;\n}\n\n/*!\n  If \\ref setTwoColored is set to true, this function controls the brush that is used to draw fills\n  of data points with a positive trend (i.e. bars/candlesticks with close >= open).\n  \n  If \\a twoColored is false, the normal plottable's pen and brush are used (\\ref setPen, \\ref\n  setBrush).\n  \n  \\see setBrushNegative, setPenPositive, setPenNegative\n*/\nvoid QCPFinancial::setBrushPositive(const QBrush &brush)\n{\n  mBrushPositive = brush;\n}\n\n/*!\n  If \\ref setTwoColored is set to true, this function controls the brush that is used to draw fills\n  of data points with a negative trend (i.e. bars/candlesticks with close < open).\n  \n  If \\a twoColored is false, the normal plottable's pen and brush are used (\\ref setPen, \\ref\n  setBrush).\n  \n  \\see setBrushPositive, setPenNegative, setPenPositive\n*/\nvoid QCPFinancial::setBrushNegative(const QBrush &brush)\n{\n  mBrushNegative = brush;\n}\n\n/*!\n  If \\ref setTwoColored is set to true, this function controls the pen that is used to draw\n  outlines of data points with a positive trend (i.e. bars/candlesticks with close >= open).\n  \n  If \\a twoColored is false, the normal plottable's pen and brush are used (\\ref setPen, \\ref\n  setBrush).\n  \n  \\see setPenNegative, setBrushPositive, setBrushNegative\n*/\nvoid QCPFinancial::setPenPositive(const QPen &pen)\n{\n  mPenPositive = pen;\n}\n\n/*!\n  If \\ref setTwoColored is set to true, this function controls the pen that is used to draw\n  outlines of data points with a negative trend (i.e. bars/candlesticks with close < open).\n  \n  If \\a twoColored is false, the normal plottable's pen and brush are used (\\ref setPen, \\ref\n  setBrush).\n  \n  \\see setPenPositive, setBrushNegative, setBrushPositive\n*/\nvoid QCPFinancial::setPenNegative(const QPen &pen)\n{\n  mPenNegative = pen;\n}\n\n/*! \\overload\n  \n  Adds the provided points in \\a keys, \\a open, \\a high, \\a low and \\a close to the current data.\n  The provided vectors should have equal length. Else, the number of added points will be the size\n  of the smallest vector.\n  \n  If you can guarantee that the passed data points are sorted by \\a keys in ascending order, you\n  can set \\a alreadySorted to true, to improve performance by saving a sorting run.\n  \n  Alternatively, you can also access and modify the data directly via the \\ref data method, which\n  returns a pointer to the internal data container.\n  \n  \\see timeSeriesToOhlc\n*/\nvoid QCPFinancial::addData(const QVector<double> &keys, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close, bool alreadySorted)\n{\n  if (keys.size() != open.size() || open.size() != high.size() || high.size() != low.size() || low.size() != close.size() || close.size() != keys.size())\n    qDebug() << Q_FUNC_INFO << \"keys, open, high, low, close have different sizes:\" << keys.size() << open.size() << high.size() << low.size() << close.size();\n  const int n = qMin(keys.size(), qMin(open.size(), qMin(high.size(), qMin(low.size(), close.size()))));\n  QVector<QCPFinancialData> tempData(n);\n  QVector<QCPFinancialData>::iterator it = tempData.begin();\n  const QVector<QCPFinancialData>::iterator itEnd = tempData.end();\n  int i = 0;\n  while (it != itEnd)\n  {\n    it->key = keys[i];\n    it->open = open[i];\n    it->high = high[i];\n    it->low = low[i];\n    it->close = close[i];\n    ++it;\n    ++i;\n  }\n  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write\n}\n\n/*! \\overload\n  \n  Adds the provided data point as \\a key, \\a open, \\a high, \\a low and \\a close to the current\n  data.\n  \n  Alternatively, you can also access and modify the data directly via the \\ref data method, which\n  returns a pointer to the internal data container.\n  \n  \\see timeSeriesToOhlc\n*/\nvoid QCPFinancial::addData(double key, double open, double high, double low, double close)\n{\n  mDataContainer->add(QCPFinancialData(key, open, high, low, close));\n}\n\n/*!\n  \\copydoc QCPPlottableInterface1D::selectTestRect\n*/\nQCPDataSelection QCPFinancial::selectTestRect(const QRectF &rect, bool onlySelectable) const\n{\n  QCPDataSelection result;\n  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())\n    return result;\n  if (!mKeyAxis || !mValueAxis)\n    return result;\n  \n  QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;\n  getVisibleDataBounds(visibleBegin, visibleEnd);\n  \n  for (QCPFinancialDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)\n  {\n    if (rect.intersects(selectionHitBox(it)))\n      result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false);\n  }\n  result.simplify();\n  return result;\n}\n\n/*!\n  Implements a selectTest specific to this plottable's point geometry.\n\n  If \\a details is not 0, it will be set to a \\ref QCPDataSelection, describing the closest data\n  point to \\a pos.\n  \n  \\seebaseclassmethod \\ref QCPAbstractPlottable::selectTest\n*/\ndouble QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(details)\n  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())\n    return -1;\n  if (!mKeyAxis || !mValueAxis)\n    return -1;\n  \n  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))\n  {\n    // get visible data range:\n    QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;\n    QCPFinancialDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();\n    getVisibleDataBounds(visibleBegin, visibleEnd);\n    // perform select test according to configured style:\n    double result = -1;\n    switch (mChartStyle)\n    {\n      case QCPFinancial::csOhlc:\n        result = ohlcSelectTest(pos, visibleBegin, visibleEnd, closestDataPoint); break;\n      case QCPFinancial::csCandlestick:\n        result = candlestickSelectTest(pos, visibleBegin, visibleEnd, closestDataPoint); break;\n    }\n    if (details)\n    {\n      int pointIndex = int(closestDataPoint-mDataContainer->constBegin());\n      details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));\n    }\n    return result;\n  }\n  \n  return -1;\n}\n\n/* inherits documentation from base class */\nQCPRange QCPFinancial::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const\n{\n  QCPRange range = mDataContainer->keyRange(foundRange, inSignDomain);\n  // determine exact range by including width of bars/flags:\n  if (foundRange)\n  {\n    if (inSignDomain != QCP::sdPositive || range.lower-mWidth*0.5 > 0)\n      range.lower -= mWidth*0.5;\n    if (inSignDomain != QCP::sdNegative || range.upper+mWidth*0.5 < 0)\n      range.upper += mWidth*0.5;\n  }\n  return range;\n}\n\n/* inherits documentation from base class */\nQCPRange QCPFinancial::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const\n{\n  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);\n}\n\n/*!\n  A convenience function that converts time series data (\\a value against \\a time) to OHLC binned\n  data points. The return value can then be passed on to \\ref QCPFinancialDataContainer::set(const\n  QCPFinancialDataContainer&).\n  \n  The size of the bins can be controlled with \\a timeBinSize in the same units as \\a time is given.\n  For example, if the unit of \\a time is seconds and single OHLC/Candlesticks should span an hour\n  each, set \\a timeBinSize to 3600.\n  \n  \\a timeBinOffset allows to control precisely at what \\a time coordinate a bin should start. The\n  value passed as \\a timeBinOffset doesn't need to be in the range encompassed by the \\a time keys.\n  It merely defines the mathematical offset/phase of the bins that will be used to process the\n  data.\n*/\nQCPFinancialDataContainer QCPFinancial::timeSeriesToOhlc(const QVector<double> &time, const QVector<double> &value, double timeBinSize, double timeBinOffset)\n{\n  QCPFinancialDataContainer data;\n  int count = qMin(time.size(), value.size());\n  if (count == 0)\n    return QCPFinancialDataContainer();\n  \n  QCPFinancialData currentBinData(0, value.first(), value.first(), value.first(), value.first());\n  int currentBinIndex = qFloor((time.first()-timeBinOffset)/timeBinSize+0.5);\n  for (int i=0; i<count; ++i)\n  {\n    int index = qFloor((time.at(i)-timeBinOffset)/timeBinSize+0.5);\n    if (currentBinIndex == index) // data point still in current bin, extend high/low:\n    {\n      if (value.at(i) < currentBinData.low) currentBinData.low = value.at(i);\n      if (value.at(i) > currentBinData.high) currentBinData.high = value.at(i);\n      if (i == count-1) // last data point is in current bin, finalize bin:\n      {\n        currentBinData.close = value.at(i);\n        currentBinData.key = timeBinOffset+(index)*timeBinSize;\n        data.add(currentBinData);\n      }\n    } else // data point not anymore in current bin, set close of old and open of new bin, and add old to map:\n    {\n      // finalize current bin:\n      currentBinData.close = value.at(i-1);\n      currentBinData.key = timeBinOffset+(index-1)*timeBinSize;\n      data.add(currentBinData);\n      // start next bin:\n      currentBinIndex = index;\n      currentBinData.open = value.at(i);\n      currentBinData.high = value.at(i);\n      currentBinData.low = value.at(i);\n    }\n  }\n  \n  return data;\n}\n\n/* inherits documentation from base class */\nvoid QCPFinancial::draw(QCPPainter *painter)\n{\n  // get visible data range:\n  QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;\n  getVisibleDataBounds(visibleBegin, visibleEnd);\n  \n  // loop over and draw segments of unselected/selected data:\n  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;\n  getDataSegments(selectedSegments, unselectedSegments);\n  allSegments << unselectedSegments << selectedSegments;\n  for (int i=0; i<allSegments.size(); ++i)\n  {\n    bool isSelectedSegment = i >= unselectedSegments.size();\n    QCPFinancialDataContainer::const_iterator begin = visibleBegin;\n    QCPFinancialDataContainer::const_iterator end = visibleEnd;\n    mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));\n    if (begin == end)\n      continue;\n    \n    // draw data segment according to configured style:\n    switch (mChartStyle)\n    {\n      case QCPFinancial::csOhlc:\n        drawOhlcPlot(painter, begin, end, isSelectedSegment); break;\n      case QCPFinancial::csCandlestick:\n        drawCandlestickPlot(painter, begin, end, isSelectedSegment); break;\n    }\n  }\n  \n  // draw other selection decoration that isn't just line/scatter pens and brushes:\n  if (mSelectionDecorator)\n    mSelectionDecorator->drawDecoration(painter, selection());\n}\n\n/* inherits documentation from base class */\nvoid QCPFinancial::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const\n{\n  painter->setAntialiasing(false); // legend icon especially of csCandlestick looks better without antialiasing\n  if (mChartStyle == csOhlc)\n  {\n    if (mTwoColored)\n    {\n      // draw upper left half icon with positive color:\n      painter->setBrush(mBrushPositive);\n      painter->setPen(mPenPositive);\n      painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));\n      painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));\n      painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));\n      painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));\n      // draw bottom right half icon with negative color:\n      painter->setBrush(mBrushNegative);\n      painter->setPen(mPenNegative);\n      painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));\n      painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));\n      painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));\n      painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));\n    } else\n    {\n      painter->setBrush(mBrush);\n      painter->setPen(mPen);\n      painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));\n      painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));\n      painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));\n    }\n  } else if (mChartStyle == csCandlestick)\n  {\n    if (mTwoColored)\n    {\n      // draw upper left half icon with positive color:\n      painter->setBrush(mBrushPositive);\n      painter->setPen(mPenPositive);\n      painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));\n      painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));\n      painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));\n      painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));\n      // draw bottom right half icon with negative color:\n      painter->setBrush(mBrushNegative);\n      painter->setPen(mPenNegative);\n      painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));\n      painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));\n      painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));\n      painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));\n    } else\n    {\n      painter->setBrush(mBrush);\n      painter->setPen(mPen);\n      painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));\n      painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));\n      painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));\n    }\n  }\n}\n\n/*! \\internal\n  \n  Draws the data from \\a begin to \\a end-1 as OHLC bars with the provided \\a painter.\n\n  This method is a helper function for \\ref draw. It is used when the chart style is \\ref csOhlc.\n*/\nvoid QCPFinancial::drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected)\n{\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n  \n  if (keyAxis->orientation() == Qt::Horizontal)\n  {\n    for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)\n    {\n      if (isSelected && mSelectionDecorator)\n        mSelectionDecorator->applyPen(painter);\n      else if (mTwoColored)\n        painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);\n      else\n        painter->setPen(mPen);\n      double keyPixel = keyAxis->coordToPixel(it->key);\n      double openPixel = valueAxis->coordToPixel(it->open);\n      double closePixel = valueAxis->coordToPixel(it->close);\n      // draw backbone:\n      painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->high)), QPointF(keyPixel, valueAxis->coordToPixel(it->low)));\n      // draw open:\n      double pixelWidth = getPixelWidth(it->key, keyPixel); // sign of this makes sure open/close are on correct sides\n      painter->drawLine(QPointF(keyPixel-pixelWidth, openPixel), QPointF(keyPixel, openPixel));\n      // draw close:\n      painter->drawLine(QPointF(keyPixel, closePixel), QPointF(keyPixel+pixelWidth, closePixel));\n    }\n  } else\n  {\n    for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)\n    {\n      if (isSelected && mSelectionDecorator)\n        mSelectionDecorator->applyPen(painter);\n      else if (mTwoColored)\n        painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);\n      else\n        painter->setPen(mPen);\n      double keyPixel = keyAxis->coordToPixel(it->key);\n      double openPixel = valueAxis->coordToPixel(it->open);\n      double closePixel = valueAxis->coordToPixel(it->close);\n      // draw backbone:\n      painter->drawLine(QPointF(valueAxis->coordToPixel(it->high), keyPixel), QPointF(valueAxis->coordToPixel(it->low), keyPixel));\n      // draw open:\n      double pixelWidth = getPixelWidth(it->key, keyPixel); // sign of this makes sure open/close are on correct sides\n      painter->drawLine(QPointF(openPixel, keyPixel-pixelWidth), QPointF(openPixel, keyPixel));\n      // draw close:\n      painter->drawLine(QPointF(closePixel, keyPixel), QPointF(closePixel, keyPixel+pixelWidth));\n    }\n  }\n}\n\n/*! \\internal\n  \n  Draws the data from \\a begin to \\a end-1 as Candlesticks with the provided \\a painter.\n\n  This method is a helper function for \\ref draw. It is used when the chart style is \\ref csCandlestick.\n*/\nvoid QCPFinancial::drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected)\n{\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n  \n  if (keyAxis->orientation() == Qt::Horizontal)\n  {\n    for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)\n    {\n      if (isSelected && mSelectionDecorator)\n      {\n        mSelectionDecorator->applyPen(painter);\n        mSelectionDecorator->applyBrush(painter);\n      } else if (mTwoColored)\n      {\n        painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);\n        painter->setBrush(it->close >= it->open ? mBrushPositive : mBrushNegative);\n      } else\n      {\n        painter->setPen(mPen);\n        painter->setBrush(mBrush);\n      }\n      double keyPixel = keyAxis->coordToPixel(it->key);\n      double openPixel = valueAxis->coordToPixel(it->open);\n      double closePixel = valueAxis->coordToPixel(it->close);\n      // draw high:\n      painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it->open, it->close))));\n      // draw low:\n      painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it->open, it->close))));\n      // draw open-close box:\n      double pixelWidth = getPixelWidth(it->key, keyPixel);\n      painter->drawRect(QRectF(QPointF(keyPixel-pixelWidth, closePixel), QPointF(keyPixel+pixelWidth, openPixel)));\n    }\n  } else // keyAxis->orientation() == Qt::Vertical\n  {\n    for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)\n    {\n      if (isSelected && mSelectionDecorator)\n      {\n        mSelectionDecorator->applyPen(painter);\n        mSelectionDecorator->applyBrush(painter);\n      } else if (mTwoColored)\n      {\n        painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);\n        painter->setBrush(it->close >= it->open ? mBrushPositive : mBrushNegative);\n      } else\n      {\n        painter->setPen(mPen);\n        painter->setBrush(mBrush);\n      }\n      double keyPixel = keyAxis->coordToPixel(it->key);\n      double openPixel = valueAxis->coordToPixel(it->open);\n      double closePixel = valueAxis->coordToPixel(it->close);\n      // draw high:\n      painter->drawLine(QPointF(valueAxis->coordToPixel(it->high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it->open, it->close)), keyPixel));\n      // draw low:\n      painter->drawLine(QPointF(valueAxis->coordToPixel(it->low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it->open, it->close)), keyPixel));\n      // draw open-close box:\n      double pixelWidth = getPixelWidth(it->key, keyPixel);\n      painter->drawRect(QRectF(QPointF(closePixel, keyPixel-pixelWidth), QPointF(openPixel, keyPixel+pixelWidth)));\n    }\n  }\n}\n\n/*! \\internal\n\n  This function is used to determine the width of the bar at coordinate \\a key, according to the\n  specified width (\\ref setWidth) and width type (\\ref setWidthType). Provide the pixel position of\n  \\a key in \\a keyPixel (because usually this was already calculated via \\ref QCPAxis::coordToPixel\n  when this function is called).\n\n  It returns the number of pixels the bar extends to higher keys, relative to the \\a key\n  coordinate. So with a non-reversed horizontal axis, the return value is positive. With a reversed\n  horizontal axis, the return value is negative. This is important so the open/close flags on the\n  \\ref csOhlc bar are drawn to the correct side.\n*/\ndouble QCPFinancial::getPixelWidth(double key, double keyPixel) const\n{\n  double result = 0;\n  switch (mWidthType)\n  {\n    case wtAbsolute:\n    {\n      if (mKeyAxis)\n        result = mWidth*0.5*mKeyAxis.data()->pixelOrientation();\n      break;\n    }\n    case wtAxisRectRatio:\n    {\n      if (mKeyAxis && mKeyAxis.data()->axisRect())\n      {\n        if (mKeyAxis.data()->orientation() == Qt::Horizontal)\n          result = mKeyAxis.data()->axisRect()->width()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();\n        else\n          result = mKeyAxis.data()->axisRect()->height()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();\n      } else\n        qDebug() << Q_FUNC_INFO << \"No key axis or axis rect defined\";\n      break;\n    }\n    case wtPlotCoords:\n    {\n      if (mKeyAxis)\n        result = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel;\n      else\n        qDebug() << Q_FUNC_INFO << \"No key axis defined\";\n      break;\n    }\n  }\n  return result;\n}\n\n/*! \\internal\n\n  This method is a helper function for \\ref selectTest. It is used to test for selection when the\n  chart style is \\ref csOhlc. It only tests against the data points between \\a begin and \\a end.\n  \n  Like \\ref selectTest, this method returns the shortest distance of \\a pos to the graphical\n  representation of the plottable, and \\a closestDataPoint will point to the respective data point.\n*/\ndouble QCPFinancial::ohlcSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const\n{\n  closestDataPoint = mDataContainer->constEnd();\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return -1; }\n\n  double minDistSqr = (std::numeric_limits<double>::max)();\n  if (keyAxis->orientation() == Qt::Horizontal)\n  {\n    for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)\n    {\n      double keyPixel = keyAxis->coordToPixel(it->key);\n      // calculate distance to backbone:\n      double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->high)), QCPVector2D(keyPixel, valueAxis->coordToPixel(it->low)));\n      if (currentDistSqr < minDistSqr)\n      {\n        minDistSqr = currentDistSqr;\n        closestDataPoint = it;\n      }\n    }\n  } else // keyAxis->orientation() == Qt::Vertical\n  {\n    for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)\n    {\n      double keyPixel = keyAxis->coordToPixel(it->key);\n      // calculate distance to backbone:\n      double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->high), keyPixel), QCPVector2D(valueAxis->coordToPixel(it->low), keyPixel));\n      if (currentDistSqr < minDistSqr)\n      {\n        minDistSqr = currentDistSqr;\n        closestDataPoint = it;\n      }\n    }\n  }\n  return qSqrt(minDistSqr);\n}\n\n/*! \\internal\n  \n  This method is a helper function for \\ref selectTest. It is used to test for selection when the\n  chart style is \\ref csCandlestick. It only tests against the data points between \\a begin and \\a\n  end.\n  \n  Like \\ref selectTest, this method returns the shortest distance of \\a pos to the graphical\n  representation of the plottable, and \\a closestDataPoint will point to the respective data point.\n*/\ndouble QCPFinancial::candlestickSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const\n{\n  closestDataPoint = mDataContainer->constEnd();\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return -1; }\n\n  double minDistSqr = (std::numeric_limits<double>::max)();\n  if (keyAxis->orientation() == Qt::Horizontal)\n  {\n    for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)\n    {\n      double currentDistSqr;\n      // determine whether pos is in open-close-box:\n      QCPRange boxKeyRange(it->key-mWidth*0.5, it->key+mWidth*0.5);\n      QCPRange boxValueRange(it->close, it->open);\n      double posKey, posValue;\n      pixelsToCoords(pos, posKey, posValue);\n      if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box\n      {\n        currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;\n      } else\n      {\n        // calculate distance to high/low lines:\n        double keyPixel = keyAxis->coordToPixel(it->key);\n        double highLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->high)), QCPVector2D(keyPixel, valueAxis->coordToPixel(qMax(it->open, it->close))));\n        double lowLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->low)), QCPVector2D(keyPixel, valueAxis->coordToPixel(qMin(it->open, it->close))));\n        currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);\n      }\n      if (currentDistSqr < minDistSqr)\n      {\n        minDistSqr = currentDistSqr;\n        closestDataPoint = it;\n      }\n    }\n  } else // keyAxis->orientation() == Qt::Vertical\n  {\n    for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)\n    {\n      double currentDistSqr;\n      // determine whether pos is in open-close-box:\n      QCPRange boxKeyRange(it->key-mWidth*0.5, it->key+mWidth*0.5);\n      QCPRange boxValueRange(it->close, it->open);\n      double posKey, posValue;\n      pixelsToCoords(pos, posKey, posValue);\n      if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box\n      {\n        currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;\n      } else\n      {\n        // calculate distance to high/low lines:\n        double keyPixel = keyAxis->coordToPixel(it->key);\n        double highLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->high), keyPixel), QCPVector2D(valueAxis->coordToPixel(qMax(it->open, it->close)), keyPixel));\n        double lowLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->low), keyPixel), QCPVector2D(valueAxis->coordToPixel(qMin(it->open, it->close)), keyPixel));\n        currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);\n      }\n      if (currentDistSqr < minDistSqr)\n      {\n        minDistSqr = currentDistSqr;\n        closestDataPoint = it;\n      }\n    }\n  }\n  return qSqrt(minDistSqr);\n}\n\n/*! \\internal\n  \n  called by the drawing methods to determine which data (key) range is visible at the current key\n  axis range setting, so only that needs to be processed.\n  \n  \\a begin returns an iterator to the lowest data point that needs to be taken into account when\n  plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \\a\n  begin may still be just outside the visible range.\n  \n  \\a end returns the iterator just above the highest data point that needs to be taken into\n  account. Same as before, \\a end may also lie just outside of the visible range\n  \n  if the plottable contains no data, both \\a begin and \\a end point to \\c constEnd.\n*/\nvoid QCPFinancial::getVisibleDataBounds(QCPFinancialDataContainer::const_iterator &begin, QCPFinancialDataContainer::const_iterator &end) const\n{\n  if (!mKeyAxis)\n  {\n    qDebug() << Q_FUNC_INFO << \"invalid key axis\";\n    begin = mDataContainer->constEnd();\n    end = mDataContainer->constEnd();\n    return;\n  }\n  begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower-mWidth*0.5); // subtract half width of ohlc/candlestick to include partially visible data points\n  end = mDataContainer->findEnd(mKeyAxis.data()->range().upper+mWidth*0.5); // add half width of ohlc/candlestick to include partially visible data points\n}\n\n/*!  \\internal\n\n  Returns the hit box in pixel coordinates that will be used for data selection with the selection\n  rect (\\ref selectTestRect), of the data point given by \\a it.\n*/\nQRectF QCPFinancial::selectionHitBox(QCPFinancialDataContainer::const_iterator it) const\n{\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return {}; }\n  \n  double keyPixel = keyAxis->coordToPixel(it->key);\n  double highPixel = valueAxis->coordToPixel(it->high);\n  double lowPixel = valueAxis->coordToPixel(it->low);\n  double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it->key-mWidth*0.5);\n  if (keyAxis->orientation() == Qt::Horizontal)\n    return QRectF(keyPixel-keyWidthPixels, highPixel, keyWidthPixels*2, lowPixel-highPixel).normalized();\n  else\n    return QRectF(highPixel, keyPixel-keyWidthPixels, lowPixel-highPixel, keyWidthPixels*2).normalized();\n}\n/* end of 'src/plottables/plottable-financial.cpp' */\n\n\n/* including file 'src/plottables/plottable-errorbar.cpp' */\n/* modified 2021-03-29T02:30:44, size 37679               */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPErrorBarsData\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPErrorBarsData\n  \\brief Holds the data of one single error bar for QCPErrorBars.\n\n  The stored data is:\n  \\li \\a errorMinus: how much the error bar extends towards negative coordinates from the data\n  point position\n  \\li \\a errorPlus: how much the error bar extends towards positive coordinates from the data point\n  position\n\n  The container for storing the error bar information is \\ref QCPErrorBarsDataContainer. It is a\n  typedef for <tt>QVector<\\ref QCPErrorBarsData></tt>.\n\n  \\see QCPErrorBarsDataContainer\n*/\n\n/*!\n  Constructs an error bar with errors set to zero.\n*/\nQCPErrorBarsData::QCPErrorBarsData() :\n  errorMinus(0),\n  errorPlus(0)\n{\n}\n\n/*!\n  Constructs an error bar with equal \\a error in both negative and positive direction.\n*/\nQCPErrorBarsData::QCPErrorBarsData(double error) :\n  errorMinus(error),\n  errorPlus(error)\n{\n}\n\n/*!\n  Constructs an error bar with negative and positive errors set to \\a errorMinus and \\a errorPlus,\n  respectively.\n*/\nQCPErrorBarsData::QCPErrorBarsData(double errorMinus, double errorPlus) :\n  errorMinus(errorMinus),\n  errorPlus(errorPlus)\n{\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPErrorBars\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPErrorBars\n  \\brief A plottable that adds a set of error bars to other plottables.\n\n  \\image html QCPErrorBars.png\n\n  The \\ref QCPErrorBars plottable can be attached to other one-dimensional plottables (e.g. \\ref\n  QCPGraph, \\ref QCPCurve, \\ref QCPBars, etc.) and equips them with error bars.\n\n  Use \\ref setDataPlottable to define for which plottable the \\ref QCPErrorBars shall display the\n  error bars. The orientation of the error bars can be controlled with \\ref setErrorType.\n\n  By using \\ref setData, you can supply the actual error data, either as symmetric error or\n  plus/minus asymmetric errors. \\ref QCPErrorBars only stores the error data. The absolute\n  key/value position of each error bar will be adopted from the configured data plottable. The\n  error data of the \\ref QCPErrorBars are associated one-to-one via their index to the data points\n  of the data plottable. You can directly access and manipulate the error bar data via \\ref data.\n\n  Set either of the plus/minus errors to NaN (<tt>qQNaN()</tt> or\n  <tt>std::numeric_limits<double>::quiet_NaN()</tt>) to not show the respective error bar on the data point at\n  that index.\n\n  \\section qcperrorbars-appearance Changing the appearance\n\n  The appearance of the error bars is defined by the pen (\\ref setPen), and the width of the\n  whiskers (\\ref setWhiskerWidth). Further, the error bar backbones may leave a gap around the data\n  point center to prevent that error bars are drawn too close to or even through scatter points.\n  This gap size can be controlled via \\ref setSymbolGap.\n*/\n\n/* start of documentation of inline functions */\n\n/*! \\fn QSharedPointer<QCPErrorBarsDataContainer> QCPErrorBars::data() const\n\n  Returns a shared pointer to the internal data storage of type \\ref QCPErrorBarsDataContainer. You\n  may use it to directly manipulate the error values, which may be more convenient and faster than\n  using the regular \\ref setData methods.\n*/\n\n/* end of documentation of inline functions */\n\n/*!\n  Constructs an error bars plottable which uses \\a keyAxis as its key axis (\"x\") and \\a valueAxis as its value\n  axis (\"y\"). \\a keyAxis and \\a valueAxis must reside in the same QCustomPlot instance and not have\n  the same orientation. If either of these restrictions is violated, a corresponding message is\n  printed to the debug output (qDebug), the construction is not aborted, though.\n\n  It is also important that the \\a keyAxis and \\a valueAxis are the same for the error bars\n  plottable and the data plottable that the error bars shall be drawn on (\\ref setDataPlottable).\n\n  The created \\ref QCPErrorBars is automatically registered with the QCustomPlot instance inferred\n  from \\a keyAxis. This QCustomPlot instance takes ownership of the \\ref QCPErrorBars, so do not\n  delete it manually but use \\ref QCustomPlot::removePlottable() instead.\n*/\nQCPErrorBars::QCPErrorBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :\n  QCPAbstractPlottable(keyAxis, valueAxis),\n  mDataContainer(new QVector<QCPErrorBarsData>),\n  mErrorType(etValueError),\n  mWhiskerWidth(9),\n  mSymbolGap(10)\n{\n  setPen(QPen(Qt::black, 0));\n  setBrush(Qt::NoBrush);\n}\n\nQCPErrorBars::~QCPErrorBars()\n{\n}\n\n/*! \\overload\n\n  Replaces the current data container with the provided \\a data container.\n\n  Since a QSharedPointer is used, multiple \\ref QCPErrorBars instances may share the same data\n  container safely. Modifying the data in the container will then affect all \\ref QCPErrorBars\n  instances that share the container. Sharing can be achieved by simply exchanging the data\n  containers wrapped in shared pointers:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcperrorbars-datasharing-1\n\n  If you do not wish to share containers, but create a copy from an existing container, assign the\n  data containers directly:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcperrorbars-datasharing-2\n  (This uses different notation compared with other plottables, because the \\ref QCPErrorBars\n  uses a \\c QVector<QCPErrorBarsData> as its data container, instead of a \\ref QCPDataContainer.)\n\n  \\see addData\n*/\nvoid QCPErrorBars::setData(QSharedPointer<QCPErrorBarsDataContainer> data)\n{\n  mDataContainer = data;\n}\n\n/*! \\overload\n\n  Sets symmetrical error values as specified in \\a error. The errors will be associated one-to-one\n  by the data point index to the associated data plottable (\\ref setDataPlottable).\n\n  You can directly access and manipulate the error bar data via \\ref data.\n\n  \\see addData\n*/\nvoid QCPErrorBars::setData(const QVector<double> &error)\n{\n  mDataContainer->clear();\n  addData(error);\n}\n\n/*! \\overload\n\n  Sets asymmetrical errors as specified in \\a errorMinus and \\a errorPlus. The errors will be\n  associated one-to-one by the data point index to the associated data plottable (\\ref\n  setDataPlottable).\n\n  You can directly access and manipulate the error bar data via \\ref data.\n\n  \\see addData\n*/\nvoid QCPErrorBars::setData(const QVector<double> &errorMinus, const QVector<double> &errorPlus)\n{\n  mDataContainer->clear();\n  addData(errorMinus, errorPlus);\n}\n\n/*!\n  Sets the data plottable to which the error bars will be applied. The error values specified e.g.\n  via \\ref setData will be associated one-to-one by the data point index to the data points of \\a\n  plottable. This means that the error bars will adopt the key/value coordinates of the data point\n  with the same index.\n\n  The passed \\a plottable must be a one-dimensional plottable, i.e. it must implement the \\ref\n  QCPPlottableInterface1D. Further, it must not be a \\ref QCPErrorBars instance itself. If either\n  of these restrictions is violated, a corresponding qDebug output is generated, and the data\n  plottable of this \\ref QCPErrorBars instance is set to zero.\n\n  For proper display, care must also be taken that the key and value axes of the \\a plottable match\n  those configured for this \\ref QCPErrorBars instance.\n*/\nvoid QCPErrorBars::setDataPlottable(QCPAbstractPlottable *plottable)\n{\n  if (plottable && qobject_cast<QCPErrorBars*>(plottable))\n  {\n    mDataPlottable = nullptr;\n    qDebug() << Q_FUNC_INFO << \"can't set another QCPErrorBars instance as data plottable\";\n    return;\n  }\n  if (plottable && !plottable->interface1D())\n  {\n    mDataPlottable = nullptr;\n    qDebug() << Q_FUNC_INFO << \"passed plottable doesn't implement 1d interface, can't associate with QCPErrorBars\";\n    return;\n  }\n  \n  mDataPlottable = plottable;\n}\n\n/*!\n  Sets in which orientation the error bars shall appear on the data points. If your data needs both\n  error dimensions, create two \\ref QCPErrorBars with different \\a type.\n*/\nvoid QCPErrorBars::setErrorType(ErrorType type)\n{\n  mErrorType = type;\n}\n\n/*!\n  Sets the width of the whiskers (the short bars at the end of the actual error bar backbones) to\n  \\a pixels.\n*/\nvoid QCPErrorBars::setWhiskerWidth(double pixels)\n{\n  mWhiskerWidth = pixels;\n}\n\n/*!\n  Sets the gap diameter around the data points that will be left out when drawing the error bar\n  backbones. This gap prevents that error bars are drawn too close to or even through scatter\n  points.\n*/\nvoid QCPErrorBars::setSymbolGap(double pixels)\n{\n  mSymbolGap = pixels;\n}\n\n/*! \\overload\n\n  Adds symmetrical error values as specified in \\a error. The errors will be associated one-to-one\n  by the data point index to the associated data plottable (\\ref setDataPlottable).\n\n  You can directly access and manipulate the error bar data via \\ref data.\n\n  \\see setData\n*/\nvoid QCPErrorBars::addData(const QVector<double> &error)\n{\n  addData(error, error);\n}\n\n/*! \\overload\n\n  Adds asymmetrical errors as specified in \\a errorMinus and \\a errorPlus. The errors will be\n  associated one-to-one by the data point index to the associated data plottable (\\ref\n  setDataPlottable).\n\n  You can directly access and manipulate the error bar data via \\ref data.\n\n  \\see setData\n*/\nvoid QCPErrorBars::addData(const QVector<double> &errorMinus, const QVector<double> &errorPlus)\n{\n  if (errorMinus.size() != errorPlus.size())\n    qDebug() << Q_FUNC_INFO << \"minus and plus error vectors have different sizes:\" << errorMinus.size() << errorPlus.size();\n  const int n = qMin(errorMinus.size(), errorPlus.size());\n  mDataContainer->reserve(n);\n  for (int i=0; i<n; ++i)\n    mDataContainer->append(QCPErrorBarsData(errorMinus.at(i), errorPlus.at(i)));\n}\n\n/*! \\overload\n\n  Adds a single symmetrical error bar as specified in \\a error. The errors will be associated\n  one-to-one by the data point index to the associated data plottable (\\ref setDataPlottable).\n\n  You can directly access and manipulate the error bar data via \\ref data.\n\n  \\see setData\n*/\nvoid QCPErrorBars::addData(double error)\n{\n  mDataContainer->append(QCPErrorBarsData(error));\n}\n\n/*! \\overload\n\n  Adds a single asymmetrical error bar as specified in \\a errorMinus and \\a errorPlus. The errors\n  will be associated one-to-one by the data point index to the associated data plottable (\\ref\n  setDataPlottable).\n\n  You can directly access and manipulate the error bar data via \\ref data.\n\n  \\see setData\n*/\nvoid QCPErrorBars::addData(double errorMinus, double errorPlus)\n{\n  mDataContainer->append(QCPErrorBarsData(errorMinus, errorPlus));\n}\n\n/* inherits documentation from base class */\nint QCPErrorBars::dataCount() const\n{\n  return mDataContainer->size();\n}\n\n/* inherits documentation from base class */\ndouble QCPErrorBars::dataMainKey(int index) const\n{\n  if (mDataPlottable)\n    return mDataPlottable->interface1D()->dataMainKey(index);\n  else\n    qDebug() << Q_FUNC_INFO << \"no data plottable set\";\n  return 0;\n}\n\n/* inherits documentation from base class */\ndouble QCPErrorBars::dataSortKey(int index) const\n{\n  if (mDataPlottable)\n    return mDataPlottable->interface1D()->dataSortKey(index);\n  else\n    qDebug() << Q_FUNC_INFO << \"no data plottable set\";\n  return 0;\n}\n\n/* inherits documentation from base class */\ndouble QCPErrorBars::dataMainValue(int index) const\n{\n  if (mDataPlottable)\n    return mDataPlottable->interface1D()->dataMainValue(index);\n  else\n    qDebug() << Q_FUNC_INFO << \"no data plottable set\";\n  return 0;\n}\n\n/* inherits documentation from base class */\nQCPRange QCPErrorBars::dataValueRange(int index) const\n{\n  if (mDataPlottable)\n  {\n    const double value = mDataPlottable->interface1D()->dataMainValue(index);\n    if (index >= 0 && index < mDataContainer->size() && mErrorType == etValueError)\n      return {value-mDataContainer->at(index).errorMinus, value+mDataContainer->at(index).errorPlus};\n    else\n      return {value, value};\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"no data plottable set\";\n    return {};\n  }\n}\n\n/* inherits documentation from base class */\nQPointF QCPErrorBars::dataPixelPosition(int index) const\n{\n  if (mDataPlottable)\n    return mDataPlottable->interface1D()->dataPixelPosition(index);\n  else\n    qDebug() << Q_FUNC_INFO << \"no data plottable set\";\n  return {};\n}\n\n/* inherits documentation from base class */\nbool QCPErrorBars::sortKeyIsMainKey() const\n{\n  if (mDataPlottable)\n  {\n    return mDataPlottable->interface1D()->sortKeyIsMainKey();\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"no data plottable set\";\n    return true;\n  }\n}\n\n/*!\n  \\copydoc QCPPlottableInterface1D::selectTestRect\n*/\nQCPDataSelection QCPErrorBars::selectTestRect(const QRectF &rect, bool onlySelectable) const\n{\n  QCPDataSelection result;\n  if (!mDataPlottable)\n    return result;\n  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())\n    return result;\n  if (!mKeyAxis || !mValueAxis)\n    return result;\n  \n  QCPErrorBarsDataContainer::const_iterator visibleBegin, visibleEnd;\n  getVisibleDataBounds(visibleBegin, visibleEnd, QCPDataRange(0, dataCount()));\n  \n  QVector<QLineF> backbones, whiskers;\n  for (QCPErrorBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)\n  {\n    backbones.clear();\n    whiskers.clear();\n    getErrorBarLines(it, backbones, whiskers);\n    foreach (const QLineF &backbone, backbones)\n    {\n      if (rectIntersectsLine(rect, backbone))\n      {\n        result.addDataRange(QCPDataRange(int(it-mDataContainer->constBegin()), int(it-mDataContainer->constBegin()+1)), false);\n        break;\n      }\n    }\n  }\n  result.simplify();\n  return result;\n}\n\n/* inherits documentation from base class */\nint QCPErrorBars::findBegin(double sortKey, bool expandedRange) const\n{\n  if (mDataPlottable)\n  {\n    if (mDataContainer->isEmpty())\n      return 0;\n    int beginIndex = mDataPlottable->interface1D()->findBegin(sortKey, expandedRange);\n    if (beginIndex >= mDataContainer->size())\n      beginIndex = mDataContainer->size()-1;\n    return beginIndex;\n  } else\n    qDebug() << Q_FUNC_INFO << \"no data plottable set\";\n  return 0;\n}\n\n/* inherits documentation from base class */\nint QCPErrorBars::findEnd(double sortKey, bool expandedRange) const\n{\n  if (mDataPlottable)\n  {\n    if (mDataContainer->isEmpty())\n      return 0;\n    int endIndex = mDataPlottable->interface1D()->findEnd(sortKey, expandedRange);\n    if (endIndex > mDataContainer->size())\n      endIndex = mDataContainer->size();\n    return endIndex;\n  } else\n    qDebug() << Q_FUNC_INFO << \"no data plottable set\";\n  return 0;\n}\n\n/*!\n  Implements a selectTest specific to this plottable's point geometry.\n\n  If \\a details is not 0, it will be set to a \\ref QCPDataSelection, describing the closest data\n  point to \\a pos.\n  \n  \\seebaseclassmethod \\ref QCPAbstractPlottable::selectTest\n*/\ndouble QCPErrorBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  if (!mDataPlottable) return -1;\n  \n  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())\n    return -1;\n  if (!mKeyAxis || !mValueAxis)\n    return -1;\n  \n  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()) || mParentPlot->interactions().testFlag(QCP::iSelectPlottablesBeyondAxisRect))\n  {\n    QCPErrorBarsDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();\n    double result = pointDistance(pos, closestDataPoint);\n    if (details)\n    {\n      int pointIndex = int(closestDataPoint-mDataContainer->constBegin());\n      details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));\n    }\n    return result;\n  } else\n    return -1;\n}\n\n/* inherits documentation from base class */\nvoid QCPErrorBars::draw(QCPPainter *painter)\n{\n  if (!mDataPlottable) return;\n  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n  if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return;\n  \n  // if the sort key isn't the main key, we must check the visibility for each data point/error bar individually\n  // (getVisibleDataBounds applies range restriction, but otherwise can only return full data range):\n  bool checkPointVisibility = !mDataPlottable->interface1D()->sortKeyIsMainKey();\n      \n    // check data validity if flag set:\n#ifdef QCUSTOMPLOT_CHECK_DATA\n  QCPErrorBarsDataContainer::const_iterator it;\n  for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)\n  {\n    if (QCP::isInvalidData(it->errorMinus, it->errorPlus))\n      qDebug() << Q_FUNC_INFO << \"Data point at index\" << it-mDataContainer->constBegin() << \"invalid.\" << \"Plottable name:\" << name();\n  }\n#endif\n  \n  applyDefaultAntialiasingHint(painter);\n  painter->setBrush(Qt::NoBrush);\n  // loop over and draw segments of unselected/selected data:\n  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;\n  getDataSegments(selectedSegments, unselectedSegments);\n  allSegments << unselectedSegments << selectedSegments;\n  QVector<QLineF> backbones, whiskers;\n  for (int i=0; i<allSegments.size(); ++i)\n  {\n    QCPErrorBarsDataContainer::const_iterator begin, end;\n    getVisibleDataBounds(begin, end, allSegments.at(i));\n    if (begin == end)\n      continue;\n    \n    bool isSelectedSegment = i >= unselectedSegments.size();\n    if (isSelectedSegment && mSelectionDecorator)\n      mSelectionDecorator->applyPen(painter);\n    else\n      painter->setPen(mPen);\n    if (painter->pen().capStyle() == Qt::SquareCap)\n    {\n      QPen capFixPen(painter->pen());\n      capFixPen.setCapStyle(Qt::FlatCap);\n      painter->setPen(capFixPen);\n    }\n    backbones.clear();\n    whiskers.clear();\n    for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it)\n    {\n      if (!checkPointVisibility || errorBarVisible(int(it-mDataContainer->constBegin())))\n        getErrorBarLines(it, backbones, whiskers);\n    }\n    painter->drawLines(backbones);\n    painter->drawLines(whiskers);\n  }\n  \n  // draw other selection decoration that isn't just line/scatter pens and brushes:\n  if (mSelectionDecorator)\n    mSelectionDecorator->drawDecoration(painter, selection());\n}\n\n/* inherits documentation from base class */\nvoid QCPErrorBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const\n{\n  applyDefaultAntialiasingHint(painter);\n  painter->setPen(mPen);\n  if (mErrorType == etValueError && mValueAxis && mValueAxis->orientation() == Qt::Vertical)\n  {\n    painter->drawLine(QLineF(rect.center().x(), rect.top()+2, rect.center().x(), rect.bottom()-1));\n    painter->drawLine(QLineF(rect.center().x()-4, rect.top()+2, rect.center().x()+4, rect.top()+2));\n    painter->drawLine(QLineF(rect.center().x()-4, rect.bottom()-1, rect.center().x()+4, rect.bottom()-1));\n  } else\n  {\n    painter->drawLine(QLineF(rect.left()+2, rect.center().y(), rect.right()-2, rect.center().y()));\n    painter->drawLine(QLineF(rect.left()+2, rect.center().y()-4, rect.left()+2, rect.center().y()+4));\n    painter->drawLine(QLineF(rect.right()-2, rect.center().y()-4, rect.right()-2, rect.center().y()+4));\n  }\n}\n\n/* inherits documentation from base class */\nQCPRange QCPErrorBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const\n{\n  if (!mDataPlottable)\n  {\n    foundRange = false;\n    return {};\n  }\n  \n  QCPRange range;\n  bool haveLower = false;\n  bool haveUpper = false;\n  QCPErrorBarsDataContainer::const_iterator it;\n  for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)\n  {\n    if (mErrorType == etValueError)\n    {\n      // error bar doesn't extend in key dimension (except whisker but we ignore that here), so only use data point center\n      const double current = mDataPlottable->interface1D()->dataMainKey(int(it-mDataContainer->constBegin()));\n      if (qIsNaN(current)) continue;\n      if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))\n      {\n        if (current < range.lower || !haveLower)\n        {\n          range.lower = current;\n          haveLower = true;\n        }\n        if (current > range.upper || !haveUpper)\n        {\n          range.upper = current;\n          haveUpper = true;\n        }\n      }\n    } else // mErrorType == etKeyError\n    {\n      const double dataKey = mDataPlottable->interface1D()->dataMainKey(int(it-mDataContainer->constBegin()));\n      if (qIsNaN(dataKey)) continue;\n      // plus error:\n      double current = dataKey + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus);\n      if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))\n      {\n        if (current > range.upper || !haveUpper)\n        {\n          range.upper = current;\n          haveUpper = true;\n        }\n      }\n      // minus error:\n      current = dataKey - (qIsNaN(it->errorMinus) ? 0 : it->errorMinus);\n      if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))\n      {\n        if (current < range.lower || !haveLower)\n        {\n          range.lower = current;\n          haveLower = true;\n        }\n      }\n    }\n  }\n  \n  if (haveUpper && !haveLower)\n  {\n    range.lower = range.upper;\n    haveLower = true;\n  } else if (haveLower && !haveUpper)\n  {\n    range.upper = range.lower;\n    haveUpper = true;\n  }\n  \n  foundRange = haveLower && haveUpper;\n  return range;\n}\n\n/* inherits documentation from base class */\nQCPRange QCPErrorBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const\n{\n  if (!mDataPlottable)\n  {\n    foundRange = false;\n    return {};\n  }\n  \n  QCPRange range;\n  const bool restrictKeyRange = inKeyRange != QCPRange();\n  bool haveLower = false;\n  bool haveUpper = false;\n  QCPErrorBarsDataContainer::const_iterator itBegin = mDataContainer->constBegin();\n  QCPErrorBarsDataContainer::const_iterator itEnd = mDataContainer->constEnd();\n  if (mDataPlottable->interface1D()->sortKeyIsMainKey() && restrictKeyRange)\n  {\n    itBegin = mDataContainer->constBegin()+findBegin(inKeyRange.lower, false);\n    itEnd = mDataContainer->constBegin()+findEnd(inKeyRange.upper, false);\n  }\n  for (QCPErrorBarsDataContainer::const_iterator it = itBegin; it != itEnd; ++it)\n  {\n    if (restrictKeyRange)\n    {\n      const double dataKey = mDataPlottable->interface1D()->dataMainKey(int(it-mDataContainer->constBegin()));\n      if (dataKey < inKeyRange.lower || dataKey > inKeyRange.upper)\n        continue;\n    }\n    if (mErrorType == etValueError)\n    {\n      const double dataValue = mDataPlottable->interface1D()->dataMainValue(int(it-mDataContainer->constBegin()));\n      if (qIsNaN(dataValue)) continue;\n      // plus error:\n      double current = dataValue + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus);\n      if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))\n      {\n        if (current > range.upper || !haveUpper)\n        {\n          range.upper = current;\n          haveUpper = true;\n        }\n      }\n      // minus error:\n      current = dataValue - (qIsNaN(it->errorMinus) ? 0 : it->errorMinus);\n      if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))\n      {\n        if (current < range.lower || !haveLower)\n        {\n          range.lower = current;\n          haveLower = true;\n        }\n      }\n    } else // mErrorType == etKeyError\n    {\n      // error bar doesn't extend in value dimension (except whisker but we ignore that here), so only use data point center\n      const double current = mDataPlottable->interface1D()->dataMainValue(int(it-mDataContainer->constBegin()));\n      if (qIsNaN(current)) continue;\n      if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))\n      {\n        if (current < range.lower || !haveLower)\n        {\n          range.lower = current;\n          haveLower = true;\n        }\n        if (current > range.upper || !haveUpper)\n        {\n          range.upper = current;\n          haveUpper = true;\n        }\n      }\n    }\n  }\n  \n  if (haveUpper && !haveLower)\n  {\n    range.lower = range.upper;\n    haveLower = true;\n  } else if (haveLower && !haveUpper)\n  {\n    range.upper = range.lower;\n    haveUpper = true;\n  }\n  \n  foundRange = haveLower && haveUpper;\n  return range;\n}\n\n/*! \\internal\n\n  Calculates the lines that make up the error bar belonging to the data point \\a it.\n\n  The resulting lines are added to \\a backbones and \\a whiskers. The vectors are not cleared, so\n  calling this method with different \\a it but the same \\a backbones and \\a whiskers allows to\n  accumulate lines for multiple data points.\n\n  This method assumes that \\a it is a valid iterator within the bounds of this \\ref QCPErrorBars\n  instance and within the bounds of the associated data plottable.\n*/\nvoid QCPErrorBars::getErrorBarLines(QCPErrorBarsDataContainer::const_iterator it, QVector<QLineF> &backbones, QVector<QLineF> &whiskers) const\n{\n  if (!mDataPlottable) return;\n  \n  int index = int(it-mDataContainer->constBegin());\n  QPointF centerPixel = mDataPlottable->interface1D()->dataPixelPosition(index);\n  if (qIsNaN(centerPixel.x()) || qIsNaN(centerPixel.y()))\n    return;\n  QCPAxis *errorAxis = mErrorType == etValueError ? mValueAxis.data() : mKeyAxis.data();\n  QCPAxis *orthoAxis = mErrorType == etValueError ? mKeyAxis.data() : mValueAxis.data();\n  const double centerErrorAxisPixel = errorAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y();\n  const double centerOrthoAxisPixel = orthoAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y();\n  const double centerErrorAxisCoord = errorAxis->pixelToCoord(centerErrorAxisPixel); // depending on plottable, this might be different from just mDataPlottable->interface1D()->dataMainKey/Value\n  const double symbolGap = mSymbolGap*0.5*errorAxis->pixelOrientation();\n  // plus error:\n  double errorStart, errorEnd;\n  if (!qIsNaN(it->errorPlus))\n  {\n    errorStart = centerErrorAxisPixel+symbolGap;\n    errorEnd = errorAxis->coordToPixel(centerErrorAxisCoord+it->errorPlus);\n    if (errorAxis->orientation() == Qt::Vertical)\n    {\n      if ((errorStart > errorEnd) != errorAxis->rangeReversed())\n        backbones.append(QLineF(centerOrthoAxisPixel, errorStart, centerOrthoAxisPixel, errorEnd));\n      whiskers.append(QLineF(centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5, errorEnd));\n    } else\n    {\n      if ((errorStart < errorEnd) != errorAxis->rangeReversed())\n        backbones.append(QLineF(errorStart, centerOrthoAxisPixel, errorEnd, centerOrthoAxisPixel));\n      whiskers.append(QLineF(errorEnd, centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5));\n    }\n  }\n  // minus error:\n  if (!qIsNaN(it->errorMinus))\n  {\n    errorStart = centerErrorAxisPixel-symbolGap;\n    errorEnd = errorAxis->coordToPixel(centerErrorAxisCoord-it->errorMinus);\n    if (errorAxis->orientation() == Qt::Vertical)\n    {\n      if ((errorStart < errorEnd) != errorAxis->rangeReversed())\n        backbones.append(QLineF(centerOrthoAxisPixel, errorStart, centerOrthoAxisPixel, errorEnd));\n      whiskers.append(QLineF(centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5, errorEnd));\n    } else\n    {\n      if ((errorStart > errorEnd) != errorAxis->rangeReversed())\n        backbones.append(QLineF(errorStart, centerOrthoAxisPixel, errorEnd, centerOrthoAxisPixel));\n      whiskers.append(QLineF(errorEnd, centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5));\n    }\n  }\n}\n\n/*! \\internal\n\n  This method outputs the currently visible data range via \\a begin and \\a end. The returned range\n  will also never exceed \\a rangeRestriction.\n\n  Since error bars with type \\ref etKeyError may extend to arbitrarily positive and negative key\n  coordinates relative to their data point key, this method checks all outer error bars whether\n  they truly don't reach into the visible portion of the axis rect, by calling \\ref\n  errorBarVisible. On the other hand error bars with type \\ref etValueError that are associated\n  with data plottables whose sort key is equal to the main key (see \\ref qcpdatacontainer-datatype\n  \"QCPDataContainer DataType\") can be handled very efficiently by finding the visible range of\n  error bars through binary search (\\ref QCPPlottableInterface1D::findBegin and \\ref\n  QCPPlottableInterface1D::findEnd).\n\n  If the plottable's sort key is not equal to the main key, this method returns the full data\n  range, only restricted by \\a rangeRestriction. Drawing optimization then has to be done on a\n  point-by-point basis in the \\ref draw method.\n*/\nvoid QCPErrorBars::getVisibleDataBounds(QCPErrorBarsDataContainer::const_iterator &begin, QCPErrorBarsDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const\n{\n  QCPAxis *keyAxis = mKeyAxis.data();\n  QCPAxis *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis)\n  {\n    qDebug() << Q_FUNC_INFO << \"invalid key or value axis\";\n    end = mDataContainer->constEnd();\n    begin = end;\n    return;\n  }\n  if (!mDataPlottable || rangeRestriction.isEmpty())\n  {\n    end = mDataContainer->constEnd();\n    begin = end;\n    return;\n  }\n  if (!mDataPlottable->interface1D()->sortKeyIsMainKey())\n  {\n    // if the sort key isn't the main key, it's not possible to find a contiguous range of visible\n    // data points, so this method then only applies the range restriction and otherwise returns\n    // the full data range. Visibility checks must be done on a per-datapoin-basis during drawing\n    QCPDataRange dataRange(0, mDataContainer->size());\n    dataRange = dataRange.bounded(rangeRestriction);\n    begin = mDataContainer->constBegin()+dataRange.begin();\n    end = mDataContainer->constBegin()+dataRange.end();\n    return;\n  }\n  \n  // get visible data range via interface from data plottable, and then restrict to available error data points:\n  const int n = qMin(mDataContainer->size(), mDataPlottable->interface1D()->dataCount());\n  int beginIndex = mDataPlottable->interface1D()->findBegin(keyAxis->range().lower);\n  int endIndex = mDataPlottable->interface1D()->findEnd(keyAxis->range().upper);\n  int i = beginIndex;\n  while (i > 0 && i < n && i > rangeRestriction.begin())\n  {\n    if (errorBarVisible(i))\n      beginIndex = i;\n    --i;\n  }\n  i = endIndex;\n  while (i >= 0 && i < n && i < rangeRestriction.end())\n  {\n    if (errorBarVisible(i))\n      endIndex = i+1;\n    ++i;\n  }\n  QCPDataRange dataRange(beginIndex, endIndex);\n  dataRange = dataRange.bounded(rangeRestriction.bounded(QCPDataRange(0, mDataContainer->size())));\n  begin = mDataContainer->constBegin()+dataRange.begin();\n  end = mDataContainer->constBegin()+dataRange.end();\n}\n\n/*! \\internal\n\n  Calculates the minimum distance in pixels the error bars' representation has from the given \\a\n  pixelPoint. This is used to determine whether the error bar was clicked or not, e.g. in \\ref\n  selectTest. The closest data point to \\a pixelPoint is returned in \\a closestData.\n*/\ndouble QCPErrorBars::pointDistance(const QPointF &pixelPoint, QCPErrorBarsDataContainer::const_iterator &closestData) const\n{\n  closestData = mDataContainer->constEnd();\n  if (!mDataPlottable || mDataContainer->isEmpty())\n    return -1.0;\n  if (!mKeyAxis || !mValueAxis)\n  {\n    qDebug() << Q_FUNC_INFO << \"invalid key or value axis\";\n    return -1.0;\n  }\n  \n  QCPErrorBarsDataContainer::const_iterator begin, end;\n  getVisibleDataBounds(begin, end, QCPDataRange(0, dataCount()));\n  \n  // calculate minimum distances to error backbones (whiskers are ignored for speed) and find closestData iterator:\n  double minDistSqr = (std::numeric_limits<double>::max)();\n  QVector<QLineF> backbones, whiskers;\n  for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it)\n  {\n    getErrorBarLines(it, backbones, whiskers);\n    foreach (const QLineF &backbone, backbones)\n    {\n      const double currentDistSqr = QCPVector2D(pixelPoint).distanceSquaredToLine(backbone);\n      if (currentDistSqr < minDistSqr)\n      {\n        minDistSqr = currentDistSqr;\n        closestData = it;\n      }\n    }\n  }\n  return qSqrt(minDistSqr);\n}\n\n/*! \\internal\n\n  \\note This method is identical to \\ref QCPAbstractPlottable1D::getDataSegments but needs to be\n  reproduced here since the \\ref QCPErrorBars plottable, as a special case that doesn't have its\n  own key/value data coordinates, doesn't derive from \\ref QCPAbstractPlottable1D. See the\n  documentation there for details.\n*/\nvoid QCPErrorBars::getDataSegments(QList<QCPDataRange> &selectedSegments, QList<QCPDataRange> &unselectedSegments) const\n{\n  selectedSegments.clear();\n  unselectedSegments.clear();\n  if (mSelectable == QCP::stWhole) // stWhole selection type draws the entire plottable with selected style if mSelection isn't empty\n  {\n    if (selected())\n      selectedSegments << QCPDataRange(0, dataCount());\n    else\n      unselectedSegments << QCPDataRange(0, dataCount());\n  } else\n  {\n    QCPDataSelection sel(selection());\n    sel.simplify();\n    selectedSegments = sel.dataRanges();\n    unselectedSegments = sel.inverse(QCPDataRange(0, dataCount())).dataRanges();\n  }\n}\n\n/*! \\internal\n\n  Returns whether the error bar at the specified \\a index is visible within the current key axis\n  range.\n\n  This method assumes for performance reasons without checking that the key axis, the value axis,\n  and the data plottable (\\ref setDataPlottable) are not \\c nullptr and that \\a index is within\n  valid bounds of this \\ref QCPErrorBars instance and the bounds of the data plottable.\n*/\nbool QCPErrorBars::errorBarVisible(int index) const\n{\n  QPointF centerPixel = mDataPlottable->interface1D()->dataPixelPosition(index);\n  const double centerKeyPixel = mKeyAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y();\n  if (qIsNaN(centerKeyPixel))\n    return false;\n  \n  double keyMin, keyMax;\n  if (mErrorType == etKeyError)\n  {\n    const double centerKey = mKeyAxis->pixelToCoord(centerKeyPixel);\n    const double errorPlus = mDataContainer->at(index).errorPlus;\n    const double errorMinus = mDataContainer->at(index).errorMinus;\n    keyMax = centerKey+(qIsNaN(errorPlus) ? 0 : errorPlus);\n    keyMin = centerKey-(qIsNaN(errorMinus) ? 0 : errorMinus);\n  } else // mErrorType == etValueError\n  {\n    keyMax = mKeyAxis->pixelToCoord(centerKeyPixel+mWhiskerWidth*0.5*mKeyAxis->pixelOrientation());\n    keyMin = mKeyAxis->pixelToCoord(centerKeyPixel-mWhiskerWidth*0.5*mKeyAxis->pixelOrientation());\n  }\n  return ((keyMax > mKeyAxis->range().lower) && (keyMin < mKeyAxis->range().upper));\n}\n\n/*! \\internal\n\n  Returns whether \\a line intersects (or is contained in) \\a pixelRect.\n\n  \\a line is assumed to be either perfectly horizontal or perfectly vertical, as is the case for\n  error bar lines.\n*/\nbool QCPErrorBars::rectIntersectsLine(const QRectF &pixelRect, const QLineF &line) const\n{\n  if (pixelRect.left() > line.x1() && pixelRect.left() > line.x2())\n    return false;\n  else if (pixelRect.right() < line.x1() && pixelRect.right() < line.x2())\n    return false;\n  else if (pixelRect.top() > line.y1() && pixelRect.top() > line.y2())\n    return false;\n  else if (pixelRect.bottom() < line.y1() && pixelRect.bottom() < line.y2())\n    return false;\n  else\n    return true;\n}\n/* end of 'src/plottables/plottable-errorbar.cpp' */\n\n\n/* including file 'src/items/item-straightline.cpp' */\n/* modified 2021-03-29T02:30:44, size 7596          */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPItemStraightLine\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPItemStraightLine\n  \\brief A straight line that spans infinitely in both directions\n\n  \\image html QCPItemStraightLine.png \"Straight line example. Blue dotted circles are anchors, solid blue discs are positions.\"\n\n  It has two positions, \\a point1 and \\a point2, which define the straight line.\n*/\n\n/*!\n  Creates a straight line item and sets default values.\n  \n  The created item is automatically registered with \\a parentPlot. This QCustomPlot instance takes\n  ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead.\n*/\nQCPItemStraightLine::QCPItemStraightLine(QCustomPlot *parentPlot) :\n  QCPAbstractItem(parentPlot),\n  point1(createPosition(QLatin1String(\"point1\"))),\n  point2(createPosition(QLatin1String(\"point2\")))\n{\n  point1->setCoords(0, 0);\n  point2->setCoords(1, 1);\n  \n  setPen(QPen(Qt::black));\n  setSelectedPen(QPen(Qt::blue,2));\n}\n\nQCPItemStraightLine::~QCPItemStraightLine()\n{\n}\n\n/*!\n  Sets the pen that will be used to draw the line\n  \n  \\see setSelectedPen\n*/\nvoid QCPItemStraightLine::setPen(const QPen &pen)\n{\n  mPen = pen;\n}\n\n/*!\n  Sets the pen that will be used to draw the line when selected\n  \n  \\see setPen, setSelected\n*/\nvoid QCPItemStraightLine::setSelectedPen(const QPen &pen)\n{\n  mSelectedPen = pen;\n}\n\n/* inherits documentation from base class */\ndouble QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(details)\n  if (onlySelectable && !mSelectable)\n    return -1;\n  \n  return QCPVector2D(pos).distanceToStraightLine(point1->pixelPosition(), point2->pixelPosition()-point1->pixelPosition());\n}\n\n/* inherits documentation from base class */\nvoid QCPItemStraightLine::draw(QCPPainter *painter)\n{\n  QCPVector2D start(point1->pixelPosition());\n  QCPVector2D end(point2->pixelPosition());\n  // get visible segment of straight line inside clipRect:\n  int clipPad = qCeil(mainPen().widthF());\n  QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));\n  // paint visible segment, if existent:\n  if (!line.isNull())\n  {\n    painter->setPen(mainPen());\n    painter->drawLine(line);\n  }\n}\n\n/*! \\internal\n\n  Returns the section of the straight line defined by \\a base and direction vector \\a\n  vec, that is visible in the specified \\a rect.\n  \n  This is a helper function for \\ref draw.\n*/\nQLineF QCPItemStraightLine::getRectClippedStraightLine(const QCPVector2D &base, const QCPVector2D &vec, const QRect &rect) const\n{\n  double bx, by;\n  double gamma;\n  QLineF result;\n  if (vec.x() == 0 && vec.y() == 0)\n    return result;\n  if (qFuzzyIsNull(vec.x())) // line is vertical\n  {\n    // check top of rect:\n    bx = rect.left();\n    by = rect.top();\n    gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();\n    if (gamma >= 0 && gamma <= rect.width())\n      result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical\n  } else if (qFuzzyIsNull(vec.y())) // line is horizontal\n  {\n    // check left of rect:\n    bx = rect.left();\n    by = rect.top();\n    gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();\n    if (gamma >= 0 && gamma <= rect.height())\n      result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal\n  } else // line is skewed\n  {\n    QList<QCPVector2D> pointVectors;\n    // check top of rect:\n    bx = rect.left();\n    by = rect.top();\n    gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();\n    if (gamma >= 0 && gamma <= rect.width())\n      pointVectors.append(QCPVector2D(bx+gamma, by));\n    // check bottom of rect:\n    bx = rect.left();\n    by = rect.bottom();\n    gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();\n    if (gamma >= 0 && gamma <= rect.width())\n      pointVectors.append(QCPVector2D(bx+gamma, by));\n    // check left of rect:\n    bx = rect.left();\n    by = rect.top();\n    gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();\n    if (gamma >= 0 && gamma <= rect.height())\n      pointVectors.append(QCPVector2D(bx, by+gamma));\n    // check right of rect:\n    bx = rect.right();\n    by = rect.top();\n    gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();\n    if (gamma >= 0 && gamma <= rect.height())\n      pointVectors.append(QCPVector2D(bx, by+gamma));\n    \n    // evaluate points:\n    if (pointVectors.size() == 2)\n    {\n      result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());\n    } else if (pointVectors.size() > 2)\n    {\n      // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:\n      double distSqrMax = 0;\n      QCPVector2D pv1, pv2;\n      for (int i=0; i<pointVectors.size()-1; ++i)\n      {\n        for (int k=i+1; k<pointVectors.size(); ++k)\n        {\n          double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();\n          if (distSqr > distSqrMax)\n          {\n            pv1 = pointVectors.at(i);\n            pv2 = pointVectors.at(k);\n            distSqrMax = distSqr;\n          }\n        }\n      }\n      result.setPoints(pv1.toPointF(), pv2.toPointF());\n    }\n  }\n  return result;\n}\n\n/*! \\internal\n\n  Returns the pen that should be used for drawing lines. Returns mPen when the\n  item is not selected and mSelectedPen when it is.\n*/\nQPen QCPItemStraightLine::mainPen() const\n{\n  return mSelected ? mSelectedPen : mPen;\n}\n/* end of 'src/items/item-straightline.cpp' */\n\n\n/* including file 'src/items/item-line.cpp' */\n/* modified 2021-03-29T02:30:44, size 8525  */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPItemLine\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPItemLine\n  \\brief A line from one point to another\n\n  \\image html QCPItemLine.png \"Line example. Blue dotted circles are anchors, solid blue discs are positions.\"\n\n  It has two positions, \\a start and \\a end, which define the end points of the line.\n  \n  With \\ref setHead and \\ref setTail you may set different line ending styles, e.g. to create an arrow.\n*/\n\n/*!\n  Creates a line item and sets default values.\n  \n  The created item is automatically registered with \\a parentPlot. This QCustomPlot instance takes\n  ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead.\n*/\nQCPItemLine::QCPItemLine(QCustomPlot *parentPlot) :\n  QCPAbstractItem(parentPlot),\n  start(createPosition(QLatin1String(\"start\"))),\n  end(createPosition(QLatin1String(\"end\")))\n{\n  start->setCoords(0, 0);\n  end->setCoords(1, 1);\n  \n  setPen(QPen(Qt::black));\n  setSelectedPen(QPen(Qt::blue,2));\n}\n\nQCPItemLine::~QCPItemLine()\n{\n}\n\n/*!\n  Sets the pen that will be used to draw the line\n  \n  \\see setSelectedPen\n*/\nvoid QCPItemLine::setPen(const QPen &pen)\n{\n  mPen = pen;\n}\n\n/*!\n  Sets the pen that will be used to draw the line when selected\n  \n  \\see setPen, setSelected\n*/\nvoid QCPItemLine::setSelectedPen(const QPen &pen)\n{\n  mSelectedPen = pen;\n}\n\n/*!\n  Sets the line ending style of the head. The head corresponds to the \\a end position.\n  \n  Note that due to the overloaded QCPLineEnding constructor, you may directly specify\n  a QCPLineEnding::EndingStyle here, e.g. \\code setHead(QCPLineEnding::esSpikeArrow) \\endcode\n  \n  \\see setTail\n*/\nvoid QCPItemLine::setHead(const QCPLineEnding &head)\n{\n  mHead = head;\n}\n\n/*!\n  Sets the line ending style of the tail. The tail corresponds to the \\a start position.\n  \n  Note that due to the overloaded QCPLineEnding constructor, you may directly specify\n  a QCPLineEnding::EndingStyle here, e.g. \\code setTail(QCPLineEnding::esSpikeArrow) \\endcode\n  \n  \\see setHead\n*/\nvoid QCPItemLine::setTail(const QCPLineEnding &tail)\n{\n  mTail = tail;\n}\n\n/* inherits documentation from base class */\ndouble QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(details)\n  if (onlySelectable && !mSelectable)\n    return -1;\n  \n  return qSqrt(QCPVector2D(pos).distanceSquaredToLine(start->pixelPosition(), end->pixelPosition()));\n}\n\n/* inherits documentation from base class */\nvoid QCPItemLine::draw(QCPPainter *painter)\n{\n  QCPVector2D startVec(start->pixelPosition());\n  QCPVector2D endVec(end->pixelPosition());\n  if (qFuzzyIsNull((startVec-endVec).lengthSquared()))\n    return;\n  // get visible segment of straight line inside clipRect:\n  int clipPad = int(qMax(mHead.boundingDistance(), mTail.boundingDistance()));\n  clipPad = qMax(clipPad, qCeil(mainPen().widthF()));\n  QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));\n  // paint visible segment, if existent:\n  if (!line.isNull())\n  {\n    painter->setPen(mainPen());\n    painter->drawLine(line);\n    painter->setBrush(Qt::SolidPattern);\n    if (mTail.style() != QCPLineEnding::esNone)\n      mTail.draw(painter, startVec, startVec-endVec);\n    if (mHead.style() != QCPLineEnding::esNone)\n      mHead.draw(painter, endVec, endVec-startVec);\n  }\n}\n\n/*! \\internal\n\n  Returns the section of the line defined by \\a start and \\a end, that is visible in the specified\n  \\a rect.\n  \n  This is a helper function for \\ref draw.\n*/\nQLineF QCPItemLine::getRectClippedLine(const QCPVector2D &start, const QCPVector2D &end, const QRect &rect) const\n{\n  bool containsStart = rect.contains(qRound(start.x()), qRound(start.y()));\n  bool containsEnd = rect.contains(qRound(end.x()), qRound(end.y()));\n  if (containsStart && containsEnd)\n    return {start.toPointF(), end.toPointF()};\n  \n  QCPVector2D base = start;\n  QCPVector2D vec = end-start;\n  double bx, by;\n  double gamma, mu;\n  QLineF result;\n  QList<QCPVector2D> pointVectors;\n\n  if (!qFuzzyIsNull(vec.y())) // line is not horizontal\n  {\n    // check top of rect:\n    bx = rect.left();\n    by = rect.top();\n    mu = (by-base.y())/vec.y();\n    if (mu >= 0 && mu <= 1)\n    {\n      gamma = base.x()-bx + mu*vec.x();\n      if (gamma >= 0 && gamma <= rect.width())\n        pointVectors.append(QCPVector2D(bx+gamma, by));\n    }\n    // check bottom of rect:\n    bx = rect.left();\n    by = rect.bottom();\n    mu = (by-base.y())/vec.y();\n    if (mu >= 0 && mu <= 1)\n    {\n      gamma = base.x()-bx + mu*vec.x();\n      if (gamma >= 0 && gamma <= rect.width())\n        pointVectors.append(QCPVector2D(bx+gamma, by));\n    }\n  }\n  if (!qFuzzyIsNull(vec.x())) // line is not vertical\n  {\n    // check left of rect:\n    bx = rect.left();\n    by = rect.top();\n    mu = (bx-base.x())/vec.x();\n    if (mu >= 0 && mu <= 1)\n    {\n      gamma = base.y()-by + mu*vec.y();\n      if (gamma >= 0 && gamma <= rect.height())\n        pointVectors.append(QCPVector2D(bx, by+gamma));\n    }\n    // check right of rect:\n    bx = rect.right();\n    by = rect.top();\n    mu = (bx-base.x())/vec.x();\n    if (mu >= 0 && mu <= 1)\n    {\n      gamma = base.y()-by + mu*vec.y();\n      if (gamma >= 0 && gamma <= rect.height())\n        pointVectors.append(QCPVector2D(bx, by+gamma));\n    }\n  }\n  \n  if (containsStart)\n    pointVectors.append(start);\n  if (containsEnd)\n    pointVectors.append(end);\n  \n  // evaluate points:\n  if (pointVectors.size() == 2)\n  {\n    result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());\n  } else if (pointVectors.size() > 2)\n  {\n    // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:\n    double distSqrMax = 0;\n    QCPVector2D pv1, pv2;\n    for (int i=0; i<pointVectors.size()-1; ++i)\n    {\n      for (int k=i+1; k<pointVectors.size(); ++k)\n      {\n        double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();\n        if (distSqr > distSqrMax)\n        {\n          pv1 = pointVectors.at(i);\n          pv2 = pointVectors.at(k);\n          distSqrMax = distSqr;\n        }\n      }\n    }\n    result.setPoints(pv1.toPointF(), pv2.toPointF());\n  }\n  return result;\n}\n\n/*! \\internal\n\n  Returns the pen that should be used for drawing lines. Returns mPen when the\n  item is not selected and mSelectedPen when it is.\n*/\nQPen QCPItemLine::mainPen() const\n{\n  return mSelected ? mSelectedPen : mPen;\n}\n/* end of 'src/items/item-line.cpp' */\n\n\n/* including file 'src/items/item-curve.cpp' */\n/* modified 2021-03-29T02:30:44, size 7273   */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPItemCurve\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPItemCurve\n  \\brief A curved line from one point to another\n\n  \\image html QCPItemCurve.png \"Curve example. Blue dotted circles are anchors, solid blue discs are positions.\"\n\n  It has four positions, \\a start and \\a end, which define the end points of the line, and two\n  control points which define the direction the line exits from the start and the direction from\n  which it approaches the end: \\a startDir and \\a endDir.\n  \n  With \\ref setHead and \\ref setTail you may set different line ending styles, e.g. to create an\n  arrow.\n  \n  Often it is desirable for the control points to stay at fixed relative positions to the start/end\n  point. This can be achieved by setting the parent anchor e.g. of \\a startDir simply to \\a start,\n  and then specify the desired pixel offset with QCPItemPosition::setCoords on \\a startDir.\n*/\n\n/*!\n  Creates a curve item and sets default values.\n  \n  The created item is automatically registered with \\a parentPlot. This QCustomPlot instance takes\n  ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead.\n*/\nQCPItemCurve::QCPItemCurve(QCustomPlot *parentPlot) :\n  QCPAbstractItem(parentPlot),\n  start(createPosition(QLatin1String(\"start\"))),\n  startDir(createPosition(QLatin1String(\"startDir\"))),\n  endDir(createPosition(QLatin1String(\"endDir\"))),\n  end(createPosition(QLatin1String(\"end\")))\n{\n  start->setCoords(0, 0);\n  startDir->setCoords(0.5, 0);\n  endDir->setCoords(0, 0.5);\n  end->setCoords(1, 1);\n  \n  setPen(QPen(Qt::black));\n  setSelectedPen(QPen(Qt::blue,2));\n}\n\nQCPItemCurve::~QCPItemCurve()\n{\n}\n\n/*!\n  Sets the pen that will be used to draw the line\n  \n  \\see setSelectedPen\n*/\nvoid QCPItemCurve::setPen(const QPen &pen)\n{\n  mPen = pen;\n}\n\n/*!\n  Sets the pen that will be used to draw the line when selected\n  \n  \\see setPen, setSelected\n*/\nvoid QCPItemCurve::setSelectedPen(const QPen &pen)\n{\n  mSelectedPen = pen;\n}\n\n/*!\n  Sets the line ending style of the head. The head corresponds to the \\a end position.\n  \n  Note that due to the overloaded QCPLineEnding constructor, you may directly specify\n  a QCPLineEnding::EndingStyle here, e.g. \\code setHead(QCPLineEnding::esSpikeArrow) \\endcode\n  \n  \\see setTail\n*/\nvoid QCPItemCurve::setHead(const QCPLineEnding &head)\n{\n  mHead = head;\n}\n\n/*!\n  Sets the line ending style of the tail. The tail corresponds to the \\a start position.\n  \n  Note that due to the overloaded QCPLineEnding constructor, you may directly specify\n  a QCPLineEnding::EndingStyle here, e.g. \\code setTail(QCPLineEnding::esSpikeArrow) \\endcode\n  \n  \\see setHead\n*/\nvoid QCPItemCurve::setTail(const QCPLineEnding &tail)\n{\n  mTail = tail;\n}\n\n/* inherits documentation from base class */\ndouble QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(details)\n  if (onlySelectable && !mSelectable)\n    return -1;\n  \n  QPointF startVec(start->pixelPosition());\n  QPointF startDirVec(startDir->pixelPosition());\n  QPointF endDirVec(endDir->pixelPosition());\n  QPointF endVec(end->pixelPosition());\n\n  QPainterPath cubicPath(startVec);\n  cubicPath.cubicTo(startDirVec, endDirVec, endVec);\n  \n  QList<QPolygonF> polygons = cubicPath.toSubpathPolygons();\n  if (polygons.isEmpty())\n    return -1;\n  const QPolygonF polygon = polygons.first();\n  QCPVector2D p(pos);\n  double minDistSqr = (std::numeric_limits<double>::max)();\n  for (int i=1; i<polygon.size(); ++i)\n  {\n    double distSqr = p.distanceSquaredToLine(polygon.at(i-1), polygon.at(i));\n    if (distSqr < minDistSqr)\n      minDistSqr = distSqr;\n  }\n  return qSqrt(minDistSqr);\n}\n\n/* inherits documentation from base class */\nvoid QCPItemCurve::draw(QCPPainter *painter)\n{\n  QCPVector2D startVec(start->pixelPosition());\n  QCPVector2D startDirVec(startDir->pixelPosition());\n  QCPVector2D endDirVec(endDir->pixelPosition());\n  QCPVector2D endVec(end->pixelPosition());\n  if ((endVec-startVec).length() > 1e10) // too large curves cause crash\n    return;\n\n  QPainterPath cubicPath(startVec.toPointF());\n  cubicPath.cubicTo(startDirVec.toPointF(), endDirVec.toPointF(), endVec.toPointF());\n\n  // paint visible segment, if existent:\n  const int clipEnlarge = qCeil(mainPen().widthF());\n  QRect clip = clipRect().adjusted(-clipEnlarge, -clipEnlarge, clipEnlarge, clipEnlarge);\n  QRect cubicRect = cubicPath.controlPointRect().toRect();\n  if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position\n    cubicRect.adjust(0, 0, 1, 1);\n  if (clip.intersects(cubicRect))\n  {\n    painter->setPen(mainPen());\n    painter->drawPath(cubicPath);\n    painter->setBrush(Qt::SolidPattern);\n    if (mTail.style() != QCPLineEnding::esNone)\n      mTail.draw(painter, startVec, M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI);\n    if (mHead.style() != QCPLineEnding::esNone)\n      mHead.draw(painter, endVec, -cubicPath.angleAtPercent(1)/180.0*M_PI);\n  }\n}\n\n/*! \\internal\n\n  Returns the pen that should be used for drawing lines. Returns mPen when the\n  item is not selected and mSelectedPen when it is.\n*/\nQPen QCPItemCurve::mainPen() const\n{\n  return mSelected ? mSelectedPen : mPen;\n}\n/* end of 'src/items/item-curve.cpp' */\n\n\n/* including file 'src/items/item-rect.cpp' */\n/* modified 2021-03-29T02:30:44, size 6472  */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPItemRect\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPItemRect\n  \\brief A rectangle\n\n  \\image html QCPItemRect.png \"Rectangle example. Blue dotted circles are anchors, solid blue discs are positions.\"\n\n  It has two positions, \\a topLeft and \\a bottomRight, which define the rectangle.\n*/\n\n/*!\n  Creates a rectangle item and sets default values.\n  \n  The created item is automatically registered with \\a parentPlot. This QCustomPlot instance takes\n  ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead.\n*/\nQCPItemRect::QCPItemRect(QCustomPlot *parentPlot) :\n  QCPAbstractItem(parentPlot),\n  topLeft(createPosition(QLatin1String(\"topLeft\"))),\n  bottomRight(createPosition(QLatin1String(\"bottomRight\"))),\n  top(createAnchor(QLatin1String(\"top\"), aiTop)),\n  topRight(createAnchor(QLatin1String(\"topRight\"), aiTopRight)),\n  right(createAnchor(QLatin1String(\"right\"), aiRight)),\n  bottom(createAnchor(QLatin1String(\"bottom\"), aiBottom)),\n  bottomLeft(createAnchor(QLatin1String(\"bottomLeft\"), aiBottomLeft)),\n  left(createAnchor(QLatin1String(\"left\"), aiLeft))\n{\n  topLeft->setCoords(0, 1);\n  bottomRight->setCoords(1, 0);\n  \n  setPen(QPen(Qt::black));\n  setSelectedPen(QPen(Qt::blue,2));\n  setBrush(Qt::NoBrush);\n  setSelectedBrush(Qt::NoBrush);\n}\n\nQCPItemRect::~QCPItemRect()\n{\n}\n\n/*!\n  Sets the pen that will be used to draw the line of the rectangle\n  \n  \\see setSelectedPen, setBrush\n*/\nvoid QCPItemRect::setPen(const QPen &pen)\n{\n  mPen = pen;\n}\n\n/*!\n  Sets the pen that will be used to draw the line of the rectangle when selected\n  \n  \\see setPen, setSelected\n*/\nvoid QCPItemRect::setSelectedPen(const QPen &pen)\n{\n  mSelectedPen = pen;\n}\n\n/*!\n  Sets the brush that will be used to fill the rectangle. To disable filling, set \\a brush to\n  Qt::NoBrush.\n  \n  \\see setSelectedBrush, setPen\n*/\nvoid QCPItemRect::setBrush(const QBrush &brush)\n{\n  mBrush = brush;\n}\n\n/*!\n  Sets the brush that will be used to fill the rectangle when selected. To disable filling, set \\a\n  brush to Qt::NoBrush.\n  \n  \\see setBrush\n*/\nvoid QCPItemRect::setSelectedBrush(const QBrush &brush)\n{\n  mSelectedBrush = brush;\n}\n\n/* inherits documentation from base class */\ndouble QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(details)\n  if (onlySelectable && !mSelectable)\n    return -1;\n  \n  QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition()).normalized();\n  bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;\n  return rectDistance(rect, pos, filledRect);\n}\n\n/* inherits documentation from base class */\nvoid QCPItemRect::draw(QCPPainter *painter)\n{\n  QPointF p1 = topLeft->pixelPosition();\n  QPointF p2 = bottomRight->pixelPosition();\n  if (p1.toPoint() == p2.toPoint())\n    return;\n  QRectF rect = QRectF(p1, p2).normalized();\n  double clipPad = mainPen().widthF();\n  QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);\n  if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect\n  {\n    painter->setPen(mainPen());\n    painter->setBrush(mainBrush());\n    painter->drawRect(rect);\n  }\n}\n\n/* inherits documentation from base class */\nQPointF QCPItemRect::anchorPixelPosition(int anchorId) const\n{\n  QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition());\n  switch (anchorId)\n  {\n    case aiTop:         return (rect.topLeft()+rect.topRight())*0.5;\n    case aiTopRight:    return rect.topRight();\n    case aiRight:       return (rect.topRight()+rect.bottomRight())*0.5;\n    case aiBottom:      return (rect.bottomLeft()+rect.bottomRight())*0.5;\n    case aiBottomLeft:  return rect.bottomLeft();\n    case aiLeft:        return (rect.topLeft()+rect.bottomLeft())*0.5;\n  }\n  \n  qDebug() << Q_FUNC_INFO << \"invalid anchorId\" << anchorId;\n  return {};\n}\n\n/*! \\internal\n\n  Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected\n  and mSelectedPen when it is.\n*/\nQPen QCPItemRect::mainPen() const\n{\n  return mSelected ? mSelectedPen : mPen;\n}\n\n/*! \\internal\n\n  Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item\n  is not selected and mSelectedBrush when it is.\n*/\nQBrush QCPItemRect::mainBrush() const\n{\n  return mSelected ? mSelectedBrush : mBrush;\n}\n/* end of 'src/items/item-rect.cpp' */\n\n\n/* including file 'src/items/item-text.cpp' */\n/* modified 2021-03-29T02:30:44, size 13335 */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPItemText\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPItemText\n  \\brief A text label\n\n  \\image html QCPItemText.png \"Text example. Blue dotted circles are anchors, solid blue discs are positions.\"\n\n  Its position is defined by the member \\a position and the setting of \\ref setPositionAlignment.\n  The latter controls which part of the text rect shall be aligned with \\a position.\n  \n  The text alignment itself (i.e. left, center, right) can be controlled with \\ref\n  setTextAlignment.\n  \n  The text may be rotated around the \\a position point with \\ref setRotation.\n*/\n\n/*!\n  Creates a text item and sets default values.\n  \n  The created item is automatically registered with \\a parentPlot. This QCustomPlot instance takes\n  ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead.\n*/\nQCPItemText::QCPItemText(QCustomPlot *parentPlot) :\n  QCPAbstractItem(parentPlot),\n  position(createPosition(QLatin1String(\"position\"))),\n  topLeft(createAnchor(QLatin1String(\"topLeft\"), aiTopLeft)),\n  top(createAnchor(QLatin1String(\"top\"), aiTop)),\n  topRight(createAnchor(QLatin1String(\"topRight\"), aiTopRight)),\n  right(createAnchor(QLatin1String(\"right\"), aiRight)),\n  bottomRight(createAnchor(QLatin1String(\"bottomRight\"), aiBottomRight)),\n  bottom(createAnchor(QLatin1String(\"bottom\"), aiBottom)),\n  bottomLeft(createAnchor(QLatin1String(\"bottomLeft\"), aiBottomLeft)),\n  left(createAnchor(QLatin1String(\"left\"), aiLeft)),\n  mText(QLatin1String(\"text\")),\n  mPositionAlignment(Qt::AlignCenter),\n  mTextAlignment(Qt::AlignTop|Qt::AlignHCenter),\n  mRotation(0)\n{\n  position->setCoords(0, 0);\n  \n  setPen(Qt::NoPen);\n  setSelectedPen(Qt::NoPen);\n  setBrush(Qt::NoBrush);\n  setSelectedBrush(Qt::NoBrush);\n  setColor(Qt::black);\n  setSelectedColor(Qt::blue);\n}\n\nQCPItemText::~QCPItemText()\n{\n}\n\n/*!\n  Sets the color of the text.\n*/\nvoid QCPItemText::setColor(const QColor &color)\n{\n  mColor = color;\n}\n\n/*!\n  Sets the color of the text that will be used when the item is selected.\n*/\nvoid QCPItemText::setSelectedColor(const QColor &color)\n{\n  mSelectedColor = color;\n}\n\n/*!\n  Sets the pen that will be used do draw a rectangular border around the text. To disable the\n  border, set \\a pen to Qt::NoPen.\n  \n  \\see setSelectedPen, setBrush, setPadding\n*/\nvoid QCPItemText::setPen(const QPen &pen)\n{\n  mPen = pen;\n}\n\n/*!\n  Sets the pen that will be used do draw a rectangular border around the text, when the item is\n  selected. To disable the border, set \\a pen to Qt::NoPen.\n  \n  \\see setPen\n*/\nvoid QCPItemText::setSelectedPen(const QPen &pen)\n{\n  mSelectedPen = pen;\n}\n\n/*!\n  Sets the brush that will be used do fill the background of the text. To disable the\n  background, set \\a brush to Qt::NoBrush.\n  \n  \\see setSelectedBrush, setPen, setPadding\n*/\nvoid QCPItemText::setBrush(const QBrush &brush)\n{\n  mBrush = brush;\n}\n\n/*!\n  Sets the brush that will be used do fill the background of the text, when the item is selected. To disable the\n  background, set \\a brush to Qt::NoBrush.\n  \n  \\see setBrush\n*/\nvoid QCPItemText::setSelectedBrush(const QBrush &brush)\n{\n  mSelectedBrush = brush;\n}\n\n/*!\n  Sets the font of the text.\n  \n  \\see setSelectedFont, setColor\n*/\nvoid QCPItemText::setFont(const QFont &font)\n{\n  mFont = font;\n}\n\n/*!\n  Sets the font of the text that will be used when the item is selected.\n  \n  \\see setFont\n*/\nvoid QCPItemText::setSelectedFont(const QFont &font)\n{\n  mSelectedFont = font;\n}\n\n/*!\n  Sets the text that will be displayed. Multi-line texts are supported by inserting a line break\n  character, e.g. '\\n'.\n  \n  \\see setFont, setColor, setTextAlignment\n*/\nvoid QCPItemText::setText(const QString &text)\n{\n  mText = text;\n}\n\n/*!\n  Sets which point of the text rect shall be aligned with \\a position.\n  \n  Examples:\n  \\li If \\a alignment is <tt>Qt::AlignHCenter | Qt::AlignTop</tt>, the text will be positioned such\n  that the top of the text rect will be horizontally centered on \\a position.\n  \\li If \\a alignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt>, \\a position will indicate the\n  bottom left corner of the text rect.\n  \n  If you want to control the alignment of (multi-lined) text within the text rect, use \\ref\n  setTextAlignment.\n*/\nvoid QCPItemText::setPositionAlignment(Qt::Alignment alignment)\n{\n  mPositionAlignment = alignment;\n}\n\n/*!\n  Controls how (multi-lined) text is aligned inside the text rect (typically Qt::AlignLeft, Qt::AlignCenter or Qt::AlignRight).\n*/\nvoid QCPItemText::setTextAlignment(Qt::Alignment alignment)\n{\n  mTextAlignment = alignment;\n}\n\n/*!\n  Sets the angle in degrees by which the text (and the text rectangle, if visible) will be rotated\n  around \\a position.\n*/\nvoid QCPItemText::setRotation(double degrees)\n{\n  mRotation = degrees;\n}\n\n/*!\n  Sets the distance between the border of the text rectangle and the text. The appearance (and\n  visibility) of the text rectangle can be controlled with \\ref setPen and \\ref setBrush.\n*/\nvoid QCPItemText::setPadding(const QMargins &padding)\n{\n  mPadding = padding;\n}\n\n/* inherits documentation from base class */\ndouble QCPItemText::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(details)\n  if (onlySelectable && !mSelectable)\n    return -1;\n  \n  // The rect may be rotated, so we transform the actual clicked pos to the rotated\n  // coordinate system, so we can use the normal rectDistance function for non-rotated rects:\n  QPointF positionPixels(position->pixelPosition());\n  QTransform inputTransform;\n  inputTransform.translate(positionPixels.x(), positionPixels.y());\n  inputTransform.rotate(-mRotation);\n  inputTransform.translate(-positionPixels.x(), -positionPixels.y());\n  QPointF rotatedPos = inputTransform.map(pos);\n  QFontMetrics fontMetrics(mFont);\n  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);\n  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());\n  QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);\n  textBoxRect.moveTopLeft(textPos.toPoint());\n\n  return rectDistance(textBoxRect, rotatedPos, true);\n}\n\n/* inherits documentation from base class */\nvoid QCPItemText::draw(QCPPainter *painter)\n{\n  QPointF pos(position->pixelPosition());\n  QTransform transform = painter->transform();\n  transform.translate(pos.x(), pos.y());\n  if (!qFuzzyIsNull(mRotation))\n    transform.rotate(mRotation);\n  painter->setFont(mainFont());\n  QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);\n  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());\n  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation\n  textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));\n  textBoxRect.moveTopLeft(textPos.toPoint());\n  int clipPad = qCeil(mainPen().widthF());\n  QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);\n  if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect())))\n  {\n    painter->setTransform(transform);\n    if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||\n        (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))\n    {\n      painter->setPen(mainPen());\n      painter->setBrush(mainBrush());\n      painter->drawRect(textBoxRect);\n    }\n    painter->setBrush(Qt::NoBrush);\n    painter->setPen(QPen(mainColor()));\n    painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText);\n  }\n}\n\n/* inherits documentation from base class */\nQPointF QCPItemText::anchorPixelPosition(int anchorId) const\n{\n  // get actual rect points (pretty much copied from draw function):\n  QPointF pos(position->pixelPosition());\n  QTransform transform;\n  transform.translate(pos.x(), pos.y());\n  if (!qFuzzyIsNull(mRotation))\n    transform.rotate(mRotation);\n  QFontMetrics fontMetrics(mainFont());\n  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);\n  QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());\n  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation\n  textBoxRect.moveTopLeft(textPos.toPoint());\n  QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));\n  \n  switch (anchorId)\n  {\n    case aiTopLeft:     return rectPoly.at(0);\n    case aiTop:         return (rectPoly.at(0)+rectPoly.at(1))*0.5;\n    case aiTopRight:    return rectPoly.at(1);\n    case aiRight:       return (rectPoly.at(1)+rectPoly.at(2))*0.5;\n    case aiBottomRight: return rectPoly.at(2);\n    case aiBottom:      return (rectPoly.at(2)+rectPoly.at(3))*0.5;\n    case aiBottomLeft:  return rectPoly.at(3);\n    case aiLeft:        return (rectPoly.at(3)+rectPoly.at(0))*0.5;\n  }\n  \n  qDebug() << Q_FUNC_INFO << \"invalid anchorId\" << anchorId;\n  return {};\n}\n\n/*! \\internal\n  \n  Returns the point that must be given to the QPainter::drawText function (which expects the top\n  left point of the text rect), according to the position \\a pos, the text bounding box \\a rect and\n  the requested \\a positionAlignment.\n  \n  For example, if \\a positionAlignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt> the returned point\n  will be shifted upward by the height of \\a rect, starting from \\a pos. So if the text is finally\n  drawn at that point, the lower left corner of the resulting text rect is at \\a pos.\n*/\nQPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const\n{\n  if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))\n    return pos;\n  \n  QPointF result = pos; // start at top left\n  if (positionAlignment.testFlag(Qt::AlignHCenter))\n    result.rx() -= rect.width()/2.0;\n  else if (positionAlignment.testFlag(Qt::AlignRight))\n    result.rx() -= rect.width();\n  if (positionAlignment.testFlag(Qt::AlignVCenter))\n    result.ry() -= rect.height()/2.0;\n  else if (positionAlignment.testFlag(Qt::AlignBottom))\n    result.ry() -= rect.height();\n  return result;\n}\n\n/*! \\internal\n\n  Returns the font that should be used for drawing text. Returns mFont when the item is not selected\n  and mSelectedFont when it is.\n*/\nQFont QCPItemText::mainFont() const\n{\n  return mSelected ? mSelectedFont : mFont;\n}\n\n/*! \\internal\n\n  Returns the color that should be used for drawing text. Returns mColor when the item is not\n  selected and mSelectedColor when it is.\n*/\nQColor QCPItemText::mainColor() const\n{\n  return mSelected ? mSelectedColor : mColor;\n}\n\n/*! \\internal\n\n  Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected\n  and mSelectedPen when it is.\n*/\nQPen QCPItemText::mainPen() const\n{\n  return mSelected ? mSelectedPen : mPen;\n}\n\n/*! \\internal\n\n  Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item\n  is not selected and mSelectedBrush when it is.\n*/\nQBrush QCPItemText::mainBrush() const\n{\n  return mSelected ? mSelectedBrush : mBrush;\n}\n/* end of 'src/items/item-text.cpp' */\n\n\n/* including file 'src/items/item-ellipse.cpp' */\n/* modified 2021-03-29T02:30:44, size 7881     */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPItemEllipse\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPItemEllipse\n  \\brief An ellipse\n\n  \\image html QCPItemEllipse.png \"Ellipse example. Blue dotted circles are anchors, solid blue discs are positions.\"\n\n  It has two positions, \\a topLeft and \\a bottomRight, which define the rect the ellipse will be drawn in.\n*/\n\n/*!\n  Creates an ellipse item and sets default values.\n  \n  The created item is automatically registered with \\a parentPlot. This QCustomPlot instance takes\n  ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead.\n*/\nQCPItemEllipse::QCPItemEllipse(QCustomPlot *parentPlot) :\n  QCPAbstractItem(parentPlot),\n  topLeft(createPosition(QLatin1String(\"topLeft\"))),\n  bottomRight(createPosition(QLatin1String(\"bottomRight\"))),\n  topLeftRim(createAnchor(QLatin1String(\"topLeftRim\"), aiTopLeftRim)),\n  top(createAnchor(QLatin1String(\"top\"), aiTop)),\n  topRightRim(createAnchor(QLatin1String(\"topRightRim\"), aiTopRightRim)),\n  right(createAnchor(QLatin1String(\"right\"), aiRight)),\n  bottomRightRim(createAnchor(QLatin1String(\"bottomRightRim\"), aiBottomRightRim)),\n  bottom(createAnchor(QLatin1String(\"bottom\"), aiBottom)),\n  bottomLeftRim(createAnchor(QLatin1String(\"bottomLeftRim\"), aiBottomLeftRim)),\n  left(createAnchor(QLatin1String(\"left\"), aiLeft)),\n  center(createAnchor(QLatin1String(\"center\"), aiCenter))\n{\n  topLeft->setCoords(0, 1);\n  bottomRight->setCoords(1, 0);\n  \n  setPen(QPen(Qt::black));\n  setSelectedPen(QPen(Qt::blue, 2));\n  setBrush(Qt::NoBrush);\n  setSelectedBrush(Qt::NoBrush);\n}\n\nQCPItemEllipse::~QCPItemEllipse()\n{\n}\n\n/*!\n  Sets the pen that will be used to draw the line of the ellipse\n  \n  \\see setSelectedPen, setBrush\n*/\nvoid QCPItemEllipse::setPen(const QPen &pen)\n{\n  mPen = pen;\n}\n\n/*!\n  Sets the pen that will be used to draw the line of the ellipse when selected\n  \n  \\see setPen, setSelected\n*/\nvoid QCPItemEllipse::setSelectedPen(const QPen &pen)\n{\n  mSelectedPen = pen;\n}\n\n/*!\n  Sets the brush that will be used to fill the ellipse. To disable filling, set \\a brush to\n  Qt::NoBrush.\n  \n  \\see setSelectedBrush, setPen\n*/\nvoid QCPItemEllipse::setBrush(const QBrush &brush)\n{\n  mBrush = brush;\n}\n\n/*!\n  Sets the brush that will be used to fill the ellipse when selected. To disable filling, set \\a\n  brush to Qt::NoBrush.\n  \n  \\see setBrush\n*/\nvoid QCPItemEllipse::setSelectedBrush(const QBrush &brush)\n{\n  mSelectedBrush = brush;\n}\n\n/* inherits documentation from base class */\ndouble QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(details)\n  if (onlySelectable && !mSelectable)\n    return -1;\n  \n  QPointF p1 = topLeft->pixelPosition();\n  QPointF p2 = bottomRight->pixelPosition();\n  QPointF center((p1+p2)/2.0);\n  double a = qAbs(p1.x()-p2.x())/2.0;\n  double b = qAbs(p1.y()-p2.y())/2.0;\n  double x = pos.x()-center.x();\n  double y = pos.y()-center.y();\n  \n  // distance to border:\n  double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b));\n  double result = qAbs(c-1)*qSqrt(x*x+y*y);\n  // filled ellipse, allow click inside to count as hit:\n  if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)\n  {\n    if (x*x/(a*a) + y*y/(b*b) <= 1)\n      result = mParentPlot->selectionTolerance()*0.99;\n  }\n  return result;\n}\n\n/* inherits documentation from base class */\nvoid QCPItemEllipse::draw(QCPPainter *painter)\n{\n  QPointF p1 = topLeft->pixelPosition();\n  QPointF p2 = bottomRight->pixelPosition();\n  if (p1.toPoint() == p2.toPoint())\n    return;\n  QRectF ellipseRect = QRectF(p1, p2).normalized();\n  const int clipEnlarge = qCeil(mainPen().widthF());\n  QRect clip = clipRect().adjusted(-clipEnlarge, -clipEnlarge, clipEnlarge, clipEnlarge);\n  if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect\n  {\n    painter->setPen(mainPen());\n    painter->setBrush(mainBrush());\n#ifdef __EXCEPTIONS\n    try // drawEllipse sometimes throws exceptions if ellipse is too big\n    {\n#endif\n      painter->drawEllipse(ellipseRect);\n#ifdef __EXCEPTIONS\n    } catch (...)\n    {\n      qDebug() << Q_FUNC_INFO << \"Item too large for memory, setting invisible\";\n      setVisible(false);\n    }\n#endif\n  }\n}\n\n/* inherits documentation from base class */\nQPointF QCPItemEllipse::anchorPixelPosition(int anchorId) const\n{\n  QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition());\n  switch (anchorId)\n  {\n    case aiTopLeftRim:     return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2);\n    case aiTop:            return (rect.topLeft()+rect.topRight())*0.5;\n    case aiTopRightRim:    return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2);\n    case aiRight:          return (rect.topRight()+rect.bottomRight())*0.5;\n    case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2);\n    case aiBottom:         return (rect.bottomLeft()+rect.bottomRight())*0.5;\n    case aiBottomLeftRim:  return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2);\n    case aiLeft:           return (rect.topLeft()+rect.bottomLeft())*0.5;\n    case aiCenter:         return (rect.topLeft()+rect.bottomRight())*0.5;\n  }\n  \n  qDebug() << Q_FUNC_INFO << \"invalid anchorId\" << anchorId;\n  return {};\n}\n\n/*! \\internal\n\n  Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected\n  and mSelectedPen when it is.\n*/\nQPen QCPItemEllipse::mainPen() const\n{\n  return mSelected ? mSelectedPen : mPen;\n}\n\n/*! \\internal\n\n  Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item\n  is not selected and mSelectedBrush when it is.\n*/\nQBrush QCPItemEllipse::mainBrush() const\n{\n  return mSelected ? mSelectedBrush : mBrush;\n}\n/* end of 'src/items/item-ellipse.cpp' */\n\n\n/* including file 'src/items/item-pixmap.cpp' */\n/* modified 2021-03-29T02:30:44, size 10622   */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPItemPixmap\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPItemPixmap\n  \\brief An arbitrary pixmap\n\n  \\image html QCPItemPixmap.png \"Pixmap example. Blue dotted circles are anchors, solid blue discs are positions.\"\n\n  It has two positions, \\a topLeft and \\a bottomRight, which define the rectangle the pixmap will\n  be drawn in. Depending on the scale setting (\\ref setScaled), the pixmap will be either scaled to\n  fit the rectangle or be drawn aligned to the topLeft position.\n  \n  If scaling is enabled and \\a topLeft is further to the bottom/right than \\a bottomRight (as shown\n  on the right side of the example image), the pixmap will be flipped in the respective\n  orientations.\n*/\n\n/*!\n  Creates a rectangle item and sets default values.\n  \n  The created item is automatically registered with \\a parentPlot. This QCustomPlot instance takes\n  ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead.\n*/\nQCPItemPixmap::QCPItemPixmap(QCustomPlot *parentPlot) :\n  QCPAbstractItem(parentPlot),\n  topLeft(createPosition(QLatin1String(\"topLeft\"))),\n  bottomRight(createPosition(QLatin1String(\"bottomRight\"))),\n  top(createAnchor(QLatin1String(\"top\"), aiTop)),\n  topRight(createAnchor(QLatin1String(\"topRight\"), aiTopRight)),\n  right(createAnchor(QLatin1String(\"right\"), aiRight)),\n  bottom(createAnchor(QLatin1String(\"bottom\"), aiBottom)),\n  bottomLeft(createAnchor(QLatin1String(\"bottomLeft\"), aiBottomLeft)),\n  left(createAnchor(QLatin1String(\"left\"), aiLeft)),\n  mScaled(false),\n  mScaledPixmapInvalidated(true),\n  mAspectRatioMode(Qt::KeepAspectRatio),\n  mTransformationMode(Qt::SmoothTransformation)\n{\n  topLeft->setCoords(0, 1);\n  bottomRight->setCoords(1, 0);\n  \n  setPen(Qt::NoPen);\n  setSelectedPen(QPen(Qt::blue));\n}\n\nQCPItemPixmap::~QCPItemPixmap()\n{\n}\n\n/*!\n  Sets the pixmap that will be displayed.\n*/\nvoid QCPItemPixmap::setPixmap(const QPixmap &pixmap)\n{\n  mPixmap = pixmap;\n  mScaledPixmapInvalidated = true;\n  if (mPixmap.isNull())\n    qDebug() << Q_FUNC_INFO << \"pixmap is null\";\n}\n\n/*!\n  Sets whether the pixmap will be scaled to fit the rectangle defined by the \\a topLeft and \\a\n  bottomRight positions.\n*/\nvoid QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformationMode)\n{\n  mScaled = scaled;\n  mAspectRatioMode = aspectRatioMode;\n  mTransformationMode = transformationMode;\n  mScaledPixmapInvalidated = true;\n}\n\n/*!\n  Sets the pen that will be used to draw a border around the pixmap.\n  \n  \\see setSelectedPen, setBrush\n*/\nvoid QCPItemPixmap::setPen(const QPen &pen)\n{\n  mPen = pen;\n}\n\n/*!\n  Sets the pen that will be used to draw a border around the pixmap when selected\n  \n  \\see setPen, setSelected\n*/\nvoid QCPItemPixmap::setSelectedPen(const QPen &pen)\n{\n  mSelectedPen = pen;\n}\n\n/* inherits documentation from base class */\ndouble QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(details)\n  if (onlySelectable && !mSelectable)\n    return -1;\n  \n  return rectDistance(getFinalRect(), pos, true);\n}\n\n/* inherits documentation from base class */\nvoid QCPItemPixmap::draw(QCPPainter *painter)\n{\n  bool flipHorz = false;\n  bool flipVert = false;\n  QRect rect = getFinalRect(&flipHorz, &flipVert);\n  int clipPad = mainPen().style() == Qt::NoPen ? 0 : qCeil(mainPen().widthF());\n  QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);\n  if (boundingRect.intersects(clipRect()))\n  {\n    updateScaledPixmap(rect, flipHorz, flipVert);\n    painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);\n    QPen pen = mainPen();\n    if (pen.style() != Qt::NoPen)\n    {\n      painter->setPen(pen);\n      painter->setBrush(Qt::NoBrush);\n      painter->drawRect(rect);\n    }\n  }\n}\n\n/* inherits documentation from base class */\nQPointF QCPItemPixmap::anchorPixelPosition(int anchorId) const\n{\n  bool flipHorz = false;\n  bool flipVert = false;\n  QRect rect = getFinalRect(&flipHorz, &flipVert);\n  // we actually want denormal rects (negative width/height) here, so restore\n  // the flipped state:\n  if (flipHorz)\n    rect.adjust(rect.width(), 0, -rect.width(), 0);\n  if (flipVert)\n    rect.adjust(0, rect.height(), 0, -rect.height());\n  \n  switch (anchorId)\n  {\n    case aiTop:         return (rect.topLeft()+rect.topRight())*0.5;\n    case aiTopRight:    return rect.topRight();\n    case aiRight:       return (rect.topRight()+rect.bottomRight())*0.5;\n    case aiBottom:      return (rect.bottomLeft()+rect.bottomRight())*0.5;\n    case aiBottomLeft:  return rect.bottomLeft();\n    case aiLeft:        return (rect.topLeft()+rect.bottomLeft())*0.5;\n  }\n  \n  qDebug() << Q_FUNC_INFO << \"invalid anchorId\" << anchorId;\n  return {};\n}\n\n/*! \\internal\n  \n  Creates the buffered scaled image (\\a mScaledPixmap) to fit the specified \\a finalRect. The\n  parameters \\a flipHorz and \\a flipVert control whether the resulting image shall be flipped\n  horizontally or vertically. (This is used when \\a topLeft is further to the bottom/right than \\a\n  bottomRight.)\n  \n  This function only creates the scaled pixmap when the buffered pixmap has a different size than\n  the expected result, so calling this function repeatedly, e.g. in the \\ref draw function, does\n  not cause expensive rescaling every time.\n  \n  If scaling is disabled, sets mScaledPixmap to a null QPixmap.\n*/\nvoid QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert)\n{\n  if (mPixmap.isNull())\n    return;\n  \n  if (mScaled)\n  {\n#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED\n    double devicePixelRatio = mPixmap.devicePixelRatio();\n#else\n    double devicePixelRatio = 1.0;\n#endif\n    if (finalRect.isNull())\n      finalRect = getFinalRect(&flipHorz, &flipVert);\n    if (mScaledPixmapInvalidated || finalRect.size() != mScaledPixmap.size()/devicePixelRatio)\n    {\n      mScaledPixmap = mPixmap.scaled(finalRect.size()*devicePixelRatio, mAspectRatioMode, mTransformationMode);\n      if (flipHorz || flipVert)\n        mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert));\n#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED\n      mScaledPixmap.setDevicePixelRatio(devicePixelRatio);\n#endif\n    }\n  } else if (!mScaledPixmap.isNull())\n    mScaledPixmap = QPixmap();\n  mScaledPixmapInvalidated = false;\n}\n\n/*! \\internal\n  \n  Returns the final (tight) rect the pixmap is drawn in, depending on the current item positions\n  and scaling settings.\n  \n  The output parameters \\a flippedHorz and \\a flippedVert return whether the pixmap should be drawn\n  flipped horizontally or vertically in the returned rect. (The returned rect itself is always\n  normalized, i.e. the top left corner of the rect is actually further to the top/left than the\n  bottom right corner). This is the case when the item position \\a topLeft is further to the\n  bottom/right than \\a bottomRight.\n  \n  If scaling is disabled, returns a rect with size of the original pixmap and the top left corner\n  aligned with the item position \\a topLeft. The position \\a bottomRight is ignored.\n*/\nQRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const\n{\n  QRect result;\n  bool flipHorz = false;\n  bool flipVert = false;\n  QPoint p1 = topLeft->pixelPosition().toPoint();\n  QPoint p2 = bottomRight->pixelPosition().toPoint();\n  if (p1 == p2)\n    return {p1, QSize(0, 0)};\n  if (mScaled)\n  {\n    QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y());\n    QPoint topLeft = p1;\n    if (newSize.width() < 0)\n    {\n      flipHorz = true;\n      newSize.rwidth() *= -1;\n      topLeft.setX(p2.x());\n    }\n    if (newSize.height() < 0)\n    {\n      flipVert = true;\n      newSize.rheight() *= -1;\n      topLeft.setY(p2.y());\n    }\n    QSize scaledSize = mPixmap.size();\n#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED\n    scaledSize /= mPixmap.devicePixelRatio();\n    scaledSize.scale(newSize*mPixmap.devicePixelRatio(), mAspectRatioMode);\n#else\n    scaledSize.scale(newSize, mAspectRatioMode);\n#endif\n    result = QRect(topLeft, scaledSize);\n  } else\n  {\n#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED\n    result = QRect(p1, mPixmap.size()/mPixmap.devicePixelRatio());\n#else\n    result = QRect(p1, mPixmap.size());\n#endif\n  }\n  if (flippedHorz)\n    *flippedHorz = flipHorz;\n  if (flippedVert)\n    *flippedVert = flipVert;\n  return result;\n}\n\n/*! \\internal\n\n  Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected\n  and mSelectedPen when it is.\n*/\nQPen QCPItemPixmap::mainPen() const\n{\n  return mSelected ? mSelectedPen : mPen;\n}\n/* end of 'src/items/item-pixmap.cpp' */\n\n\n/* including file 'src/items/item-tracer.cpp' */\n/* modified 2021-03-29T02:30:44, size 14645   */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPItemTracer\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPItemTracer\n  \\brief Item that sticks to QCPGraph data points\n\n  \\image html QCPItemTracer.png \"Tracer example. Blue dotted circles are anchors, solid blue discs are positions.\"\n\n  The tracer can be connected with a QCPGraph via \\ref setGraph. Then it will automatically adopt\n  the coordinate axes of the graph and update its \\a position to be on the graph's data. This means\n  the key stays controllable via \\ref setGraphKey, but the value will follow the graph data. If a\n  QCPGraph is connected, note that setting the coordinates of the tracer item directly via \\a\n  position will have no effect because they will be overriden in the next redraw (this is when the\n  coordinate update happens).\n  \n  If the specified key in \\ref setGraphKey is outside the key bounds of the graph, the tracer will\n  stay at the corresponding end of the graph.\n  \n  With \\ref setInterpolating you may specify whether the tracer may only stay exactly on data\n  points or whether it interpolates data points linearly, if given a key that lies between two data\n  points of the graph.\n  \n  The tracer has different visual styles, see \\ref setStyle. It is also possible to make the tracer\n  have no own visual appearance (set the style to \\ref tsNone), and just connect other item\n  positions to the tracer \\a position (used as an anchor) via \\ref\n  QCPItemPosition::setParentAnchor.\n  \n  \\note The tracer position is only automatically updated upon redraws. So when the data of the\n  graph changes and immediately afterwards (without a redraw) the position coordinates of the\n  tracer are retrieved, they will not reflect the updated data of the graph. In this case \\ref\n  updatePosition must be called manually, prior to reading the tracer coordinates.\n*/\n\n/*!\n  Creates a tracer item and sets default values.\n  \n  The created item is automatically registered with \\a parentPlot. This QCustomPlot instance takes\n  ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead.\n*/\nQCPItemTracer::QCPItemTracer(QCustomPlot *parentPlot) :\n  QCPAbstractItem(parentPlot),\n  position(createPosition(QLatin1String(\"position\"))),\n  mSize(6),\n  mStyle(tsCrosshair),\n  mGraph(nullptr),\n  mGraphKey(0),\n  mInterpolating(false)\n{\n  position->setCoords(0, 0);\n\n  setBrush(Qt::NoBrush);\n  setSelectedBrush(Qt::NoBrush);\n  setPen(QPen(Qt::black));\n  setSelectedPen(QPen(Qt::blue, 2));\n}\n\nQCPItemTracer::~QCPItemTracer()\n{\n}\n\n/*!\n  Sets the pen that will be used to draw the line of the tracer\n  \n  \\see setSelectedPen, setBrush\n*/\nvoid QCPItemTracer::setPen(const QPen &pen)\n{\n  mPen = pen;\n}\n\n/*!\n  Sets the pen that will be used to draw the line of the tracer when selected\n  \n  \\see setPen, setSelected\n*/\nvoid QCPItemTracer::setSelectedPen(const QPen &pen)\n{\n  mSelectedPen = pen;\n}\n\n/*!\n  Sets the brush that will be used to draw any fills of the tracer\n  \n  \\see setSelectedBrush, setPen\n*/\nvoid QCPItemTracer::setBrush(const QBrush &brush)\n{\n  mBrush = brush;\n}\n\n/*!\n  Sets the brush that will be used to draw any fills of the tracer, when selected.\n  \n  \\see setBrush, setSelected\n*/\nvoid QCPItemTracer::setSelectedBrush(const QBrush &brush)\n{\n  mSelectedBrush = brush;\n}\n\n/*!\n  Sets the size of the tracer in pixels, if the style supports setting a size (e.g. \\ref tsSquare\n  does, \\ref tsCrosshair does not).\n*/\nvoid QCPItemTracer::setSize(double size)\n{\n  mSize = size;\n}\n\n/*!\n  Sets the style/visual appearance of the tracer.\n  \n  If you only want to use the tracer \\a position as an anchor for other items, set \\a style to\n  \\ref tsNone.\n*/\nvoid QCPItemTracer::setStyle(QCPItemTracer::TracerStyle style)\n{\n  mStyle = style;\n}\n\n/*!\n  Sets the QCPGraph this tracer sticks to. The tracer \\a position will be set to type\n  QCPItemPosition::ptPlotCoords and the axes will be set to the axes of \\a graph.\n  \n  To free the tracer from any graph, set \\a graph to \\c nullptr. The tracer \\a position can then be\n  placed freely like any other item position. This is the state the tracer will assume when its\n  graph gets deleted while still attached to it.\n  \n  \\see setGraphKey\n*/\nvoid QCPItemTracer::setGraph(QCPGraph *graph)\n{\n  if (graph)\n  {\n    if (graph->parentPlot() == mParentPlot)\n    {\n      position->setType(QCPItemPosition::ptPlotCoords);\n      position->setAxes(graph->keyAxis(), graph->valueAxis());\n      mGraph = graph;\n      updatePosition();\n    } else\n      qDebug() << Q_FUNC_INFO << \"graph isn't in same QCustomPlot instance as this item\";\n  } else\n  {\n    mGraph = nullptr;\n  }\n}\n\n/*!\n  Sets the key of the graph's data point the tracer will be positioned at. This is the only free\n  coordinate of a tracer when attached to a graph.\n  \n  Depending on \\ref setInterpolating, the tracer will be either positioned on the data point\n  closest to \\a key, or will stay exactly at \\a key and interpolate the value linearly.\n  \n  \\see setGraph, setInterpolating\n*/\nvoid QCPItemTracer::setGraphKey(double key)\n{\n  mGraphKey = key;\n}\n\n/*!\n  Sets whether the value of the graph's data points shall be interpolated, when positioning the\n  tracer.\n  \n  If \\a enabled is set to false and a key is given with \\ref setGraphKey, the tracer is placed on\n  the data point of the graph which is closest to the key, but which is not necessarily exactly\n  there. If \\a enabled is true, the tracer will be positioned exactly at the specified key, and\n  the appropriate value will be interpolated from the graph's data points linearly.\n  \n  \\see setGraph, setGraphKey\n*/\nvoid QCPItemTracer::setInterpolating(bool enabled)\n{\n  mInterpolating = enabled;\n}\n\n/* inherits documentation from base class */\ndouble QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(details)\n  if (onlySelectable && !mSelectable)\n    return -1;\n\n  QPointF center(position->pixelPosition());\n  double w = mSize/2.0;\n  QRect clip = clipRect();\n  switch (mStyle)\n  {\n    case tsNone: return -1;\n    case tsPlus:\n    {\n      if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))\n        return qSqrt(qMin(QCPVector2D(pos).distanceSquaredToLine(center+QPointF(-w, 0), center+QPointF(w, 0)),\n                          QCPVector2D(pos).distanceSquaredToLine(center+QPointF(0, -w), center+QPointF(0, w))));\n      break;\n    }\n    case tsCrosshair:\n    {\n      return qSqrt(qMin(QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(clip.left(), center.y()), QCPVector2D(clip.right(), center.y())),\n                        QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(center.x(), clip.top()), QCPVector2D(center.x(), clip.bottom()))));\n    }\n    case tsCircle:\n    {\n      if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))\n      {\n        // distance to border:\n        double centerDist = QCPVector2D(center-pos).length();\n        double circleLine = w;\n        double result = qAbs(centerDist-circleLine);\n        // filled ellipse, allow click inside to count as hit:\n        if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)\n        {\n          if (centerDist <= circleLine)\n            result = mParentPlot->selectionTolerance()*0.99;\n        }\n        return result;\n      }\n      break;\n    }\n    case tsSquare:\n    {\n      if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))\n      {\n        QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w));\n        bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;\n        return rectDistance(rect, pos, filledRect);\n      }\n      break;\n    }\n  }\n  return -1;\n}\n\n/* inherits documentation from base class */\nvoid QCPItemTracer::draw(QCPPainter *painter)\n{\n  updatePosition();\n  if (mStyle == tsNone)\n    return;\n\n  painter->setPen(mainPen());\n  painter->setBrush(mainBrush());\n  QPointF center(position->pixelPosition());\n  double w = mSize/2.0;\n  QRect clip = clipRect();\n  switch (mStyle)\n  {\n    case tsNone: return;\n    case tsPlus:\n    {\n      if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))\n      {\n        painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0)));\n        painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w)));\n      }\n      break;\n    }\n    case tsCrosshair:\n    {\n      if (center.y() > clip.top() && center.y() < clip.bottom())\n        painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y()));\n      if (center.x() > clip.left() && center.x() < clip.right())\n        painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom()));\n      break;\n    }\n    case tsCircle:\n    {\n      if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))\n        painter->drawEllipse(center, w, w);\n      break;\n    }\n    case tsSquare:\n    {\n      if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))\n        painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w)));\n      break;\n    }\n  }\n}\n\n/*!\n  If the tracer is connected with a graph (\\ref setGraph), this function updates the tracer's \\a\n  position to reside on the graph data, depending on the configured key (\\ref setGraphKey).\n  \n  It is called automatically on every redraw and normally doesn't need to be called manually. One\n  exception is when you want to read the tracer coordinates via \\a position and are not sure that\n  the graph's data (or the tracer key with \\ref setGraphKey) hasn't changed since the last redraw.\n  In that situation, call this function before accessing \\a position, to make sure you don't get\n  out-of-date coordinates.\n  \n  If there is no graph set on this tracer, this function does nothing.\n*/\nvoid QCPItemTracer::updatePosition()\n{\n  if (mGraph)\n  {\n    if (mParentPlot->hasPlottable(mGraph))\n    {\n      if (mGraph->data()->size() > 1)\n      {\n        QCPGraphDataContainer::const_iterator first = mGraph->data()->constBegin();\n        QCPGraphDataContainer::const_iterator last = mGraph->data()->constEnd()-1;\n        if (mGraphKey <= first->key)\n          position->setCoords(first->key, first->value);\n        else if (mGraphKey >= last->key)\n          position->setCoords(last->key, last->value);\n        else\n        {\n          QCPGraphDataContainer::const_iterator it = mGraph->data()->findBegin(mGraphKey);\n          if (it != mGraph->data()->constEnd()) // mGraphKey is not exactly on last iterator, but somewhere between iterators\n          {\n            QCPGraphDataContainer::const_iterator prevIt = it;\n            ++it; // won't advance to constEnd because we handled that case (mGraphKey >= last->key) before\n            if (mInterpolating)\n            {\n              // interpolate between iterators around mGraphKey:\n              double slope = 0;\n              if (!qFuzzyCompare(double(it->key), double(prevIt->key)))\n                slope = (it->value-prevIt->value)/(it->key-prevIt->key);\n              position->setCoords(mGraphKey, (mGraphKey-prevIt->key)*slope+prevIt->value);\n            } else\n            {\n              // find iterator with key closest to mGraphKey:\n              if (mGraphKey < (prevIt->key+it->key)*0.5)\n                position->setCoords(prevIt->key, prevIt->value);\n              else\n                position->setCoords(it->key, it->value);\n            }\n          } else // mGraphKey is exactly on last iterator (should actually be caught when comparing first/last keys, but this is a failsafe for fp uncertainty)\n            position->setCoords(it->key, it->value);\n        }\n      } else if (mGraph->data()->size() == 1)\n      {\n        QCPGraphDataContainer::const_iterator it = mGraph->data()->constBegin();\n        position->setCoords(it->key, it->value);\n      } else\n        qDebug() << Q_FUNC_INFO << \"graph has no data\";\n    } else\n      qDebug() << Q_FUNC_INFO << \"graph not contained in QCustomPlot instance (anymore)\";\n  }\n}\n\n/*! \\internal\n\n  Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected\n  and mSelectedPen when it is.\n*/\nQPen QCPItemTracer::mainPen() const\n{\n  return mSelected ? mSelectedPen : mPen;\n}\n\n/*! \\internal\n\n  Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item\n  is not selected and mSelectedBrush when it is.\n*/\nQBrush QCPItemTracer::mainBrush() const\n{\n  return mSelected ? mSelectedBrush : mBrush;\n}\n/* end of 'src/items/item-tracer.cpp' */\n\n\n/* including file 'src/items/item-bracket.cpp' */\n/* modified 2021-03-29T02:30:44, size 10705    */\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPItemBracket\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPItemBracket\n  \\brief A bracket for referencing/highlighting certain parts in the plot.\n\n  \\image html QCPItemBracket.png \"Bracket example. Blue dotted circles are anchors, solid blue discs are positions.\"\n\n  It has two positions, \\a left and \\a right, which define the span of the bracket. If \\a left is\n  actually farther to the left than \\a right, the bracket is opened to the bottom, as shown in the\n  example image.\n  \n  The bracket supports multiple styles via \\ref setStyle. The length, i.e. how far the bracket\n  stretches away from the embraced span, can be controlled with \\ref setLength.\n  \n  \\image html QCPItemBracket-length.png\n  <center>Demonstrating the effect of different values for \\ref setLength, for styles \\ref\n  bsCalligraphic and \\ref bsSquare. Anchors and positions are displayed for reference.</center>\n  \n  It provides an anchor \\a center, to allow connection of other items, e.g. an arrow (QCPItemLine\n  or QCPItemCurve) or a text label (QCPItemText), to the bracket.\n*/\n\n/*!\n  Creates a bracket item and sets default values.\n  \n  The created item is automatically registered with \\a parentPlot. This QCustomPlot instance takes\n  ownership of the item, so do not delete it manually but use QCustomPlot::removeItem() instead.\n*/\nQCPItemBracket::QCPItemBracket(QCustomPlot *parentPlot) :\n  QCPAbstractItem(parentPlot),\n  left(createPosition(QLatin1String(\"left\"))),\n  right(createPosition(QLatin1String(\"right\"))),\n  center(createAnchor(QLatin1String(\"center\"), aiCenter)),\n  mLength(8),\n  mStyle(bsCalligraphic)\n{\n  left->setCoords(0, 0);\n  right->setCoords(1, 1);\n  \n  setPen(QPen(Qt::black));\n  setSelectedPen(QPen(Qt::blue, 2));\n}\n\nQCPItemBracket::~QCPItemBracket()\n{\n}\n\n/*!\n  Sets the pen that will be used to draw the bracket.\n  \n  Note that when the style is \\ref bsCalligraphic, only the color will be taken from the pen, the\n  stroke and width are ignored. To change the apparent stroke width of a calligraphic bracket, use\n  \\ref setLength, which has a similar effect.\n  \n  \\see setSelectedPen\n*/\nvoid QCPItemBracket::setPen(const QPen &pen)\n{\n  mPen = pen;\n}\n\n/*!\n  Sets the pen that will be used to draw the bracket when selected\n  \n  \\see setPen, setSelected\n*/\nvoid QCPItemBracket::setSelectedPen(const QPen &pen)\n{\n  mSelectedPen = pen;\n}\n\n/*!\n  Sets the \\a length in pixels how far the bracket extends in the direction towards the embraced\n  span of the bracket (i.e. perpendicular to the <i>left</i>-<i>right</i>-direction)\n  \n  \\image html QCPItemBracket-length.png\n  <center>Demonstrating the effect of different values for \\ref setLength, for styles \\ref\n  bsCalligraphic and \\ref bsSquare. Anchors and positions are displayed for reference.</center>\n*/\nvoid QCPItemBracket::setLength(double length)\n{\n  mLength = length;\n}\n\n/*!\n  Sets the style of the bracket, i.e. the shape/visual appearance.\n  \n  \\see setPen\n*/\nvoid QCPItemBracket::setStyle(QCPItemBracket::BracketStyle style)\n{\n  mStyle = style;\n}\n\n/* inherits documentation from base class */\ndouble QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  Q_UNUSED(details)\n  if (onlySelectable && !mSelectable)\n    return -1;\n  \n  QCPVector2D p(pos);\n  QCPVector2D leftVec(left->pixelPosition());\n  QCPVector2D rightVec(right->pixelPosition());\n  if (leftVec.toPoint() == rightVec.toPoint())\n    return -1;\n  \n  QCPVector2D widthVec = (rightVec-leftVec)*0.5;\n  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;\n  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;\n  \n  switch (mStyle)\n  {\n    case QCPItemBracket::bsSquare:\n    case QCPItemBracket::bsRound:\n    {\n      double a = p.distanceSquaredToLine(centerVec-widthVec, centerVec+widthVec);\n      double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec, centerVec-widthVec);\n      double c = p.distanceSquaredToLine(centerVec+widthVec+lengthVec, centerVec+widthVec);\n      return qSqrt(qMin(qMin(a, b), c));\n    }\n    case QCPItemBracket::bsCurly:\n    case QCPItemBracket::bsCalligraphic:\n    {\n      double a = p.distanceSquaredToLine(centerVec-widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3);\n      double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec*0.7, centerVec-widthVec*0.75+lengthVec*0.15);\n      double c = p.distanceSquaredToLine(centerVec+widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3);\n      double d = p.distanceSquaredToLine(centerVec+widthVec+lengthVec*0.7, centerVec+widthVec*0.75+lengthVec*0.15);\n      return qSqrt(qMin(qMin(a, b), qMin(c, d)));\n    }\n  }\n  return -1;\n}\n\n/* inherits documentation from base class */\nvoid QCPItemBracket::draw(QCPPainter *painter)\n{\n  QCPVector2D leftVec(left->pixelPosition());\n  QCPVector2D rightVec(right->pixelPosition());\n  if (leftVec.toPoint() == rightVec.toPoint())\n    return;\n  \n  QCPVector2D widthVec = (rightVec-leftVec)*0.5;\n  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;\n  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;\n\n  QPolygon boundingPoly;\n  boundingPoly << leftVec.toPoint() << rightVec.toPoint()\n               << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();\n  const int clipEnlarge = qCeil(mainPen().widthF());\n  QRect clip = clipRect().adjusted(-clipEnlarge, -clipEnlarge, clipEnlarge, clipEnlarge);\n  if (clip.intersects(boundingPoly.boundingRect()))\n  {\n    painter->setPen(mainPen());\n    switch (mStyle)\n    {\n      case bsSquare:\n      {\n        painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());\n        painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());\n        painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());\n        break;\n      }\n      case bsRound:\n      {\n        painter->setBrush(Qt::NoBrush);\n        QPainterPath path;\n        path.moveTo((centerVec+widthVec+lengthVec).toPointF());\n        path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());\n        path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());\n        painter->drawPath(path);\n        break;\n      }\n      case bsCurly:\n      {\n        painter->setBrush(Qt::NoBrush);\n        QPainterPath path;\n        path.moveTo((centerVec+widthVec+lengthVec).toPointF());\n        path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+lengthVec).toPointF(), centerVec.toPointF());\n        path.cubicTo((centerVec-0.4*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());\n        painter->drawPath(path);\n        break;\n      }\n      case bsCalligraphic:\n      {\n        painter->setPen(Qt::NoPen);\n        painter->setBrush(QBrush(mainPen().color()));\n        QPainterPath path;\n        path.moveTo((centerVec+widthVec+lengthVec).toPointF());\n        \n        path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+0.8*lengthVec).toPointF(), centerVec.toPointF());\n        path.cubicTo((centerVec-0.4*widthVec+0.8*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());\n        \n        path.cubicTo((centerVec-widthVec-lengthVec*0.5).toPointF(), (centerVec-0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+lengthVec*0.2).toPointF());\n        path.cubicTo((centerVec+0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5).toPointF(), (centerVec+widthVec+lengthVec).toPointF());\n        \n        painter->drawPath(path);\n        break;\n      }\n    }\n  }\n}\n\n/* inherits documentation from base class */\nQPointF QCPItemBracket::anchorPixelPosition(int anchorId) const\n{\n  QCPVector2D leftVec(left->pixelPosition());\n  QCPVector2D rightVec(right->pixelPosition());\n  if (leftVec.toPoint() == rightVec.toPoint())\n    return leftVec.toPointF();\n  \n  QCPVector2D widthVec = (rightVec-leftVec)*0.5;\n  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;\n  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;\n  \n  switch (anchorId)\n  {\n    case aiCenter:\n      return centerVec.toPointF();\n  }\n  qDebug() << Q_FUNC_INFO << \"invalid anchorId\" << anchorId;\n  return {};\n}\n\n/*! \\internal\n\n  Returns the pen that should be used for drawing lines. Returns mPen when the\n  item is not selected and mSelectedPen when it is.\n*/\nQPen QCPItemBracket::mainPen() const\n{\n    return mSelected ? mSelectedPen : mPen;\n}\n/* end of 'src/items/item-bracket.cpp' */\n\n\n/* including file 'src/polar/radialaxis.cpp' */\n/* modified 2021-03-29T02:30:44, size 49415  */\n\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPPolarAxisRadial\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPPolarAxisRadial\n  \\brief The radial axis inside a radial plot\n  \n  \\warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and\n  functionality to be incomplete, as well as changing public interfaces in the future.\n  \n  Each axis holds an instance of QCPAxisTicker which is used to generate the tick coordinates and\n  tick labels. You can access the currently installed \\ref ticker or set a new one (possibly one of\n  the specialized subclasses, or your own subclass) via \\ref setTicker. For details, see the\n  documentation of QCPAxisTicker.\n*/\n\n/* start of documentation of inline functions */\n\n/*! \\fn QSharedPointer<QCPAxisTicker> QCPPolarAxisRadial::ticker() const\n\n  Returns a modifiable shared pointer to the currently installed axis ticker. The axis ticker is\n  responsible for generating the tick positions and tick labels of this axis. You can access the\n  \\ref QCPAxisTicker with this method and modify basic properties such as the approximate tick count\n  (\\ref QCPAxisTicker::setTickCount).\n\n  You can gain more control over the axis ticks by setting a different \\ref QCPAxisTicker subclass, see\n  the documentation there. A new axis ticker can be set with \\ref setTicker.\n\n  Since the ticker is stored in the axis as a shared pointer, multiple axes may share the same axis\n  ticker simply by passing the same shared pointer to multiple axes.\n\n  \\see setTicker\n*/\n\n/* end of documentation of inline functions */\n/* start of documentation of signals */\n\n/*! \\fn void QCPPolarAxisRadial::rangeChanged(const QCPRange &newRange)\n\n  This signal is emitted when the range of this axis has changed. You can connect it to the \\ref\n  setRange slot of another axis to communicate the new range to the other axis, in order for it to\n  be synchronized.\n  \n  You may also manipulate/correct the range with \\ref setRange in a slot connected to this signal.\n  This is useful if for example a maximum range span shall not be exceeded, or if the lower/upper\n  range shouldn't go beyond certain values (see \\ref QCPRange::bounded). For example, the following\n  slot would limit the x axis to ranges between 0 and 10:\n  \\code\n  customPlot->xAxis->setRange(newRange.bounded(0, 10))\n  \\endcode\n*/\n\n/*! \\fn void QCPPolarAxisRadial::rangeChanged(const QCPRange &newRange, const QCPRange &oldRange)\n  \\overload\n  \n  Additionally to the new range, this signal also provides the previous range held by the axis as\n  \\a oldRange.\n*/\n\n/*! \\fn void QCPPolarAxisRadial::scaleTypeChanged(QCPPolarAxisRadial::ScaleType scaleType);\n  \n  This signal is emitted when the scale type changes, by calls to \\ref setScaleType\n*/\n\n/*! \\fn void QCPPolarAxisRadial::selectionChanged(QCPPolarAxisRadial::SelectableParts selection)\n  \n  This signal is emitted when the selection state of this axis has changed, either by user interaction\n  or by a direct call to \\ref setSelectedParts.\n*/\n\n/*! \\fn void QCPPolarAxisRadial::selectableChanged(const QCPPolarAxisRadial::SelectableParts &parts);\n  \n  This signal is emitted when the selectability changes, by calls to \\ref setSelectableParts\n*/\n\n/* end of documentation of signals */\n\n/*!\n  Constructs an Axis instance of Type \\a type for the axis rect \\a parent.\n  \n  Usually it isn't necessary to instantiate axes directly, because you can let QCustomPlot create\n  them for you with \\ref QCPAxisRect::addAxis. If you want to use own QCPAxis-subclasses however,\n  create them manually and then inject them also via \\ref QCPAxisRect::addAxis.\n*/\nQCPPolarAxisRadial::QCPPolarAxisRadial(QCPPolarAxisAngular *parent) :\n  QCPLayerable(parent->parentPlot(), QString(), parent),\n  mRangeDrag(true),\n  mRangeZoom(true),\n  mRangeZoomFactor(0.85),\n  // axis base:\n  mAngularAxis(parent),\n  mAngle(45),\n  mAngleReference(arAngularAxis),\n  mSelectableParts(spAxis | spTickLabels | spAxisLabel),\n  mSelectedParts(spNone),\n  mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),\n  mSelectedBasePen(QPen(Qt::blue, 2)),\n  // axis label:\n  mLabelPadding(0),\n  mLabel(),\n  mLabelFont(mParentPlot->font()),\n  mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),\n  mLabelColor(Qt::black),\n  mSelectedLabelColor(Qt::blue),\n  // tick labels:\n  // mTickLabelPadding(0), in label painter\n  mTickLabels(true),\n  // mTickLabelRotation(0), in label painter\n  mTickLabelFont(mParentPlot->font()),\n  mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),\n  mTickLabelColor(Qt::black),\n  mSelectedTickLabelColor(Qt::blue),\n  mNumberPrecision(6),\n  mNumberFormatChar('g'),\n  mNumberBeautifulPowers(true),\n  mNumberMultiplyCross(false),\n  // ticks and subticks:\n  mTicks(true),\n  mSubTicks(true),\n  mTickLengthIn(5),\n  mTickLengthOut(0),\n  mSubTickLengthIn(2),\n  mSubTickLengthOut(0),\n  mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),\n  mSelectedTickPen(QPen(Qt::blue, 2)),\n  mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),\n  mSelectedSubTickPen(QPen(Qt::blue, 2)),\n  // scale and range:\n  mRange(0, 5),\n  mRangeReversed(false),\n  mScaleType(stLinear),\n  // internal members:\n  mRadius(1), // non-zero initial value, will be overwritten in ::update() according to inner rect\n  mTicker(new QCPAxisTicker),\n  mLabelPainter(mParentPlot)\n{\n  setParent(parent);\n  setAntialiased(true);\n  \n  setTickLabelPadding(5);\n  setTickLabelRotation(0);\n  setTickLabelMode(lmUpright);\n  mLabelPainter.setAnchorReferenceType(QCPLabelPainterPrivate::artTangent);\n  mLabelPainter.setAbbreviateDecimalPowers(false);\n}\n\nQCPPolarAxisRadial::~QCPPolarAxisRadial()\n{\n}\n\nQCPPolarAxisRadial::LabelMode QCPPolarAxisRadial::tickLabelMode() const\n{\n  switch (mLabelPainter.anchorMode())\n  {\n    case QCPLabelPainterPrivate::amSkewedUpright: return lmUpright;\n    case QCPLabelPainterPrivate::amSkewedRotated: return lmRotated;\n    default: qDebug() << Q_FUNC_INFO << \"invalid mode for polar axis\"; break;\n  }\n  return lmUpright;\n}\n\n/* No documentation as it is a property getter */\nQString QCPPolarAxisRadial::numberFormat() const\n{\n  QString result;\n  result.append(mNumberFormatChar);\n  if (mNumberBeautifulPowers)\n  {\n    result.append(QLatin1Char('b'));\n    if (mNumberMultiplyCross)\n      result.append(QLatin1Char('c'));\n  }\n  return result;\n}\n\n/* No documentation as it is a property getter */\nint QCPPolarAxisRadial::tickLengthIn() const\n{\n  return mTickLengthIn;\n}\n\n/* No documentation as it is a property getter */\nint QCPPolarAxisRadial::tickLengthOut() const\n{\n  return mTickLengthOut;\n}\n\n/* No documentation as it is a property getter */\nint QCPPolarAxisRadial::subTickLengthIn() const\n{\n  return mSubTickLengthIn;\n}\n\n/* No documentation as it is a property getter */\nint QCPPolarAxisRadial::subTickLengthOut() const\n{\n  return mSubTickLengthOut;\n}\n\n/* No documentation as it is a property getter */\nint QCPPolarAxisRadial::labelPadding() const\n{\n  return mLabelPadding;\n}\n\nvoid QCPPolarAxisRadial::setRangeDrag(bool enabled)\n{\n  mRangeDrag = enabled;\n}\n\nvoid QCPPolarAxisRadial::setRangeZoom(bool enabled)\n{\n  mRangeZoom = enabled;\n}\n\nvoid QCPPolarAxisRadial::setRangeZoomFactor(double factor)\n{\n  mRangeZoomFactor = factor;\n}\n\n/*!\n  Sets whether the axis uses a linear scale or a logarithmic scale.\n  \n  Note that this method controls the coordinate transformation. For logarithmic scales, you will\n  likely also want to use a logarithmic tick spacing and labeling, which can be achieved by setting\n  the axis ticker to an instance of \\ref QCPAxisTickerLog :\n  \n  \\snippet documentation/doc-code-snippets/mainwindow.cpp qcpaxisticker-log-creation\n  \n  See the documentation of \\ref QCPAxisTickerLog about the details of logarithmic axis tick\n  creation.\n  \n  \\ref setNumberPrecision\n*/\nvoid QCPPolarAxisRadial::setScaleType(QCPPolarAxisRadial::ScaleType type)\n{\n  if (mScaleType != type)\n  {\n    mScaleType = type;\n    if (mScaleType == stLogarithmic)\n      setRange(mRange.sanitizedForLogScale());\n    //mCachedMarginValid = false;\n    emit scaleTypeChanged(mScaleType);\n  }\n}\n\n/*!\n  Sets the range of the axis.\n  \n  This slot may be connected with the \\ref rangeChanged signal of another axis so this axis\n  is always synchronized with the other axis range, when it changes.\n  \n  To invert the direction of an axis, use \\ref setRangeReversed.\n*/\nvoid QCPPolarAxisRadial::setRange(const QCPRange &range)\n{\n  if (range.lower == mRange.lower && range.upper == mRange.upper)\n    return;\n  \n  if (!QCPRange::validRange(range)) return;\n  QCPRange oldRange = mRange;\n  if (mScaleType == stLogarithmic)\n  {\n    mRange = range.sanitizedForLogScale();\n  } else\n  {\n    mRange = range.sanitizedForLinScale();\n  }\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  Sets whether the user can (de-)select the parts in \\a selectable by clicking on the QCustomPlot surface.\n  (When \\ref QCustomPlot::setInteractions contains iSelectAxes.)\n  \n  However, even when \\a selectable is set to a value not allowing the selection of a specific part,\n  it is still possible to set the selection of this part manually, by calling \\ref setSelectedParts\n  directly.\n  \n  \\see SelectablePart, setSelectedParts\n*/\nvoid QCPPolarAxisRadial::setSelectableParts(const SelectableParts &selectable)\n{\n  if (mSelectableParts != selectable)\n  {\n    mSelectableParts = selectable;\n    emit selectableChanged(mSelectableParts);\n  }\n}\n\n/*!\n  Sets the selected state of the respective axis parts described by \\ref SelectablePart. When a part\n  is selected, it uses a different pen/font.\n  \n  The entire selection mechanism for axes is handled automatically when \\ref\n  QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you\n  wish to change the selection state manually.\n  \n  This function can change the selection state of a part, independent of the \\ref setSelectableParts setting.\n  \n  emits the \\ref selectionChanged signal when \\a selected is different from the previous selection state.\n  \n  \\see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen,\n  setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor\n*/\nvoid QCPPolarAxisRadial::setSelectedParts(const SelectableParts &selected)\n{\n  if (mSelectedParts != selected)\n  {\n    mSelectedParts = selected;\n    emit selectionChanged(mSelectedParts);\n  }\n}\n\n/*!\n  \\overload\n  \n  Sets the lower and upper bound of the axis range.\n  \n  To invert the direction of an axis, use \\ref setRangeReversed.\n  \n  There is also a slot to set a range, see \\ref setRange(const QCPRange &range).\n*/\nvoid QCPPolarAxisRadial::setRange(double lower, double upper)\n{\n  if (lower == mRange.lower && upper == mRange.upper)\n    return;\n  \n  if (!QCPRange::validRange(lower, upper)) return;\n  QCPRange oldRange = mRange;\n  mRange.lower = lower;\n  mRange.upper = upper;\n  if (mScaleType == stLogarithmic)\n  {\n    mRange = mRange.sanitizedForLogScale();\n  } else\n  {\n    mRange = mRange.sanitizedForLinScale();\n  }\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  \\overload\n  \n  Sets the range of the axis.\n  \n  The \\a position coordinate indicates together with the \\a alignment parameter, where the new\n  range will be positioned. \\a size defines the size of the new axis range. \\a alignment may be\n  Qt::AlignLeft, Qt::AlignRight or Qt::AlignCenter. This will cause the left border, right border,\n  or center of the range to be aligned with \\a position. Any other values of \\a alignment will\n  default to Qt::AlignCenter.\n*/\nvoid QCPPolarAxisRadial::setRange(double position, double size, Qt::AlignmentFlag alignment)\n{\n  if (alignment == Qt::AlignLeft)\n    setRange(position, position+size);\n  else if (alignment == Qt::AlignRight)\n    setRange(position-size, position);\n  else // alignment == Qt::AlignCenter\n    setRange(position-size/2.0, position+size/2.0);\n}\n\n/*!\n  Sets the lower bound of the axis range. The upper bound is not changed.\n  \\see setRange\n*/\nvoid QCPPolarAxisRadial::setRangeLower(double lower)\n{\n  if (mRange.lower == lower)\n    return;\n  \n  QCPRange oldRange = mRange;\n  mRange.lower = lower;\n  if (mScaleType == stLogarithmic)\n  {\n    mRange = mRange.sanitizedForLogScale();\n  } else\n  {\n    mRange = mRange.sanitizedForLinScale();\n  }\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  Sets the upper bound of the axis range. The lower bound is not changed.\n  \\see setRange\n*/\nvoid QCPPolarAxisRadial::setRangeUpper(double upper)\n{\n  if (mRange.upper == upper)\n    return;\n  \n  QCPRange oldRange = mRange;\n  mRange.upper = upper;\n  if (mScaleType == stLogarithmic)\n  {\n    mRange = mRange.sanitizedForLogScale();\n  } else\n  {\n    mRange = mRange.sanitizedForLinScale();\n  }\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal\n  axes increase left to right, on vertical axes bottom to top. When \\a reversed is set to true, the\n  direction of increasing values is inverted.\n\n  Note that the range and data interface stays the same for reversed axes, e.g. the \\a lower part\n  of the \\ref setRange interface will still reference the mathematically smaller number than the \\a\n  upper part.\n*/\nvoid QCPPolarAxisRadial::setRangeReversed(bool reversed)\n{\n  mRangeReversed = reversed;\n}\n\nvoid QCPPolarAxisRadial::setAngle(double degrees)\n{\n  mAngle = degrees;\n}\n\nvoid QCPPolarAxisRadial::setAngleReference(AngleReference reference)\n{\n  mAngleReference = reference;\n}\n\n/*!\n  The axis ticker is responsible for generating the tick positions and tick labels. See the\n  documentation of QCPAxisTicker for details on how to work with axis tickers.\n  \n  You can change the tick positioning/labeling behaviour of this axis by setting a different\n  QCPAxisTicker subclass using this method. If you only wish to modify the currently installed axis\n  ticker, access it via \\ref ticker.\n  \n  Since the ticker is stored in the axis as a shared pointer, multiple axes may share the same axis\n  ticker simply by passing the same shared pointer to multiple axes.\n  \n  \\see ticker\n*/\nvoid QCPPolarAxisRadial::setTicker(QSharedPointer<QCPAxisTicker> ticker)\n{\n  if (ticker)\n    mTicker = ticker;\n  else\n    qDebug() << Q_FUNC_INFO << \"can not set 0 as axis ticker\";\n  // no need to invalidate margin cache here because produced tick labels are checked for changes in setupTickVector\n}\n\n/*!\n  Sets whether tick marks are displayed.\n\n  Note that setting \\a show to false does not imply that tick labels are invisible, too. To achieve\n  that, see \\ref setTickLabels.\n  \n  \\see setSubTicks\n*/\nvoid QCPPolarAxisRadial::setTicks(bool show)\n{\n  if (mTicks != show)\n  {\n    mTicks = show;\n    //mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets whether tick labels are displayed. Tick labels are the numbers drawn next to tick marks.\n*/\nvoid QCPPolarAxisRadial::setTickLabels(bool show)\n{\n  if (mTickLabels != show)\n  {\n    mTickLabels = show;\n    //mCachedMarginValid = false;\n    if (!mTickLabels)\n      mTickVectorLabels.clear();\n  }\n}\n\n/*!\n  Sets the distance between the axis base line (including any outward ticks) and the tick labels.\n  \\see setLabelPadding, setPadding\n*/\nvoid QCPPolarAxisRadial::setTickLabelPadding(int padding)\n{\n  mLabelPainter.setPadding(padding);\n}\n\n/*!\n  Sets the font of the tick labels.\n  \n  \\see setTickLabels, setTickLabelColor\n*/\nvoid QCPPolarAxisRadial::setTickLabelFont(const QFont &font)\n{\n  if (font != mTickLabelFont)\n  {\n    mTickLabelFont = font;\n    //mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the color of the tick labels.\n  \n  \\see setTickLabels, setTickLabelFont\n*/\nvoid QCPPolarAxisRadial::setTickLabelColor(const QColor &color)\n{\n  mTickLabelColor = color;\n}\n\n/*!\n  Sets the rotation of the tick labels. If \\a degrees is zero, the labels are drawn normally. Else,\n  the tick labels are drawn rotated by \\a degrees clockwise. The specified angle is bound to values\n  from -90 to 90 degrees.\n  \n  If \\a degrees is exactly -90, 0 or 90, the tick labels are centered on the tick coordinate. For\n  other angles, the label is drawn with an offset such that it seems to point toward or away from\n  the tick mark.\n*/\nvoid QCPPolarAxisRadial::setTickLabelRotation(double degrees)\n{\n  mLabelPainter.setRotation(degrees);\n}\n\nvoid QCPPolarAxisRadial::setTickLabelMode(LabelMode mode)\n{\n  switch (mode)\n  {\n    case lmUpright: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedUpright); break;\n    case lmRotated: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedRotated); break;\n  }\n}\n\n/*!\n  Sets the number format for the numbers in tick labels. This \\a formatCode is an extended version\n  of the format code used e.g. by QString::number() and QLocale::toString(). For reference about\n  that, see the \"Argument Formats\" section in the detailed description of the QString class.\n  \n  \\a formatCode is a string of one, two or three characters. The first character is identical to\n  the normal format code used by Qt. In short, this means: 'e'/'E' scientific format, 'f' fixed\n  format, 'g'/'G' scientific or fixed, whichever is shorter.\n  \n  The second and third characters are optional and specific to QCustomPlot:\\n\n  If the first char was 'e' or 'g', numbers are/might be displayed in the scientific format, e.g.\n  \"5.5e9\", which is ugly in a plot. So when the second char of \\a formatCode is set to 'b' (for\n  \"beautiful\"), those exponential numbers are formatted in a more natural way, i.e. \"5.5\n  [multiplication sign] 10 [superscript] 9\". By default, the multiplication sign is a centered dot.\n  If instead a cross should be shown (as is usual in the USA), the third char of \\a formatCode can\n  be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the\n  cross and 183 (0xB7) for the dot.\n  \n  Examples for \\a formatCode:\n  \\li \\c g normal format code behaviour. If number is small, fixed format is used, if number is large,\n  normal scientific format is used\n  \\li \\c gb If number is small, fixed format is used, if number is large, scientific format is used with\n  beautifully typeset decimal powers and a dot as multiplication sign\n  \\li \\c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as\n  multiplication sign\n  \\li \\c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal\n  powers. Format code will be reduced to 'f'.\n  \\li \\c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format\n  code will not be changed.\n*/\nvoid QCPPolarAxisRadial::setNumberFormat(const QString &formatCode)\n{\n  if (formatCode.isEmpty())\n  {\n    qDebug() << Q_FUNC_INFO << \"Passed formatCode is empty\";\n    return;\n  }\n  //mCachedMarginValid = false;\n  \n  // interpret first char as number format char:\n  QString allowedFormatChars(QLatin1String(\"eEfgG\"));\n  if (allowedFormatChars.contains(formatCode.at(0)))\n  {\n    mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"Invalid number format code (first char not in 'eEfgG'):\" << formatCode;\n    return;\n  }\n  \n  if (formatCode.length() < 2)\n  {\n    mNumberBeautifulPowers = false;\n    mNumberMultiplyCross = false;\n  } else\n  {\n    // interpret second char as indicator for beautiful decimal powers:\n    if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))\n      mNumberBeautifulPowers = true;\n    else\n      qDebug() << Q_FUNC_INFO << \"Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):\" << formatCode;\n    \n    if (formatCode.length() < 3)\n    {\n      mNumberMultiplyCross = false;\n    } else\n    {\n      // interpret third char as indicator for dot or cross multiplication symbol:\n      if (formatCode.at(2) == QLatin1Char('c'))\n        mNumberMultiplyCross = true;\n      else if (formatCode.at(2) == QLatin1Char('d'))\n        mNumberMultiplyCross = false;\n      else\n        qDebug() << Q_FUNC_INFO << \"Invalid number format code (third char neither 'c' nor 'd'):\" << formatCode;\n    }\n  }\n  mLabelPainter.setSubstituteExponent(mNumberBeautifulPowers);\n  mLabelPainter.setMultiplicationSymbol(mNumberMultiplyCross ? QCPLabelPainterPrivate::SymbolCross : QCPLabelPainterPrivate::SymbolDot);\n}\n\n/*!\n  Sets the precision of the tick label numbers. See QLocale::toString(double i, char f, int prec)\n  for details. The effect of precisions are most notably for number Formats starting with 'e', see\n  \\ref setNumberFormat\n*/\nvoid QCPPolarAxisRadial::setNumberPrecision(int precision)\n{\n  if (mNumberPrecision != precision)\n  {\n    mNumberPrecision = precision;\n    //mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the length of the ticks in pixels. \\a inside is the length the ticks will reach inside the\n  plot and \\a outside is the length they will reach outside the plot. If \\a outside is greater than\n  zero, the tick labels and axis label will increase their distance to the axis accordingly, so\n  they won't collide with the ticks.\n  \n  \\see setSubTickLength, setTickLengthIn, setTickLengthOut\n*/\nvoid QCPPolarAxisRadial::setTickLength(int inside, int outside)\n{\n  setTickLengthIn(inside);\n  setTickLengthOut(outside);\n}\n\n/*!\n  Sets the length of the inward ticks in pixels. \\a inside is the length the ticks will reach\n  inside the plot.\n  \n  \\see setTickLengthOut, setTickLength, setSubTickLength\n*/\nvoid QCPPolarAxisRadial::setTickLengthIn(int inside)\n{\n  if (mTickLengthIn != inside)\n  {\n    mTickLengthIn = inside;\n  }\n}\n\n/*!\n  Sets the length of the outward ticks in pixels. \\a outside is the length the ticks will reach\n  outside the plot. If \\a outside is greater than zero, the tick labels and axis label will\n  increase their distance to the axis accordingly, so they won't collide with the ticks.\n  \n  \\see setTickLengthIn, setTickLength, setSubTickLength\n*/\nvoid QCPPolarAxisRadial::setTickLengthOut(int outside)\n{\n  if (mTickLengthOut != outside)\n  {\n    mTickLengthOut = outside;\n    //mCachedMarginValid = false; // only outside tick length can change margin\n  }\n}\n\n/*!\n  Sets whether sub tick marks are displayed.\n  \n  Sub ticks are only potentially visible if (major) ticks are also visible (see \\ref setTicks)\n  \n  \\see setTicks\n*/\nvoid QCPPolarAxisRadial::setSubTicks(bool show)\n{\n  if (mSubTicks != show)\n  {\n    mSubTicks = show;\n    //mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the length of the subticks in pixels. \\a inside is the length the subticks will reach inside\n  the plot and \\a outside is the length they will reach outside the plot. If \\a outside is greater\n  than zero, the tick labels and axis label will increase their distance to the axis accordingly,\n  so they won't collide with the ticks.\n  \n  \\see setTickLength, setSubTickLengthIn, setSubTickLengthOut\n*/\nvoid QCPPolarAxisRadial::setSubTickLength(int inside, int outside)\n{\n  setSubTickLengthIn(inside);\n  setSubTickLengthOut(outside);\n}\n\n/*!\n  Sets the length of the inward subticks in pixels. \\a inside is the length the subticks will reach inside\n  the plot.\n  \n  \\see setSubTickLengthOut, setSubTickLength, setTickLength\n*/\nvoid QCPPolarAxisRadial::setSubTickLengthIn(int inside)\n{\n  if (mSubTickLengthIn != inside)\n  {\n    mSubTickLengthIn = inside;\n  }\n}\n\n/*!\n  Sets the length of the outward subticks in pixels. \\a outside is the length the subticks will reach\n  outside the plot. If \\a outside is greater than zero, the tick labels will increase their\n  distance to the axis accordingly, so they won't collide with the ticks.\n  \n  \\see setSubTickLengthIn, setSubTickLength, setTickLength\n*/\nvoid QCPPolarAxisRadial::setSubTickLengthOut(int outside)\n{\n  if (mSubTickLengthOut != outside)\n  {\n    mSubTickLengthOut = outside;\n    //mCachedMarginValid = false; // only outside tick length can change margin\n  }\n}\n\n/*!\n  Sets the pen, the axis base line is drawn with.\n  \n  \\see setTickPen, setSubTickPen\n*/\nvoid QCPPolarAxisRadial::setBasePen(const QPen &pen)\n{\n  mBasePen = pen;\n}\n\n/*!\n  Sets the pen, tick marks will be drawn with.\n  \n  \\see setTickLength, setBasePen\n*/\nvoid QCPPolarAxisRadial::setTickPen(const QPen &pen)\n{\n  mTickPen = pen;\n}\n\n/*!\n  Sets the pen, subtick marks will be drawn with.\n  \n  \\see setSubTickCount, setSubTickLength, setBasePen\n*/\nvoid QCPPolarAxisRadial::setSubTickPen(const QPen &pen)\n{\n  mSubTickPen = pen;\n}\n\n/*!\n  Sets the font of the axis label.\n  \n  \\see setLabelColor\n*/\nvoid QCPPolarAxisRadial::setLabelFont(const QFont &font)\n{\n  if (mLabelFont != font)\n  {\n    mLabelFont = font;\n    //mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the color of the axis label.\n  \n  \\see setLabelFont\n*/\nvoid QCPPolarAxisRadial::setLabelColor(const QColor &color)\n{\n  mLabelColor = color;\n}\n\n/*!\n  Sets the text of the axis label that will be shown below/above or next to the axis, depending on\n  its orientation. To disable axis labels, pass an empty string as \\a str.\n*/\nvoid QCPPolarAxisRadial::setLabel(const QString &str)\n{\n  if (mLabel != str)\n  {\n    mLabel = str;\n    //mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the distance between the tick labels and the axis label.\n  \n  \\see setTickLabelPadding, setPadding\n*/\nvoid QCPPolarAxisRadial::setLabelPadding(int padding)\n{\n  if (mLabelPadding != padding)\n  {\n    mLabelPadding = padding;\n    //mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the font that is used for tick labels when they are selected.\n  \n  \\see setTickLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPPolarAxisRadial::setSelectedTickLabelFont(const QFont &font)\n{\n  if (font != mSelectedTickLabelFont)\n  {\n    mSelectedTickLabelFont = font;\n    // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts\n  }\n}\n\n/*!\n  Sets the font that is used for the axis label when it is selected.\n  \n  \\see setLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPPolarAxisRadial::setSelectedLabelFont(const QFont &font)\n{\n  mSelectedLabelFont = font;\n  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts\n}\n\n/*!\n  Sets the color that is used for tick labels when they are selected.\n  \n  \\see setTickLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPPolarAxisRadial::setSelectedTickLabelColor(const QColor &color)\n{\n  if (color != mSelectedTickLabelColor)\n  {\n    mSelectedTickLabelColor = color;\n  }\n}\n\n/*!\n  Sets the color that is used for the axis label when it is selected.\n  \n  \\see setLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPPolarAxisRadial::setSelectedLabelColor(const QColor &color)\n{\n  mSelectedLabelColor = color;\n}\n\n/*!\n  Sets the pen that is used to draw the axis base line when selected.\n  \n  \\see setBasePen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPPolarAxisRadial::setSelectedBasePen(const QPen &pen)\n{\n  mSelectedBasePen = pen;\n}\n\n/*!\n  Sets the pen that is used to draw the (major) ticks when selected.\n  \n  \\see setTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPPolarAxisRadial::setSelectedTickPen(const QPen &pen)\n{\n  mSelectedTickPen = pen;\n}\n\n/*!\n  Sets the pen that is used to draw the subticks when selected.\n  \n  \\see setSubTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPPolarAxisRadial::setSelectedSubTickPen(const QPen &pen)\n{\n  mSelectedSubTickPen = pen;\n}\n\n/*!\n  If the scale type (\\ref setScaleType) is \\ref stLinear, \\a diff is added to the lower and upper\n  bounds of the range. The range is simply moved by \\a diff.\n  \n  If the scale type is \\ref stLogarithmic, the range bounds are multiplied by \\a diff. This\n  corresponds to an apparent \"linear\" move in logarithmic scaling by a distance of log(diff).\n*/\nvoid QCPPolarAxisRadial::moveRange(double diff)\n{\n  QCPRange oldRange = mRange;\n  if (mScaleType == stLinear)\n  {\n    mRange.lower += diff;\n    mRange.upper += diff;\n  } else // mScaleType == stLogarithmic\n  {\n    mRange.lower *= diff;\n    mRange.upper *= diff;\n  }\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  Scales the range of this axis by \\a factor around the center of the current axis range. For\n  example, if \\a factor is 2.0, then the axis range will double its size, and the point at the axis\n  range center won't have changed its position in the QCustomPlot widget (i.e. coordinates around\n  the center will have moved symmetrically closer).\n\n  If you wish to scale around a different coordinate than the current axis range center, use the\n  overload \\ref scaleRange(double factor, double center).\n*/\nvoid QCPPolarAxisRadial::scaleRange(double factor)\n{\n  scaleRange(factor, range().center());\n}\n\n/*! \\overload\n\n  Scales the range of this axis by \\a factor around the coordinate \\a center. For example, if \\a\n  factor is 2.0, \\a center is 1.0, then the axis range will double its size, and the point at\n  coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates\n  around 1.0 will have moved symmetrically closer to 1.0).\n\n  \\see scaleRange(double factor)\n*/\nvoid QCPPolarAxisRadial::scaleRange(double factor, double center)\n{\n  QCPRange oldRange = mRange;\n  if (mScaleType == stLinear)\n  {\n    QCPRange newRange;\n    newRange.lower = (mRange.lower-center)*factor + center;\n    newRange.upper = (mRange.upper-center)*factor + center;\n    if (QCPRange::validRange(newRange))\n      mRange = newRange.sanitizedForLinScale();\n  } else // mScaleType == stLogarithmic\n  {\n    if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range\n    {\n      QCPRange newRange;\n      newRange.lower = qPow(mRange.lower/center, factor)*center;\n      newRange.upper = qPow(mRange.upper/center, factor)*center;\n      if (QCPRange::validRange(newRange))\n        mRange = newRange.sanitizedForLogScale();\n    } else\n      qDebug() << Q_FUNC_INFO << \"Center of scaling operation doesn't lie in same logarithmic sign domain as range:\" << center;\n  }\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  Changes the axis range such that all plottables associated with this axis are fully visible in\n  that dimension.\n  \n  \\see QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes\n*/\nvoid QCPPolarAxisRadial::rescale(bool onlyVisiblePlottables)\n{\n  Q_UNUSED(onlyVisiblePlottables)\n  /* TODO\n  QList<QCPAbstractPlottable*> p = plottables();\n  QCPRange newRange;\n  bool haveRange = false;\n  for (int i=0; i<p.size(); ++i)\n  {\n    if (!p.at(i)->realVisibility() && onlyVisiblePlottables)\n      continue;\n    QCPRange plottableRange;\n    bool currentFoundRange;\n    QCP::SignDomain signDomain = QCP::sdBoth;\n    if (mScaleType == stLogarithmic)\n      signDomain = (mRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive);\n    if (p.at(i)->keyAxis() == this)\n      plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);\n    else\n      plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);\n    if (currentFoundRange)\n    {\n      if (!haveRange)\n        newRange = plottableRange;\n      else\n        newRange.expand(plottableRange);\n      haveRange = true;\n    }\n  }\n  if (haveRange)\n  {\n    if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable\n    {\n      double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason\n      if (mScaleType == stLinear)\n      {\n        newRange.lower = center-mRange.size()/2.0;\n        newRange.upper = center+mRange.size()/2.0;\n      } else // mScaleType == stLogarithmic\n      {\n        newRange.lower = center/qSqrt(mRange.upper/mRange.lower);\n        newRange.upper = center*qSqrt(mRange.upper/mRange.lower);\n      }\n    }\n    setRange(newRange);\n  }\n  */\n}\n\n/*!\n  Transforms \\a value, in pixel coordinates of the QCustomPlot widget, to axis coordinates.\n*/\nvoid QCPPolarAxisRadial::pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const\n{\n  QCPVector2D posVector(pixelPos-mCenter);\n  radiusCoord = radiusToCoord(posVector.length());\n  angleCoord = mAngularAxis->angleRadToCoord(posVector.angle());\n}\n\n/*!\n  Transforms \\a value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget.\n*/\nQPointF QCPPolarAxisRadial::coordToPixel(double angleCoord, double radiusCoord) const\n{\n  const double radiusPixel = coordToRadius(radiusCoord);\n  const double angleRad = mAngularAxis->coordToAngleRad(angleCoord);\n  return QPointF(mCenter.x()+qCos(angleRad)*radiusPixel, mCenter.y()+qSin(angleRad)*radiusPixel);\n}\n\ndouble QCPPolarAxisRadial::coordToRadius(double coord) const\n{\n  if (mScaleType == stLinear)\n  {\n    if (!mRangeReversed)\n      return (coord-mRange.lower)/mRange.size()*mRadius;\n    else\n      return (mRange.upper-coord)/mRange.size()*mRadius;\n  } else // mScaleType == stLogarithmic\n  {\n    if (coord >= 0.0 && mRange.upper < 0.0) // invalid value for logarithmic scale, just return outside visible range\n      return !mRangeReversed ? mRadius+200 : mRadius-200;\n    else if (coord <= 0.0 && mRange.upper >= 0.0) // invalid value for logarithmic scale, just return outside visible range\n      return !mRangeReversed ? mRadius-200 :mRadius+200;\n    else\n    {\n      if (!mRangeReversed)\n        return qLn(coord/mRange.lower)/qLn(mRange.upper/mRange.lower)*mRadius;\n      else\n        return qLn(mRange.upper/coord)/qLn(mRange.upper/mRange.lower)*mRadius;\n    }\n  }\n}\n\ndouble QCPPolarAxisRadial::radiusToCoord(double radius) const\n{\n  if (mScaleType == stLinear)\n  {\n    if (!mRangeReversed)\n      return (radius)/mRadius*mRange.size()+mRange.lower;\n    else\n      return -(radius)/mRadius*mRange.size()+mRange.upper;\n  } else // mScaleType == stLogarithmic\n  {\n    if (!mRangeReversed)\n      return qPow(mRange.upper/mRange.lower, (radius)/mRadius)*mRange.lower;\n    else\n      return qPow(mRange.upper/mRange.lower, (-radius)/mRadius)*mRange.upper;\n  }\n}\n\n\n/*!\n  Returns the part of the axis that is hit by \\a pos (in pixels). The return value of this function\n  is independent of the user-selectable parts defined with \\ref setSelectableParts. Further, this\n  function does not change the current selection state of the axis.\n  \n  If the axis is not visible (\\ref setVisible), this function always returns \\ref spNone.\n  \n  \\see setSelectedParts, setSelectableParts, QCustomPlot::setInteractions\n*/\nQCPPolarAxisRadial::SelectablePart QCPPolarAxisRadial::getPartAt(const QPointF &pos) const\n{\n  Q_UNUSED(pos) // TODO remove later\n  if (!mVisible)\n    return spNone;\n  \n  /*\n    TODO:\n  if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))\n    return spAxis;\n  else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))\n    return spTickLabels;\n  else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))\n    return spAxisLabel;\n  else */\n    return spNone;\n}\n\n/* inherits documentation from base class */\ndouble QCPPolarAxisRadial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  if (!mParentPlot) return -1;\n  SelectablePart part = getPartAt(pos);\n  if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)\n    return -1;\n  \n  if (details)\n    details->setValue(part);\n  return mParentPlot->selectionTolerance()*0.99;\n}\n\n/* inherits documentation from base class */\nvoid QCPPolarAxisRadial::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)\n{\n  Q_UNUSED(event)\n  SelectablePart part = details.value<SelectablePart>();\n  if (mSelectableParts.testFlag(part))\n  {\n    SelectableParts selBefore = mSelectedParts;\n    setSelectedParts(additive ? mSelectedParts^part : part);\n    if (selectionStateChanged)\n      *selectionStateChanged = mSelectedParts != selBefore;\n  }\n}\n\n/* inherits documentation from base class */\nvoid QCPPolarAxisRadial::deselectEvent(bool *selectionStateChanged)\n{\n  SelectableParts selBefore = mSelectedParts;\n  setSelectedParts(mSelectedParts & ~mSelectableParts);\n  if (selectionStateChanged)\n    *selectionStateChanged = mSelectedParts != selBefore;\n}\n\n/*! \\internal\n  \n  This mouse event reimplementation provides the functionality to let the user drag individual axes\n  exclusively, by startig the drag on top of the axis.\n\n  For the axis to accept this event and perform the single axis drag, the parent \\ref QCPAxisRect\n  must be configured accordingly, i.e. it must allow range dragging in the orientation of this axis\n  (\\ref QCPAxisRect::setRangeDrag) and this axis must be a draggable axis (\\ref\n  QCPAxisRect::setRangeDragAxes)\n  \n  \\seebaseclassmethod\n  \n  \\note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis\n  rect is handled by the axis rect's mouse event, e.g. \\ref QCPAxisRect::mousePressEvent.\n*/\nvoid QCPPolarAxisRadial::mousePressEvent(QMouseEvent *event, const QVariant &details)\n{\n  Q_UNUSED(details)\n  if (!mParentPlot->interactions().testFlag(QCP::iRangeDrag))\n  {\n    event->ignore();\n    return;\n  }\n  \n  if (event->buttons() & Qt::LeftButton)\n  {\n    mDragging = true;\n    // initialize antialiasing backup in case we start dragging:\n    if (mParentPlot->noAntialiasingOnDrag())\n    {\n      mAADragBackup = mParentPlot->antialiasedElements();\n      mNotAADragBackup = mParentPlot->notAntialiasedElements();\n    }\n    // Mouse range dragging interaction:\n    if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))\n      mDragStartRange = mRange;\n  }\n}\n\n/*! \\internal\n  \n  This mouse event reimplementation provides the functionality to let the user drag individual axes\n  exclusively, by startig the drag on top of the axis.\n  \n  \\seebaseclassmethod\n  \n  \\note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis\n  rect is handled by the axis rect's mouse event, e.g. \\ref QCPAxisRect::mousePressEvent.\n  \n  \\see QCPAxis::mousePressEvent\n*/\nvoid QCPPolarAxisRadial::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)\n{\n  Q_UNUSED(event) // TODO remove later\n  Q_UNUSED(startPos) // TODO remove later\n  if (mDragging)\n  {\n    /* TODO\n    const double startPixel = orientation() == Qt::Horizontal ? startPos.x() : startPos.y();\n    const double currentPixel = orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y();\n    if (mScaleType == QCPPolarAxisRadial::stLinear)\n    {\n      const double diff = pixelToCoord(startPixel) - pixelToCoord(currentPixel);\n      setRange(mDragStartRange.lower+diff, mDragStartRange.upper+diff);\n    } else if (mScaleType == QCPPolarAxisRadial::stLogarithmic)\n    {\n      const double diff = pixelToCoord(startPixel) / pixelToCoord(currentPixel);\n      setRange(mDragStartRange.lower*diff, mDragStartRange.upper*diff);\n    }\n    */\n    \n    if (mParentPlot->noAntialiasingOnDrag())\n      mParentPlot->setNotAntialiasedElements(QCP::aeAll);\n    mParentPlot->replot(QCustomPlot::rpQueuedReplot);\n  }\n}\n\n/*! \\internal\n  \n  This mouse event reimplementation provides the functionality to let the user drag individual axes\n  exclusively, by startig the drag on top of the axis.\n  \n  \\seebaseclassmethod\n  \n  \\note The dragging of possibly multiple axes at once by starting the drag anywhere in the axis\n  rect is handled by the axis rect's mouse event, e.g. \\ref QCPAxisRect::mousePressEvent.\n  \n  \\see QCPAxis::mousePressEvent\n*/\nvoid QCPPolarAxisRadial::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)\n{\n  Q_UNUSED(event)\n  Q_UNUSED(startPos)\n  mDragging = false;\n  if (mParentPlot->noAntialiasingOnDrag())\n  {\n    mParentPlot->setAntialiasedElements(mAADragBackup);\n    mParentPlot->setNotAntialiasedElements(mNotAADragBackup);\n  }\n}\n\n/*! \\internal\n  \n  This mouse event reimplementation provides the functionality to let the user zoom individual axes\n  exclusively, by performing the wheel event on top of the axis.\n\n  For the axis to accept this event and perform the single axis zoom, the parent \\ref QCPAxisRect\n  must be configured accordingly, i.e. it must allow range zooming in the orientation of this axis\n  (\\ref QCPAxisRect::setRangeZoom) and this axis must be a zoomable axis (\\ref\n  QCPAxisRect::setRangeZoomAxes)\n  \n  \\seebaseclassmethod\n  \n  \\note The zooming of possibly multiple axes at once by performing the wheel event anywhere in the\n  axis rect is handled by the axis rect's mouse event, e.g. \\ref QCPAxisRect::wheelEvent.\n*/\nvoid QCPPolarAxisRadial::wheelEvent(QWheelEvent *event)\n{\n  // Mouse range zooming interaction:\n  if (!mParentPlot->interactions().testFlag(QCP::iRangeZoom))\n  {\n    event->ignore();\n    return;\n  }\n  \n  // TODO:\n  //const double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually\n  //const double factor = qPow(mRangeZoomFactor, wheelSteps);\n  //scaleRange(factor, pixelToCoord(orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y()));\n  mParentPlot->replot();\n}\n\nvoid QCPPolarAxisRadial::updateGeometry(const QPointF &center, double radius)\n{\n  mCenter = center;\n  mRadius = radius;\n  if (mRadius < 1) mRadius = 1;\n}\n\n/*! \\internal\n\n  A convenience function to easily set the QPainter::Antialiased hint on the provided \\a painter\n  before drawing axis lines.\n\n  This is the antialiasing state the painter passed to the \\ref draw method is in by default.\n  \n  This function takes into account the local setting of the antialiasing flag as well as the\n  overrides set with \\ref QCustomPlot::setAntialiasedElements and \\ref\n  QCustomPlot::setNotAntialiasedElements.\n  \n  \\seebaseclassmethod\n  \n  \\see setAntialiased\n*/\nvoid QCPPolarAxisRadial::applyDefaultAntialiasingHint(QCPPainter *painter) const\n{\n  applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes);\n}\n\n/*! \\internal\n  \n  Draws the axis with the specified \\a painter, using the internal QCPAxisPainterPrivate instance.\n\n  \\seebaseclassmethod\n*/\nvoid QCPPolarAxisRadial::draw(QCPPainter *painter)\n{\n  const double axisAngleRad = (mAngle+(mAngleReference==arAngularAxis ? mAngularAxis->angle() : 0))/180.0*M_PI;\n  const QPointF axisVector(qCos(axisAngleRad), qSin(axisAngleRad)); // semantically should be QCPVector2D, but we save time in loops when we keep it as QPointF\n  const QPointF tickNormal = QCPVector2D(axisVector).perpendicular().toPointF(); // semantically should be QCPVector2D, but we save time in loops when we keep it as QPointF\n  \n  // draw baseline:\n  painter->setPen(getBasePen());\n  painter->drawLine(QLineF(mCenter, mCenter+axisVector*(mRadius-0.5)));\n  \n  // draw subticks:\n  if (!mSubTickVector.isEmpty())\n  {\n    painter->setPen(getSubTickPen());\n    for (int i=0; i<mSubTickVector.size(); ++i)\n    {\n      const QPointF tickPosition = mCenter+axisVector*coordToRadius(mSubTickVector.at(i));\n      painter->drawLine(QLineF(tickPosition-tickNormal*mSubTickLengthIn, tickPosition+tickNormal*mSubTickLengthOut));\n    }\n  }\n  \n  // draw ticks and labels:\n  if (!mTickVector.isEmpty())\n  {\n    mLabelPainter.setAnchorReference(mCenter-axisVector); // subtract (normalized) axisVector, just to prevent degenerate tangents for tick label at exact lower axis range\n    mLabelPainter.setFont(getTickLabelFont());\n    mLabelPainter.setColor(getTickLabelColor());\n    const QPen ticksPen = getTickPen();\n    painter->setPen(ticksPen);\n    for (int i=0; i<mTickVector.size(); ++i)\n    {\n      const double r = coordToRadius(mTickVector.at(i));\n      const QPointF tickPosition = mCenter+axisVector*r;\n      painter->drawLine(QLineF(tickPosition-tickNormal*mTickLengthIn, tickPosition+tickNormal*mTickLengthOut));\n      // possibly draw tick labels:\n      if (!mTickVectorLabels.isEmpty())\n      {\n        if ((!mRangeReversed && (i < mTickVectorLabels.count()-1 || mRadius-r > 10)) ||\n            (mRangeReversed && (i > 0 || mRadius-r > 10))) // skip last label if it's closer than 10 pixels to angular axis\n          mLabelPainter.drawTickLabel(painter, tickPosition+tickNormal*mSubTickLengthOut, mTickVectorLabels.at(i));\n      }\n    }\n  }\n}\n\n/*! \\internal\n  \n  Prepares the internal tick vector, sub tick vector and tick label vector. This is done by calling\n  QCPAxisTicker::generate on the currently installed ticker.\n  \n  If a change in the label text/count is detected, the cached axis margin is invalidated to make\n  sure the next margin calculation recalculates the label sizes and returns an up-to-date value.\n*/\nvoid QCPPolarAxisRadial::setupTickVectors()\n{\n  if (!mParentPlot) return;\n  if ((!mTicks && !mTickLabels) || mRange.size() <= 0) return;\n  \n  mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar, mNumberPrecision, mTickVector, mSubTicks ? &mSubTickVector : 0, mTickLabels ? &mTickVectorLabels : 0);\n}\n\n/*! \\internal\n  \n  Returns the pen that is used to draw the axis base line. Depending on the selection state, this\n  is either mSelectedBasePen or mBasePen.\n*/\nQPen QCPPolarAxisRadial::getBasePen() const\n{\n  return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;\n}\n\n/*! \\internal\n  \n  Returns the pen that is used to draw the (major) ticks. Depending on the selection state, this\n  is either mSelectedTickPen or mTickPen.\n*/\nQPen QCPPolarAxisRadial::getTickPen() const\n{\n  return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;\n}\n\n/*! \\internal\n  \n  Returns the pen that is used to draw the subticks. Depending on the selection state, this\n  is either mSelectedSubTickPen or mSubTickPen.\n*/\nQPen QCPPolarAxisRadial::getSubTickPen() const\n{\n  return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen;\n}\n\n/*! \\internal\n  \n  Returns the font that is used to draw the tick labels. Depending on the selection state, this\n  is either mSelectedTickLabelFont or mTickLabelFont.\n*/\nQFont QCPPolarAxisRadial::getTickLabelFont() const\n{\n  return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont;\n}\n\n/*! \\internal\n  \n  Returns the font that is used to draw the axis label. Depending on the selection state, this\n  is either mSelectedLabelFont or mLabelFont.\n*/\nQFont QCPPolarAxisRadial::getLabelFont() const\n{\n  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont;\n}\n\n/*! \\internal\n  \n  Returns the color that is used to draw the tick labels. Depending on the selection state, this\n  is either mSelectedTickLabelColor or mTickLabelColor.\n*/\nQColor QCPPolarAxisRadial::getTickLabelColor() const\n{\n  return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor;\n}\n\n/*! \\internal\n  \n  Returns the color that is used to draw the axis label. Depending on the selection state, this\n  is either mSelectedLabelColor or mLabelColor.\n*/\nQColor QCPPolarAxisRadial::getLabelColor() const\n{\n  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor;\n}\n\n\n/* inherits documentation from base class */\nQCP::Interaction QCPPolarAxisRadial::selectionCategory() const\n{\n  return QCP::iSelectAxes;\n}\n/* end of 'src/polar/radialaxis.cpp' */\n\n\n/* including file 'src/polar/layoutelement-angularaxis.cpp' */\n/* modified 2021-03-29T02:30:44, size 57266                 */\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPPolarAxisAngular\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPPolarAxisAngular\n  \\brief The main container for polar plots, representing the angular axis as a circle\n\n  \\warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and\n  functionality to be incomplete, as well as changing public interfaces in the future.\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn QCPLayoutInset *QCPPolarAxisAngular::insetLayout() const\n  \n  Returns the inset layout of this axis rect. It can be used to place other layout elements (or\n  even layouts with multiple other elements) inside/on top of an axis rect.\n  \n  \\see QCPLayoutInset\n*/\n\n/*! \\fn int QCPPolarAxisAngular::left() const\n  \n  Returns the pixel position of the left border of this axis rect. Margins are not taken into\n  account here, so the returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn int QCPPolarAxisAngular::right() const\n  \n  Returns the pixel position of the right border of this axis rect. Margins are not taken into\n  account here, so the returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn int QCPPolarAxisAngular::top() const\n  \n  Returns the pixel position of the top border of this axis rect. Margins are not taken into\n  account here, so the returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn int QCPPolarAxisAngular::bottom() const\n  \n  Returns the pixel position of the bottom border of this axis rect. Margins are not taken into\n  account here, so the returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn int QCPPolarAxisAngular::width() const\n  \n  Returns the pixel width of this axis rect. Margins are not taken into account here, so the\n  returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn int QCPPolarAxisAngular::height() const\n  \n  Returns the pixel height of this axis rect. Margins are not taken into account here, so the\n  returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn QSize QCPPolarAxisAngular::size() const\n  \n  Returns the pixel size of this axis rect. Margins are not taken into account here, so the\n  returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn QPoint QCPPolarAxisAngular::topLeft() const\n  \n  Returns the top left corner of this axis rect in pixels. Margins are not taken into account here,\n  so the returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn QPoint QCPPolarAxisAngular::topRight() const\n  \n  Returns the top right corner of this axis rect in pixels. Margins are not taken into account\n  here, so the returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn QPoint QCPPolarAxisAngular::bottomLeft() const\n  \n  Returns the bottom left corner of this axis rect in pixels. Margins are not taken into account\n  here, so the returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn QPoint QCPPolarAxisAngular::bottomRight() const\n  \n  Returns the bottom right corner of this axis rect in pixels. Margins are not taken into account\n  here, so the returned value is with respect to the inner \\ref rect.\n*/\n\n/*! \\fn QPoint QCPPolarAxisAngular::center() const\n  \n  Returns the center of this axis rect in pixels. Margins are not taken into account here, so the\n  returned value is with respect to the inner \\ref rect.\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Creates a QCPPolarAxis instance and sets default values. An axis is added for each of the four\n  sides, the top and right axes are set invisible initially.\n*/\nQCPPolarAxisAngular::QCPPolarAxisAngular(QCustomPlot *parentPlot) :\n  QCPLayoutElement(parentPlot),\n  mBackgroundBrush(Qt::NoBrush),\n  mBackgroundScaled(true),\n  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),\n  mInsetLayout(new QCPLayoutInset),\n  mRangeDrag(false),\n  mRangeZoom(false),\n  mRangeZoomFactor(0.85),\n  // axis base:\n  mAngle(-90),\n  mAngleRad(mAngle/180.0*M_PI),\n  mSelectableParts(spAxis | spTickLabels | spAxisLabel),\n  mSelectedParts(spNone),\n  mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),\n  mSelectedBasePen(QPen(Qt::blue, 2)),\n  // axis label:\n  mLabelPadding(0),\n  mLabel(),\n  mLabelFont(mParentPlot->font()),\n  mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),\n  mLabelColor(Qt::black),\n  mSelectedLabelColor(Qt::blue),\n  // tick labels:\n  //mTickLabelPadding(0), in label painter\n  mTickLabels(true),\n  //mTickLabelRotation(0), in label painter\n  mTickLabelFont(mParentPlot->font()),\n  mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),\n  mTickLabelColor(Qt::black),\n  mSelectedTickLabelColor(Qt::blue),\n  mNumberPrecision(6),\n  mNumberFormatChar('g'),\n  mNumberBeautifulPowers(true),\n  mNumberMultiplyCross(false),\n  // ticks and subticks:\n  mTicks(true),\n  mSubTicks(true),\n  mTickLengthIn(5),\n  mTickLengthOut(0),\n  mSubTickLengthIn(2),\n  mSubTickLengthOut(0),\n  mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),\n  mSelectedTickPen(QPen(Qt::blue, 2)),\n  mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),\n  mSelectedSubTickPen(QPen(Qt::blue, 2)),\n  // scale and range:\n  mRange(0, 360),\n  mRangeReversed(false),\n  // internal members:\n  mRadius(1), // non-zero initial value, will be overwritten in ::update() according to inner rect\n  mGrid(new QCPPolarGrid(this)),\n  mTicker(new QCPAxisTickerFixed),\n  mDragging(false),\n  mLabelPainter(parentPlot)\n{\n  // TODO:\n  //mInsetLayout->initializeParentPlot(mParentPlot);\n  //mInsetLayout->setParentLayerable(this);\n  //mInsetLayout->setParent(this);\n \n  if (QCPAxisTickerFixed *fixedTicker = mTicker.dynamicCast<QCPAxisTickerFixed>().data())\n  {\n    fixedTicker->setTickStep(30);\n  }\n  setAntialiased(true);\n  setLayer(mParentPlot->currentLayer()); // it's actually on that layer already, but we want it in front of the grid, so we place it on there again\n  \n  setTickLabelPadding(5);\n  setTickLabelRotation(0);\n  setTickLabelMode(lmUpright);\n  mLabelPainter.setAnchorReferenceType(QCPLabelPainterPrivate::artNormal);\n  mLabelPainter.setAbbreviateDecimalPowers(false);\n  mLabelPainter.setCacheSize(24); // so we can cache up to 15-degree intervals, polar angular axis uses a bit larger cache than normal axes\n  \n  setMinimumSize(50, 50);\n  setMinimumMargins(QMargins(30, 30, 30, 30));\n  \n  addRadialAxis();\n  mGrid->setRadialAxis(radialAxis());\n}\n\nQCPPolarAxisAngular::~QCPPolarAxisAngular()\n{\n  delete mGrid; // delete grid here instead of via parent ~QObject for better defined deletion order\n  mGrid = 0;\n  \n  delete mInsetLayout;\n  mInsetLayout = 0;\n  \n  QList<QCPPolarAxisRadial*> radialAxesList = radialAxes();\n  for (int i=0; i<radialAxesList.size(); ++i)\n    removeRadialAxis(radialAxesList.at(i));\n}\n\nQCPPolarAxisAngular::LabelMode QCPPolarAxisAngular::tickLabelMode() const\n{\n  switch (mLabelPainter.anchorMode())\n  {\n    case QCPLabelPainterPrivate::amSkewedUpright: return lmUpright;\n    case QCPLabelPainterPrivate::amSkewedRotated: return lmRotated;\n    default: qDebug() << Q_FUNC_INFO << \"invalid mode for polar axis\"; break;\n  }\n  return lmUpright;\n}\n\n/* No documentation as it is a property getter */\nQString QCPPolarAxisAngular::numberFormat() const\n{\n  QString result;\n  result.append(mNumberFormatChar);\n  if (mNumberBeautifulPowers)\n  {\n    result.append(QLatin1Char('b'));\n    if (mLabelPainter.multiplicationSymbol() == QCPLabelPainterPrivate::SymbolCross)\n      result.append(QLatin1Char('c'));\n  }\n  return result;\n}\n\n/*!\n  Returns the number of axes on the axis rect side specified with \\a type.\n  \n  \\see axis\n*/\nint QCPPolarAxisAngular::radialAxisCount() const\n{\n  return mRadialAxes.size();\n}\n\n/*!\n  Returns the axis with the given \\a index on the axis rect side specified with \\a type.\n  \n  \\see axisCount, axes\n*/\nQCPPolarAxisRadial *QCPPolarAxisAngular::radialAxis(int index) const\n{\n  if (index >= 0 && index < mRadialAxes.size())\n  {\n    return mRadialAxes.at(index);\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"Axis index out of bounds:\" << index;\n    return 0;\n  }\n}\n\n/*!\n  Returns all axes on the axis rect sides specified with \\a types.\n  \n  \\a types may be a single \\ref QCPAxis::AxisType or an <tt>or</tt>-combination, to get the axes of\n  multiple sides.\n  \n  \\see axis\n*/\nQList<QCPPolarAxisRadial*> QCPPolarAxisAngular::radialAxes() const\n{\n  return mRadialAxes;\n}\n\n\n/*!\n  Adds a new axis to the axis rect side specified with \\a type, and returns it. If \\a axis is 0, a\n  new QCPAxis instance is created internally. QCustomPlot owns the returned axis, so if you want to\n  remove an axis, use \\ref removeAxis instead of deleting it manually.\n\n  You may inject QCPAxis instances (or subclasses of QCPAxis) by setting \\a axis to an axis that was\n  previously created outside QCustomPlot. It is important to note that QCustomPlot takes ownership\n  of the axis, so you may not delete it afterwards. Further, the \\a axis must have been created\n  with this axis rect as parent and with the same axis type as specified in \\a type. If this is not\n  the case, a debug output is generated, the axis is not added, and the method returns 0.\n\n  This method can not be used to move \\a axis between axis rects. The same \\a axis instance must\n  not be added multiple times to the same or different axis rects.\n\n  If an axis rect side already contains one or more axes, the lower and upper endings of the new\n  axis (\\ref QCPAxis::setLowerEnding, \\ref QCPAxis::setUpperEnding) are set to \\ref\n  QCPLineEnding::esHalfBar.\n\n  \\see addAxes, setupFullAxesBox\n*/\nQCPPolarAxisRadial *QCPPolarAxisAngular::addRadialAxis(QCPPolarAxisRadial *axis)\n{\n  QCPPolarAxisRadial *newAxis = axis;\n  if (!newAxis)\n  {\n    newAxis = new QCPPolarAxisRadial(this);\n  } else // user provided existing axis instance, do some sanity checks\n  {\n    if (newAxis->angularAxis() != this)\n    {\n      qDebug() << Q_FUNC_INFO << \"passed radial axis doesn't have this angular axis as parent angular axis\";\n      return 0;\n    }\n    if (radialAxes().contains(newAxis))\n    {\n      qDebug() << Q_FUNC_INFO << \"passed axis is already owned by this angular axis\";\n      return 0;\n    }\n  }\n  mRadialAxes.append(newAxis);\n  return newAxis;\n}\n\n/*!\n  Removes the specified \\a axis from the axis rect and deletes it.\n  \n  Returns true on success, i.e. if \\a axis was a valid axis in this axis rect.\n  \n  \\see addAxis\n*/\nbool QCPPolarAxisAngular::removeRadialAxis(QCPPolarAxisRadial *radialAxis)\n{\n  if (mRadialAxes.contains(radialAxis))\n  {\n    mRadialAxes.removeOne(radialAxis);\n    delete radialAxis;\n    return true;\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"Radial axis isn't associated with this angular axis:\" << reinterpret_cast<quintptr>(radialAxis);\n    return false;\n  }\n}\n\nQRegion QCPPolarAxisAngular::exactClipRegion() const\n{\n  return QRegion(mCenter.x()-mRadius, mCenter.y()-mRadius, qRound(2*mRadius), qRound(2*mRadius), QRegion::Ellipse);\n}\n\n/*!\n  If the scale type (\\ref setScaleType) is \\ref stLinear, \\a diff is added to the lower and upper\n  bounds of the range. The range is simply moved by \\a diff.\n  \n  If the scale type is \\ref stLogarithmic, the range bounds are multiplied by \\a diff. This\n  corresponds to an apparent \"linear\" move in logarithmic scaling by a distance of log(diff).\n*/\nvoid QCPPolarAxisAngular::moveRange(double diff)\n{\n  QCPRange oldRange = mRange;\n  mRange.lower += diff;\n  mRange.upper += diff;\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  Scales the range of this axis by \\a factor around the center of the current axis range. For\n  example, if \\a factor is 2.0, then the axis range will double its size, and the point at the axis\n  range center won't have changed its position in the QCustomPlot widget (i.e. coordinates around\n  the center will have moved symmetrically closer).\n\n  If you wish to scale around a different coordinate than the current axis range center, use the\n  overload \\ref scaleRange(double factor, double center).\n*/\nvoid QCPPolarAxisAngular::scaleRange(double factor)\n{\n  scaleRange(factor, range().center());\n}\n\n/*! \\overload\n\n  Scales the range of this axis by \\a factor around the coordinate \\a center. For example, if \\a\n  factor is 2.0, \\a center is 1.0, then the axis range will double its size, and the point at\n  coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates\n  around 1.0 will have moved symmetrically closer to 1.0).\n\n  \\see scaleRange(double factor)\n*/\nvoid QCPPolarAxisAngular::scaleRange(double factor, double center)\n{\n  QCPRange oldRange = mRange;\n  QCPRange newRange;\n  newRange.lower = (mRange.lower-center)*factor + center;\n  newRange.upper = (mRange.upper-center)*factor + center;\n  if (QCPRange::validRange(newRange))\n    mRange = newRange.sanitizedForLinScale();\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  Changes the axis range such that all plottables associated with this axis are fully visible in\n  that dimension.\n  \n  \\see QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes\n*/\nvoid QCPPolarAxisAngular::rescale(bool onlyVisiblePlottables)\n{\n  QCPRange newRange;\n  bool haveRange = false;\n  for (int i=0; i<mGraphs.size(); ++i)\n  {\n    if (!mGraphs.at(i)->realVisibility() && onlyVisiblePlottables)\n      continue;\n    QCPRange range;\n    bool currentFoundRange;\n    if (mGraphs.at(i)->keyAxis() == this)\n      range = mGraphs.at(i)->getKeyRange(currentFoundRange, QCP::sdBoth);\n    else\n      range = mGraphs.at(i)->getValueRange(currentFoundRange, QCP::sdBoth);\n    if (currentFoundRange)\n    {\n      if (!haveRange)\n        newRange = range;\n      else\n        newRange.expand(range);\n      haveRange = true;\n    }\n  }\n  if (haveRange)\n  {\n    if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable\n    {\n      double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason\n      newRange.lower = center-mRange.size()/2.0;\n      newRange.upper = center+mRange.size()/2.0;\n    }\n    setRange(newRange);\n  }\n}\n\n/*!\n  Transforms \\a value, in pixel coordinates of the QCustomPlot widget, to axis coordinates.\n*/\nvoid QCPPolarAxisAngular::pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const\n{\n  if (!mRadialAxes.isEmpty())\n    mRadialAxes.first()->pixelToCoord(pixelPos, angleCoord, radiusCoord);\n  else\n    qDebug() << Q_FUNC_INFO << \"no radial axis configured\";\n}\n\n/*!\n  Transforms \\a value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget.\n*/\nQPointF QCPPolarAxisAngular::coordToPixel(double angleCoord, double radiusCoord) const\n{\n  if (!mRadialAxes.isEmpty())\n  {\n    return mRadialAxes.first()->coordToPixel(angleCoord, radiusCoord);\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"no radial axis configured\";\n    return QPointF();\n  }\n}\n\n/*!\n  Returns the part of the axis that is hit by \\a pos (in pixels). The return value of this function\n  is independent of the user-selectable parts defined with \\ref setSelectableParts. Further, this\n  function does not change the current selection state of the axis.\n  \n  If the axis is not visible (\\ref setVisible), this function always returns \\ref spNone.\n  \n  \\see setSelectedParts, setSelectableParts, QCustomPlot::setInteractions\n*/\nQCPPolarAxisAngular::SelectablePart QCPPolarAxisAngular::getPartAt(const QPointF &pos) const\n{\n  Q_UNUSED(pos) // TODO remove later\n  \n  if (!mVisible)\n    return spNone;\n  \n  /*\n    TODO:\n  if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))\n    return spAxis;\n  else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))\n    return spTickLabels;\n  else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))\n    return spAxisLabel;\n  else */\n    return spNone;\n}\n\n/* inherits documentation from base class */\ndouble QCPPolarAxisAngular::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  /*\n  if (!mParentPlot) return -1;\n  SelectablePart part = getPartAt(pos);\n  if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)\n    return -1;\n  \n  if (details)\n    details->setValue(part);\n  return mParentPlot->selectionTolerance()*0.99;\n  */\n  \n  Q_UNUSED(details)\n  \n  if (onlySelectable)\n    return -1;\n  \n  if (QRectF(mOuterRect).contains(pos))\n  {\n    if (mParentPlot)\n      return mParentPlot->selectionTolerance()*0.99;\n    else\n    {\n      qDebug() << Q_FUNC_INFO << \"parent plot not defined\";\n      return -1;\n    }\n  } else\n    return -1;\n}\n\n/*!\n  This method is called automatically upon replot and doesn't need to be called by users of\n  QCPPolarAxisAngular.\n  \n  Calls the base class implementation to update the margins (see \\ref QCPLayoutElement::update),\n  and finally passes the \\ref rect to the inset layout (\\ref insetLayout) and calls its\n  QCPInsetLayout::update function.\n  \n  \\seebaseclassmethod\n*/\nvoid QCPPolarAxisAngular::update(UpdatePhase phase)\n{\n  QCPLayoutElement::update(phase);\n  \n  switch (phase)\n  {\n    case upPreparation:\n    {\n      setupTickVectors();\n      for (int i=0; i<mRadialAxes.size(); ++i)\n        mRadialAxes.at(i)->setupTickVectors();\n      break;\n    }\n    case upLayout:\n    {\n      mCenter = mRect.center();\n      mRadius = 0.5*qMin(qAbs(mRect.width()), qAbs(mRect.height()));\n      if (mRadius < 1) mRadius = 1; // prevent cases where radius might become 0 which causes trouble\n      for (int i=0; i<mRadialAxes.size(); ++i)\n        mRadialAxes.at(i)->updateGeometry(mCenter, mRadius);\n      \n      mInsetLayout->setOuterRect(rect());\n      break;\n    }\n    default: break;\n  }\n  \n  // pass update call on to inset layout (doesn't happen automatically, because QCPPolarAxis doesn't derive from QCPLayout):\n  mInsetLayout->update(phase);\n}\n\n/* inherits documentation from base class */\nQList<QCPLayoutElement*> QCPPolarAxisAngular::elements(bool recursive) const\n{\n  QList<QCPLayoutElement*> result;\n  if (mInsetLayout)\n  {\n    result << mInsetLayout;\n    if (recursive)\n      result << mInsetLayout->elements(recursive);\n  }\n  return result;\n}\n\nbool QCPPolarAxisAngular::removeGraph(QCPPolarGraph *graph)\n{\n  if (!mGraphs.contains(graph))\n  {\n    qDebug() << Q_FUNC_INFO << \"graph not in list:\" << reinterpret_cast<quintptr>(graph);\n    return false;\n  }\n  \n  // remove plottable from legend:\n  graph->removeFromLegend();\n  // remove plottable:\n  delete graph;\n  mGraphs.removeOne(graph);\n  return true;\n}\n\n/* inherits documentation from base class */\nvoid QCPPolarAxisAngular::applyDefaultAntialiasingHint(QCPPainter *painter) const\n{\n  applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes);\n}\n\n/* inherits documentation from base class */\nvoid QCPPolarAxisAngular::draw(QCPPainter *painter)\n{\n  drawBackground(painter, mCenter, mRadius);\n  \n  // draw baseline circle:\n  painter->setPen(getBasePen());\n  painter->drawEllipse(mCenter, mRadius, mRadius);\n  \n  // draw subticks:\n  if (!mSubTickVector.isEmpty())\n  {\n    painter->setPen(getSubTickPen());\n    for (int i=0; i<mSubTickVector.size(); ++i)\n    {\n      painter->drawLine(mCenter+mSubTickVectorCosSin.at(i)*(mRadius-mSubTickLengthIn),\n                        mCenter+mSubTickVectorCosSin.at(i)*(mRadius+mSubTickLengthOut));\n    }\n  }\n  \n  // draw ticks and labels:\n  if (!mTickVector.isEmpty())\n  {\n    mLabelPainter.setAnchorReference(mCenter);\n    mLabelPainter.setFont(getTickLabelFont());\n    mLabelPainter.setColor(getTickLabelColor());\n    const QPen ticksPen = getTickPen();\n    painter->setPen(ticksPen);\n    for (int i=0; i<mTickVector.size(); ++i)\n    {\n      const QPointF outerTick = mCenter+mTickVectorCosSin.at(i)*(mRadius+mTickLengthOut);\n      painter->drawLine(mCenter+mTickVectorCosSin.at(i)*(mRadius-mTickLengthIn), outerTick);\n      // draw tick labels:\n      if (!mTickVectorLabels.isEmpty())\n      {\n        if (i < mTickVectorLabels.count()-1 || (mTickVectorCosSin.at(i)-mTickVectorCosSin.first()).manhattanLength() > 5/180.0*M_PI) // skip last label if it's closer than approx 5 degrees to first\n          mLabelPainter.drawTickLabel(painter, outerTick, mTickVectorLabels.at(i));\n      }\n    }\n  }\n}\n\n/* inherits documentation from base class */\nQCP::Interaction QCPPolarAxisAngular::selectionCategory() const\n{\n  return QCP::iSelectAxes;\n}\n\n\n/*!\n  Sets \\a pm as the axis background pixmap. The axis background pixmap will be drawn inside the\n  axis rect. Since axis rects place themselves on the \"background\" layer by default, the axis rect\n  backgrounds are usually drawn below everything else.\n\n  For cases where the provided pixmap doesn't have the same size as the axis rect, scaling can be\n  enabled with \\ref setBackgroundScaled and the scaling mode (i.e. whether and how the aspect ratio\n  is preserved) can be set with \\ref setBackgroundScaledMode. To set all these options in one call,\n  consider using the overloaded version of this function.\n\n  Below the pixmap, the axis rect may be optionally filled with a brush, if specified with \\ref\n  setBackground(const QBrush &brush).\n  \n  \\see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush &brush)\n*/\nvoid QCPPolarAxisAngular::setBackground(const QPixmap &pm)\n{\n  mBackgroundPixmap = pm;\n  mScaledBackgroundPixmap = QPixmap();\n}\n\n/*! \\overload\n  \n  Sets \\a brush as the background brush. The axis rect background will be filled with this brush.\n  Since axis rects place themselves on the \"background\" layer by default, the axis rect backgrounds\n  are usually drawn below everything else.\n\n  The brush will be drawn before (under) any background pixmap, which may be specified with \\ref\n  setBackground(const QPixmap &pm).\n\n  To disable drawing of a background brush, set \\a brush to Qt::NoBrush.\n  \n  \\see setBackground(const QPixmap &pm)\n*/\nvoid QCPPolarAxisAngular::setBackground(const QBrush &brush)\n{\n  mBackgroundBrush = brush;\n}\n\n/*! \\overload\n  \n  Allows setting the background pixmap of the axis rect, whether it shall be scaled and how it\n  shall be scaled in one call.\n\n  \\see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode\n*/\nvoid QCPPolarAxisAngular::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)\n{\n  mBackgroundPixmap = pm;\n  mScaledBackgroundPixmap = QPixmap();\n  mBackgroundScaled = scaled;\n  mBackgroundScaledMode = mode;\n}\n\n/*!\n  Sets whether the axis background pixmap shall be scaled to fit the axis rect or not. If \\a scaled\n  is set to true, you may control whether and how the aspect ratio of the original pixmap is\n  preserved with \\ref setBackgroundScaledMode.\n  \n  Note that the scaled version of the original pixmap is buffered, so there is no performance\n  penalty on replots. (Except when the axis rect dimensions are changed continuously.)\n  \n  \\see setBackground, setBackgroundScaledMode\n*/\nvoid QCPPolarAxisAngular::setBackgroundScaled(bool scaled)\n{\n  mBackgroundScaled = scaled;\n}\n\n/*!\n  If scaling of the axis background pixmap is enabled (\\ref setBackgroundScaled), use this function to\n  define whether and how the aspect ratio of the original pixmap passed to \\ref setBackground is preserved.\n  \\see setBackground, setBackgroundScaled\n*/\nvoid QCPPolarAxisAngular::setBackgroundScaledMode(Qt::AspectRatioMode mode)\n{\n  mBackgroundScaledMode = mode;\n}\n\nvoid QCPPolarAxisAngular::setRangeDrag(bool enabled)\n{\n  mRangeDrag = enabled;\n}\n\nvoid QCPPolarAxisAngular::setRangeZoom(bool enabled)\n{\n  mRangeZoom = enabled;\n}\n\nvoid QCPPolarAxisAngular::setRangeZoomFactor(double factor)\n{\n  mRangeZoomFactor = factor;\n}\n\n\n\n\n\n\n\n/*!\n  Sets the range of the axis.\n  \n  This slot may be connected with the \\ref rangeChanged signal of another axis so this axis\n  is always synchronized with the other axis range, when it changes.\n  \n  To invert the direction of an axis, use \\ref setRangeReversed.\n*/\nvoid QCPPolarAxisAngular::setRange(const QCPRange &range)\n{\n  if (range.lower == mRange.lower && range.upper == mRange.upper)\n    return;\n  \n  if (!QCPRange::validRange(range)) return;\n  QCPRange oldRange = mRange;\n  mRange = range.sanitizedForLinScale();\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  Sets whether the user can (de-)select the parts in \\a selectable by clicking on the QCustomPlot surface.\n  (When \\ref QCustomPlot::setInteractions contains iSelectAxes.)\n  \n  However, even when \\a selectable is set to a value not allowing the selection of a specific part,\n  it is still possible to set the selection of this part manually, by calling \\ref setSelectedParts\n  directly.\n  \n  \\see SelectablePart, setSelectedParts\n*/\nvoid QCPPolarAxisAngular::setSelectableParts(const SelectableParts &selectable)\n{\n  if (mSelectableParts != selectable)\n  {\n    mSelectableParts = selectable;\n    emit selectableChanged(mSelectableParts);\n  }\n}\n\n/*!\n  Sets the selected state of the respective axis parts described by \\ref SelectablePart. When a part\n  is selected, it uses a different pen/font.\n  \n  The entire selection mechanism for axes is handled automatically when \\ref\n  QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you\n  wish to change the selection state manually.\n  \n  This function can change the selection state of a part, independent of the \\ref setSelectableParts setting.\n  \n  emits the \\ref selectionChanged signal when \\a selected is different from the previous selection state.\n  \n  \\see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen,\n  setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor\n*/\nvoid QCPPolarAxisAngular::setSelectedParts(const SelectableParts &selected)\n{\n  if (mSelectedParts != selected)\n  {\n    mSelectedParts = selected;\n    emit selectionChanged(mSelectedParts);\n  }\n}\n\n/*!\n  \\overload\n  \n  Sets the lower and upper bound of the axis range.\n  \n  To invert the direction of an axis, use \\ref setRangeReversed.\n  \n  There is also a slot to set a range, see \\ref setRange(const QCPRange &range).\n*/\nvoid QCPPolarAxisAngular::setRange(double lower, double upper)\n{\n  if (lower == mRange.lower && upper == mRange.upper)\n    return;\n  \n  if (!QCPRange::validRange(lower, upper)) return;\n  QCPRange oldRange = mRange;\n  mRange.lower = lower;\n  mRange.upper = upper;\n  mRange = mRange.sanitizedForLinScale();\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  \\overload\n  \n  Sets the range of the axis.\n  \n  The \\a position coordinate indicates together with the \\a alignment parameter, where the new\n  range will be positioned. \\a size defines the size of the new axis range. \\a alignment may be\n  Qt::AlignLeft, Qt::AlignRight or Qt::AlignCenter. This will cause the left border, right border,\n  or center of the range to be aligned with \\a position. Any other values of \\a alignment will\n  default to Qt::AlignCenter.\n*/\nvoid QCPPolarAxisAngular::setRange(double position, double size, Qt::AlignmentFlag alignment)\n{\n  if (alignment == Qt::AlignLeft)\n    setRange(position, position+size);\n  else if (alignment == Qt::AlignRight)\n    setRange(position-size, position);\n  else // alignment == Qt::AlignCenter\n    setRange(position-size/2.0, position+size/2.0);\n}\n\n/*!\n  Sets the lower bound of the axis range. The upper bound is not changed.\n  \\see setRange\n*/\nvoid QCPPolarAxisAngular::setRangeLower(double lower)\n{\n  if (mRange.lower == lower)\n    return;\n  \n  QCPRange oldRange = mRange;\n  mRange.lower = lower;\n  mRange = mRange.sanitizedForLinScale();\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  Sets the upper bound of the axis range. The lower bound is not changed.\n  \\see setRange\n*/\nvoid QCPPolarAxisAngular::setRangeUpper(double upper)\n{\n  if (mRange.upper == upper)\n    return;\n  \n  QCPRange oldRange = mRange;\n  mRange.upper = upper;\n  mRange = mRange.sanitizedForLinScale();\n  emit rangeChanged(mRange);\n  emit rangeChanged(mRange, oldRange);\n}\n\n/*!\n  Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal\n  axes increase left to right, on vertical axes bottom to top. When \\a reversed is set to true, the\n  direction of increasing values is inverted.\n\n  Note that the range and data interface stays the same for reversed axes, e.g. the \\a lower part\n  of the \\ref setRange interface will still reference the mathematically smaller number than the \\a\n  upper part.\n*/\nvoid QCPPolarAxisAngular::setRangeReversed(bool reversed)\n{\n  mRangeReversed = reversed;\n}\n\nvoid QCPPolarAxisAngular::setAngle(double degrees)\n{\n  mAngle = degrees;\n  mAngleRad = mAngle/180.0*M_PI;\n}\n\n/*!\n  The axis ticker is responsible for generating the tick positions and tick labels. See the\n  documentation of QCPAxisTicker for details on how to work with axis tickers.\n  \n  You can change the tick positioning/labeling behaviour of this axis by setting a different\n  QCPAxisTicker subclass using this method. If you only wish to modify the currently installed axis\n  ticker, access it via \\ref ticker.\n  \n  Since the ticker is stored in the axis as a shared pointer, multiple axes may share the same axis\n  ticker simply by passing the same shared pointer to multiple axes.\n  \n  \\see ticker\n*/\nvoid QCPPolarAxisAngular::setTicker(QSharedPointer<QCPAxisTicker> ticker)\n{\n  if (ticker)\n    mTicker = ticker;\n  else\n    qDebug() << Q_FUNC_INFO << \"can not set 0 as axis ticker\";\n  // no need to invalidate margin cache here because produced tick labels are checked for changes in setupTickVector\n}\n\n/*!\n  Sets whether tick marks are displayed.\n\n  Note that setting \\a show to false does not imply that tick labels are invisible, too. To achieve\n  that, see \\ref setTickLabels.\n  \n  \\see setSubTicks\n*/\nvoid QCPPolarAxisAngular::setTicks(bool show)\n{\n  if (mTicks != show)\n  {\n    mTicks = show;\n    //mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets whether tick labels are displayed. Tick labels are the numbers drawn next to tick marks.\n*/\nvoid QCPPolarAxisAngular::setTickLabels(bool show)\n{\n  if (mTickLabels != show)\n  {\n    mTickLabels = show;\n    //mCachedMarginValid = false;\n    if (!mTickLabels)\n      mTickVectorLabels.clear();\n  }\n}\n\n/*!\n  Sets the distance between the axis base line (including any outward ticks) and the tick labels.\n  \\see setLabelPadding, setPadding\n*/\nvoid QCPPolarAxisAngular::setTickLabelPadding(int padding)\n{\n  mLabelPainter.setPadding(padding);\n}\n\n/*!\n  Sets the font of the tick labels.\n  \n  \\see setTickLabels, setTickLabelColor\n*/\nvoid QCPPolarAxisAngular::setTickLabelFont(const QFont &font)\n{\n  mTickLabelFont = font;\n}\n\n/*!\n  Sets the color of the tick labels.\n  \n  \\see setTickLabels, setTickLabelFont\n*/\nvoid QCPPolarAxisAngular::setTickLabelColor(const QColor &color)\n{\n  mTickLabelColor = color;\n}\n\n/*!\n  Sets the rotation of the tick labels. If \\a degrees is zero, the labels are drawn normally. Else,\n  the tick labels are drawn rotated by \\a degrees clockwise. The specified angle is bound to values\n  from -90 to 90 degrees.\n  \n  If \\a degrees is exactly -90, 0 or 90, the tick labels are centered on the tick coordinate. For\n  other angles, the label is drawn with an offset such that it seems to point toward or away from\n  the tick mark.\n*/\nvoid QCPPolarAxisAngular::setTickLabelRotation(double degrees)\n{\n  mLabelPainter.setRotation(degrees);\n}\n\nvoid QCPPolarAxisAngular::setTickLabelMode(LabelMode mode)\n{\n  switch (mode)\n  {\n    case lmUpright: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedUpright); break;\n    case lmRotated: mLabelPainter.setAnchorMode(QCPLabelPainterPrivate::amSkewedRotated); break;\n  }\n}\n\n/*!\n  Sets the number format for the numbers in tick labels. This \\a formatCode is an extended version\n  of the format code used e.g. by QString::number() and QLocale::toString(). For reference about\n  that, see the \"Argument Formats\" section in the detailed description of the QString class.\n  \n  \\a formatCode is a string of one, two or three characters. The first character is identical to\n  the normal format code used by Qt. In short, this means: 'e'/'E' scientific format, 'f' fixed\n  format, 'g'/'G' scientific or fixed, whichever is shorter.\n  \n  The second and third characters are optional and specific to QCustomPlot:\\n If the first char was\n  'e' or 'g', numbers are/might be displayed in the scientific format, e.g. \"5.5e9\", which might be\n  visually unappealing in a plot. So when the second char of \\a formatCode is set to 'b' (for\n  \"beautiful\"), those exponential numbers are formatted in a more natural way, i.e. \"5.5\n  [multiplication sign] 10 [superscript] 9\". By default, the multiplication sign is a centered dot.\n  If instead a cross should be shown (as is usual in the USA), the third char of \\a formatCode can\n  be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the\n  cross and 183 (0xB7) for the dot.\n  \n  Examples for \\a formatCode:\n  \\li \\c g normal format code behaviour. If number is small, fixed format is used, if number is large,\n  normal scientific format is used\n  \\li \\c gb If number is small, fixed format is used, if number is large, scientific format is used with\n  beautifully typeset decimal powers and a dot as multiplication sign\n  \\li \\c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as\n  multiplication sign\n  \\li \\c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal\n  powers. Format code will be reduced to 'f'.\n  \\li \\c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format\n  code will not be changed.\n*/\nvoid QCPPolarAxisAngular::setNumberFormat(const QString &formatCode)\n{\n  if (formatCode.isEmpty())\n  {\n    qDebug() << Q_FUNC_INFO << \"Passed formatCode is empty\";\n    return;\n  }\n  //mCachedMarginValid = false;\n  \n  // interpret first char as number format char:\n  QString allowedFormatChars(QLatin1String(\"eEfgG\"));\n  if (allowedFormatChars.contains(formatCode.at(0)))\n  {\n    mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"Invalid number format code (first char not in 'eEfgG'):\" << formatCode;\n    return;\n  }\n  \n  if (formatCode.length() < 2)\n  {\n    mNumberBeautifulPowers = false;\n    mNumberMultiplyCross = false;\n  } else\n  {\n    // interpret second char as indicator for beautiful decimal powers:\n    if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))\n      mNumberBeautifulPowers = true;\n    else\n      qDebug() << Q_FUNC_INFO << \"Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):\" << formatCode;\n    \n    if (formatCode.length() < 3)\n    {\n      mNumberMultiplyCross = false;\n    } else\n    {\n      // interpret third char as indicator for dot or cross multiplication symbol:\n      if (formatCode.at(2) == QLatin1Char('c'))\n        mNumberMultiplyCross = true;\n      else if (formatCode.at(2) == QLatin1Char('d'))\n        mNumberMultiplyCross = false;\n      else\n        qDebug() << Q_FUNC_INFO << \"Invalid number format code (third char neither 'c' nor 'd'):\" << formatCode;\n    }\n  }\n  mLabelPainter.setSubstituteExponent(mNumberBeautifulPowers);\n  mLabelPainter.setMultiplicationSymbol(mNumberMultiplyCross ? QCPLabelPainterPrivate::SymbolCross : QCPLabelPainterPrivate::SymbolDot);\n}\n\n/*!\n  Sets the precision of the tick label numbers. See QLocale::toString(double i, char f, int prec)\n  for details. The effect of precisions are most notably for number Formats starting with 'e', see\n  \\ref setNumberFormat\n*/\nvoid QCPPolarAxisAngular::setNumberPrecision(int precision)\n{\n  if (mNumberPrecision != precision)\n  {\n    mNumberPrecision = precision;\n    //mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the length of the ticks in pixels. \\a inside is the length the ticks will reach inside the\n  plot and \\a outside is the length they will reach outside the plot. If \\a outside is greater than\n  zero, the tick labels and axis label will increase their distance to the axis accordingly, so\n  they won't collide with the ticks.\n  \n  \\see setSubTickLength, setTickLengthIn, setTickLengthOut\n*/\nvoid QCPPolarAxisAngular::setTickLength(int inside, int outside)\n{\n  setTickLengthIn(inside);\n  setTickLengthOut(outside);\n}\n\n/*!\n  Sets the length of the inward ticks in pixels. \\a inside is the length the ticks will reach\n  inside the plot.\n  \n  \\see setTickLengthOut, setTickLength, setSubTickLength\n*/\nvoid QCPPolarAxisAngular::setTickLengthIn(int inside)\n{\n  if (mTickLengthIn != inside)\n  {\n    mTickLengthIn = inside;\n  }\n}\n\n/*!\n  Sets the length of the outward ticks in pixels. \\a outside is the length the ticks will reach\n  outside the plot. If \\a outside is greater than zero, the tick labels and axis label will\n  increase their distance to the axis accordingly, so they won't collide with the ticks.\n  \n  \\see setTickLengthIn, setTickLength, setSubTickLength\n*/\nvoid QCPPolarAxisAngular::setTickLengthOut(int outside)\n{\n  if (mTickLengthOut != outside)\n  {\n    mTickLengthOut = outside;\n    //mCachedMarginValid = false; // only outside tick length can change margin\n  }\n}\n\n/*!\n  Sets whether sub tick marks are displayed.\n  \n  Sub ticks are only potentially visible if (major) ticks are also visible (see \\ref setTicks)\n  \n  \\see setTicks\n*/\nvoid QCPPolarAxisAngular::setSubTicks(bool show)\n{\n  if (mSubTicks != show)\n  {\n    mSubTicks = show;\n    //mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the length of the subticks in pixels. \\a inside is the length the subticks will reach inside\n  the plot and \\a outside is the length they will reach outside the plot. If \\a outside is greater\n  than zero, the tick labels and axis label will increase their distance to the axis accordingly,\n  so they won't collide with the ticks.\n  \n  \\see setTickLength, setSubTickLengthIn, setSubTickLengthOut\n*/\nvoid QCPPolarAxisAngular::setSubTickLength(int inside, int outside)\n{\n  setSubTickLengthIn(inside);\n  setSubTickLengthOut(outside);\n}\n\n/*!\n  Sets the length of the inward subticks in pixels. \\a inside is the length the subticks will reach inside\n  the plot.\n  \n  \\see setSubTickLengthOut, setSubTickLength, setTickLength\n*/\nvoid QCPPolarAxisAngular::setSubTickLengthIn(int inside)\n{\n  if (mSubTickLengthIn != inside)\n  {\n    mSubTickLengthIn = inside;\n  }\n}\n\n/*!\n  Sets the length of the outward subticks in pixels. \\a outside is the length the subticks will reach\n  outside the plot. If \\a outside is greater than zero, the tick labels will increase their\n  distance to the axis accordingly, so they won't collide with the ticks.\n  \n  \\see setSubTickLengthIn, setSubTickLength, setTickLength\n*/\nvoid QCPPolarAxisAngular::setSubTickLengthOut(int outside)\n{\n  if (mSubTickLengthOut != outside)\n  {\n    mSubTickLengthOut = outside;\n    //mCachedMarginValid = false; // only outside tick length can change margin\n  }\n}\n\n/*!\n  Sets the pen, the axis base line is drawn with.\n  \n  \\see setTickPen, setSubTickPen\n*/\nvoid QCPPolarAxisAngular::setBasePen(const QPen &pen)\n{\n  mBasePen = pen;\n}\n\n/*!\n  Sets the pen, tick marks will be drawn with.\n  \n  \\see setTickLength, setBasePen\n*/\nvoid QCPPolarAxisAngular::setTickPen(const QPen &pen)\n{\n  mTickPen = pen;\n}\n\n/*!\n  Sets the pen, subtick marks will be drawn with.\n  \n  \\see setSubTickCount, setSubTickLength, setBasePen\n*/\nvoid QCPPolarAxisAngular::setSubTickPen(const QPen &pen)\n{\n  mSubTickPen = pen;\n}\n\n/*!\n  Sets the font of the axis label.\n  \n  \\see setLabelColor\n*/\nvoid QCPPolarAxisAngular::setLabelFont(const QFont &font)\n{\n  if (mLabelFont != font)\n  {\n    mLabelFont = font;\n    //mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the color of the axis label.\n  \n  \\see setLabelFont\n*/\nvoid QCPPolarAxisAngular::setLabelColor(const QColor &color)\n{\n  mLabelColor = color;\n}\n\n/*!\n  Sets the text of the axis label that will be shown below/above or next to the axis, depending on\n  its orientation. To disable axis labels, pass an empty string as \\a str.\n*/\nvoid QCPPolarAxisAngular::setLabel(const QString &str)\n{\n  if (mLabel != str)\n  {\n    mLabel = str;\n    //mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the distance between the tick labels and the axis label.\n  \n  \\see setTickLabelPadding, setPadding\n*/\nvoid QCPPolarAxisAngular::setLabelPadding(int padding)\n{\n  if (mLabelPadding != padding)\n  {\n    mLabelPadding = padding;\n    //mCachedMarginValid = false;\n  }\n}\n\n/*!\n  Sets the font that is used for tick labels when they are selected.\n  \n  \\see setTickLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPPolarAxisAngular::setSelectedTickLabelFont(const QFont &font)\n{\n  if (font != mSelectedTickLabelFont)\n  {\n    mSelectedTickLabelFont = font;\n    // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts\n  }\n}\n\n/*!\n  Sets the font that is used for the axis label when it is selected.\n  \n  \\see setLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPPolarAxisAngular::setSelectedLabelFont(const QFont &font)\n{\n  mSelectedLabelFont = font;\n  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts\n}\n\n/*!\n  Sets the color that is used for tick labels when they are selected.\n  \n  \\see setTickLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPPolarAxisAngular::setSelectedTickLabelColor(const QColor &color)\n{\n  if (color != mSelectedTickLabelColor)\n  {\n    mSelectedTickLabelColor = color;\n  }\n}\n\n/*!\n  Sets the color that is used for the axis label when it is selected.\n  \n  \\see setLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPPolarAxisAngular::setSelectedLabelColor(const QColor &color)\n{\n  mSelectedLabelColor = color;\n}\n\n/*!\n  Sets the pen that is used to draw the axis base line when selected.\n  \n  \\see setBasePen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPPolarAxisAngular::setSelectedBasePen(const QPen &pen)\n{\n  mSelectedBasePen = pen;\n}\n\n/*!\n  Sets the pen that is used to draw the (major) ticks when selected.\n  \n  \\see setTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPPolarAxisAngular::setSelectedTickPen(const QPen &pen)\n{\n  mSelectedTickPen = pen;\n}\n\n/*!\n  Sets the pen that is used to draw the subticks when selected.\n  \n  \\see setSubTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions\n*/\nvoid QCPPolarAxisAngular::setSelectedSubTickPen(const QPen &pen)\n{\n  mSelectedSubTickPen = pen;\n}\n\n/*! \\internal\n  \n  Draws the background of this axis rect. It may consist of a background fill (a QBrush) and a\n  pixmap.\n  \n  If a brush was given via \\ref setBackground(const QBrush &brush), this function first draws an\n  according filling inside the axis rect with the provided \\a painter.\n  \n  Then, if a pixmap was provided via \\ref setBackground, this function buffers the scaled version\n  depending on \\ref setBackgroundScaled and \\ref setBackgroundScaledMode and then draws it inside\n  the axis rect with the provided \\a painter. The scaled version is buffered in\n  mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when\n  the axis rect has changed in a way that requires a rescale of the background pixmap (this is\n  dependent on the \\ref setBackgroundScaledMode), or when a differend axis background pixmap was\n  set.\n  \n  \\see setBackground, setBackgroundScaled, setBackgroundScaledMode\n*/\nvoid QCPPolarAxisAngular::drawBackground(QCPPainter *painter, const QPointF &center, double radius)\n{\n  // draw background fill (don't use circular clip, looks bad):\n  if (mBackgroundBrush != Qt::NoBrush)\n  {\n    QPainterPath ellipsePath;\n    ellipsePath.addEllipse(center, radius, radius);\n    painter->fillPath(ellipsePath, mBackgroundBrush);\n  }\n  \n  // draw background pixmap (on top of fill, if brush specified):\n  if (!mBackgroundPixmap.isNull())\n  {\n    QRegion clipCircle(center.x()-radius, center.y()-radius, qRound(2*radius), qRound(2*radius), QRegion::Ellipse);\n    QRegion originalClip = painter->clipRegion();\n    painter->setClipRegion(clipCircle);\n    if (mBackgroundScaled)\n    {\n      // check whether mScaledBackground needs to be updated:\n      QSize scaledSize(mBackgroundPixmap.size());\n      scaledSize.scale(mRect.size(), mBackgroundScaledMode);\n      if (mScaledBackgroundPixmap.size() != scaledSize)\n        mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);\n      painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());\n    } else\n    {\n      painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));\n    }\n    painter->setClipRegion(originalClip);\n  }\n}\n\n/*! \\internal\n  \n  Prepares the internal tick vector, sub tick vector and tick label vector. This is done by calling\n  QCPAxisTicker::generate on the currently installed ticker.\n  \n  If a change in the label text/count is detected, the cached axis margin is invalidated to make\n  sure the next margin calculation recalculates the label sizes and returns an up-to-date value.\n*/\nvoid QCPPolarAxisAngular::setupTickVectors()\n{\n  if (!mParentPlot) return;\n  if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;\n  \n  mSubTickVector.clear(); // since we might not pass it to mTicker->generate(), and we don't want old data in there\n  mTicker->generate(mRange, mParentPlot->locale(), mNumberFormatChar, mNumberPrecision, mTickVector, mSubTicks ? &mSubTickVector : 0, mTickLabels ? &mTickVectorLabels : 0);\n  \n  // fill cos/sin buffers which will be used by draw() and QCPPolarGrid::draw(), so we don't have to calculate it twice:\n  mTickVectorCosSin.resize(mTickVector.size());\n  for (int i=0; i<mTickVector.size(); ++i)\n  {\n    const double theta = coordToAngleRad(mTickVector.at(i));\n    mTickVectorCosSin[i] = QPointF(qCos(theta), qSin(theta));\n  }\n  mSubTickVectorCosSin.resize(mSubTickVector.size());\n  for (int i=0; i<mSubTickVector.size(); ++i)\n  {\n    const double theta = coordToAngleRad(mSubTickVector.at(i));\n    mSubTickVectorCosSin[i] = QPointF(qCos(theta), qSin(theta));\n  }\n}\n\n/*! \\internal\n  \n  Returns the pen that is used to draw the axis base line. Depending on the selection state, this\n  is either mSelectedBasePen or mBasePen.\n*/\nQPen QCPPolarAxisAngular::getBasePen() const\n{\n  return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;\n}\n\n/*! \\internal\n  \n  Returns the pen that is used to draw the (major) ticks. Depending on the selection state, this\n  is either mSelectedTickPen or mTickPen.\n*/\nQPen QCPPolarAxisAngular::getTickPen() const\n{\n  return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;\n}\n\n/*! \\internal\n  \n  Returns the pen that is used to draw the subticks. Depending on the selection state, this\n  is either mSelectedSubTickPen or mSubTickPen.\n*/\nQPen QCPPolarAxisAngular::getSubTickPen() const\n{\n  return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen;\n}\n\n/*! \\internal\n  \n  Returns the font that is used to draw the tick labels. Depending on the selection state, this\n  is either mSelectedTickLabelFont or mTickLabelFont.\n*/\nQFont QCPPolarAxisAngular::getTickLabelFont() const\n{\n  return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont;\n}\n\n/*! \\internal\n  \n  Returns the font that is used to draw the axis label. Depending on the selection state, this\n  is either mSelectedLabelFont or mLabelFont.\n*/\nQFont QCPPolarAxisAngular::getLabelFont() const\n{\n  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont;\n}\n\n/*! \\internal\n  \n  Returns the color that is used to draw the tick labels. Depending on the selection state, this\n  is either mSelectedTickLabelColor or mTickLabelColor.\n*/\nQColor QCPPolarAxisAngular::getTickLabelColor() const\n{\n  return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor;\n}\n\n/*! \\internal\n  \n  Returns the color that is used to draw the axis label. Depending on the selection state, this\n  is either mSelectedLabelColor or mLabelColor.\n*/\nQColor QCPPolarAxisAngular::getLabelColor() const\n{\n  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor;\n}\n\n/*! \\internal\n  \n  Event handler for when a mouse button is pressed on the axis rect. If the left mouse button is\n  pressed, the range dragging interaction is initialized (the actual range manipulation happens in\n  the \\ref mouseMoveEvent).\n\n  The mDragging flag is set to true and some anchor points are set that are needed to determine the\n  distance the mouse was dragged in the mouse move/release events later.\n  \n  \\see mouseMoveEvent, mouseReleaseEvent\n*/\nvoid QCPPolarAxisAngular::mousePressEvent(QMouseEvent *event, const QVariant &details)\n{\n  Q_UNUSED(details)\n  if (event->buttons() & Qt::LeftButton)\n  {\n    mDragging = true;\n    // initialize antialiasing backup in case we start dragging:\n    if (mParentPlot->noAntialiasingOnDrag())\n    {\n      mAADragBackup = mParentPlot->antialiasedElements();\n      mNotAADragBackup = mParentPlot->notAntialiasedElements();\n    }\n    // Mouse range dragging interaction:\n    if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))\n    {\n      mDragAngularStart = range();\n      mDragRadialStart.clear();\n      for (int i=0; i<mRadialAxes.size(); ++i)\n        mDragRadialStart.append(mRadialAxes.at(i)->range());\n    }\n  }\n}\n\n/*! \\internal\n  \n  Event handler for when the mouse is moved on the axis rect. If range dragging was activated in a\n  preceding \\ref mousePressEvent, the range is moved accordingly.\n  \n  \\see mousePressEvent, mouseReleaseEvent\n*/\nvoid QCPPolarAxisAngular::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)\n{\n  Q_UNUSED(startPos)\n  bool doReplot = false;\n  // Mouse range dragging interaction:\n  if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))\n  {\n    if (mRangeDrag)\n    {\n      doReplot = true;\n      double angleCoordStart, radiusCoordStart;\n      double angleCoord, radiusCoord;\n      pixelToCoord(startPos, angleCoordStart, radiusCoordStart);\n      pixelToCoord(event->pos(), angleCoord, radiusCoord);\n      double diff = angleCoordStart - angleCoord;\n      setRange(mDragAngularStart.lower+diff, mDragAngularStart.upper+diff);\n    }\n    \n    for (int i=0; i<mRadialAxes.size(); ++i)\n    {\n      QCPPolarAxisRadial *ax = mRadialAxes.at(i);\n      if (!ax->rangeDrag())\n        continue;\n      doReplot = true;\n      double angleCoordStart, radiusCoordStart;\n      double angleCoord, radiusCoord;\n      ax->pixelToCoord(startPos, angleCoordStart, radiusCoordStart);\n      ax->pixelToCoord(event->pos(), angleCoord, radiusCoord);\n      if (ax->scaleType() == QCPPolarAxisRadial::stLinear)\n      {\n        double diff = radiusCoordStart - radiusCoord;\n        ax->setRange(mDragRadialStart.at(i).lower+diff, mDragRadialStart.at(i).upper+diff);\n      } else if (ax->scaleType() == QCPPolarAxisRadial::stLogarithmic)\n      {\n        if (radiusCoord != 0)\n        {\n          double diff = radiusCoordStart/radiusCoord;\n          ax->setRange(mDragRadialStart.at(i).lower*diff, mDragRadialStart.at(i).upper*diff);\n        }\n      }\n    }\n    \n    if (doReplot) // if either vertical or horizontal drag was enabled, do a replot\n    {\n      if (mParentPlot->noAntialiasingOnDrag())\n        mParentPlot->setNotAntialiasedElements(QCP::aeAll);\n      mParentPlot->replot(QCustomPlot::rpQueuedReplot);\n    }\n  }\n}\n\n/* inherits documentation from base class */\nvoid QCPPolarAxisAngular::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)\n{\n  Q_UNUSED(event)\n  Q_UNUSED(startPos)\n  mDragging = false;\n  if (mParentPlot->noAntialiasingOnDrag())\n  {\n    mParentPlot->setAntialiasedElements(mAADragBackup);\n    mParentPlot->setNotAntialiasedElements(mNotAADragBackup);\n  }\n}\n\n/*! \\internal\n  \n  Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the\n  ranges of the axes defined as rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of\n  the scaling operation is the current cursor position inside the axis rect. The scaling factor is\n  dependent on the mouse wheel delta (which direction the wheel was rotated) to provide a natural\n  zooming feel. The Strength of the zoom can be controlled via \\ref setRangeZoomFactor.\n  \n  Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse\n  wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be\n  multiples of 120. This is taken into account here, by calculating \\a wheelSteps and using it as\n  exponent of the range zoom factor. This takes care of the wheel direction automatically, by\n  inverting the factor, when the wheel step is negative (f^-1 = 1/f).\n*/\nvoid QCPPolarAxisAngular::wheelEvent(QWheelEvent *event)\n{\n  bool doReplot = false;\n  // Mouse range zooming interaction:\n  if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))\n  {\n#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)\n    const double delta = event->delta();\n#else\n    const double delta = event->angleDelta().y();\n#endif\n\n#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)\n    const QPointF pos = event->pos();\n#else\n    const QPointF pos = event->position();\n#endif\n    const double wheelSteps = delta/120.0; // a single step delta is +/-120 usually\n    if (mRangeZoom)\n    {\n      double angleCoord, radiusCoord;\n      pixelToCoord(pos, angleCoord, radiusCoord);\n      scaleRange(qPow(mRangeZoomFactor, wheelSteps), angleCoord);\n    }\n\n    for (int i=0; i<mRadialAxes.size(); ++i)\n    {\n      QCPPolarAxisRadial *ax = mRadialAxes.at(i);\n      if (!ax->rangeZoom())\n        continue;\n      doReplot = true;\n      double angleCoord, radiusCoord;\n      ax->pixelToCoord(pos, angleCoord, radiusCoord);\n      ax->scaleRange(qPow(ax->rangeZoomFactor(), wheelSteps), radiusCoord);\n    }\n  }\n  if (doReplot)\n    mParentPlot->replot();\n}\n\nbool QCPPolarAxisAngular::registerPolarGraph(QCPPolarGraph *graph)\n{\n  if (mGraphs.contains(graph))\n  {\n    qDebug() << Q_FUNC_INFO << \"plottable already added:\" << reinterpret_cast<quintptr>(graph);\n    return false;\n  }\n  if (graph->keyAxis() != this)\n  {\n    qDebug() << Q_FUNC_INFO << \"plottable not created with this as axis:\" << reinterpret_cast<quintptr>(graph);\n    return false;\n  }\n  \n  mGraphs.append(graph);\n  // possibly add plottable to legend:\n  if (mParentPlot->autoAddPlottableToLegend())\n    graph->addToLegend();\n  if (!graph->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)\n    graph->setLayer(mParentPlot->currentLayer());\n  return true;\n}\n/* end of 'src/polar/layoutelement-angularaxis.cpp' */\n\n\n/* including file 'src/polar/polargrid.cpp' */\n/* modified 2021-03-29T02:30:44, size 7493  */\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPPolarGrid\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPPolarGrid\n  \\brief The grid in both angular and radial dimensions for polar plots\n\n  \\warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and\n  functionality to be incomplete, as well as changing public interfaces in the future.\n*/\n\n/*!\n  Creates a QCPPolarGrid instance and sets default values.\n  \n  You shouldn't instantiate grids on their own, since every axis brings its own grid.\n*/\nQCPPolarGrid::QCPPolarGrid(QCPPolarAxisAngular *parentAxis) :\n  QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),\n  mType(gtNone),\n  mSubGridType(gtNone),\n  mAntialiasedSubGrid(true),\n  mAntialiasedZeroLine(true),\n  mParentAxis(parentAxis)\n{\n  // warning: this is called in QCPPolarAxisAngular constructor, so parentAxis members should not be accessed/called\n  setParent(parentAxis);\n  setType(gtAll);\n  setSubGridType(gtNone);\n  \n  setAngularPen(QPen(QColor(200,200,200), 0, Qt::DotLine));\n  setAngularSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));\n  \n  setRadialPen(QPen(QColor(200,200,200), 0, Qt::DotLine));\n  setRadialSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));\n  setRadialZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));\n  \n  setAntialiased(true);\n}\n\nvoid QCPPolarGrid::setRadialAxis(QCPPolarAxisRadial *axis)\n{\n  mRadialAxis = axis;\n}\n\nvoid QCPPolarGrid::setType(GridTypes type)\n{\n  mType = type;\n}\n\nvoid QCPPolarGrid::setSubGridType(GridTypes type)\n{\n  mSubGridType = type;\n}\n\n/*!\n  Sets whether sub grid lines are drawn antialiased.\n*/\nvoid QCPPolarGrid::setAntialiasedSubGrid(bool enabled)\n{\n  mAntialiasedSubGrid = enabled;\n}\n\n/*!\n  Sets whether zero lines are drawn antialiased.\n*/\nvoid QCPPolarGrid::setAntialiasedZeroLine(bool enabled)\n{\n  mAntialiasedZeroLine = enabled;\n}\n\n/*!\n  Sets the pen with which (major) grid lines are drawn.\n*/\nvoid QCPPolarGrid::setAngularPen(const QPen &pen)\n{\n  mAngularPen = pen;\n}\n\n/*!\n  Sets the pen with which sub grid lines are drawn.\n*/\nvoid QCPPolarGrid::setAngularSubGridPen(const QPen &pen)\n{\n  mAngularSubGridPen = pen;\n}\n\nvoid QCPPolarGrid::setRadialPen(const QPen &pen)\n{\n  mRadialPen = pen;\n}\n\nvoid QCPPolarGrid::setRadialSubGridPen(const QPen &pen)\n{\n  mRadialSubGridPen = pen;\n}\n\nvoid QCPPolarGrid::setRadialZeroLinePen(const QPen &pen)\n{\n  mRadialZeroLinePen = pen;\n}\n\n/*! \\internal\n\n  A convenience function to easily set the QPainter::Antialiased hint on the provided \\a painter\n  before drawing the major grid lines.\n\n  This is the antialiasing state the painter passed to the \\ref draw method is in by default.\n  \n  This function takes into account the local setting of the antialiasing flag as well as the\n  overrides set with \\ref QCustomPlot::setAntialiasedElements and \\ref\n  QCustomPlot::setNotAntialiasedElements.\n  \n  \\see setAntialiased\n*/\nvoid QCPPolarGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const\n{\n  applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid);\n}\n\n/*! \\internal\n  \n  Draws grid lines and sub grid lines at the positions of (sub) ticks of the parent axis, spanning\n  over the complete axis rect. Also draws the zero line, if appropriate (\\ref setZeroLinePen).\n*/\nvoid QCPPolarGrid::draw(QCPPainter *painter)\n{\n  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << \"invalid parent axis\"; return; }\n  \n  const QPointF center = mParentAxis->mCenter;\n  const double radius = mParentAxis->mRadius;\n  \n  painter->setBrush(Qt::NoBrush);\n  // draw main angular grid:\n  if (mType.testFlag(gtAngular))\n    drawAngularGrid(painter, center, radius, mParentAxis->mTickVectorCosSin, mAngularPen);\n  // draw main radial grid:\n  if (mType.testFlag(gtRadial) && mRadialAxis)\n    drawRadialGrid(painter, center, mRadialAxis->tickVector(), mRadialPen, mRadialZeroLinePen);\n  \n  applyAntialiasingHint(painter, mAntialiasedSubGrid, QCP::aeGrid);\n  // draw sub angular grid:\n  if (mSubGridType.testFlag(gtAngular))\n    drawAngularGrid(painter, center, radius, mParentAxis->mSubTickVectorCosSin, mAngularSubGridPen);\n  // draw sub radial grid:\n  if (mSubGridType.testFlag(gtRadial) && mRadialAxis)\n    drawRadialGrid(painter, center, mRadialAxis->subTickVector(), mRadialSubGridPen);\n}\n\nvoid QCPPolarGrid::drawRadialGrid(QCPPainter *painter, const QPointF &center, const QVector<double> &coords, const QPen &pen, const QPen &zeroPen)\n{\n  if (!mRadialAxis) return;\n  if (coords.isEmpty()) return;\n  const bool drawZeroLine = zeroPen != Qt::NoPen;\n  const double zeroLineEpsilon = qAbs(coords.last()-coords.first())*1e-6;\n  \n  painter->setPen(pen);\n  for (int i=0; i<coords.size(); ++i)\n  {\n    const double r = mRadialAxis->coordToRadius(coords.at(i));\n    if (drawZeroLine && qAbs(coords.at(i)) < zeroLineEpsilon)\n    {\n      applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);\n      painter->setPen(zeroPen);\n      painter->drawEllipse(center, r, r);\n      painter->setPen(pen);\n      applyDefaultAntialiasingHint(painter);\n    } else\n    {\n      painter->drawEllipse(center, r, r);\n    }\n  }\n}\n\nvoid QCPPolarGrid::drawAngularGrid(QCPPainter *painter, const QPointF &center, double radius, const QVector<QPointF> &ticksCosSin, const QPen &pen)\n{\n  if (ticksCosSin.isEmpty()) return;\n  \n  painter->setPen(pen);\n  for (int i=0; i<ticksCosSin.size(); ++i)\n    painter->drawLine(center, center+ticksCosSin.at(i)*radius);\n}\n/* end of 'src/polar/polargrid.cpp' */\n\n\n/* including file 'src/polar/polargraph.cpp' */\n/* modified 2021-03-29T02:30:44, size 44035  */\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPPolarLegendItem\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPPolarLegendItem\n  \\brief A legend item for polar plots\n\n  \\warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and\n  functionality to be incomplete, as well as changing public interfaces in the future.\n*/\nQCPPolarLegendItem::QCPPolarLegendItem(QCPLegend *parent, QCPPolarGraph *graph) :\n  QCPAbstractLegendItem(parent),\n  mPolarGraph(graph)\n{\n  setAntialiased(false);\n}\n\nvoid QCPPolarLegendItem::draw(QCPPainter *painter)\n{\n  if (!mPolarGraph) return;\n  painter->setFont(getFont());\n  painter->setPen(QPen(getTextColor()));\n  QSizeF iconSize = mParentLegend->iconSize();\n  QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPolarGraph->name());\n  QRectF iconRect(mRect.topLeft(), iconSize);\n  int textHeight = qMax(textRect.height(), iconSize.height());  // if text has smaller height than icon, center text vertically in icon height, else align tops\n  painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPolarGraph->name());\n  // draw icon:\n  painter->save();\n  painter->setClipRect(iconRect, Qt::IntersectClip);\n  mPolarGraph->drawLegendIcon(painter, iconRect);\n  painter->restore();\n  // draw icon border:\n  if (getIconBorderPen().style() != Qt::NoPen)\n  {\n    painter->setPen(getIconBorderPen());\n    painter->setBrush(Qt::NoBrush);\n    int halfPen = qCeil(painter->pen().widthF()*0.5)+1;\n    painter->setClipRect(mOuterRect.adjusted(-halfPen, -halfPen, halfPen, halfPen)); // extend default clip rect so thicker pens (especially during selection) are not clipped\n    painter->drawRect(iconRect);\n  }\n}\n\nQSize QCPPolarLegendItem::minimumOuterSizeHint() const\n{\n  if (!mPolarGraph) return QSize();\n  QSize result(0, 0);\n  QRect textRect;\n  QFontMetrics fontMetrics(getFont());\n  QSize iconSize = mParentLegend->iconSize();\n  textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPolarGraph->name());\n  result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width());\n  result.setHeight(qMax(textRect.height(), iconSize.height()));\n  result.rwidth() += mMargins.left()+mMargins.right();\n  result.rheight() += mMargins.top()+mMargins.bottom();\n  return result;\n}\n\nQPen QCPPolarLegendItem::getIconBorderPen() const\n{\n  return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen();\n}\n\nQColor QCPPolarLegendItem::getTextColor() const\n{\n  return mSelected ? mSelectedTextColor : mTextColor;\n}\n\nQFont QCPPolarLegendItem::getFont() const\n{\n  return mSelected ? mSelectedFont : mFont;\n}\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPPolarGraph\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPPolarGraph\n  \\brief A radial graph used to display data in polar plots\n\n  \\warning In this QCustomPlot version, polar plots are a tech preview. Expect documentation and\n  functionality to be incomplete, as well as changing public interfaces in the future.\n*/\n\n/* start of documentation of inline functions */\n\n// TODO\n\n/* end of documentation of inline functions */\n\n/*!\n  Constructs a graph which uses \\a keyAxis as its angular and \\a valueAxis as its radial axis. \\a\n  keyAxis and \\a valueAxis must reside in the same QCustomPlot, and the radial axis must be\n  associated with the angular axis. If either of these restrictions is violated, a corresponding\n  message is printed to the debug output (qDebug), the construction is not aborted, though.\n\n  The created QCPPolarGraph is automatically registered with the QCustomPlot instance inferred from\n  \\a keyAxis. This QCustomPlot instance takes ownership of the QCPPolarGraph, so do not delete it\n  manually but use QCPPolarAxisAngular::removeGraph() instead.\n\n  To directly create a QCPPolarGraph inside a plot, you shoud use the QCPPolarAxisAngular::addGraph\n  method.\n*/\nQCPPolarGraph::QCPPolarGraph(QCPPolarAxisAngular *keyAxis, QCPPolarAxisRadial *valueAxis) :\n  QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis),\n  mDataContainer(new QCPGraphDataContainer),\n  mName(),\n  mAntialiasedFill(true),\n  mAntialiasedScatters(true),\n  mPen(Qt::black),\n  mBrush(Qt::NoBrush),\n  mPeriodic(true),\n  mKeyAxis(keyAxis),\n  mValueAxis(valueAxis),\n  mSelectable(QCP::stWhole)\n  //mSelectionDecorator(0) // TODO\n{\n  if (keyAxis->parentPlot() != valueAxis->parentPlot())\n    qDebug() << Q_FUNC_INFO << \"Parent plot of keyAxis is not the same as that of valueAxis.\";\n  \n  mKeyAxis->registerPolarGraph(this);\n  \n  //setSelectionDecorator(new QCPSelectionDecorator); // TODO\n  \n  setPen(QPen(Qt::blue, 0));\n  setBrush(Qt::NoBrush);\n  setLineStyle(lsLine);\n}\n\nQCPPolarGraph::~QCPPolarGraph()\n{\n  /* TODO\n  if (mSelectionDecorator)\n  {\n    delete mSelectionDecorator;\n    mSelectionDecorator = 0;\n  }\n  */\n}\n\n/*!\n   The name is the textual representation of this plottable as it is displayed in the legend\n   (\\ref QCPLegend). It may contain any UTF-8 characters, including newlines.\n*/\nvoid QCPPolarGraph::setName(const QString &name)\n{\n  mName = name;\n}\n\n/*!\n  Sets whether fills of this plottable are drawn antialiased or not.\n  \n  Note that this setting may be overridden by \\ref QCustomPlot::setAntialiasedElements and \\ref\n  QCustomPlot::setNotAntialiasedElements.\n*/\nvoid QCPPolarGraph::setAntialiasedFill(bool enabled)\n{\n  mAntialiasedFill = enabled;\n}\n\n/*!\n  Sets whether the scatter symbols of this plottable are drawn antialiased or not.\n  \n  Note that this setting may be overridden by \\ref QCustomPlot::setAntialiasedElements and \\ref\n  QCustomPlot::setNotAntialiasedElements.\n*/\nvoid QCPPolarGraph::setAntialiasedScatters(bool enabled)\n{\n  mAntialiasedScatters = enabled;\n}\n\n/*!\n  The pen is used to draw basic lines that make up the plottable representation in the\n  plot.\n  \n  For example, the \\ref QCPGraph subclass draws its graph lines with this pen.\n\n  \\see setBrush\n*/\nvoid QCPPolarGraph::setPen(const QPen &pen)\n{\n  mPen = pen;\n}\n\n/*!\n  The brush is used to draw basic fills of the plottable representation in the\n  plot. The Fill can be a color, gradient or texture, see the usage of QBrush.\n  \n  For example, the \\ref QCPGraph subclass draws the fill under the graph with this brush, when\n  it's not set to Qt::NoBrush.\n\n  \\see setPen\n*/\nvoid QCPPolarGraph::setBrush(const QBrush &brush)\n{\n  mBrush = brush;\n}\n\nvoid QCPPolarGraph::setPeriodic(bool enabled)\n{\n  mPeriodic = enabled;\n}\n\n/*!\n  The key axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal\n  to the plottable's value axis. This function performs no checks to make sure this is the case.\n  The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and the\n  y-axis (QCustomPlot::yAxis) as value axis.\n  \n  Normally, the key and value axes are set in the constructor of the plottable (or \\ref\n  QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).\n\n  \\see setValueAxis\n*/\nvoid QCPPolarGraph::setKeyAxis(QCPPolarAxisAngular *axis)\n{\n  mKeyAxis = axis;\n}\n\n/*!\n  The value axis of a plottable can be set to any axis of a QCustomPlot, as long as it is\n  orthogonal to the plottable's key axis. This function performs no checks to make sure this is the\n  case. The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and\n  the y-axis (QCustomPlot::yAxis) as value axis.\n\n  Normally, the key and value axes are set in the constructor of the plottable (or \\ref\n  QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).\n  \n  \\see setKeyAxis\n*/\nvoid QCPPolarGraph::setValueAxis(QCPPolarAxisRadial *axis)\n{\n  mValueAxis = axis;\n}\n\n/*!\n  Sets whether and to which granularity this plottable can be selected.\n\n  A selection can happen by clicking on the QCustomPlot surface (When \\ref\n  QCustomPlot::setInteractions contains \\ref QCP::iSelectPlottables), by dragging a selection rect\n  (When \\ref QCustomPlot::setSelectionRectMode is \\ref QCP::srmSelect), or programmatically by\n  calling \\ref setSelection.\n  \n  \\see setSelection, QCP::SelectionType\n*/\nvoid QCPPolarGraph::setSelectable(QCP::SelectionType selectable)\n{\n  if (mSelectable != selectable)\n  {\n    mSelectable = selectable;\n    QCPDataSelection oldSelection = mSelection;\n    mSelection.enforceType(mSelectable);\n    emit selectableChanged(mSelectable);\n    if (mSelection != oldSelection)\n    {\n      emit selectionChanged(selected());\n      emit selectionChanged(mSelection);\n    }\n  }\n}\n\n/*!\n  Sets which data ranges of this plottable are selected. Selected data ranges are drawn differently\n  (e.g. color) in the plot. This can be controlled via the selection decorator (see \\ref\n  selectionDecorator).\n  \n  The entire selection mechanism for plottables is handled automatically when \\ref\n  QCustomPlot::setInteractions contains iSelectPlottables. You only need to call this function when\n  you wish to change the selection state programmatically.\n  \n  Using \\ref setSelectable you can further specify for each plottable whether and to which\n  granularity it is selectable. If \\a selection is not compatible with the current \\ref\n  QCP::SelectionType set via \\ref setSelectable, the resulting selection will be adjusted\n  accordingly (see \\ref QCPDataSelection::enforceType).\n  \n  emits the \\ref selectionChanged signal when \\a selected is different from the previous selection state.\n  \n  \\see setSelectable, selectTest\n*/\nvoid QCPPolarGraph::setSelection(QCPDataSelection selection)\n{\n  selection.enforceType(mSelectable);\n  if (mSelection != selection)\n  {\n    mSelection = selection;\n    emit selectionChanged(selected());\n    emit selectionChanged(mSelection);\n  }\n}\n\n/*! \\overload\n  \n  Replaces the current data container with the provided \\a data container.\n  \n  Since a QSharedPointer is used, multiple QCPPolarGraphs may share the same data container safely.\n  Modifying the data in the container will then affect all graphs that share the container. Sharing\n  can be achieved by simply exchanging the data containers wrapped in shared pointers:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp QCPPolarGraph-datasharing-1\n  \n  If you do not wish to share containers, but create a copy from an existing container, rather use\n  the \\ref QCPDataContainer<DataType>::set method on the graph's data container directly:\n  \\snippet documentation/doc-code-snippets/mainwindow.cpp QCPPolarGraph-datasharing-2\n  \n  \\see addData\n*/\nvoid QCPPolarGraph::setData(QSharedPointer<QCPGraphDataContainer> data)\n{\n  mDataContainer = data;\n}\n\n/*! \\overload\n  \n  Replaces the current data with the provided points in \\a keys and \\a values. The provided\n  vectors should have equal length. Else, the number of added points will be the size of the\n  smallest vector.\n  \n  If you can guarantee that the passed data points are sorted by \\a keys in ascending order, you\n  can set \\a alreadySorted to true, to improve performance by saving a sorting run.\n  \n  \\see addData\n*/\nvoid QCPPolarGraph::setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)\n{\n  mDataContainer->clear();\n  addData(keys, values, alreadySorted);\n}\n\n/*!\n  Sets how the single data points are connected in the plot. For scatter-only plots, set \\a ls to\n  \\ref lsNone and \\ref setScatterStyle to the desired scatter style.\n  \n  \\see setScatterStyle\n*/\nvoid QCPPolarGraph::setLineStyle(LineStyle ls)\n{\n  mLineStyle = ls;\n}\n\n/*!\n  Sets the visual appearance of single data points in the plot. If set to \\ref QCPScatterStyle::ssNone, no scatter points\n  are drawn (e.g. for line-only-plots with appropriate line style).\n  \n  \\see QCPScatterStyle, setLineStyle\n*/\nvoid QCPPolarGraph::setScatterStyle(const QCPScatterStyle &style)\n{\n  mScatterStyle = style;\n}\n\nvoid QCPPolarGraph::addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)\n{\n  if (keys.size() != values.size())\n    qDebug() << Q_FUNC_INFO << \"keys and values have different sizes:\" << keys.size() << values.size();\n  const int n = qMin(keys.size(), values.size());\n  QVector<QCPGraphData> tempData(n);\n  QVector<QCPGraphData>::iterator it = tempData.begin();\n  const QVector<QCPGraphData>::iterator itEnd = tempData.end();\n  int i = 0;\n  while (it != itEnd)\n  {\n    it->key = keys[i];\n    it->value = values[i];\n    ++it;\n    ++i;\n  }\n  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write\n}\n\nvoid QCPPolarGraph::addData(double key, double value)\n{\n  mDataContainer->add(QCPGraphData(key, value));\n}\n\n/*!\n  Use this method to set an own QCPSelectionDecorator (subclass) instance. This allows you to\n  customize the visual representation of selected data ranges further than by using the default\n  QCPSelectionDecorator.\n  \n  The plottable takes ownership of the \\a decorator.\n  \n  The currently set decorator can be accessed via \\ref selectionDecorator.\n*/\n/*\nvoid QCPPolarGraph::setSelectionDecorator(QCPSelectionDecorator *decorator)\n{\n  if (decorator)\n  {\n    if (decorator->registerWithPlottable(this))\n    {\n      if (mSelectionDecorator) // delete old decorator if necessary\n        delete mSelectionDecorator;\n      mSelectionDecorator = decorator;\n    }\n  } else if (mSelectionDecorator) // just clear decorator\n  {\n    delete mSelectionDecorator;\n    mSelectionDecorator = 0;\n  }\n}\n*/\n\nvoid QCPPolarGraph::coordsToPixels(double key, double value, double &x, double &y) const\n{\n  if (mValueAxis)\n  {\n    const QPointF point = mValueAxis->coordToPixel(key, value);\n    x = point.x();\n    y = point.y();\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"invalid key or value axis\";\n  }\n}\n\nconst QPointF QCPPolarGraph::coordsToPixels(double key, double value) const\n{\n  if (mValueAxis)\n  {\n    return mValueAxis->coordToPixel(key, value);\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"invalid key or value axis\";\n    return QPointF();\n  }\n}\n\nvoid QCPPolarGraph::pixelsToCoords(double x, double y, double &key, double &value) const\n{\n  if (mValueAxis)\n  {\n    mValueAxis->pixelToCoord(QPointF(x, y), key, value);\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"invalid key or value axis\";\n  }\n}\n\nvoid QCPPolarGraph::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const\n{\n  if (mValueAxis)\n  {\n    mValueAxis->pixelToCoord(pixelPos, key, value);\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"invalid key or value axis\";\n  }\n}\n\nvoid QCPPolarGraph::rescaleAxes(bool onlyEnlarge) const\n{\n  rescaleKeyAxis(onlyEnlarge);\n  rescaleValueAxis(onlyEnlarge);\n}\n\nvoid QCPPolarGraph::rescaleKeyAxis(bool onlyEnlarge) const\n{\n  QCPPolarAxisAngular *keyAxis = mKeyAxis.data();\n  if (!keyAxis) { qDebug() << Q_FUNC_INFO << \"invalid key axis\"; return; }\n  \n  bool foundRange;\n  QCPRange newRange = getKeyRange(foundRange, QCP::sdBoth);\n  if (foundRange)\n  {\n    if (onlyEnlarge)\n      newRange.expand(keyAxis->range());\n    if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable\n    {\n      double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason\n      newRange.lower = center-keyAxis->range().size()/2.0;\n      newRange.upper = center+keyAxis->range().size()/2.0;\n    }\n    keyAxis->setRange(newRange);\n  }\n}\n\nvoid QCPPolarGraph::rescaleValueAxis(bool onlyEnlarge, bool inKeyRange) const\n{\n  QCPPolarAxisAngular *keyAxis = mKeyAxis.data();\n  QCPPolarAxisRadial *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n  \n  QCP::SignDomain signDomain = QCP::sdBoth;\n  if (valueAxis->scaleType() == QCPPolarAxisRadial::stLogarithmic)\n    signDomain = (valueAxis->range().upper < 0 ? QCP::sdNegative : QCP::sdPositive);\n  \n  bool foundRange;\n  QCPRange newRange = getValueRange(foundRange, signDomain, inKeyRange ? keyAxis->range() : QCPRange());\n  if (foundRange)\n  {\n    if (onlyEnlarge)\n      newRange.expand(valueAxis->range());\n    if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable\n    {\n      double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason\n      if (valueAxis->scaleType() == QCPPolarAxisRadial::stLinear)\n      {\n        newRange.lower = center-valueAxis->range().size()/2.0;\n        newRange.upper = center+valueAxis->range().size()/2.0;\n      } else // scaleType() == stLogarithmic\n      {\n        newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);\n        newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);\n      }\n    }\n    valueAxis->setRange(newRange);\n  }\n}\n\nbool QCPPolarGraph::addToLegend(QCPLegend *legend)\n{\n  if (!legend)\n  {\n    qDebug() << Q_FUNC_INFO << \"passed legend is null\";\n    return false;\n  }\n  if (legend->parentPlot() != mParentPlot)\n  {\n    qDebug() << Q_FUNC_INFO << \"passed legend isn't in the same QCustomPlot as this plottable\";\n    return false;\n  }\n  \n  //if (!legend->hasItemWithPlottable(this)) // TODO\n  //{\n    legend->addItem(new QCPPolarLegendItem(legend, this));\n    return true;\n  //} else\n  //  return false;\n}\n\nbool QCPPolarGraph::addToLegend()\n{\n  if (!mParentPlot || !mParentPlot->legend)\n    return false;\n  else\n    return addToLegend(mParentPlot->legend);\n}\n\nbool QCPPolarGraph::removeFromLegend(QCPLegend *legend) const\n{\n  if (!legend)\n  {\n    qDebug() << Q_FUNC_INFO << \"passed legend is null\";\n    return false;\n  }\n  \n  \n  QCPPolarLegendItem *removableItem = 0;\n  for (int i=0; i<legend->itemCount(); ++i) // TODO: reduce this to code in QCPAbstractPlottable::removeFromLegend once unified\n  {\n    if (QCPPolarLegendItem *pli = qobject_cast<QCPPolarLegendItem*>(legend->item(i)))\n    {\n      if (pli->polarGraph() == this)\n      {\n        removableItem = pli;\n        break;\n      }\n    }\n  }\n  \n  if (removableItem)\n    return legend->removeItem(removableItem);\n  else\n    return false;\n}\n\nbool QCPPolarGraph::removeFromLegend() const\n{\n  if (!mParentPlot || !mParentPlot->legend)\n    return false;\n  else\n    return removeFromLegend(mParentPlot->legend);\n}\n\ndouble QCPPolarGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())\n    return -1;\n  if (!mKeyAxis || !mValueAxis)\n    return -1;\n  \n  if (mKeyAxis->rect().contains(pos.toPoint()))\n  {\n    QCPGraphDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();\n    double result = pointDistance(pos, closestDataPoint);\n    if (details)\n    {\n      int pointIndex = closestDataPoint-mDataContainer->constBegin();\n      details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));\n    }\n    return result;\n  } else\n    return -1;\n}\n\n/* inherits documentation from base class */\nQCPRange QCPPolarGraph::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const\n{\n  return mDataContainer->keyRange(foundRange, inSignDomain);\n}\n\n/* inherits documentation from base class */\nQCPRange QCPPolarGraph::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const\n{\n  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);\n}\n\n/* inherits documentation from base class */\nQRect QCPPolarGraph::clipRect() const\n{\n  if (mKeyAxis)\n    return mKeyAxis.data()->rect();\n  else\n    return QRect();\n}\n\nvoid QCPPolarGraph::draw(QCPPainter *painter)\n{\n  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n  if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return;\n  if (mLineStyle == lsNone && mScatterStyle.isNone()) return;\n  \n  painter->setClipRegion(mKeyAxis->exactClipRegion());\n  \n  QVector<QPointF> lines, scatters; // line and (if necessary) scatter pixel coordinates will be stored here while iterating over segments\n  \n  // loop over and draw segments of unselected/selected data:\n  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;\n  getDataSegments(selectedSegments, unselectedSegments);\n  allSegments << unselectedSegments << selectedSegments;\n  for (int i=0; i<allSegments.size(); ++i)\n  {\n    bool isSelectedSegment = i >= unselectedSegments.size();\n    // get line pixel points appropriate to line style:\n    QCPDataRange lineDataRange = isSelectedSegment ? allSegments.at(i) : allSegments.at(i).adjusted(-1, 1); // unselected segments extend lines to bordering selected data point (safe to exceed total data bounds in first/last segment, getLines takes care)\n    getLines(&lines, lineDataRange);\n    \n    // check data validity if flag set:\n#ifdef QCUSTOMPLOT_CHECK_DATA\n    QCPGraphDataContainer::const_iterator it;\n    for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)\n    {\n      if (QCP::isInvalidData(it->key, it->value))\n        qDebug() << Q_FUNC_INFO << \"Data point at\" << it->key << \"invalid.\" << \"Plottable name:\" << name();\n    }\n#endif\n    \n    // draw fill of graph:\n    //if (isSelectedSegment && mSelectionDecorator)\n    //  mSelectionDecorator->applyBrush(painter);\n    //else\n      painter->setBrush(mBrush);\n    painter->setPen(Qt::NoPen);\n    drawFill(painter, &lines);\n    \n    \n    // draw line:\n    if (mLineStyle != lsNone)\n    {\n      //if (isSelectedSegment && mSelectionDecorator)\n      //  mSelectionDecorator->applyPen(painter);\n      //else\n        painter->setPen(mPen);\n      painter->setBrush(Qt::NoBrush);\n      drawLinePlot(painter, lines);\n    }\n    \n    // draw scatters:\n    \n    QCPScatterStyle finalScatterStyle = mScatterStyle;\n    //if (isSelectedSegment && mSelectionDecorator)\n    //  finalScatterStyle = mSelectionDecorator->getFinalScatterStyle(mScatterStyle);\n    if (!finalScatterStyle.isNone())\n    {\n      getScatters(&scatters, allSegments.at(i));\n      drawScatterPlot(painter, scatters, finalScatterStyle);\n    }\n  }\n  \n  // draw other selection decoration that isn't just line/scatter pens and brushes:\n  //if (mSelectionDecorator)\n  //  mSelectionDecorator->drawDecoration(painter, selection());\n}\n\nQCP::Interaction QCPPolarGraph::selectionCategory() const\n{\n  return QCP::iSelectPlottables;\n}\n\nvoid QCPPolarGraph::applyDefaultAntialiasingHint(QCPPainter *painter) const\n{\n  applyAntialiasingHint(painter, mAntialiased, QCP::aePlottables);\n}\n\n/* inherits documentation from base class */\nvoid QCPPolarGraph::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)\n{\n  Q_UNUSED(event)\n  \n  if (mSelectable != QCP::stNone)\n  {\n    QCPDataSelection newSelection = details.value<QCPDataSelection>();\n    QCPDataSelection selectionBefore = mSelection;\n    if (additive)\n    {\n      if (mSelectable == QCP::stWhole) // in whole selection mode, we toggle to no selection even if currently unselected point was hit\n      {\n        if (selected())\n          setSelection(QCPDataSelection());\n        else\n          setSelection(newSelection);\n      } else // in all other selection modes we toggle selections of homogeneously selected/unselected segments\n      {\n        if (mSelection.contains(newSelection)) // if entire newSelection is already selected, toggle selection\n          setSelection(mSelection-newSelection);\n        else\n          setSelection(mSelection+newSelection);\n      }\n    } else\n      setSelection(newSelection);\n    if (selectionStateChanged)\n      *selectionStateChanged = mSelection != selectionBefore;\n  }\n}\n\n/* inherits documentation from base class */\nvoid QCPPolarGraph::deselectEvent(bool *selectionStateChanged)\n{\n  if (mSelectable != QCP::stNone)\n  {\n    QCPDataSelection selectionBefore = mSelection;\n    setSelection(QCPDataSelection());\n    if (selectionStateChanged)\n      *selectionStateChanged = mSelection != selectionBefore;\n  }\n}\n\n/*!  \\internal\n  \n  Draws lines between the points in \\a lines, given in pixel coordinates.\n  \n  \\see drawScatterPlot, drawImpulsePlot, QCPAbstractPlottable1D::drawPolyline\n*/\nvoid QCPPolarGraph::drawLinePlot(QCPPainter *painter, const QVector<QPointF> &lines) const\n{\n  if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)\n  {\n    applyDefaultAntialiasingHint(painter);\n    drawPolyline(painter, lines);\n  }\n}\n\n/*! \\internal\n  \n  Draws the fill of the graph using the specified \\a painter, with the currently set brush.\n  \n  Depending on whether a normal fill or a channel fill (\\ref setChannelFillGraph) is needed, \\ref\n  getFillPolygon or \\ref getChannelFillPolygon are used to find the according fill polygons.\n  \n  In order to handle NaN Data points correctly (the fill needs to be split into disjoint areas),\n  this method first determines a list of non-NaN segments with \\ref getNonNanSegments, on which to\n  operate. In the channel fill case, \\ref getOverlappingSegments is used to consolidate the non-NaN\n  segments of the two involved graphs, before passing the overlapping pairs to \\ref\n  getChannelFillPolygon.\n  \n  Pass the points of this graph's line as \\a lines, in pixel coordinates.\n\n  \\see drawLinePlot, drawImpulsePlot, drawScatterPlot\n*/\nvoid QCPPolarGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lines) const\n{\n  applyFillAntialiasingHint(painter);\n  if (painter->brush().style() != Qt::NoBrush && painter->brush().color().alpha() != 0)\n    painter->drawPolygon(QPolygonF(*lines));\n}\n\n/*! \\internal\n\n  Draws scatter symbols at every point passed in \\a scatters, given in pixel coordinates. The\n  scatters will be drawn with \\a painter and have the appearance as specified in \\a style.\n\n  \\see drawLinePlot, drawImpulsePlot\n*/\nvoid QCPPolarGraph::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &scatters, const QCPScatterStyle &style) const\n{\n  applyScattersAntialiasingHint(painter);\n  style.applyTo(painter, mPen);\n  for (int i=0; i<scatters.size(); ++i)\n    style.drawShape(painter, scatters.at(i).x(), scatters.at(i).y());\n}\n\nvoid QCPPolarGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const\n{\n  // draw fill:\n  if (mBrush.style() != Qt::NoBrush)\n  {\n    applyFillAntialiasingHint(painter);\n    painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);\n  }\n  // draw line vertically centered:\n  if (mLineStyle != lsNone)\n  {\n    applyDefaultAntialiasingHint(painter);\n    painter->setPen(mPen);\n    painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens\n  }\n  // draw scatter symbol:\n  if (!mScatterStyle.isNone())\n  {\n    applyScattersAntialiasingHint(painter);\n    // scale scatter pixmap if it's too large to fit in legend icon rect:\n    if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))\n    {\n      QCPScatterStyle scaledStyle(mScatterStyle);\n      scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));\n      scaledStyle.applyTo(painter, mPen);\n      scaledStyle.drawShape(painter, QRectF(rect).center());\n    } else\n    {\n      mScatterStyle.applyTo(painter, mPen);\n      mScatterStyle.drawShape(painter, QRectF(rect).center());\n    }\n  }\n}\n\nvoid QCPPolarGraph::applyFillAntialiasingHint(QCPPainter *painter) const\n{\n  applyAntialiasingHint(painter, mAntialiasedFill, QCP::aeFills);\n}\n\nvoid QCPPolarGraph::applyScattersAntialiasingHint(QCPPainter *painter) const\n{\n  applyAntialiasingHint(painter, mAntialiasedScatters, QCP::aeScatters);\n}\n\ndouble QCPPolarGraph::pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const\n{\n  closestData = mDataContainer->constEnd();\n  if (mDataContainer->isEmpty())\n    return -1.0;\n  if (mLineStyle == lsNone && mScatterStyle.isNone())\n    return -1.0;\n  \n  // calculate minimum distances to graph data points and find closestData iterator:\n  double minDistSqr = (std::numeric_limits<double>::max)();\n  // determine which key range comes into question, taking selection tolerance around pos into account:\n  double posKeyMin, posKeyMax, dummy;\n  pixelsToCoords(pixelPoint-QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMin, dummy);\n  pixelsToCoords(pixelPoint+QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMax, dummy);\n  if (posKeyMin > posKeyMax)\n    qSwap(posKeyMin, posKeyMax);\n  // iterate over found data points and then choose the one with the shortest distance to pos:\n  QCPGraphDataContainer::const_iterator begin = mDataContainer->findBegin(posKeyMin, true);\n  QCPGraphDataContainer::const_iterator end = mDataContainer->findEnd(posKeyMax, true);\n  for (QCPGraphDataContainer::const_iterator it=begin; it!=end; ++it)\n  {\n    const double currentDistSqr = QCPVector2D(coordsToPixels(it->key, it->value)-pixelPoint).lengthSquared();\n    if (currentDistSqr < minDistSqr)\n    {\n      minDistSqr = currentDistSqr;\n      closestData = it;\n    }\n  }\n    \n  // calculate distance to graph line if there is one (if so, will probably be smaller than distance to closest data point):\n  if (mLineStyle != lsNone)\n  {\n    // line displayed, calculate distance to line segments:\n    QVector<QPointF> lineData;\n    getLines(&lineData, QCPDataRange(0, dataCount()));\n    QCPVector2D p(pixelPoint);\n    for (int i=0; i<lineData.size()-1; ++i)\n    {\n      const double currentDistSqr = p.distanceSquaredToLine(lineData.at(i), lineData.at(i+1));\n      if (currentDistSqr < minDistSqr)\n        minDistSqr = currentDistSqr;\n    }\n  }\n  \n  return qSqrt(minDistSqr);\n}\n\nint QCPPolarGraph::dataCount() const\n{\n  return mDataContainer->size();\n}\n\nvoid QCPPolarGraph::getDataSegments(QList<QCPDataRange> &selectedSegments, QList<QCPDataRange> &unselectedSegments) const\n{\n  selectedSegments.clear();\n  unselectedSegments.clear();\n  if (mSelectable == QCP::stWhole) // stWhole selection type draws the entire plottable with selected style if mSelection isn't empty\n  {\n    if (selected())\n      selectedSegments << QCPDataRange(0, dataCount());\n    else\n      unselectedSegments << QCPDataRange(0, dataCount());\n  } else\n  {\n    QCPDataSelection sel(selection());\n    sel.simplify();\n    selectedSegments = sel.dataRanges();\n    unselectedSegments = sel.inverse(QCPDataRange(0, dataCount())).dataRanges();\n  }\n}\n\nvoid QCPPolarGraph::drawPolyline(QCPPainter *painter, const QVector<QPointF> &lineData) const\n{\n  // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:\n  if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&\n      painter->pen().style() == Qt::SolidLine &&\n      !painter->modes().testFlag(QCPPainter::pmVectorized) &&\n      !painter->modes().testFlag(QCPPainter::pmNoCaching))\n  {\n    int i = 0;\n    bool lastIsNan = false;\n    const int lineDataSize = lineData.size();\n    while (i < lineDataSize && (qIsNaN(lineData.at(i).y()) || qIsNaN(lineData.at(i).x()))) // make sure first point is not NaN\n      ++i;\n    ++i; // because drawing works in 1 point retrospect\n    while (i < lineDataSize)\n    {\n      if (!qIsNaN(lineData.at(i).y()) && !qIsNaN(lineData.at(i).x())) // NaNs create a gap in the line\n      {\n        if (!lastIsNan)\n          painter->drawLine(lineData.at(i-1), lineData.at(i));\n        else\n          lastIsNan = false;\n      } else\n        lastIsNan = true;\n      ++i;\n    }\n  } else\n  {\n    int segmentStart = 0;\n    int i = 0;\n    const int lineDataSize = lineData.size();\n    while (i < lineDataSize)\n    {\n      if (qIsNaN(lineData.at(i).y()) || qIsNaN(lineData.at(i).x()) || qIsInf(lineData.at(i).y())) // NaNs create a gap in the line. Also filter Infs which make drawPolyline block\n      {\n        painter->drawPolyline(lineData.constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point\n        segmentStart = i+1;\n      }\n      ++i;\n    }\n    // draw last segment:\n    painter->drawPolyline(lineData.constData()+segmentStart, lineDataSize-segmentStart);\n  }\n}\n\nvoid QCPPolarGraph::getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const\n{\n  if (rangeRestriction.isEmpty())\n  {\n    end = mDataContainer->constEnd();\n    begin = end;\n  } else\n  {\n    QCPPolarAxisAngular *keyAxis = mKeyAxis.data();\n    QCPPolarAxisRadial *valueAxis = mValueAxis.data();\n    if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n    // get visible data range:\n    if (mPeriodic)\n    {\n      begin = mDataContainer->constBegin();\n      end = mDataContainer->constEnd();\n    } else\n    {\n      begin = mDataContainer->findBegin(keyAxis->range().lower);\n      end = mDataContainer->findEnd(keyAxis->range().upper);\n    }\n    // limit lower/upperEnd to rangeRestriction:\n    mDataContainer->limitIteratorsToDataRange(begin, end, rangeRestriction); // this also ensures rangeRestriction outside data bounds doesn't break anything\n  }\n}\n\n/*! \\internal\n\n  This method retrieves an optimized set of data points via \\ref getOptimizedLineData, an branches\n  out to the line style specific functions such as \\ref dataToLines, \\ref dataToStepLeftLines, etc.\n  according to the line style of the graph.\n\n  \\a lines will be filled with points in pixel coordinates, that can be drawn with the according\n  draw functions like \\ref drawLinePlot and \\ref drawImpulsePlot. The points returned in \\a lines\n  aren't necessarily the original data points. For example, step line styles require additional\n  points to form the steps when drawn. If the line style of the graph is \\ref lsNone, the \\a\n  lines vector will be empty.\n\n  \\a dataRange specifies the beginning and ending data indices that will be taken into account for\n  conversion. In this function, the specified range may exceed the total data bounds without harm:\n  a correspondingly trimmed data range will be used. This takes the burden off the user of this\n  function to check for valid indices in \\a dataRange, e.g. when extending ranges coming from \\ref\n  getDataSegments.\n\n  \\see getScatters\n*/\nvoid QCPPolarGraph::getLines(QVector<QPointF> *lines, const QCPDataRange &dataRange) const\n{\n  if (!lines) return;\n  QCPGraphDataContainer::const_iterator begin, end;\n  getVisibleDataBounds(begin, end, dataRange);\n  if (begin == end)\n  {\n    lines->clear();\n    return;\n  }\n  \n  QVector<QCPGraphData> lineData;\n  if (mLineStyle != lsNone)\n    getOptimizedLineData(&lineData, begin, end);\n\n  switch (mLineStyle)\n  {\n    case lsNone: lines->clear(); break;\n    case lsLine: *lines = dataToLines(lineData); break;\n  }\n}\n\nvoid QCPPolarGraph::getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange) const\n{\n  QCPPolarAxisAngular *keyAxis = mKeyAxis.data();\n  QCPPolarAxisRadial *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return; }\n  \n  if (!scatters) return;\n  QCPGraphDataContainer::const_iterator begin, end;\n  getVisibleDataBounds(begin, end, dataRange);\n  if (begin == end)\n  {\n    scatters->clear();\n    return;\n  }\n  \n  QVector<QCPGraphData> data;\n  getOptimizedScatterData(&data, begin, end);\n  \n  scatters->resize(data.size());\n  for (int i=0; i<data.size(); ++i)\n  {\n    if (!qIsNaN(data.at(i).value))\n      (*scatters)[i] = valueAxis->coordToPixel(data.at(i).key, data.at(i).value);\n  }\n}\n\nvoid QCPPolarGraph::getOptimizedLineData(QVector<QCPGraphData> *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const\n{\n  lineData->clear();\n  \n  // TODO: fix for log axes and thick line style\n  \n  const QCPRange range = mValueAxis->range();\n  bool reversed = mValueAxis->rangeReversed();\n  const double clipMargin = range.size()*0.05; // extra distance from visible circle, so optimized outside lines can cover more angle before having to place a dummy point to prevent tangents\n  const double upperClipValue = range.upper + (reversed ? 0 : range.size()*0.05+clipMargin); // clip slightly outside of actual range to avoid line thicknesses to peek into visible circle\n  const double lowerClipValue = range.lower - (reversed ? range.size()*0.05+clipMargin : 0); // clip slightly outside of actual range to avoid line thicknesses to peek into visible circle\n  const double maxKeySkip = qAsin(qSqrt(clipMargin*(clipMargin+2*range.size()))/(range.size()+clipMargin))/M_PI*mKeyAxis->range().size(); // the maximum angle between two points on outer circle (r=clipValue+clipMargin) before connecting line becomes tangent to inner circle (r=clipValue)\n  double skipBegin = 0;\n  bool belowRange = false;\n  bool aboveRange = false;\n  QCPGraphDataContainer::const_iterator it = begin;\n  while (it != end)\n  {\n    if (it->value < lowerClipValue)\n    {\n      if (aboveRange) // jumped directly from above to below visible range, draw previous point so entry angle is correct\n      {\n        aboveRange = false;\n        if (!reversed) // TODO: with inner radius, we'll need else case here with projected border point\n          lineData->append(*(it-1));\n      }\n      if (!belowRange)\n      {\n        skipBegin = it->key;\n        lineData->append(QCPGraphData(it->key, lowerClipValue));\n        belowRange = true;\n      }\n      if (it->key-skipBegin > maxKeySkip) // add dummy point if we're exceeding the maximum skippable angle (to prevent unintentional intersections with visible circle)\n      {\n        skipBegin += maxKeySkip;\n        lineData->append(QCPGraphData(skipBegin, lowerClipValue));\n      }\n    } else if (it->value > upperClipValue)\n    {\n      if (belowRange) // jumped directly from below to above visible range, draw previous point so entry angle is correct (if lower means outer, so if reversed axis)\n      {\n        belowRange = false;\n        if (reversed)\n          lineData->append(*(it-1));\n      }\n      if (!aboveRange)\n      {\n        skipBegin = it->key;\n        lineData->append(QCPGraphData(it->key, upperClipValue));\n        aboveRange = true;\n      }\n      if (it->key-skipBegin > maxKeySkip) // add dummy point if we're exceeding the maximum skippable angle (to prevent unintentional intersections with visible circle)\n      {\n        skipBegin += maxKeySkip;\n        lineData->append(QCPGraphData(skipBegin, upperClipValue));\n      }\n    } else // value within bounds where we don't optimize away points\n    {\n      if (aboveRange)\n      {\n        aboveRange = false;\n        if (!reversed)\n          lineData->append(*(it-1)); // just entered from above, draw previous point so entry angle is correct (if above means outer, so if not reversed axis)\n      }\n      if (belowRange)\n      {\n        belowRange = false;\n        if (reversed)\n          lineData->append(*(it-1)); // just entered from below, draw previous point so entry angle is correct (if below means outer, so if reversed axis)\n      }\n      lineData->append(*it); // inside visible circle, add point normally\n    }\n    ++it;\n  }\n  // to make fill not erratic, add last point normally if it was outside visible circle:\n  if (aboveRange)\n  {\n    aboveRange = false;\n    if (!reversed)\n      lineData->append(*(it-1)); // just entered from above, draw previous point so entry angle is correct (if above means outer, so if not reversed axis)\n  }\n  if (belowRange)\n  {\n    belowRange = false;\n    if (reversed)\n      lineData->append(*(it-1)); // just entered from below, draw previous point so entry angle is correct (if below means outer, so if reversed axis)\n  }\n}\n\nvoid QCPPolarGraph::getOptimizedScatterData(QVector<QCPGraphData> *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const\n{\n  scatterData->clear();\n  \n  const QCPRange range = mValueAxis->range();\n  bool reversed = mValueAxis->rangeReversed();\n  const double clipMargin = range.size()*0.05;\n  const double upperClipValue = range.upper + (reversed ? 0 : clipMargin); // clip slightly outside of actual range to avoid scatter size to peek into visible circle\n  const double lowerClipValue = range.lower - (reversed ? clipMargin : 0); // clip slightly outside of actual range to avoid scatter size to peek into visible circle\n  QCPGraphDataContainer::const_iterator it = begin;\n  while (it != end)\n  {\n    if (it->value > lowerClipValue && it->value < upperClipValue)\n      scatterData->append(*it);\n    ++it;\n  }\n}\n\n/*! \\internal\n\n  Takes raw data points in plot coordinates as \\a data, and returns a vector containing pixel\n  coordinate points which are suitable for drawing the line style \\ref lsLine.\n  \n  The source of \\a data is usually \\ref getOptimizedLineData, and this method is called in \\a\n  getLines if the line style is set accordingly.\n\n  \\see dataToStepLeftLines, dataToStepRightLines, dataToStepCenterLines, dataToImpulseLines, getLines, drawLinePlot\n*/\nQVector<QPointF> QCPPolarGraph::dataToLines(const QVector<QCPGraphData> &data) const\n{\n  QVector<QPointF> result;\n  QCPPolarAxisAngular *keyAxis = mKeyAxis.data();\n  QCPPolarAxisRadial *valueAxis = mValueAxis.data();\n  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << \"invalid key or value axis\"; return result; }\n\n  // transform data points to pixels:\n  result.resize(data.size());\n  for (int i=0; i<data.size(); ++i)\n    result[i] = mValueAxis->coordToPixel(data.at(i).key, data.at(i).value);\n  return result;\n}\n/* end of 'src/polar/polargraph.cpp' */\n\n\n"
  },
  {
    "path": "ext/qcustomplot.h",
    "content": "/***************************************************************************\n**                                                                        **\n**  QCustomPlot, an easy to use, modern plotting widget for Qt            **\n**  Copyright (C) 2011-2021 Emanuel Eichhammer                            **\n**                                                                        **\n**  This program is free software: you can redistribute it and/or modify  **\n**  it under the terms of the GNU General Public License as published by  **\n**  the Free Software Foundation, either version 3 of the License, or     **\n**  (at your option) any later version.                                   **\n**                                                                        **\n**  This program is distributed in the hope that it will be useful,       **\n**  but WITHOUT ANY WARRANTY; without even the implied warranty of        **\n**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         **\n**  GNU General Public License for more details.                          **\n**                                                                        **\n**  You should have received a copy of the GNU General Public License     **\n**  along with this program.  If not, see http://www.gnu.org/licenses/.   **\n**                                                                        **\n****************************************************************************\n**           Author: Emanuel Eichhammer                                   **\n**  Website/Contact: http://www.qcustomplot.com/                          **\n**             Date: 29.03.21                                             **\n**          Version: 2.1.0                                                **\n****************************************************************************/\n\n#ifndef QCUSTOMPLOT_H\n#define QCUSTOMPLOT_H\n\n#include <QtCore/qglobal.h>\n\n// some Qt version/configuration dependent macros to include or exclude certain code paths:\n#ifdef QCUSTOMPLOT_USE_OPENGL\n#  if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)\n#    define QCP_OPENGL_PBUFFER\n#  else\n#    define QCP_OPENGL_FBO\n#  endif\n#  if QT_VERSION >= QT_VERSION_CHECK(5, 3, 0)\n#    define QCP_OPENGL_OFFSCREENSURFACE\n#  endif\n#endif\n\n#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)\n#  define QCP_DEVICEPIXELRATIO_SUPPORTED\n#  if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)\n#    define QCP_DEVICEPIXELRATIO_FLOAT\n#  endif\n#endif\n\n#include <QtCore/QObject>\n#include <QtCore/QPointer>\n#include <QtCore/QSharedPointer>\n#include <QtCore/QTimer>\n#include <QtGui/QPainter>\n#include <QtGui/QPainterPath>\n#include <QtGui/QPaintEvent>\n#include <QtGui/QMouseEvent>\n#include <QtGui/QWheelEvent>\n#include <QtGui/QPixmap>\n#include <QtCore/QVector>\n#include <QtCore/QString>\n#include <QtCore/QDateTime>\n#include <QtCore/QMultiMap>\n#include <QtCore/QFlags>\n#include <QtCore/QDebug>\n#include <QtCore/QStack>\n#include <QtCore/QCache>\n#include <QtCore/QMargins>\n#include <qmath.h>\n#include <limits>\n#include <algorithm>\n#ifdef QCP_OPENGL_FBO\n#  include <QtGui/QOpenGLContext>\n#  if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)\n#    include <QtGui/QOpenGLFramebufferObject>\n#  else\n#    include <QOpenGLFramebufferObject>\n#    include <QOpenGLPaintDevice>\n#  endif\n#  ifdef QCP_OPENGL_OFFSCREENSURFACE\n#    include <QtGui/QOffscreenSurface>\n#  else\n#    include <QtGui/QWindow>\n#  endif\n#endif\n#ifdef QCP_OPENGL_PBUFFER\n#  include <QtOpenGL/QGLPixelBuffer>\n#endif\n#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)\n#  include <qnumeric.h>\n#  include <QtGui/QWidget>\n#  include <QtGui/QPrinter>\n#  include <QtGui/QPrintEngine>\n#else\n#  include <QtNumeric>\n#  include <QtWidgets/QWidget>\n#  include <QtPrintSupport/QtPrintSupport>\n#endif\n#if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0)\n#  include <QtCore/QElapsedTimer>\n#endif\n# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)\n#  include <QtCore/QTimeZone>\n#endif\n\nclass QCPPainter;\nclass QCustomPlot;\nclass QCPLayerable;\nclass QCPLayoutElement;\nclass QCPLayout;\nclass QCPAxis;\nclass QCPAxisRect;\nclass QCPAxisPainterPrivate;\nclass QCPAbstractPlottable;\nclass QCPGraph;\nclass QCPAbstractItem;\nclass QCPPlottableInterface1D;\nclass QCPLegend;\nclass QCPItemPosition;\nclass QCPLayer;\nclass QCPAbstractLegendItem;\nclass QCPSelectionRect;\nclass QCPColorMap;\nclass QCPColorScale;\nclass QCPBars;\nclass QCPPolarAxisRadial;\nclass QCPPolarAxisAngular;\nclass QCPPolarGrid;\nclass QCPPolarGraph;\n\n/* including file 'src/global.h'            */\n/* modified 2021-03-29T02:30:44, size 16981 */\n\n#define QCUSTOMPLOT_VERSION_STR \"2.1.0\"\n#define QCUSTOMPLOT_VERSION 0x020100\n\n// decl definitions for shared library compilation/usage:\n#if defined(QT_STATIC_BUILD)\n#  define QCP_LIB_DECL\n#elif defined(QCUSTOMPLOT_COMPILE_LIBRARY)\n#  define QCP_LIB_DECL Q_DECL_EXPORT\n#elif defined(QCUSTOMPLOT_USE_LIBRARY)\n#  define QCP_LIB_DECL Q_DECL_IMPORT\n#else\n#  define QCP_LIB_DECL\n#endif\n\n// define empty macro for Q_DECL_OVERRIDE if it doesn't exist (Qt < 5)\n#ifndef Q_DECL_OVERRIDE\n#  define Q_DECL_OVERRIDE\n#endif\n\n/*!\n  The QCP Namespace contains general enums, QFlags and functions used throughout the QCustomPlot\n  library.\n  \n  It provides QMetaObject-based reflection of its enums and flags via \\a QCP::staticMetaObject.\n*/\n#ifndef Q_MOC_RUN\nnamespace QCP {\n#else\nclass QCP { // when in moc-run, make it look like a class, so we get Q_GADGET, Q_ENUMS/Q_FLAGS features in namespace\n  Q_GADGET\n  Q_ENUMS(ExportPen)\n  Q_ENUMS(ResolutionUnit)\n  Q_ENUMS(SignDomain)\n  Q_ENUMS(MarginSide)\n  Q_FLAGS(MarginSides)\n  Q_ENUMS(AntialiasedElement)\n  Q_FLAGS(AntialiasedElements)\n  Q_ENUMS(PlottingHint)\n  Q_FLAGS(PlottingHints)\n  Q_ENUMS(Interaction)\n  Q_FLAGS(Interactions)\n  Q_ENUMS(SelectionRectMode)\n  Q_ENUMS(SelectionType)\npublic:\n#endif\n\n/*!\n  Defines the different units in which the image resolution can be specified in the export\n  functions.\n\n  \\see QCustomPlot::savePng, QCustomPlot::saveJpg, QCustomPlot::saveBmp, QCustomPlot::saveRastered\n*/\nenum ResolutionUnit { ruDotsPerMeter       ///< Resolution is given in dots per meter (dpm)\n                      ,ruDotsPerCentimeter ///< Resolution is given in dots per centimeter (dpcm)\n                      ,ruDotsPerInch       ///< Resolution is given in dots per inch (DPI/PPI)\n                    };\n\n/*!\n  Defines how cosmetic pens (pens with numerical width 0) are handled during export.\n\n  \\see QCustomPlot::savePdf\n*/\nenum ExportPen { epNoCosmetic     ///< Cosmetic pens are converted to pens with pixel width 1 when exporting\n                 ,epAllowCosmetic ///< Cosmetic pens are exported normally (e.g. in PDF exports, cosmetic pens always appear as 1 pixel on screen, independent of viewer zoom level)\n               };\n\n/*!\n  Represents negative and positive sign domain, e.g. for passing to \\ref\n  QCPAbstractPlottable::getKeyRange and \\ref QCPAbstractPlottable::getValueRange.\n  \n  This is primarily needed when working with logarithmic axis scales, since only one of the sign\n  domains can be visible at a time.\n*/\nenum SignDomain { sdNegative  ///< The negative sign domain, i.e. numbers smaller than zero\n                  ,sdBoth     ///< Both sign domains, including zero, i.e. all numbers\n                  ,sdPositive ///< The positive sign domain, i.e. numbers greater than zero\n                };\n\n/*!\n  Defines the sides of a rectangular entity to which margins can be applied.\n  \n  \\see QCPLayoutElement::setAutoMargins, QCPAxisRect::setAutoMargins\n*/\nenum MarginSide { msLeft     = 0x01 ///< <tt>0x01</tt> left margin\n                  ,msRight   = 0x02 ///< <tt>0x02</tt> right margin\n                  ,msTop     = 0x04 ///< <tt>0x04</tt> top margin\n                  ,msBottom  = 0x08 ///< <tt>0x08</tt> bottom margin\n                  ,msAll     = 0xFF ///< <tt>0xFF</tt> all margins\n                  ,msNone    = 0x00 ///< <tt>0x00</tt> no margin\n                };\nQ_DECLARE_FLAGS(MarginSides, MarginSide)\n\n/*!\n  Defines what objects of a plot can be forcibly drawn antialiased/not antialiased. If an object is\n  neither forcibly drawn antialiased nor forcibly drawn not antialiased, it is up to the respective\n  element how it is drawn. Typically it provides a \\a setAntialiased function for this.\n  \n  \\c AntialiasedElements is a flag of or-combined elements of this enum type.\n  \n  \\see QCustomPlot::setAntialiasedElements, QCustomPlot::setNotAntialiasedElements\n*/\nenum AntialiasedElement { aeAxes           = 0x0001 ///< <tt>0x0001</tt> Axis base line and tick marks\n                          ,aeGrid          = 0x0002 ///< <tt>0x0002</tt> Grid lines\n                          ,aeSubGrid       = 0x0004 ///< <tt>0x0004</tt> Sub grid lines\n                          ,aeLegend        = 0x0008 ///< <tt>0x0008</tt> Legend box\n                          ,aeLegendItems   = 0x0010 ///< <tt>0x0010</tt> Legend items\n                          ,aePlottables    = 0x0020 ///< <tt>0x0020</tt> Main lines of plottables\n                          ,aeItems         = 0x0040 ///< <tt>0x0040</tt> Main lines of items\n                          ,aeScatters      = 0x0080 ///< <tt>0x0080</tt> Scatter symbols of plottables (excluding scatter symbols of type ssPixmap)\n                          ,aeFills         = 0x0100 ///< <tt>0x0100</tt> Borders of fills (e.g. under or between graphs)\n                          ,aeZeroLine      = 0x0200 ///< <tt>0x0200</tt> Zero-lines, see \\ref QCPGrid::setZeroLinePen\n                          ,aeOther         = 0x8000 ///< <tt>0x8000</tt> Other elements that don't fit into any of the existing categories\n                          ,aeAll           = 0xFFFF ///< <tt>0xFFFF</tt> All elements\n                          ,aeNone          = 0x0000 ///< <tt>0x0000</tt> No elements\n                        };\nQ_DECLARE_FLAGS(AntialiasedElements, AntialiasedElement)\n\n/*!\n  Defines plotting hints that control various aspects of the quality and speed of plotting.\n  \n  \\see QCustomPlot::setPlottingHints\n*/\nenum PlottingHint { phNone              = 0x000 ///< <tt>0x000</tt> No hints are set\n                    ,phFastPolylines    = 0x001 ///< <tt>0x001</tt> Graph/Curve lines are drawn with a faster method. This reduces the quality especially of the line segment\n                                                ///<                joins, thus is most effective for pen sizes larger than 1. It is only used for solid line pens.\n                    ,phImmediateRefresh = 0x002 ///< <tt>0x002</tt> causes an immediate repaint() instead of a soft update() when QCustomPlot::replot() is called with parameter \\ref QCustomPlot::rpRefreshHint.\n                                                ///<                This is set by default to prevent the plot from freezing on fast consecutive replots (e.g. user drags ranges with mouse).\n                    ,phCacheLabels      = 0x004 ///< <tt>0x004</tt> axis (tick) labels will be cached as pixmaps, increasing replot performance.\n                  };\nQ_DECLARE_FLAGS(PlottingHints, PlottingHint)\n\n/*!\n  Defines the mouse interactions possible with QCustomPlot.\n  \n  \\c Interactions is a flag of or-combined elements of this enum type.\n  \n  \\see QCustomPlot::setInteractions\n*/\nenum Interaction { iNone              = 0x000 ///< <tt>0x000</tt> None of the interactions are possible\n                   ,iRangeDrag        = 0x001 ///< <tt>0x001</tt> Axis ranges are draggable (see \\ref QCPAxisRect::setRangeDrag, \\ref QCPAxisRect::setRangeDragAxes)\n                   ,iRangeZoom        = 0x002 ///< <tt>0x002</tt> Axis ranges are zoomable with the mouse wheel (see \\ref QCPAxisRect::setRangeZoom, \\ref QCPAxisRect::setRangeZoomAxes)\n                   ,iMultiSelect      = 0x004 ///< <tt>0x004</tt> The user can select multiple objects by holding the modifier set by \\ref QCustomPlot::setMultiSelectModifier while clicking\n                   ,iSelectPlottables = 0x008 ///< <tt>0x008</tt> Plottables are selectable (e.g. graphs, curves, bars,... see QCPAbstractPlottable)\n                   ,iSelectAxes       = 0x010 ///< <tt>0x010</tt> Axes are selectable (or parts of them, see QCPAxis::setSelectableParts)\n                   ,iSelectLegend     = 0x020 ///< <tt>0x020</tt> Legends are selectable (or their child items, see QCPLegend::setSelectableParts)\n                   ,iSelectItems      = 0x040 ///< <tt>0x040</tt> Items are selectable (Rectangles, Arrows, Textitems, etc. see \\ref QCPAbstractItem)\n                   ,iSelectOther      = 0x080 ///< <tt>0x080</tt> All other objects are selectable (e.g. your own derived layerables, other layout elements,...)\n                   ,iSelectPlottablesBeyondAxisRect = 0x100 ///< <tt>0x100</tt> When performing plottable selection/hit tests, this flag extends the sensitive area beyond the axis rect\n                 };\nQ_DECLARE_FLAGS(Interactions, Interaction)\n\n/*!\n  Defines the behaviour of the selection rect.\n  \n  \\see QCustomPlot::setSelectionRectMode, QCustomPlot::selectionRect, QCPSelectionRect\n*/\nenum SelectionRectMode { srmNone    ///< The selection rect is disabled, and all mouse events are forwarded to the underlying objects, e.g. for axis range dragging\n                         ,srmZoom   ///< When dragging the mouse, a selection rect becomes active. Upon releasing, the axes that are currently set as range zoom axes (\\ref QCPAxisRect::setRangeZoomAxes) will have their ranges zoomed accordingly.\n                         ,srmSelect ///< When dragging the mouse, a selection rect becomes active. Upon releasing, plottable data points that were within the selection rect are selected, if the plottable's selectability setting permits. (See  \\ref dataselection \"data selection mechanism\" for details.)\n                         ,srmCustom ///< When dragging the mouse, a selection rect becomes active. It is the programmer's responsibility to connect according slots to the selection rect's signals (e.g. \\ref QCPSelectionRect::accepted) in order to process the user interaction.\n                       };\n\n/*!\n  Defines the different ways a plottable can be selected. These images show the effect of the\n  different selection types, when the indicated selection rect was dragged:\n  \n  <center>\n  <table>\n  <tr>\n    <td>\\image html selectiontype-none.png stNone</td>\n    <td>\\image html selectiontype-whole.png stWhole</td>\n    <td>\\image html selectiontype-singledata.png stSingleData</td>\n    <td>\\image html selectiontype-datarange.png stDataRange</td>\n    <td>\\image html selectiontype-multipledataranges.png stMultipleDataRanges</td>\n  </tr>\n  </table>\n  </center>\n  \n  \\see QCPAbstractPlottable::setSelectable, QCPDataSelection::enforceType\n*/\nenum SelectionType { stNone                ///< The plottable is not selectable\n                     ,stWhole              ///< Selection behaves like \\ref stMultipleDataRanges, but if there are any data points selected, the entire plottable is drawn as selected.\n                     ,stSingleData         ///< One individual data point can be selected at a time\n                     ,stDataRange          ///< Multiple contiguous data points (a data range) can be selected\n                     ,stMultipleDataRanges ///< Any combination of data points/ranges can be selected\n                    };\n\n/*! \\internal\n  \n  Returns whether the specified \\a value is considered an invalid data value for plottables (i.e.\n  is \\e nan or \\e +/-inf). This function is used to check data validity upon replots, when the\n  compiler flag \\c QCUSTOMPLOT_CHECK_DATA is set.\n*/\ninline bool isInvalidData(double value)\n{\n  return qIsNaN(value) || qIsInf(value);\n}\n\n/*! \\internal\n  \\overload\n  \n  Checks two arguments instead of one.\n*/\ninline bool isInvalidData(double value1, double value2)\n{\n  return isInvalidData(value1) || isInvalidData(value2);\n}\n\n/*! \\internal\n  \n  Sets the specified \\a side of \\a margins to \\a value\n  \n  \\see getMarginValue\n*/\ninline void setMarginValue(QMargins &margins, QCP::MarginSide side, int value)\n{\n  switch (side)\n  {\n    case QCP::msLeft: margins.setLeft(value); break;\n    case QCP::msRight: margins.setRight(value); break;\n    case QCP::msTop: margins.setTop(value); break;\n    case QCP::msBottom: margins.setBottom(value); break;\n    case QCP::msAll: margins = QMargins(value, value, value, value); break;\n    default: break;\n  }\n}\n\n/*! \\internal\n  \n  Returns the value of the specified \\a side of \\a margins. If \\a side is \\ref QCP::msNone or\n  \\ref QCP::msAll, returns 0.\n  \n  \\see setMarginValue\n*/\ninline int getMarginValue(const QMargins &margins, QCP::MarginSide side)\n{\n  switch (side)\n  {\n    case QCP::msLeft: return margins.left();\n    case QCP::msRight: return margins.right();\n    case QCP::msTop: return margins.top();\n    case QCP::msBottom: return margins.bottom();\n    default: break;\n  }\n  return 0;\n}\n\n\nextern const QMetaObject staticMetaObject; // in moc-run we create a static meta object for QCP \"fake\" object. This line is the link to it via QCP::staticMetaObject in normal operation as namespace\n\n} // end of namespace QCP\nQ_DECLARE_OPERATORS_FOR_FLAGS(QCP::AntialiasedElements)\nQ_DECLARE_OPERATORS_FOR_FLAGS(QCP::PlottingHints)\nQ_DECLARE_OPERATORS_FOR_FLAGS(QCP::MarginSides)\nQ_DECLARE_OPERATORS_FOR_FLAGS(QCP::Interactions)\nQ_DECLARE_METATYPE(QCP::ExportPen)\nQ_DECLARE_METATYPE(QCP::ResolutionUnit)\nQ_DECLARE_METATYPE(QCP::SignDomain)\nQ_DECLARE_METATYPE(QCP::MarginSide)\nQ_DECLARE_METATYPE(QCP::AntialiasedElement)\nQ_DECLARE_METATYPE(QCP::PlottingHint)\nQ_DECLARE_METATYPE(QCP::Interaction)\nQ_DECLARE_METATYPE(QCP::SelectionRectMode)\nQ_DECLARE_METATYPE(QCP::SelectionType)\n\n/* end of 'src/global.h' */\n\n\n/* including file 'src/vector2d.h'         */\n/* modified 2021-03-29T02:30:44, size 4988 */\n\nclass QCP_LIB_DECL QCPVector2D\n{\npublic:\n  QCPVector2D();\n  QCPVector2D(double x, double y);\n  QCPVector2D(const QPoint &point);\n  QCPVector2D(const QPointF &point);\n  \n  // getters:\n  double x() const { return mX; }\n  double y() const { return mY; }\n  double &rx() { return mX; }\n  double &ry() { return mY; }\n  \n  // setters:\n  void setX(double x) { mX = x; }\n  void setY(double y) { mY = y; }\n  \n  // non-virtual methods:\n  double length() const { return qSqrt(mX*mX+mY*mY); }\n  double lengthSquared() const { return mX*mX+mY*mY; }\n  double angle() const { return qAtan2(mY, mX); }\n  QPoint toPoint() const { return QPoint(int(mX), int(mY)); }\n  QPointF toPointF() const { return QPointF(mX, mY); }\n  \n  bool isNull() const { return qIsNull(mX) && qIsNull(mY); }\n  void normalize();\n  QCPVector2D normalized() const;\n  QCPVector2D perpendicular() const { return QCPVector2D(-mY, mX); }\n  double dot(const QCPVector2D &vec) const { return mX*vec.mX+mY*vec.mY; }\n  double distanceSquaredToLine(const QCPVector2D &start, const QCPVector2D &end) const;\n  double distanceSquaredToLine(const QLineF &line) const;\n  double distanceToStraightLine(const QCPVector2D &base, const QCPVector2D &direction) const;\n  \n  QCPVector2D &operator*=(double factor);\n  QCPVector2D &operator/=(double divisor);\n  QCPVector2D &operator+=(const QCPVector2D &vector);\n  QCPVector2D &operator-=(const QCPVector2D &vector);\n  \nprivate:\n  // property members:\n  double mX, mY;\n  \n  friend inline const QCPVector2D operator*(double factor, const QCPVector2D &vec);\n  friend inline const QCPVector2D operator*(const QCPVector2D &vec, double factor);\n  friend inline const QCPVector2D operator/(const QCPVector2D &vec, double divisor);\n  friend inline const QCPVector2D operator+(const QCPVector2D &vec1, const QCPVector2D &vec2);\n  friend inline const QCPVector2D operator-(const QCPVector2D &vec1, const QCPVector2D &vec2);\n  friend inline const QCPVector2D operator-(const QCPVector2D &vec);\n};\nQ_DECLARE_TYPEINFO(QCPVector2D, Q_MOVABLE_TYPE);\n\ninline const QCPVector2D operator*(double factor, const QCPVector2D &vec) { return QCPVector2D(vec.mX*factor, vec.mY*factor); }\ninline const QCPVector2D operator*(const QCPVector2D &vec, double factor) { return QCPVector2D(vec.mX*factor, vec.mY*factor); }\ninline const QCPVector2D operator/(const QCPVector2D &vec, double divisor) { return QCPVector2D(vec.mX/divisor, vec.mY/divisor); }\ninline const QCPVector2D operator+(const QCPVector2D &vec1, const QCPVector2D &vec2) { return QCPVector2D(vec1.mX+vec2.mX, vec1.mY+vec2.mY); }\ninline const QCPVector2D operator-(const QCPVector2D &vec1, const QCPVector2D &vec2) { return QCPVector2D(vec1.mX-vec2.mX, vec1.mY-vec2.mY); }\ninline const QCPVector2D operator-(const QCPVector2D &vec) { return QCPVector2D(-vec.mX, -vec.mY); }\n\n/*! \\relates QCPVector2D\n\n  Prints \\a vec in a human readable format to the qDebug output.\n*/\ninline QDebug operator<< (QDebug d, const QCPVector2D &vec)\n{\n    d.nospace() << \"QCPVector2D(\" << vec.x() << \", \" << vec.y() << \")\";\n    return d.space();\n}\n\n/* end of 'src/vector2d.h' */\n\n\n/* including file 'src/painter.h'          */\n/* modified 2021-03-29T02:30:44, size 4035 */\n\nclass QCP_LIB_DECL QCPPainter : public QPainter\n{\n  Q_GADGET\npublic:\n  /*!\n    Defines special modes the painter can operate in. They disable or enable certain subsets of features/fixes/workarounds,\n    depending on whether they are wanted on the respective output device.\n  */\n  enum PainterMode { pmDefault       = 0x00   ///< <tt>0x00</tt> Default mode for painting on screen devices\n                     ,pmVectorized   = 0x01   ///< <tt>0x01</tt> Mode for vectorized painting (e.g. PDF export). For example, this prevents some antialiasing fixes.\n                     ,pmNoCaching    = 0x02   ///< <tt>0x02</tt> Mode for all sorts of exports (e.g. PNG, PDF,...). For example, this prevents using cached pixmap labels\n                     ,pmNonCosmetic  = 0x04   ///< <tt>0x04</tt> Turns pen widths 0 to 1, i.e. disables cosmetic pens. (A cosmetic pen is always drawn with width 1 pixel in the vector image/pdf viewer, independent of zoom.)\n                   };\n  Q_ENUMS(PainterMode)\n  Q_FLAGS(PainterModes)\n  Q_DECLARE_FLAGS(PainterModes, PainterMode)\n  \n  QCPPainter();\n  explicit QCPPainter(QPaintDevice *device);\n  \n  // getters:\n  bool antialiasing() const { return testRenderHint(QPainter::Antialiasing); }\n  PainterModes modes() const { return mModes; }\n\n  // setters:\n  void setAntialiasing(bool enabled);\n  void setMode(PainterMode mode, bool enabled=true);\n  void setModes(PainterModes modes);\n\n  // methods hiding non-virtual base class functions (QPainter bug workarounds):\n  bool begin(QPaintDevice *device);\n  void setPen(const QPen &pen);\n  void setPen(const QColor &color);\n  void setPen(Qt::PenStyle penStyle);\n  void drawLine(const QLineF &line);\n  void drawLine(const QPointF &p1, const QPointF &p2) {drawLine(QLineF(p1, p2));}\n  void save();\n  void restore();\n  \n  // non-virtual methods:\n  void makeNonCosmetic();\n  \nprotected:\n  // property members:\n  PainterModes mModes;\n  bool mIsAntialiasing;\n  \n  // non-property members:\n  QStack<bool> mAntialiasingStack;\n};\nQ_DECLARE_OPERATORS_FOR_FLAGS(QCPPainter::PainterModes)\nQ_DECLARE_METATYPE(QCPPainter::PainterMode)\n\n/* end of 'src/painter.h' */\n\n\n/* including file 'src/paintbuffer.h'      */\n/* modified 2021-03-29T02:30:44, size 5006 */\n\nclass QCP_LIB_DECL QCPAbstractPaintBuffer\n{\npublic:\n  explicit QCPAbstractPaintBuffer(const QSize &size, double devicePixelRatio);\n  virtual ~QCPAbstractPaintBuffer();\n  \n  // getters:\n  QSize size() const { return mSize; }\n  bool invalidated() const { return mInvalidated; }\n  double devicePixelRatio() const { return mDevicePixelRatio; }\n  \n  // setters:\n  void setSize(const QSize &size);\n  void setInvalidated(bool invalidated=true);\n  void setDevicePixelRatio(double ratio);\n  \n  // introduced virtual methods:\n  virtual QCPPainter *startPainting() = 0;\n  virtual void donePainting() {}\n  virtual void draw(QCPPainter *painter) const = 0;\n  virtual void clear(const QColor &color) = 0;\n  \nprotected:\n  // property members:\n  QSize mSize;\n  double mDevicePixelRatio;\n  \n  // non-property members:\n  bool mInvalidated;\n  \n  // introduced virtual methods:\n  virtual void reallocateBuffer() = 0;\n};\n\n\nclass QCP_LIB_DECL QCPPaintBufferPixmap : public QCPAbstractPaintBuffer\n{\npublic:\n  explicit QCPPaintBufferPixmap(const QSize &size, double devicePixelRatio);\n  virtual ~QCPPaintBufferPixmap() Q_DECL_OVERRIDE;\n  \n  // reimplemented virtual methods:\n  virtual QCPPainter *startPainting() Q_DECL_OVERRIDE;\n  virtual void draw(QCPPainter *painter) const Q_DECL_OVERRIDE;\n  void clear(const QColor &color) Q_DECL_OVERRIDE;\n  \nprotected:\n  // non-property members:\n  QPixmap mBuffer;\n  \n  // reimplemented virtual methods:\n  virtual void reallocateBuffer() Q_DECL_OVERRIDE;\n};\n\n\n#ifdef QCP_OPENGL_PBUFFER\nclass QCP_LIB_DECL QCPPaintBufferGlPbuffer : public QCPAbstractPaintBuffer\n{\npublic:\n  explicit QCPPaintBufferGlPbuffer(const QSize &size, double devicePixelRatio, int multisamples);\n  virtual ~QCPPaintBufferGlPbuffer() Q_DECL_OVERRIDE;\n  \n  // reimplemented virtual methods:\n  virtual QCPPainter *startPainting() Q_DECL_OVERRIDE;\n  virtual void draw(QCPPainter *painter) const Q_DECL_OVERRIDE;\n  void clear(const QColor &color) Q_DECL_OVERRIDE;\n  \nprotected:\n  // non-property members:\n  QGLPixelBuffer *mGlPBuffer;\n  int mMultisamples;\n  \n  // reimplemented virtual methods:\n  virtual void reallocateBuffer() Q_DECL_OVERRIDE;\n};\n#endif // QCP_OPENGL_PBUFFER\n\n\n#ifdef QCP_OPENGL_FBO\nclass QCP_LIB_DECL QCPPaintBufferGlFbo : public QCPAbstractPaintBuffer\n{\npublic:\n  explicit QCPPaintBufferGlFbo(const QSize &size, double devicePixelRatio, QWeakPointer<QOpenGLContext> glContext, QWeakPointer<QOpenGLPaintDevice> glPaintDevice);\n  virtual ~QCPPaintBufferGlFbo() Q_DECL_OVERRIDE;\n  \n  // reimplemented virtual methods:\n  virtual QCPPainter *startPainting() Q_DECL_OVERRIDE;\n  virtual void donePainting() Q_DECL_OVERRIDE;\n  virtual void draw(QCPPainter *painter) const Q_DECL_OVERRIDE;\n  void clear(const QColor &color) Q_DECL_OVERRIDE;\n  \nprotected:\n  // non-property members:\n  QWeakPointer<QOpenGLContext> mGlContext;\n  QWeakPointer<QOpenGLPaintDevice> mGlPaintDevice;\n  QOpenGLFramebufferObject *mGlFrameBuffer;\n  \n  // reimplemented virtual methods:\n  virtual void reallocateBuffer() Q_DECL_OVERRIDE;\n};\n#endif // QCP_OPENGL_FBO\n\n/* end of 'src/paintbuffer.h' */\n\n\n/* including file 'src/layer.h'            */\n/* modified 2021-03-29T02:30:44, size 7038 */\n\nclass QCP_LIB_DECL QCPLayer : public QObject\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QCustomPlot* parentPlot READ parentPlot)\n  Q_PROPERTY(QString name READ name)\n  Q_PROPERTY(int index READ index)\n  Q_PROPERTY(QList<QCPLayerable*> children READ children)\n  Q_PROPERTY(bool visible READ visible WRITE setVisible)\n  Q_PROPERTY(LayerMode mode READ mode WRITE setMode)\n  /// \\endcond\npublic:\n  \n  /*!\n    Defines the different rendering modes of a layer. Depending on the mode, certain layers can be\n    replotted individually, without the need to replot (possibly complex) layerables on other\n    layers.\n\n    \\see setMode\n  */\n  enum LayerMode { lmLogical   ///< Layer is used only for rendering order, and shares paint buffer with all other adjacent logical layers.\n                   ,lmBuffered ///< Layer has its own paint buffer and may be replotted individually (see \\ref replot).\n                 };\n  Q_ENUMS(LayerMode)\n  \n  QCPLayer(QCustomPlot* parentPlot, const QString &layerName);\n  virtual ~QCPLayer();\n  \n  // getters:\n  QCustomPlot *parentPlot() const { return mParentPlot; }\n  QString name() const { return mName; }\n  int index() const { return mIndex; }\n  QList<QCPLayerable*> children() const { return mChildren; }\n  bool visible() const { return mVisible; }\n  LayerMode mode() const { return mMode; }\n  \n  // setters:\n  void setVisible(bool visible);\n  void setMode(LayerMode mode);\n  \n  // non-virtual methods:\n  void replot();\n  \nprotected:\n  // property members:\n  QCustomPlot *mParentPlot;\n  QString mName;\n  int mIndex;\n  QList<QCPLayerable*> mChildren;\n  bool mVisible;\n  LayerMode mMode;\n  \n  // non-property members:\n  QWeakPointer<QCPAbstractPaintBuffer> mPaintBuffer;\n  \n  // non-virtual methods:\n  void draw(QCPPainter *painter);\n  void drawToPaintBuffer();\n  void addChild(QCPLayerable *layerable, bool prepend);\n  void removeChild(QCPLayerable *layerable);\n  \nprivate:\n  Q_DISABLE_COPY(QCPLayer)\n  \n  friend class QCustomPlot;\n  friend class QCPLayerable;\n};\nQ_DECLARE_METATYPE(QCPLayer::LayerMode)\n\nclass QCP_LIB_DECL QCPLayerable : public QObject\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(bool visible READ visible WRITE setVisible)\n  Q_PROPERTY(QCustomPlot* parentPlot READ parentPlot)\n  Q_PROPERTY(QCPLayerable* parentLayerable READ parentLayerable)\n  Q_PROPERTY(QCPLayer* layer READ layer WRITE setLayer NOTIFY layerChanged)\n  Q_PROPERTY(bool antialiased READ antialiased WRITE setAntialiased)\n  /// \\endcond\npublic:\n  QCPLayerable(QCustomPlot *plot, QString targetLayer=QString(), QCPLayerable *parentLayerable=nullptr);\n  virtual ~QCPLayerable();\n  \n  // getters:\n  bool visible() const { return mVisible; }\n  QCustomPlot *parentPlot() const { return mParentPlot; }\n  QCPLayerable *parentLayerable() const { return mParentLayerable.data(); }\n  QCPLayer *layer() const { return mLayer; }\n  bool antialiased() const { return mAntialiased; }\n  \n  // setters:\n  void setVisible(bool on);\n  Q_SLOT bool setLayer(QCPLayer *layer);\n  bool setLayer(const QString &layerName);\n  void setAntialiased(bool enabled);\n  \n  // introduced virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const;\n\n  // non-property methods:\n  bool realVisibility() const;\n  \nsignals:\n  void layerChanged(QCPLayer *newLayer);\n  \nprotected:\n  // property members:\n  bool mVisible;\n  QCustomPlot *mParentPlot;\n  QPointer<QCPLayerable> mParentLayerable;\n  QCPLayer *mLayer;\n  bool mAntialiased;\n  \n  // introduced virtual methods:\n  virtual void parentPlotInitialized(QCustomPlot *parentPlot);\n  virtual QCP::Interaction selectionCategory() const;\n  virtual QRect clipRect() const;\n  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const = 0;\n  virtual void draw(QCPPainter *painter) = 0;\n  // selection events:\n  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);\n  virtual void deselectEvent(bool *selectionStateChanged);\n  // low-level mouse events:\n  virtual void mousePressEvent(QMouseEvent *event, const QVariant &details);\n  virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos);\n  virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos);\n  virtual void mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details);\n  virtual void wheelEvent(QWheelEvent *event);\n  \n  // non-property methods:\n  void initializeParentPlot(QCustomPlot *parentPlot);\n  void setParentLayerable(QCPLayerable* parentLayerable);\n  bool moveToLayer(QCPLayer *layer, bool prepend);\n  void applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const;\n  \nprivate:\n  Q_DISABLE_COPY(QCPLayerable)\n  \n  friend class QCustomPlot;\n  friend class QCPLayer;\n  friend class QCPAxisRect;\n};\n\n/* end of 'src/layer.h' */\n\n\n/* including file 'src/axis/range.h'       */\n/* modified 2021-03-29T02:30:44, size 5280 */\n\nclass QCP_LIB_DECL QCPRange\n{\npublic:\n  double lower, upper;\n  \n  QCPRange();\n  QCPRange(double lower, double upper);\n  \n  bool operator==(const QCPRange& other) const { return lower == other.lower && upper == other.upper; }\n  bool operator!=(const QCPRange& other) const { return !(*this == other); }\n  \n  QCPRange &operator+=(const double& value) { lower+=value; upper+=value; return *this; }\n  QCPRange &operator-=(const double& value) { lower-=value; upper-=value; return *this; }\n  QCPRange &operator*=(const double& value) { lower*=value; upper*=value; return *this; }\n  QCPRange &operator/=(const double& value) { lower/=value; upper/=value; return *this; }\n  friend inline const QCPRange operator+(const QCPRange&, double);\n  friend inline const QCPRange operator+(double, const QCPRange&);\n  friend inline const QCPRange operator-(const QCPRange& range, double value);\n  friend inline const QCPRange operator*(const QCPRange& range, double value);\n  friend inline const QCPRange operator*(double value, const QCPRange& range);\n  friend inline const QCPRange operator/(const QCPRange& range, double value);\n  \n  double size() const { return upper-lower; }\n  double center() const { return (upper+lower)*0.5; }\n  void normalize() { if (lower > upper) qSwap(lower, upper); }\n  void expand(const QCPRange &otherRange);\n  void expand(double includeCoord);\n  QCPRange expanded(const QCPRange &otherRange) const;\n  QCPRange expanded(double includeCoord) const;\n  QCPRange bounded(double lowerBound, double upperBound) const;\n  QCPRange sanitizedForLogScale() const;\n  QCPRange sanitizedForLinScale() const;\n  bool contains(double value) const { return value >= lower && value <= upper; }\n  \n  static bool validRange(double lower, double upper);\n  static bool validRange(const QCPRange &range);\n  static const double minRange;\n  static const double maxRange;\n  \n};\nQ_DECLARE_TYPEINFO(QCPRange, Q_MOVABLE_TYPE);\n\n/*! \\relates QCPRange\n\n  Prints \\a range in a human readable format to the qDebug output.\n*/\ninline QDebug operator<< (QDebug d, const QCPRange &range)\n{\n    d.nospace() << \"QCPRange(\" << range.lower << \", \" << range.upper << \")\";\n    return d.space();\n}\n\n/*!\n  Adds \\a value to both boundaries of the range.\n*/\ninline const QCPRange operator+(const QCPRange& range, double value)\n{\n  QCPRange result(range);\n  result += value;\n  return result;\n}\n\n/*!\n  Adds \\a value to both boundaries of the range.\n*/\ninline const QCPRange operator+(double value, const QCPRange& range)\n{\n  QCPRange result(range);\n  result += value;\n  return result;\n}\n\n/*!\n  Subtracts \\a value from both boundaries of the range.\n*/\ninline const QCPRange operator-(const QCPRange& range, double value)\n{\n  QCPRange result(range);\n  result -= value;\n  return result;\n}\n\n/*!\n  Multiplies both boundaries of the range by \\a value.\n*/\ninline const QCPRange operator*(const QCPRange& range, double value)\n{\n  QCPRange result(range);\n  result *= value;\n  return result;\n}\n\n/*!\n  Multiplies both boundaries of the range by \\a value.\n*/\ninline const QCPRange operator*(double value, const QCPRange& range)\n{\n  QCPRange result(range);\n  result *= value;\n  return result;\n}\n\n/*!\n  Divides both boundaries of the range by \\a value.\n*/\ninline const QCPRange operator/(const QCPRange& range, double value)\n{\n  QCPRange result(range);\n  result /= value;\n  return result;\n}\n\n/* end of 'src/axis/range.h' */\n\n\n/* including file 'src/selection.h'        */\n/* modified 2021-03-29T02:30:44, size 8569 */\n\nclass QCP_LIB_DECL QCPDataRange\n{\npublic:\n  QCPDataRange();\n  QCPDataRange(int begin, int end);\n  \n  bool operator==(const QCPDataRange& other) const { return mBegin == other.mBegin && mEnd == other.mEnd; }\n  bool operator!=(const QCPDataRange& other) const { return !(*this == other); }\n  \n  // getters:\n  int begin() const { return mBegin; }\n  int end() const { return mEnd; }\n  int size() const { return mEnd-mBegin; }\n  int length() const { return size(); }\n  \n  // setters:\n  void setBegin(int begin) { mBegin = begin; }\n  void setEnd(int end)  { mEnd = end; }\n  \n  // non-property methods:\n  bool isValid() const { return (mEnd >= mBegin) && (mBegin >= 0); }\n  bool isEmpty() const { return length() == 0; }\n  QCPDataRange bounded(const QCPDataRange &other) const;\n  QCPDataRange expanded(const QCPDataRange &other) const;\n  QCPDataRange intersection(const QCPDataRange &other) const;\n  QCPDataRange adjusted(int changeBegin, int changeEnd) const { return QCPDataRange(mBegin+changeBegin, mEnd+changeEnd); }\n  bool intersects(const QCPDataRange &other) const;\n  bool contains(const QCPDataRange &other) const;\n  \nprivate:\n  // property members:\n  int mBegin, mEnd;\n\n};\nQ_DECLARE_TYPEINFO(QCPDataRange, Q_MOVABLE_TYPE);\n\n\nclass QCP_LIB_DECL QCPDataSelection\n{\npublic:\n  explicit QCPDataSelection();\n  explicit QCPDataSelection(const QCPDataRange &range);\n  \n  bool operator==(const QCPDataSelection& other) const;\n  bool operator!=(const QCPDataSelection& other) const { return !(*this == other); }\n  QCPDataSelection &operator+=(const QCPDataSelection& other);\n  QCPDataSelection &operator+=(const QCPDataRange& other);\n  QCPDataSelection &operator-=(const QCPDataSelection& other);\n  QCPDataSelection &operator-=(const QCPDataRange& other);\n  friend inline const QCPDataSelection operator+(const QCPDataSelection& a, const QCPDataSelection& b);\n  friend inline const QCPDataSelection operator+(const QCPDataRange& a, const QCPDataSelection& b);\n  friend inline const QCPDataSelection operator+(const QCPDataSelection& a, const QCPDataRange& b);\n  friend inline const QCPDataSelection operator+(const QCPDataRange& a, const QCPDataRange& b);\n  friend inline const QCPDataSelection operator-(const QCPDataSelection& a, const QCPDataSelection& b);\n  friend inline const QCPDataSelection operator-(const QCPDataRange& a, const QCPDataSelection& b);\n  friend inline const QCPDataSelection operator-(const QCPDataSelection& a, const QCPDataRange& b);\n  friend inline const QCPDataSelection operator-(const QCPDataRange& a, const QCPDataRange& b);\n  \n  // getters:\n  int dataRangeCount() const { return mDataRanges.size(); }\n  int dataPointCount() const;\n  QCPDataRange dataRange(int index=0) const;\n  QList<QCPDataRange> dataRanges() const { return mDataRanges; }\n  QCPDataRange span() const;\n  \n  // non-property methods:\n  void addDataRange(const QCPDataRange &dataRange, bool simplify=true);\n  void clear();\n  bool isEmpty() const { return mDataRanges.isEmpty(); }\n  void simplify();\n  void enforceType(QCP::SelectionType type);\n  bool contains(const QCPDataSelection &other) const;\n  QCPDataSelection intersection(const QCPDataRange &other) const;\n  QCPDataSelection intersection(const QCPDataSelection &other) const;\n  QCPDataSelection inverse(const QCPDataRange &outerRange) const;\n  \nprivate:\n  // property members:\n  QList<QCPDataRange> mDataRanges;\n  \n  inline static bool lessThanDataRangeBegin(const QCPDataRange &a, const QCPDataRange &b) { return a.begin() < b.begin(); }\n};\nQ_DECLARE_METATYPE(QCPDataSelection)\n\n\n/*!\n  Return a \\ref QCPDataSelection with the data points in \\a a joined with the data points in \\a b.\n  The resulting data selection is already simplified (see \\ref QCPDataSelection::simplify).\n*/\ninline const QCPDataSelection operator+(const QCPDataSelection& a, const QCPDataSelection& b)\n{\n  QCPDataSelection result(a);\n  result += b;\n  return result;\n}\n\n/*!\n  Return a \\ref QCPDataSelection with the data points in \\a a joined with the data points in \\a b.\n  The resulting data selection is already simplified (see \\ref QCPDataSelection::simplify).\n*/\ninline const QCPDataSelection operator+(const QCPDataRange& a, const QCPDataSelection& b)\n{\n  QCPDataSelection result(a);\n  result += b;\n  return result;\n}\n\n/*!\n  Return a \\ref QCPDataSelection with the data points in \\a a joined with the data points in \\a b.\n  The resulting data selection is already simplified (see \\ref QCPDataSelection::simplify).\n*/\ninline const QCPDataSelection operator+(const QCPDataSelection& a, const QCPDataRange& b)\n{\n  QCPDataSelection result(a);\n  result += b;\n  return result;\n}\n\n/*!\n  Return a \\ref QCPDataSelection with the data points in \\a a joined with the data points in \\a b.\n  The resulting data selection is already simplified (see \\ref QCPDataSelection::simplify).\n*/\ninline const QCPDataSelection operator+(const QCPDataRange& a, const QCPDataRange& b)\n{\n  QCPDataSelection result(a);\n  result += b;\n  return result;\n}\n\n/*!\n  Return a \\ref QCPDataSelection with the data points which are in \\a a but not in \\a b.\n*/\ninline const QCPDataSelection operator-(const QCPDataSelection& a, const QCPDataSelection& b)\n{\n  QCPDataSelection result(a);\n  result -= b;\n  return result;\n}\n\n/*!\n  Return a \\ref QCPDataSelection with the data points which are in \\a a but not in \\a b.\n*/\ninline const QCPDataSelection operator-(const QCPDataRange& a, const QCPDataSelection& b)\n{\n  QCPDataSelection result(a);\n  result -= b;\n  return result;\n}\n\n/*!\n  Return a \\ref QCPDataSelection with the data points which are in \\a a but not in \\a b.\n*/\ninline const QCPDataSelection operator-(const QCPDataSelection& a, const QCPDataRange& b)\n{\n  QCPDataSelection result(a);\n  result -= b;\n  return result;\n}\n\n/*!\n  Return a \\ref QCPDataSelection with the data points which are in \\a a but not in \\a b.\n*/\ninline const QCPDataSelection operator-(const QCPDataRange& a, const QCPDataRange& b)\n{\n  QCPDataSelection result(a);\n  result -= b;\n  return result;\n}\n\n/*! \\relates QCPDataRange\n\n  Prints \\a dataRange in a human readable format to the qDebug output.\n*/\ninline QDebug operator<< (QDebug d, const QCPDataRange &dataRange)\n{\n  d.nospace() << \"QCPDataRange(\" << dataRange.begin() << \", \" << dataRange.end() << \")\";\n  return d;\n}\n\n/*! \\relates QCPDataSelection\n\n  Prints \\a selection in a human readable format to the qDebug output.\n*/\ninline QDebug operator<< (QDebug d, const QCPDataSelection &selection)\n{\n    d.nospace() << \"QCPDataSelection(\";\n    for (int i=0; i<selection.dataRangeCount(); ++i)\n    {\n      if (i != 0)\n        d << \", \";\n      d << selection.dataRange(i);\n    }\n    d << \")\";\n    return d;\n}\n\n\n\n/* end of 'src/selection.h' */\n\n\n/* including file 'src/selectionrect.h'    */\n/* modified 2021-03-29T02:30:44, size 3354 */\n\nclass QCP_LIB_DECL QCPSelectionRect : public QCPLayerable\n{\n  Q_OBJECT\npublic:\n  explicit QCPSelectionRect(QCustomPlot *parentPlot);\n  virtual ~QCPSelectionRect() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QRect rect() const { return mRect; }\n  QCPRange range(const QCPAxis *axis) const;\n  QPen pen() const { return mPen; }\n  QBrush brush() const { return mBrush; }\n  bool isActive() const { return mActive; }\n  \n  // setters:\n  void setPen(const QPen &pen);\n  void setBrush(const QBrush &brush);\n  \n  // non-property methods:\n  Q_SLOT void cancel();\n  \nsignals:\n  void started(QMouseEvent *event);\n  void changed(const QRect &rect, QMouseEvent *event);\n  void canceled(const QRect &rect, QInputEvent *event);\n  void accepted(const QRect &rect, QMouseEvent *event);\n  \nprotected:\n  // property members:\n  QRect mRect;\n  QPen mPen;\n  QBrush mBrush;\n  // non-property members:\n  bool mActive;\n  \n  // introduced virtual methods:\n  virtual void startSelection(QMouseEvent *event);\n  virtual void moveSelection(QMouseEvent *event);\n  virtual void endSelection(QMouseEvent *event);\n  virtual void keyPressEvent(QKeyEvent *event);\n  \n  // reimplemented virtual methods\n  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  \n  friend class QCustomPlot;\n};\n\n/* end of 'src/selectionrect.h' */\n\n\n/* including file 'src/layout.h'            */\n/* modified 2021-03-29T02:30:44, size 14279 */\n\nclass QCP_LIB_DECL QCPMarginGroup : public QObject\n{\n  Q_OBJECT\npublic:\n  explicit QCPMarginGroup(QCustomPlot *parentPlot);\n  virtual ~QCPMarginGroup();\n  \n  // non-virtual methods:\n  QList<QCPLayoutElement*> elements(QCP::MarginSide side) const { return mChildren.value(side); }\n  bool isEmpty() const;\n  void clear();\n  \nprotected:\n  // non-property members:\n  QCustomPlot *mParentPlot;\n  QHash<QCP::MarginSide, QList<QCPLayoutElement*> > mChildren;\n  \n  // introduced virtual methods:\n  virtual int commonMargin(QCP::MarginSide side) const;\n  \n  // non-virtual methods:\n  void addChild(QCP::MarginSide side, QCPLayoutElement *element);\n  void removeChild(QCP::MarginSide side, QCPLayoutElement *element);\n  \nprivate:\n  Q_DISABLE_COPY(QCPMarginGroup)\n  \n  friend class QCPLayoutElement;\n};\n\n\nclass QCP_LIB_DECL QCPLayoutElement : public QCPLayerable\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QCPLayout* layout READ layout)\n  Q_PROPERTY(QRect rect READ rect)\n  Q_PROPERTY(QRect outerRect READ outerRect WRITE setOuterRect)\n  Q_PROPERTY(QMargins margins READ margins WRITE setMargins)\n  Q_PROPERTY(QMargins minimumMargins READ minimumMargins WRITE setMinimumMargins)\n  Q_PROPERTY(QSize minimumSize READ minimumSize WRITE setMinimumSize)\n  Q_PROPERTY(QSize maximumSize READ maximumSize WRITE setMaximumSize)\n  Q_PROPERTY(SizeConstraintRect sizeConstraintRect READ sizeConstraintRect WRITE setSizeConstraintRect)\n  /// \\endcond\npublic:\n  /*!\n    Defines the phases of the update process, that happens just before a replot. At each phase,\n    \\ref update is called with the according UpdatePhase value.\n  */\n  enum UpdatePhase { upPreparation ///< Phase used for any type of preparation that needs to be done before margin calculation and layout\n                     ,upMargins    ///< Phase in which the margins are calculated and set\n                     ,upLayout     ///< Final phase in which the layout system places the rects of the elements\n                   };\n  Q_ENUMS(UpdatePhase)\n  \n  /*!\n    Defines to which rect of a layout element the size constraints that can be set via \\ref\n    setMinimumSize and \\ref setMaximumSize apply. The outer rect (\\ref outerRect) includes the\n    margins (e.g. in the case of a QCPAxisRect the axis labels), whereas the inner rect (\\ref rect)\n    does not.\n    \n    \\see setSizeConstraintRect\n  */\n  enum SizeConstraintRect { scrInnerRect ///< Minimum/Maximum size constraints apply to inner rect\n                            , scrOuterRect ///< Minimum/Maximum size constraints apply to outer rect, thus include layout element margins\n                          };\n  Q_ENUMS(SizeConstraintRect)\n\n  explicit QCPLayoutElement(QCustomPlot *parentPlot=nullptr);\n  virtual ~QCPLayoutElement() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QCPLayout *layout() const { return mParentLayout; }\n  QRect rect() const { return mRect; }\n  QRect outerRect() const { return mOuterRect; }\n  QMargins margins() const { return mMargins; }\n  QMargins minimumMargins() const { return mMinimumMargins; }\n  QCP::MarginSides autoMargins() const { return mAutoMargins; }\n  QSize minimumSize() const { return mMinimumSize; }\n  QSize maximumSize() const { return mMaximumSize; }\n  SizeConstraintRect sizeConstraintRect() const { return mSizeConstraintRect; }\n  QCPMarginGroup *marginGroup(QCP::MarginSide side) const { return mMarginGroups.value(side, nullptr); }\n  QHash<QCP::MarginSide, QCPMarginGroup*> marginGroups() const { return mMarginGroups; }\n  \n  // setters:\n  void setOuterRect(const QRect &rect);\n  void setMargins(const QMargins &margins);\n  void setMinimumMargins(const QMargins &margins);\n  void setAutoMargins(QCP::MarginSides sides);\n  void setMinimumSize(const QSize &size);\n  void setMinimumSize(int width, int height);\n  void setMaximumSize(const QSize &size);\n  void setMaximumSize(int width, int height);\n  void setSizeConstraintRect(SizeConstraintRect constraintRect);\n  void setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group);\n  \n  // introduced virtual methods:\n  virtual void update(UpdatePhase phase);\n  virtual QSize minimumOuterSizeHint() const;\n  virtual QSize maximumOuterSizeHint() const;\n  virtual QList<QCPLayoutElement*> elements(bool recursive) const;\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  \nprotected:\n  // property members:\n  QCPLayout *mParentLayout;\n  QSize mMinimumSize, mMaximumSize;\n  SizeConstraintRect mSizeConstraintRect;\n  QRect mRect, mOuterRect;\n  QMargins mMargins, mMinimumMargins;\n  QCP::MarginSides mAutoMargins;\n  QHash<QCP::MarginSide, QCPMarginGroup*> mMarginGroups;\n  \n  // introduced virtual methods:\n  virtual int calculateAutoMargin(QCP::MarginSide side);\n  virtual void layoutChanged();\n  \n  // reimplemented virtual methods:\n  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE { Q_UNUSED(painter) }\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE { Q_UNUSED(painter) }\n  virtual void parentPlotInitialized(QCustomPlot *parentPlot) Q_DECL_OVERRIDE;\n\nprivate:\n  Q_DISABLE_COPY(QCPLayoutElement)\n  \n  friend class QCustomPlot;\n  friend class QCPLayout;\n  friend class QCPMarginGroup;\n};\nQ_DECLARE_METATYPE(QCPLayoutElement::UpdatePhase)\n\n\nclass QCP_LIB_DECL QCPLayout : public QCPLayoutElement\n{\n  Q_OBJECT\npublic:\n  explicit QCPLayout();\n  \n  // reimplemented virtual methods:\n  virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE;\n  virtual QList<QCPLayoutElement*> elements(bool recursive) const Q_DECL_OVERRIDE;\n  \n  // introduced virtual methods:\n  virtual int elementCount() const = 0;\n  virtual QCPLayoutElement* elementAt(int index) const = 0;\n  virtual QCPLayoutElement* takeAt(int index) = 0;\n  virtual bool take(QCPLayoutElement* element) = 0;\n  virtual void simplify();\n  \n  // non-virtual methods:\n  bool removeAt(int index);\n  bool remove(QCPLayoutElement* element);\n  void clear();\n  \nprotected:\n  // introduced virtual methods:\n  virtual void updateLayout();\n  \n  // non-virtual methods:\n  void sizeConstraintsChanged() const;\n  void adoptElement(QCPLayoutElement *el);\n  void releaseElement(QCPLayoutElement *el);\n  QVector<int> getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const;\n  static QSize getFinalMinimumOuterSize(const QCPLayoutElement *el);\n  static QSize getFinalMaximumOuterSize(const QCPLayoutElement *el);\n  \nprivate:\n  Q_DISABLE_COPY(QCPLayout)\n  friend class QCPLayoutElement;\n};\n\n\nclass QCP_LIB_DECL QCPLayoutGrid : public QCPLayout\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(int rowCount READ rowCount)\n  Q_PROPERTY(int columnCount READ columnCount)\n  Q_PROPERTY(QList<double> columnStretchFactors READ columnStretchFactors WRITE setColumnStretchFactors)\n  Q_PROPERTY(QList<double> rowStretchFactors READ rowStretchFactors WRITE setRowStretchFactors)\n  Q_PROPERTY(int columnSpacing READ columnSpacing WRITE setColumnSpacing)\n  Q_PROPERTY(int rowSpacing READ rowSpacing WRITE setRowSpacing)\n  Q_PROPERTY(FillOrder fillOrder READ fillOrder WRITE setFillOrder)\n  Q_PROPERTY(int wrap READ wrap WRITE setWrap)\n  /// \\endcond\npublic:\n  \n  /*!\n    Defines in which direction the grid is filled when using \\ref addElement(QCPLayoutElement*).\n    The column/row at which wrapping into the next row/column occurs can be specified with \\ref\n    setWrap.\n\n    \\see setFillOrder\n  */\n  enum FillOrder { foRowsFirst    ///< Rows are filled first, and a new element is wrapped to the next column if the row count would exceed \\ref setWrap.\n                  ,foColumnsFirst ///< Columns are filled first, and a new element is wrapped to the next row if the column count would exceed \\ref setWrap.\n                };\n  Q_ENUMS(FillOrder)\n  \n  explicit QCPLayoutGrid();\n  virtual ~QCPLayoutGrid() Q_DECL_OVERRIDE;\n  \n  // getters:\n  int rowCount() const { return mElements.size(); }\n  int columnCount() const { return mElements.size() > 0 ? mElements.first().size() : 0; }\n  QList<double> columnStretchFactors() const { return mColumnStretchFactors; }\n  QList<double> rowStretchFactors() const { return mRowStretchFactors; }\n  int columnSpacing() const { return mColumnSpacing; }\n  int rowSpacing() const { return mRowSpacing; }\n  int wrap() const { return mWrap; }\n  FillOrder fillOrder() const { return mFillOrder; }\n  \n  // setters:\n  void setColumnStretchFactor(int column, double factor);\n  void setColumnStretchFactors(const QList<double> &factors);\n  void setRowStretchFactor(int row, double factor);\n  void setRowStretchFactors(const QList<double> &factors);\n  void setColumnSpacing(int pixels);\n  void setRowSpacing(int pixels);\n  void setWrap(int count);\n  void setFillOrder(FillOrder order, bool rearrange=true);\n  \n  // reimplemented virtual methods:\n  virtual void updateLayout() Q_DECL_OVERRIDE;\n  virtual int elementCount() const Q_DECL_OVERRIDE { return rowCount()*columnCount(); }\n  virtual QCPLayoutElement* elementAt(int index) const Q_DECL_OVERRIDE;\n  virtual QCPLayoutElement* takeAt(int index) Q_DECL_OVERRIDE;\n  virtual bool take(QCPLayoutElement* element) Q_DECL_OVERRIDE;\n  virtual QList<QCPLayoutElement*> elements(bool recursive) const Q_DECL_OVERRIDE;\n  virtual void simplify() Q_DECL_OVERRIDE;\n  virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE;\n  virtual QSize maximumOuterSizeHint() const Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  QCPLayoutElement *element(int row, int column) const;\n  bool addElement(int row, int column, QCPLayoutElement *element);\n  bool addElement(QCPLayoutElement *element);\n  bool hasElement(int row, int column);\n  void expandTo(int newRowCount, int newColumnCount);\n  void insertRow(int newIndex);\n  void insertColumn(int newIndex);\n  int rowColToIndex(int row, int column) const;\n  void indexToRowCol(int index, int &row, int &column) const;\n  \nprotected:\n  // property members:\n  QList<QList<QCPLayoutElement*> > mElements;\n  QList<double> mColumnStretchFactors;\n  QList<double> mRowStretchFactors;\n  int mColumnSpacing, mRowSpacing;\n  int mWrap;\n  FillOrder mFillOrder;\n  \n  // non-virtual methods:\n  void getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const;\n  void getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const;\n  \nprivate:\n  Q_DISABLE_COPY(QCPLayoutGrid)\n};\nQ_DECLARE_METATYPE(QCPLayoutGrid::FillOrder)\n\n\nclass QCP_LIB_DECL QCPLayoutInset : public QCPLayout\n{\n  Q_OBJECT\npublic:\n  /*!\n    Defines how the placement and sizing is handled for a certain element in a QCPLayoutInset.\n  */\n  enum InsetPlacement { ipFree            ///< The element may be positioned/sized arbitrarily, see \\ref setInsetRect\n                        ,ipBorderAligned  ///< The element is aligned to one of the layout sides, see \\ref setInsetAlignment\n                      };\n  Q_ENUMS(InsetPlacement)\n  \n  explicit QCPLayoutInset();\n  virtual ~QCPLayoutInset() Q_DECL_OVERRIDE;\n  \n  // getters:\n  InsetPlacement insetPlacement(int index) const;\n  Qt::Alignment insetAlignment(int index) const;\n  QRectF insetRect(int index) const;\n  \n  // setters:\n  void setInsetPlacement(int index, InsetPlacement placement);\n  void setInsetAlignment(int index, Qt::Alignment alignment);\n  void setInsetRect(int index, const QRectF &rect);\n  \n  // reimplemented virtual methods:\n  virtual void updateLayout() Q_DECL_OVERRIDE;\n  virtual int elementCount() const Q_DECL_OVERRIDE;\n  virtual QCPLayoutElement* elementAt(int index) const Q_DECL_OVERRIDE;\n  virtual QCPLayoutElement* takeAt(int index) Q_DECL_OVERRIDE;\n  virtual bool take(QCPLayoutElement* element) Q_DECL_OVERRIDE;\n  virtual void simplify() Q_DECL_OVERRIDE {}\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  void addElement(QCPLayoutElement *element, Qt::Alignment alignment);\n  void addElement(QCPLayoutElement *element, const QRectF &rect);\n  \nprotected:\n  // property members:\n  QList<QCPLayoutElement*> mElements;\n  QList<InsetPlacement> mInsetPlacement;\n  QList<Qt::Alignment> mInsetAlignment;\n  QList<QRectF> mInsetRect;\n  \nprivate:\n  Q_DISABLE_COPY(QCPLayoutInset)\n};\nQ_DECLARE_METATYPE(QCPLayoutInset::InsetPlacement)\n\n/* end of 'src/layout.h' */\n\n\n/* including file 'src/lineending.h'       */\n/* modified 2021-03-29T02:30:44, size 4426 */\n\nclass QCP_LIB_DECL QCPLineEnding\n{\n  Q_GADGET\npublic:\n  /*!\n    Defines the type of ending decoration for line-like items, e.g. an arrow.\n    \n    \\image html QCPLineEnding.png\n    \n    The width and length of these decorations can be controlled with the functions \\ref setWidth\n    and \\ref setLength. Some decorations like \\ref esDisc, \\ref esSquare, \\ref esDiamond and \\ref esBar only\n    support a width, the length property is ignored.\n    \n    \\see QCPItemLine::setHead, QCPItemLine::setTail, QCPItemCurve::setHead, QCPItemCurve::setTail, QCPAxis::setLowerEnding, QCPAxis::setUpperEnding\n  */\n  enum EndingStyle { esNone          ///< No ending decoration\n                     ,esFlatArrow    ///< A filled arrow head with a straight/flat back (a triangle)\n                     ,esSpikeArrow   ///< A filled arrow head with an indented back\n                     ,esLineArrow    ///< A non-filled arrow head with open back\n                     ,esDisc         ///< A filled circle\n                     ,esSquare       ///< A filled square\n                     ,esDiamond      ///< A filled diamond (45 degrees rotated square)\n                     ,esBar          ///< A bar perpendicular to the line\n                     ,esHalfBar      ///< A bar perpendicular to the line, pointing out to only one side (to which side can be changed with \\ref setInverted)\n                     ,esSkewedBar    ///< A bar that is skewed (skew controllable via \\ref setLength)\n                   };\n  Q_ENUMS(EndingStyle)\n  \n  QCPLineEnding();\n  QCPLineEnding(EndingStyle style, double width=8, double length=10, bool inverted=false);\n  \n  // getters:\n  EndingStyle style() const { return mStyle; }\n  double width() const { return mWidth; }\n  double length() const { return mLength; }\n  bool inverted() const { return mInverted; }\n  \n  // setters:\n  void setStyle(EndingStyle style);\n  void setWidth(double width);\n  void setLength(double length);\n  void setInverted(bool inverted);\n  \n  // non-property methods:\n  double boundingDistance() const;\n  double realLength() const;\n  void draw(QCPPainter *painter, const QCPVector2D &pos, const QCPVector2D &dir) const;\n  void draw(QCPPainter *painter, const QCPVector2D &pos, double angle) const;\n  \nprotected:\n  // property members:\n  EndingStyle mStyle;\n  double mWidth, mLength;\n  bool mInverted;\n};\nQ_DECLARE_TYPEINFO(QCPLineEnding, Q_MOVABLE_TYPE);\nQ_DECLARE_METATYPE(QCPLineEnding::EndingStyle)\n\n/* end of 'src/lineending.h' */\n\n\n/* including file 'src/axis/labelpainter.h' */\n/* modified 2021-03-29T02:30:44, size 7086  */\n\nclass QCPLabelPainterPrivate\n{\n  Q_GADGET\npublic:\n  /*!\n    TODO\n  */\n  enum AnchorMode { amRectangular    ///< \n                    ,amSkewedUpright ///<\n                    ,amSkewedRotated ///<\n                   };\n  Q_ENUMS(AnchorMode)\n  \n  /*!\n    TODO\n  */\n  enum AnchorReferenceType { artNormal    ///< \n                             ,artTangent ///<\n                           };\n  Q_ENUMS(AnchorReferenceType)\n  \n  /*!\n    TODO\n  */\n  enum AnchorSide { asLeft      ///< \n                    ,asRight    ///< \n                    ,asTop      ///< \n                    ,asBottom   ///< \n                    ,asTopLeft\n                    ,asTopRight\n                    ,asBottomRight\n                    ,asBottomLeft\n                   };\n  Q_ENUMS(AnchorSide)\n  \n  explicit QCPLabelPainterPrivate(QCustomPlot *parentPlot);\n  virtual ~QCPLabelPainterPrivate();\n  \n  // setters:\n  void setAnchorSide(AnchorSide side);\n  void setAnchorMode(AnchorMode mode);\n  void setAnchorReference(const QPointF &pixelPoint);\n  void setAnchorReferenceType(AnchorReferenceType type);\n  void setFont(const QFont &font);\n  void setColor(const QColor &color);\n  void setPadding(int padding);\n  void setRotation(double rotation);\n  void setSubstituteExponent(bool enabled);\n  void setMultiplicationSymbol(QChar symbol);\n  void setAbbreviateDecimalPowers(bool enabled);\n  void setCacheSize(int labelCount);\n  \n  // getters:\n  AnchorMode anchorMode() const { return mAnchorMode; }\n  AnchorSide anchorSide() const { return mAnchorSide; }\n  QPointF anchorReference() const { return mAnchorReference; }\n  AnchorReferenceType anchorReferenceType() const { return mAnchorReferenceType; }\n  QFont font() const { return mFont; }\n  QColor color() const { return mColor; }\n  int padding() const { return mPadding; }\n  double rotation() const { return mRotation; }\n  bool substituteExponent() const { return mSubstituteExponent; }\n  QChar multiplicationSymbol() const { return mMultiplicationSymbol; }\n  bool abbreviateDecimalPowers() const { return mAbbreviateDecimalPowers; }\n  int cacheSize() const;\n  \n  //virtual int size() const;\n  \n  // non-property methods: \n  void drawTickLabel(QCPPainter *painter, const QPointF &tickPos, const QString &text);\n  void clearCache();\n  \n  // constants that may be used with setMultiplicationSymbol:\n  static const QChar SymbolDot;\n  static const QChar SymbolCross;\n  \nprotected:\n  struct CachedLabel\n  {\n    QPoint offset;\n    QPixmap pixmap;\n  };\n  struct LabelData\n  {\n    AnchorSide side;\n    double rotation; // angle in degrees\n    QTransform transform; // the transform about the label anchor which is at (0, 0). Does not contain final absolute x/y positioning on the plot/axis\n    QString basePart, expPart, suffixPart;\n    QRect baseBounds, expBounds, suffixBounds;\n    QRect totalBounds; // is in a coordinate system where label top left is at (0, 0)\n    QRect rotatedTotalBounds; // is in a coordinate system where the label anchor is at (0, 0)\n    QFont baseFont, expFont;\n    QColor color;\n  };\n  \n  // property members:\n  AnchorMode mAnchorMode;\n  AnchorSide mAnchorSide;\n  QPointF mAnchorReference;\n  AnchorReferenceType mAnchorReferenceType;\n  QFont mFont;\n  QColor mColor;\n  int mPadding;\n  double mRotation; // this is the rotation applied uniformly to all labels, not the heterogeneous rotation in amCircularRotated mode\n  bool mSubstituteExponent;\n  QChar mMultiplicationSymbol;\n  bool mAbbreviateDecimalPowers;\n  // non-property members:\n  QCustomPlot *mParentPlot;\n  QByteArray mLabelParameterHash; // to determine whether mLabelCache needs to be cleared due to changed parameters\n  QCache<QString, CachedLabel> mLabelCache;\n  QRect mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox;\n  int mLetterCapHeight, mLetterDescent;\n  \n  // introduced virtual methods:\n  virtual void drawLabelMaybeCached(QCPPainter *painter, const QFont &font, const QColor &color, const QPointF &pos, AnchorSide side, double rotation, const QString &text);\n  virtual QByteArray generateLabelParameterHash() const; // TODO: get rid of this in favor of invalidation flag upon setters?\n\n  // non-virtual methods:\n  QPointF getAnchorPos(const QPointF &tickPos);\n  void drawText(QCPPainter *painter, const QPointF &pos, const LabelData &labelData) const;\n  LabelData getTickLabelData(const QFont &font, const QColor &color, double rotation, AnchorSide side, const QString &text) const;\n  void applyAnchorTransform(LabelData &labelData) const;\n  //void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const;\n  CachedLabel *createCachedLabel(const LabelData &labelData) const;\n  QByteArray cacheKey(const QString &text, const QColor &color, double rotation, AnchorSide side) const;\n  AnchorSide skewedAnchorSide(const QPointF &tickPos, double sideExpandHorz, double sideExpandVert) const;\n  AnchorSide rotationCorrectedSide(AnchorSide side, double rotation) const;\n  void analyzeFontMetrics();\n};\nQ_DECLARE_METATYPE(QCPLabelPainterPrivate::AnchorMode)\nQ_DECLARE_METATYPE(QCPLabelPainterPrivate::AnchorSide)\n\n\n/* end of 'src/axis/labelpainter.h' */\n\n\n/* including file 'src/axis/axisticker.h'  */\n/* modified 2021-03-29T02:30:44, size 4230 */\n\nclass QCP_LIB_DECL QCPAxisTicker\n{\n  Q_GADGET\npublic:\n  /*!\n    Defines the strategies that the axis ticker may follow when choosing the size of the tick step.\n    \n    \\see setTickStepStrategy\n  */\n  enum TickStepStrategy\n  {\n    tssReadability    ///< A nicely readable tick step is prioritized over matching the requested number of ticks (see \\ref setTickCount)\n    ,tssMeetTickCount ///< Less readable tick steps are allowed which in turn facilitates getting closer to the requested tick count\n  };\n  Q_ENUMS(TickStepStrategy)\n  \n  QCPAxisTicker();\n  virtual ~QCPAxisTicker();\n  \n  // getters:\n  TickStepStrategy tickStepStrategy() const { return mTickStepStrategy; }\n  int tickCount() const { return mTickCount; }\n  double tickOrigin() const { return mTickOrigin; }\n  \n  // setters:\n  void setTickStepStrategy(TickStepStrategy strategy);\n  void setTickCount(int count);\n  void setTickOrigin(double origin);\n  \n  // introduced virtual methods:\n  virtual void generate(const QCPRange &range, const QLocale &locale, QChar formatChar, int precision, QVector<double> &ticks, QVector<double> *subTicks, QVector<QString> *tickLabels);\n  \nprotected:\n  // property members:\n  TickStepStrategy mTickStepStrategy;\n  int mTickCount;\n  double mTickOrigin;\n  \n  // introduced virtual methods:\n  virtual double getTickStep(const QCPRange &range);\n  virtual int getSubTickCount(double tickStep);\n  virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision);\n  virtual QVector<double> createTickVector(double tickStep, const QCPRange &range);\n  virtual QVector<double> createSubTickVector(int subTickCount, const QVector<double> &ticks);\n  virtual QVector<QString> createLabelVector(const QVector<double> &ticks, const QLocale &locale, QChar formatChar, int precision);\n  \n  // non-virtual methods:\n  void trimTicks(const QCPRange &range, QVector<double> &ticks, bool keepOneOutlier) const;\n  double pickClosest(double target, const QVector<double> &candidates) const;\n  double getMantissa(double input, double *magnitude=nullptr) const;\n  double cleanMantissa(double input) const;\n  \nprivate:\n  Q_DISABLE_COPY(QCPAxisTicker)\n  \n};\nQ_DECLARE_METATYPE(QCPAxisTicker::TickStepStrategy)\nQ_DECLARE_METATYPE(QSharedPointer<QCPAxisTicker>)\n\n/* end of 'src/axis/axisticker.h' */\n\n\n/* including file 'src/axis/axistickerdatetime.h' */\n/* modified 2021-03-29T02:30:44, size 3600        */\n\nclass QCP_LIB_DECL QCPAxisTickerDateTime : public QCPAxisTicker\n{\npublic:\n  QCPAxisTickerDateTime();\n  \n  // getters:\n  QString dateTimeFormat() const { return mDateTimeFormat; }\n  Qt::TimeSpec dateTimeSpec() const { return mDateTimeSpec; }\n# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)\n  QTimeZone timeZone() const { return mTimeZone; }\n#endif\n  \n  // setters:\n  void setDateTimeFormat(const QString &format);\n  void setDateTimeSpec(Qt::TimeSpec spec);\n# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)\n  void setTimeZone(const QTimeZone &zone);\n# endif\n  void setTickOrigin(double origin); // hides base class method but calls baseclass implementation (\"using\" throws off IDEs and doxygen)\n  void setTickOrigin(const QDateTime &origin);\n  \n  // static methods:\n  static QDateTime keyToDateTime(double key);\n  static double dateTimeToKey(const QDateTime &dateTime);\n  static double dateTimeToKey(const QDate &date, Qt::TimeSpec timeSpec=Qt::LocalTime);\n  \nprotected:\n  // property members:\n  QString mDateTimeFormat;\n  Qt::TimeSpec mDateTimeSpec;\n# if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)\n  QTimeZone mTimeZone;\n# endif\n  // non-property members:\n  enum DateStrategy {dsNone, dsUniformTimeInDay, dsUniformDayInMonth} mDateStrategy;\n  \n  // reimplemented virtual methods:\n  virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE;\n  virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE;\n  virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE;\n  virtual QVector<double> createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE;\n};\n\n/* end of 'src/axis/axistickerdatetime.h' */\n\n\n/* including file 'src/axis/axistickertime.h' */\n/* modified 2021-03-29T02:30:44, size 3542    */\n\nclass QCP_LIB_DECL QCPAxisTickerTime : public QCPAxisTicker\n{\n  Q_GADGET\npublic:\n  /*!\n    Defines the logical units in which fractions of time spans can be expressed.\n    \n    \\see setFieldWidth, setTimeFormat\n  */\n  enum TimeUnit { tuMilliseconds ///< Milliseconds, one thousandth of a second (%%z in \\ref setTimeFormat)\n                  ,tuSeconds     ///< Seconds (%%s in \\ref setTimeFormat)\n                  ,tuMinutes     ///< Minutes (%%m in \\ref setTimeFormat)\n                  ,tuHours       ///< Hours (%%h in \\ref setTimeFormat)\n                  ,tuDays        ///< Days (%%d in \\ref setTimeFormat)\n                };\n  Q_ENUMS(TimeUnit)\n  \n  QCPAxisTickerTime();\n\n  // getters:\n  QString timeFormat() const { return mTimeFormat; }\n  int fieldWidth(TimeUnit unit) const { return mFieldWidth.value(unit); }\n  \n  // setters:\n  void setTimeFormat(const QString &format);\n  void setFieldWidth(TimeUnit unit, int width);\n  \nprotected:\n  // property members:\n  QString mTimeFormat;\n  QHash<TimeUnit, int> mFieldWidth;\n  \n  // non-property members:\n  TimeUnit mSmallestUnit, mBiggestUnit;\n  QHash<TimeUnit, QString> mFormatPattern;\n  \n  // reimplemented virtual methods:\n  virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE;\n  virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE;\n  virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  void replaceUnit(QString &text, TimeUnit unit, int value) const;\n};\nQ_DECLARE_METATYPE(QCPAxisTickerTime::TimeUnit)\n\n/* end of 'src/axis/axistickertime.h' */\n\n\n/* including file 'src/axis/axistickerfixed.h' */\n/* modified 2021-03-29T02:30:44, size 3308     */\n\nclass QCP_LIB_DECL QCPAxisTickerFixed : public QCPAxisTicker\n{\n  Q_GADGET\npublic:\n  /*!\n    Defines how the axis ticker may modify the specified tick step (\\ref setTickStep) in order to\n    control the number of ticks in the axis range.\n    \n    \\see setScaleStrategy\n  */\n  enum ScaleStrategy { ssNone      ///< Modifications are not allowed, the specified tick step is absolutely fixed. This might cause a high tick density and overlapping labels if the axis range is zoomed out.\n                       ,ssMultiples ///< An integer multiple of the specified tick step is allowed. The used factor follows the base class properties of \\ref setTickStepStrategy and \\ref setTickCount.\n                       ,ssPowers    ///< An integer power of the specified tick step is allowed.\n                     };\n  Q_ENUMS(ScaleStrategy)\n  \n  QCPAxisTickerFixed();\n  \n  // getters:\n  double tickStep() const { return mTickStep; }\n  ScaleStrategy scaleStrategy() const { return mScaleStrategy; }\n  \n  // setters:\n  void setTickStep(double step);\n  void setScaleStrategy(ScaleStrategy strategy);\n  \nprotected:\n  // property members:\n  double mTickStep;\n  ScaleStrategy mScaleStrategy;\n  \n  // reimplemented virtual methods:\n  virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE;\n};\nQ_DECLARE_METATYPE(QCPAxisTickerFixed::ScaleStrategy)\n\n/* end of 'src/axis/axistickerfixed.h' */\n\n\n/* including file 'src/axis/axistickertext.h' */\n/* modified 2021-03-29T02:30:44, size 3090    */\n\nclass QCP_LIB_DECL QCPAxisTickerText : public QCPAxisTicker\n{\npublic:\n  QCPAxisTickerText();\n  \n  // getters:\n  QMap<double, QString> &ticks() { return mTicks; }\n  int subTickCount() const { return mSubTickCount; }\n  \n  // setters:\n  void setTicks(const QMap<double, QString> &ticks);\n  void setTicks(const QVector<double> &positions, const QVector<QString> &labels);\n  void setSubTickCount(int subTicks);\n  \n  // non-virtual methods:\n  void clear();\n  void addTick(double position, const QString &label);\n  void addTicks(const QMap<double, QString> &ticks);\n  void addTicks(const QVector<double> &positions, const QVector<QString> &labels);\n  \nprotected:\n  // property members:\n  QMap<double, QString> mTicks;\n  int mSubTickCount;\n  \n  // reimplemented virtual methods:\n  virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE;\n  virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE;\n  virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE;\n  virtual QVector<double> createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE;\n};\n\n/* end of 'src/axis/axistickertext.h' */\n\n\n/* including file 'src/axis/axistickerpi.h' */\n/* modified 2021-03-29T02:30:44, size 3911  */\n\nclass QCP_LIB_DECL QCPAxisTickerPi : public QCPAxisTicker\n{\n  Q_GADGET\npublic:\n  /*!\n    Defines how fractions should be displayed in tick labels.\n    \n    \\see setFractionStyle\n  */\n  enum FractionStyle { fsFloatingPoint     ///< Fractions are displayed as regular decimal floating point numbers, e.g. \"0.25\" or \"0.125\".\n                       ,fsAsciiFractions   ///< Fractions are written as rationals using ASCII characters only, e.g. \"1/4\" or \"1/8\"\n                       ,fsUnicodeFractions ///< Fractions are written using sub- and superscript UTF-8 digits and the fraction symbol.\n                     };\n  Q_ENUMS(FractionStyle)\n  \n  QCPAxisTickerPi();\n  \n  // getters:\n  QString piSymbol() const { return mPiSymbol; }\n  double piValue() const { return mPiValue; }\n  bool periodicity() const { return mPeriodicity; }\n  FractionStyle fractionStyle() const { return mFractionStyle; }\n  \n  // setters:\n  void setPiSymbol(QString symbol);\n  void setPiValue(double pi);\n  void setPeriodicity(int multiplesOfPi);\n  void setFractionStyle(FractionStyle style);\n  \nprotected:\n  // property members:\n  QString mPiSymbol;\n  double mPiValue;\n  int mPeriodicity;\n  FractionStyle mFractionStyle;\n  \n  // non-property members:\n  double mPiTickStep; // size of one tick step in units of mPiValue\n  \n  // reimplemented virtual methods:\n  virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE;\n  virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE;\n  virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  void simplifyFraction(int &numerator, int &denominator) const;\n  QString fractionToString(int numerator, int denominator) const;\n  QString unicodeFraction(int numerator, int denominator) const;\n  QString unicodeSuperscript(int number) const;\n  QString unicodeSubscript(int number) const;\n};\nQ_DECLARE_METATYPE(QCPAxisTickerPi::FractionStyle)\n\n/* end of 'src/axis/axistickerpi.h' */\n\n\n/* including file 'src/axis/axistickerlog.h' */\n/* modified 2021-03-29T02:30:44, size 2594   */\n\nclass QCP_LIB_DECL QCPAxisTickerLog : public QCPAxisTicker\n{\npublic:\n  QCPAxisTickerLog();\n  \n  // getters:\n  double logBase() const { return mLogBase; }\n  int subTickCount() const { return mSubTickCount; }\n  \n  // setters:\n  void setLogBase(double base);\n  void setSubTickCount(int subTicks);\n  \nprotected:\n  // property members:\n  double mLogBase;\n  int mSubTickCount;\n  \n  // non-property members:\n  double mLogBaseLnInv;\n  \n  // reimplemented virtual methods:\n  virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE;\n  virtual QVector<double> createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE;\n};\n\n/* end of 'src/axis/axistickerlog.h' */\n\n\n/* including file 'src/axis/axis.h'         */\n/* modified 2021-03-29T02:30:44, size 20913 */\n\nclass QCP_LIB_DECL QCPGrid :public QCPLayerable\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(bool subGridVisible READ subGridVisible WRITE setSubGridVisible)\n  Q_PROPERTY(bool antialiasedSubGrid READ antialiasedSubGrid WRITE setAntialiasedSubGrid)\n  Q_PROPERTY(bool antialiasedZeroLine READ antialiasedZeroLine WRITE setAntialiasedZeroLine)\n  Q_PROPERTY(QPen pen READ pen WRITE setPen)\n  Q_PROPERTY(QPen subGridPen READ subGridPen WRITE setSubGridPen)\n  Q_PROPERTY(QPen zeroLinePen READ zeroLinePen WRITE setZeroLinePen)\n  /// \\endcond\npublic:\n  explicit QCPGrid(QCPAxis *parentAxis);\n  \n  // getters:\n  bool subGridVisible() const { return mSubGridVisible; }\n  bool antialiasedSubGrid() const { return mAntialiasedSubGrid; }\n  bool antialiasedZeroLine() const { return mAntialiasedZeroLine; }\n  QPen pen() const { return mPen; }\n  QPen subGridPen() const { return mSubGridPen; }\n  QPen zeroLinePen() const { return mZeroLinePen; }\n  \n  // setters:\n  void setSubGridVisible(bool visible);\n  void setAntialiasedSubGrid(bool enabled);\n  void setAntialiasedZeroLine(bool enabled);\n  void setPen(const QPen &pen);\n  void setSubGridPen(const QPen &pen);\n  void setZeroLinePen(const QPen &pen);\n  \nprotected:\n  // property members:\n  bool mSubGridVisible;\n  bool mAntialiasedSubGrid, mAntialiasedZeroLine;\n  QPen mPen, mSubGridPen, mZeroLinePen;\n  \n  // non-property members:\n  QCPAxis *mParentAxis;\n  \n  // reimplemented virtual methods:\n  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  void drawGridLines(QCPPainter *painter) const;\n  void drawSubGridLines(QCPPainter *painter) const;\n  \n  friend class QCPAxis;\n};\n\n\nclass QCP_LIB_DECL QCPAxis : public QCPLayerable\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(AxisType axisType READ axisType)\n  Q_PROPERTY(QCPAxisRect* axisRect READ axisRect)\n  Q_PROPERTY(ScaleType scaleType READ scaleType WRITE setScaleType NOTIFY scaleTypeChanged)\n  Q_PROPERTY(QCPRange range READ range WRITE setRange NOTIFY rangeChanged)\n  Q_PROPERTY(bool rangeReversed READ rangeReversed WRITE setRangeReversed)\n  Q_PROPERTY(QSharedPointer<QCPAxisTicker> ticker READ ticker WRITE setTicker)\n  Q_PROPERTY(bool ticks READ ticks WRITE setTicks)\n  Q_PROPERTY(bool tickLabels READ tickLabels WRITE setTickLabels)\n  Q_PROPERTY(int tickLabelPadding READ tickLabelPadding WRITE setTickLabelPadding)\n  Q_PROPERTY(QFont tickLabelFont READ tickLabelFont WRITE setTickLabelFont)\n  Q_PROPERTY(QColor tickLabelColor READ tickLabelColor WRITE setTickLabelColor)\n  Q_PROPERTY(double tickLabelRotation READ tickLabelRotation WRITE setTickLabelRotation)\n  Q_PROPERTY(LabelSide tickLabelSide READ tickLabelSide WRITE setTickLabelSide)\n  Q_PROPERTY(QString numberFormat READ numberFormat WRITE setNumberFormat)\n  Q_PROPERTY(int numberPrecision READ numberPrecision WRITE setNumberPrecision)\n  Q_PROPERTY(QVector<double> tickVector READ tickVector)\n  Q_PROPERTY(QVector<QString> tickVectorLabels READ tickVectorLabels)\n  Q_PROPERTY(int tickLengthIn READ tickLengthIn WRITE setTickLengthIn)\n  Q_PROPERTY(int tickLengthOut READ tickLengthOut WRITE setTickLengthOut)\n  Q_PROPERTY(bool subTicks READ subTicks WRITE setSubTicks)\n  Q_PROPERTY(int subTickLengthIn READ subTickLengthIn WRITE setSubTickLengthIn)\n  Q_PROPERTY(int subTickLengthOut READ subTickLengthOut WRITE setSubTickLengthOut)\n  Q_PROPERTY(QPen basePen READ basePen WRITE setBasePen)\n  Q_PROPERTY(QPen tickPen READ tickPen WRITE setTickPen)\n  Q_PROPERTY(QPen subTickPen READ subTickPen WRITE setSubTickPen)\n  Q_PROPERTY(QFont labelFont READ labelFont WRITE setLabelFont)\n  Q_PROPERTY(QColor labelColor READ labelColor WRITE setLabelColor)\n  Q_PROPERTY(QString label READ label WRITE setLabel)\n  Q_PROPERTY(int labelPadding READ labelPadding WRITE setLabelPadding)\n  Q_PROPERTY(int padding READ padding WRITE setPadding)\n  Q_PROPERTY(int offset READ offset WRITE setOffset)\n  Q_PROPERTY(SelectableParts selectedParts READ selectedParts WRITE setSelectedParts NOTIFY selectionChanged)\n  Q_PROPERTY(SelectableParts selectableParts READ selectableParts WRITE setSelectableParts NOTIFY selectableChanged)\n  Q_PROPERTY(QFont selectedTickLabelFont READ selectedTickLabelFont WRITE setSelectedTickLabelFont)\n  Q_PROPERTY(QFont selectedLabelFont READ selectedLabelFont WRITE setSelectedLabelFont)\n  Q_PROPERTY(QColor selectedTickLabelColor READ selectedTickLabelColor WRITE setSelectedTickLabelColor)\n  Q_PROPERTY(QColor selectedLabelColor READ selectedLabelColor WRITE setSelectedLabelColor)\n  Q_PROPERTY(QPen selectedBasePen READ selectedBasePen WRITE setSelectedBasePen)\n  Q_PROPERTY(QPen selectedTickPen READ selectedTickPen WRITE setSelectedTickPen)\n  Q_PROPERTY(QPen selectedSubTickPen READ selectedSubTickPen WRITE setSelectedSubTickPen)\n  Q_PROPERTY(QCPLineEnding lowerEnding READ lowerEnding WRITE setLowerEnding)\n  Q_PROPERTY(QCPLineEnding upperEnding READ upperEnding WRITE setUpperEnding)\n  Q_PROPERTY(QCPGrid* grid READ grid)\n  /// \\endcond\npublic:\n  /*!\n    Defines at which side of the axis rect the axis will appear. This also affects how the tick\n    marks are drawn, on which side the labels are placed etc.\n  */\n  enum AxisType { atLeft    = 0x01  ///< <tt>0x01</tt> Axis is vertical and on the left side of the axis rect\n                  ,atRight  = 0x02  ///< <tt>0x02</tt> Axis is vertical and on the right side of the axis rect\n                  ,atTop    = 0x04  ///< <tt>0x04</tt> Axis is horizontal and on the top side of the axis rect\n                  ,atBottom = 0x08  ///< <tt>0x08</tt> Axis is horizontal and on the bottom side of the axis rect\n                };\n  Q_ENUMS(AxisType)\n  Q_FLAGS(AxisTypes)\n  Q_DECLARE_FLAGS(AxisTypes, AxisType)\n  /*!\n    Defines on which side of the axis the tick labels (numbers) shall appear.\n    \n    \\see setTickLabelSide\n  */\n  enum LabelSide { lsInside    ///< Tick labels will be displayed inside the axis rect and clipped to the inner axis rect\n                   ,lsOutside  ///< Tick labels will be displayed outside the axis rect\n                 };\n  Q_ENUMS(LabelSide)\n  /*!\n    Defines the scale of an axis.\n    \\see setScaleType\n  */\n  enum ScaleType { stLinear       ///< Linear scaling\n                   ,stLogarithmic ///< Logarithmic scaling with correspondingly transformed axis coordinates (possibly also \\ref setTicker to a \\ref QCPAxisTickerLog instance).\n                 };\n  Q_ENUMS(ScaleType)\n  /*!\n    Defines the selectable parts of an axis.\n    \\see setSelectableParts, setSelectedParts\n  */\n  enum SelectablePart { spNone        = 0      ///< None of the selectable parts\n                        ,spAxis       = 0x001  ///< The axis backbone and tick marks\n                        ,spTickLabels = 0x002  ///< Tick labels (numbers) of this axis (as a whole, not individually)\n                        ,spAxisLabel  = 0x004  ///< The axis label\n                      };\n  Q_ENUMS(SelectablePart)\n  Q_FLAGS(SelectableParts)\n  Q_DECLARE_FLAGS(SelectableParts, SelectablePart)\n  \n  explicit QCPAxis(QCPAxisRect *parent, AxisType type);\n  virtual ~QCPAxis() Q_DECL_OVERRIDE;\n  \n  // getters:\n  AxisType axisType() const { return mAxisType; }\n  QCPAxisRect *axisRect() const { return mAxisRect; }\n  ScaleType scaleType() const { return mScaleType; }\n  const QCPRange range() const { return mRange; }\n  bool rangeReversed() const { return mRangeReversed; }\n  QSharedPointer<QCPAxisTicker> ticker() const { return mTicker; }\n  bool ticks() const { return mTicks; }\n  bool tickLabels() const { return mTickLabels; }\n  int tickLabelPadding() const;\n  QFont tickLabelFont() const { return mTickLabelFont; }\n  QColor tickLabelColor() const { return mTickLabelColor; }\n  double tickLabelRotation() const;\n  LabelSide tickLabelSide() const;\n  QString numberFormat() const;\n  int numberPrecision() const { return mNumberPrecision; }\n  QVector<double> tickVector() const { return mTickVector; }\n  QVector<QString> tickVectorLabels() const { return mTickVectorLabels; }\n  int tickLengthIn() const;\n  int tickLengthOut() const;\n  bool subTicks() const { return mSubTicks; }\n  int subTickLengthIn() const;\n  int subTickLengthOut() const;\n  QPen basePen() const { return mBasePen; }\n  QPen tickPen() const { return mTickPen; }\n  QPen subTickPen() const { return mSubTickPen; }\n  QFont labelFont() const { return mLabelFont; }\n  QColor labelColor() const { return mLabelColor; }\n  QString label() const { return mLabel; }\n  int labelPadding() const;\n  int padding() const { return mPadding; }\n  int offset() const;\n  SelectableParts selectedParts() const { return mSelectedParts; }\n  SelectableParts selectableParts() const { return mSelectableParts; }\n  QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; }\n  QFont selectedLabelFont() const { return mSelectedLabelFont; }\n  QColor selectedTickLabelColor() const { return mSelectedTickLabelColor; }\n  QColor selectedLabelColor() const { return mSelectedLabelColor; }\n  QPen selectedBasePen() const { return mSelectedBasePen; }\n  QPen selectedTickPen() const { return mSelectedTickPen; }\n  QPen selectedSubTickPen() const { return mSelectedSubTickPen; }\n  QCPLineEnding lowerEnding() const;\n  QCPLineEnding upperEnding() const;\n  QCPGrid *grid() const { return mGrid; }\n  \n  // setters:\n  Q_SLOT void setScaleType(QCPAxis::ScaleType type);\n  Q_SLOT void setRange(const QCPRange &range);\n  void setRange(double lower, double upper);\n  void setRange(double position, double size, Qt::AlignmentFlag alignment);\n  void setRangeLower(double lower);\n  void setRangeUpper(double upper);\n  void setRangeReversed(bool reversed);\n  void setTicker(QSharedPointer<QCPAxisTicker> ticker);\n  void setTicks(bool show);\n  void setTickLabels(bool show);\n  void setTickLabelPadding(int padding);\n  void setTickLabelFont(const QFont &font);\n  void setTickLabelColor(const QColor &color);\n  void setTickLabelRotation(double degrees);\n  void setTickLabelSide(LabelSide side);\n  void setNumberFormat(const QString &formatCode);\n  void setNumberPrecision(int precision);\n  void setTickLength(int inside, int outside=0);\n  void setTickLengthIn(int inside);\n  void setTickLengthOut(int outside);\n  void setSubTicks(bool show);\n  void setSubTickLength(int inside, int outside=0);\n  void setSubTickLengthIn(int inside);\n  void setSubTickLengthOut(int outside);\n  void setBasePen(const QPen &pen);\n  void setTickPen(const QPen &pen);\n  void setSubTickPen(const QPen &pen);\n  void setLabelFont(const QFont &font);\n  void setLabelColor(const QColor &color);\n  void setLabel(const QString &str);\n  void setLabelPadding(int padding);\n  void setPadding(int padding);\n  void setOffset(int offset);\n  void setSelectedTickLabelFont(const QFont &font);\n  void setSelectedLabelFont(const QFont &font);\n  void setSelectedTickLabelColor(const QColor &color);\n  void setSelectedLabelColor(const QColor &color);\n  void setSelectedBasePen(const QPen &pen);\n  void setSelectedTickPen(const QPen &pen);\n  void setSelectedSubTickPen(const QPen &pen);\n  Q_SLOT void setSelectableParts(const QCPAxis::SelectableParts &selectableParts);\n  Q_SLOT void setSelectedParts(const QCPAxis::SelectableParts &selectedParts);\n  void setLowerEnding(const QCPLineEnding &ending);\n  void setUpperEnding(const QCPLineEnding &ending);\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  \n  // non-property methods:\n  Qt::Orientation orientation() const { return mOrientation; }\n  int pixelOrientation() const { return rangeReversed() != (orientation()==Qt::Vertical) ? -1 : 1; }\n  void moveRange(double diff);\n  void scaleRange(double factor);\n  void scaleRange(double factor, double center);\n  void setScaleRatio(const QCPAxis *otherAxis, double ratio=1.0);\n  void rescale(bool onlyVisiblePlottables=false);\n  double pixelToCoord(double value) const;\n  double coordToPixel(double value) const;\n  SelectablePart getPartAt(const QPointF &pos) const;\n  QList<QCPAbstractPlottable*> plottables() const;\n  QList<QCPGraph*> graphs() const;\n  QList<QCPAbstractItem*> items() const;\n  \n  static AxisType marginSideToAxisType(QCP::MarginSide side);\n  static Qt::Orientation orientation(AxisType type) { return type==atBottom || type==atTop ? Qt::Horizontal : Qt::Vertical; }\n  static AxisType opposite(AxisType type);\n  \nsignals:\n  void rangeChanged(const QCPRange &newRange);\n  void rangeChanged(const QCPRange &newRange, const QCPRange &oldRange);\n  void scaleTypeChanged(QCPAxis::ScaleType scaleType);\n  void selectionChanged(const QCPAxis::SelectableParts &parts);\n  void selectableChanged(const QCPAxis::SelectableParts &parts);\n\nprotected:\n  // property members:\n  // axis base:\n  AxisType mAxisType;\n  QCPAxisRect *mAxisRect;\n  //int mOffset; // in QCPAxisPainter\n  int mPadding;\n  Qt::Orientation mOrientation;\n  SelectableParts mSelectableParts, mSelectedParts;\n  QPen mBasePen, mSelectedBasePen;\n  //QCPLineEnding mLowerEnding, mUpperEnding; // in QCPAxisPainter\n  // axis label:\n  //int mLabelPadding; // in QCPAxisPainter\n  QString mLabel;\n  QFont mLabelFont, mSelectedLabelFont;\n  QColor mLabelColor, mSelectedLabelColor;\n  // tick labels:\n  //int mTickLabelPadding; // in QCPAxisPainter\n  bool mTickLabels;\n  //double mTickLabelRotation; // in QCPAxisPainter\n  QFont mTickLabelFont, mSelectedTickLabelFont;\n  QColor mTickLabelColor, mSelectedTickLabelColor;\n  int mNumberPrecision;\n  QLatin1Char mNumberFormatChar;\n  bool mNumberBeautifulPowers;\n  //bool mNumberMultiplyCross; // QCPAxisPainter\n  // ticks and subticks:\n  bool mTicks;\n  bool mSubTicks;\n  //int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut; // QCPAxisPainter\n  QPen mTickPen, mSelectedTickPen;\n  QPen mSubTickPen, mSelectedSubTickPen;\n  // scale and range:\n  QCPRange mRange;\n  bool mRangeReversed;\n  ScaleType mScaleType;\n  \n  // non-property members:\n  QCPGrid *mGrid;\n  QCPAxisPainterPrivate *mAxisPainter;\n  QSharedPointer<QCPAxisTicker> mTicker;\n  QVector<double> mTickVector;\n  QVector<QString> mTickVectorLabels;\n  QVector<double> mSubTickVector;\n  bool mCachedMarginValid;\n  int mCachedMargin;\n  bool mDragging;\n  QCPRange mDragStartRange;\n  QCP::AntialiasedElements mAADragBackup, mNotAADragBackup;\n  \n  // introduced virtual methods:\n  virtual int calculateMargin();\n  \n  // reimplemented virtual methods:\n  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE;\n  // events:\n  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE;\n  virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE;\n  // mouse events:\n  virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE;\n  virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;\n  virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;\n  virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  void setupTickVectors();\n  QPen getBasePen() const;\n  QPen getTickPen() const;\n  QPen getSubTickPen() const;\n  QFont getTickLabelFont() const;\n  QFont getLabelFont() const;\n  QColor getTickLabelColor() const;\n  QColor getLabelColor() const;\n  \nprivate:\n  Q_DISABLE_COPY(QCPAxis)\n  \n  friend class QCustomPlot;\n  friend class QCPGrid;\n  friend class QCPAxisRect;\n};\nQ_DECLARE_OPERATORS_FOR_FLAGS(QCPAxis::SelectableParts)\nQ_DECLARE_OPERATORS_FOR_FLAGS(QCPAxis::AxisTypes)\nQ_DECLARE_METATYPE(QCPAxis::AxisType)\nQ_DECLARE_METATYPE(QCPAxis::LabelSide)\nQ_DECLARE_METATYPE(QCPAxis::ScaleType)\nQ_DECLARE_METATYPE(QCPAxis::SelectablePart)\n\n\nclass QCPAxisPainterPrivate\n{\npublic:\n  explicit QCPAxisPainterPrivate(QCustomPlot *parentPlot);\n  virtual ~QCPAxisPainterPrivate();\n  \n  virtual void draw(QCPPainter *painter);\n  virtual int size();\n  void clearCache();\n  \n  QRect axisSelectionBox() const { return mAxisSelectionBox; }\n  QRect tickLabelsSelectionBox() const { return mTickLabelsSelectionBox; }\n  QRect labelSelectionBox() const { return mLabelSelectionBox; }\n  \n  // public property members:\n  QCPAxis::AxisType type;\n  QPen basePen;\n  QCPLineEnding lowerEnding, upperEnding; // directly accessed by QCPAxis setters/getters\n  int labelPadding; // directly accessed by QCPAxis setters/getters\n  QFont labelFont;\n  QColor labelColor;\n  QString label;\n  int tickLabelPadding; // directly accessed by QCPAxis setters/getters\n  double tickLabelRotation; // directly accessed by QCPAxis setters/getters\n  QCPAxis::LabelSide tickLabelSide; // directly accessed by QCPAxis setters/getters\n  bool substituteExponent;\n  bool numberMultiplyCross; // directly accessed by QCPAxis setters/getters\n  int tickLengthIn, tickLengthOut, subTickLengthIn, subTickLengthOut; // directly accessed by QCPAxis setters/getters\n  QPen tickPen, subTickPen;\n  QFont tickLabelFont;\n  QColor tickLabelColor;\n  QRect axisRect, viewportRect;\n  int offset; // directly accessed by QCPAxis setters/getters\n  bool abbreviateDecimalPowers;\n  bool reversedEndings;\n  \n  QVector<double> subTickPositions;\n  QVector<double> tickPositions;\n  QVector<QString> tickLabels;\n  \nprotected:\n  struct CachedLabel\n  {\n    QPointF offset;\n    QPixmap pixmap;\n  };\n  struct TickLabelData\n  {\n    QString basePart, expPart, suffixPart;\n    QRect baseBounds, expBounds, suffixBounds, totalBounds, rotatedTotalBounds;\n    QFont baseFont, expFont;\n  };\n  QCustomPlot *mParentPlot;\n  QByteArray mLabelParameterHash; // to determine whether mLabelCache needs to be cleared due to changed parameters\n  QCache<QString, CachedLabel> mLabelCache;\n  QRect mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox;\n  \n  virtual QByteArray generateLabelParameterHash() const;\n  \n  virtual void placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize);\n  virtual void drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const;\n  virtual TickLabelData getTickLabelData(const QFont &font, const QString &text) const;\n  virtual QPointF getTickLabelDrawOffset(const TickLabelData &labelData) const;\n  virtual void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const;\n};\n\n/* end of 'src/axis/axis.h' */\n\n\n/* including file 'src/scatterstyle.h'     */\n/* modified 2021-03-29T02:30:44, size 7275 */\n\nclass QCP_LIB_DECL QCPScatterStyle\n{\n  Q_GADGET\npublic:\n  /*!\n    Represents the various properties of a scatter style instance. For example, this enum is used\n    to specify which properties of \\ref QCPSelectionDecorator::setScatterStyle will be used when\n    highlighting selected data points.\n\n    Specific scatter properties can be transferred between \\ref QCPScatterStyle instances via \\ref\n    setFromOther.\n  */\n  enum ScatterProperty { spNone  = 0x00  ///< <tt>0x00</tt> None\n                         ,spPen   = 0x01  ///< <tt>0x01</tt> The pen property, see \\ref setPen\n                         ,spBrush = 0x02  ///< <tt>0x02</tt> The brush property, see \\ref setBrush\n                         ,spSize  = 0x04  ///< <tt>0x04</tt> The size property, see \\ref setSize\n                         ,spShape = 0x08  ///< <tt>0x08</tt> The shape property, see \\ref setShape\n                         ,spAll   = 0xFF  ///< <tt>0xFF</tt> All properties\n                       };\n  Q_ENUMS(ScatterProperty)\n  Q_FLAGS(ScatterProperties)\n  Q_DECLARE_FLAGS(ScatterProperties, ScatterProperty)\n\n  /*!\n    Defines the shape used for scatter points.\n\n    On plottables/items that draw scatters, the sizes of these visualizations (with exception of\n    \\ref ssDot and \\ref ssPixmap) can be controlled with the \\ref setSize function. Scatters are\n    drawn with the pen and brush specified with \\ref setPen and \\ref setBrush.\n  */\n  enum ScatterShape { ssNone       ///< no scatter symbols are drawn (e.g. in QCPGraph, data only represented with lines)\n                      ,ssDot       ///< \\enumimage{ssDot.png} a single pixel (use \\ref ssDisc or \\ref ssCircle if you want a round shape with a certain radius)\n                      ,ssCross     ///< \\enumimage{ssCross.png} a cross\n                      ,ssPlus      ///< \\enumimage{ssPlus.png} a plus\n                      ,ssCircle    ///< \\enumimage{ssCircle.png} a circle\n                      ,ssDisc      ///< \\enumimage{ssDisc.png} a circle which is filled with the pen's color (not the brush as with ssCircle)\n                      ,ssSquare    ///< \\enumimage{ssSquare.png} a square\n                      ,ssDiamond   ///< \\enumimage{ssDiamond.png} a diamond\n                      ,ssStar      ///< \\enumimage{ssStar.png} a star with eight arms, i.e. a combination of cross and plus\n                      ,ssTriangle  ///< \\enumimage{ssTriangle.png} an equilateral triangle, standing on baseline\n                      ,ssTriangleInverted ///< \\enumimage{ssTriangleInverted.png} an equilateral triangle, standing on corner\n                      ,ssCrossSquare      ///< \\enumimage{ssCrossSquare.png} a square with a cross inside\n                      ,ssPlusSquare       ///< \\enumimage{ssPlusSquare.png} a square with a plus inside\n                      ,ssCrossCircle      ///< \\enumimage{ssCrossCircle.png} a circle with a cross inside\n                      ,ssPlusCircle       ///< \\enumimage{ssPlusCircle.png} a circle with a plus inside\n                      ,ssPeace     ///< \\enumimage{ssPeace.png} a circle, with one vertical and two downward diagonal lines\n                      ,ssPixmap    ///< a custom pixmap specified by \\ref setPixmap, centered on the data point coordinates\n                      ,ssCustom    ///< custom painter operations are performed per scatter (As QPainterPath, see \\ref setCustomPath)\n                    };\n  Q_ENUMS(ScatterShape)\n\n  QCPScatterStyle();\n  QCPScatterStyle(ScatterShape shape, double size=6);\n  QCPScatterStyle(ScatterShape shape, const QColor &color, double size);\n  QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size);\n  QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size);\n  QCPScatterStyle(const QPixmap &pixmap);\n  QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush=Qt::NoBrush, double size=6);\n  \n  // getters:\n  double size() const { return mSize; }\n  ScatterShape shape() const { return mShape; }\n  QPen pen() const { return mPen; }\n  QBrush brush() const { return mBrush; }\n  QPixmap pixmap() const { return mPixmap; }\n  QPainterPath customPath() const { return mCustomPath; }\n\n  // setters:\n  void setFromOther(const QCPScatterStyle &other, ScatterProperties properties);\n  void setSize(double size);\n  void setShape(ScatterShape shape);\n  void setPen(const QPen &pen);\n  void setBrush(const QBrush &brush);\n  void setPixmap(const QPixmap &pixmap);\n  void setCustomPath(const QPainterPath &customPath);\n\n  // non-property methods:\n  bool isNone() const { return mShape == ssNone; }\n  bool isPenDefined() const { return mPenDefined; }\n  void undefinePen();\n  void applyTo(QCPPainter *painter, const QPen &defaultPen) const;\n  void drawShape(QCPPainter *painter, const QPointF &pos) const;\n  void drawShape(QCPPainter *painter, double x, double y) const;\n\nprotected:\n  // property members:\n  double mSize;\n  ScatterShape mShape;\n  QPen mPen;\n  QBrush mBrush;\n  QPixmap mPixmap;\n  QPainterPath mCustomPath;\n  \n  // non-property members:\n  bool mPenDefined;\n};\nQ_DECLARE_TYPEINFO(QCPScatterStyle, Q_MOVABLE_TYPE);\nQ_DECLARE_OPERATORS_FOR_FLAGS(QCPScatterStyle::ScatterProperties)\nQ_DECLARE_METATYPE(QCPScatterStyle::ScatterProperty)\nQ_DECLARE_METATYPE(QCPScatterStyle::ScatterShape)\n\n/* end of 'src/scatterstyle.h' */\n\n\n/* including file 'src/datacontainer.h'     */\n/* modified 2021-03-29T02:30:44, size 34070 */\n\n/*! \\relates QCPDataContainer\n  Returns whether the sort key of \\a a is less than the sort key of \\a b.\n\n  \\see QCPDataContainer::sort\n*/\ntemplate <class DataType>\ninline bool qcpLessThanSortKey(const DataType &a, const DataType &b) { return a.sortKey() < b.sortKey(); }\n\ntemplate <class DataType>\nclass QCPDataContainer // no QCP_LIB_DECL, template class ends up in header (cpp included below)\n{\npublic:\n  typedef typename QVector<DataType>::const_iterator const_iterator;\n  typedef typename QVector<DataType>::iterator iterator;\n  \n  QCPDataContainer();\n  \n  // getters:\n  int size() const { return mData.size()-mPreallocSize; }\n  bool isEmpty() const { return size() == 0; }\n  bool autoSqueeze() const { return mAutoSqueeze; }\n  \n  // setters:\n  void setAutoSqueeze(bool enabled);\n  \n  // non-virtual methods:\n  void set(const QCPDataContainer<DataType> &data);\n  void set(const QVector<DataType> &data, bool alreadySorted=false);\n  void add(const QCPDataContainer<DataType> &data);\n  void add(const QVector<DataType> &data, bool alreadySorted=false);\n  void add(const DataType &data);\n  void removeBefore(double sortKey);\n  void removeAfter(double sortKey);\n  void remove(double sortKeyFrom, double sortKeyTo);\n  void remove(double sortKey);\n  void clear();\n  void sort();\n  void squeeze(bool preAllocation=true, bool postAllocation=true);\n  \n  const_iterator constBegin() const { return mData.constBegin()+mPreallocSize; }\n  const_iterator constEnd() const { return mData.constEnd(); }\n  iterator begin() { return mData.begin()+mPreallocSize; }\n  iterator end() { return mData.end(); }\n  const_iterator findBegin(double sortKey, bool expandedRange=true) const;\n  const_iterator findEnd(double sortKey, bool expandedRange=true) const;\n  const_iterator at(int index) const { return constBegin()+qBound(0, index, size()); }\n  QCPRange keyRange(bool &foundRange, QCP::SignDomain signDomain=QCP::sdBoth);\n  QCPRange valueRange(bool &foundRange, QCP::SignDomain signDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange());\n  QCPDataRange dataRange() const { return QCPDataRange(0, size()); }\n  void limitIteratorsToDataRange(const_iterator &begin, const_iterator &end, const QCPDataRange &dataRange) const;\n  \nprotected:\n  // property members:\n  bool mAutoSqueeze;\n  \n  // non-property memebers:\n  QVector<DataType> mData;\n  int mPreallocSize;\n  int mPreallocIteration;\n  \n  // non-virtual methods:\n  void preallocateGrow(int minimumPreallocSize);\n  void performAutoSqueeze();\n};\n\n\n\n// include implementation in header since it is a class template:\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPDataContainer\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPDataContainer\n  \\brief The generic data container for one-dimensional plottables\n\n  This class template provides a fast container for data storage of one-dimensional data. The data\n  type is specified as template parameter (called \\a DataType in the following) and must provide\n  some methods as described in the \\ref qcpdatacontainer-datatype \"next section\".\n\n  The data is stored in a sorted fashion, which allows very quick lookups by the sorted key as well\n  as retrieval of ranges (see \\ref findBegin, \\ref findEnd, \\ref keyRange) using binary search. The\n  container uses a preallocation and a postallocation scheme, such that appending and prepending\n  data (with respect to the sort key) is very fast and minimizes reallocations. If data is added\n  which needs to be inserted between existing keys, the merge usually can be done quickly too,\n  using the fact that existing data is always sorted. The user can further improve performance by\n  specifying that added data is already itself sorted by key, if he can guarantee that this is the\n  case (see for example \\ref add(const QVector<DataType> &data, bool alreadySorted)).\n\n  The data can be accessed with the provided const iterators (\\ref constBegin, \\ref constEnd). If\n  it is necessary to alter existing data in-place, the non-const iterators can be used (\\ref begin,\n  \\ref end). Changing data members that are not the sort key (for most data types called \\a key) is\n  safe from the container's perspective.\n\n  Great care must be taken however if the sort key is modified through the non-const iterators. For\n  performance reasons, the iterators don't automatically cause a re-sorting upon their\n  manipulation. It is thus the responsibility of the user to leave the container in a sorted state\n  when finished with the data manipulation, before calling any other methods on the container. A\n  complete re-sort (e.g. after finishing all sort key manipulation) can be done by calling \\ref\n  sort. Failing to do so can not be detected by the container efficiently and will cause both\n  rendering artifacts and potential data loss.\n\n  Implementing one-dimensional plottables that make use of a \\ref QCPDataContainer<T> is usually\n  done by subclassing from \\ref QCPAbstractPlottable1D \"QCPAbstractPlottable1D<T>\", which\n  introduces an according \\a mDataContainer member and some convenience methods.\n\n  \\section qcpdatacontainer-datatype Requirements for the DataType template parameter\n\n  The template parameter <tt>DataType</tt> is the type of the stored data points. It must be\n  trivially copyable and have the following public methods, preferably inline:\n\n  \\li <tt>double sortKey() const</tt>\\n Returns the member variable of this data point that is the\n  sort key, defining the ordering in the container. Often this variable is simply called \\a key.\n\n  \\li <tt>static DataType fromSortKey(double sortKey)</tt>\\n Returns a new instance of the data\n  type initialized with its sort key set to \\a sortKey.\n\n  \\li <tt>static bool sortKeyIsMainKey()</tt>\\n Returns true if the sort key is equal to the main\n  key (see method \\c mainKey below). For most plottables this is the case. It is not the case for\n  example for \\ref QCPCurve, which uses \\a t as sort key and \\a key as main key. This is the reason\n  why QCPCurve unlike QCPGraph can display parametric curves with loops.\n\n  \\li <tt>double mainKey() const</tt>\\n Returns the variable of this data point considered the main\n  key. This is commonly the variable that is used as the coordinate of this data point on the key\n  axis of the plottable. This method is used for example when determining the automatic axis\n  rescaling of key axes (\\ref QCPAxis::rescale).\n\n  \\li <tt>double mainValue() const</tt>\\n Returns the variable of this data point considered the\n  main value. This is commonly the variable that is used as the coordinate of this data point on\n  the value axis of the plottable.\n\n  \\li <tt>QCPRange valueRange() const</tt>\\n Returns the range this data point spans in the value\n  axis coordinate. If the data is single-valued (e.g. QCPGraphData), this is simply a range with\n  both lower and upper set to the main data point value. However if the data points can represent\n  multiple values at once (e.g QCPFinancialData with its \\a high, \\a low, \\a open and \\a close\n  values at each \\a key) this method should return the range those values span. This method is used\n  for example when determining the automatic axis rescaling of value axes (\\ref\n  QCPAxis::rescale).\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn int QCPDataContainer<DataType>::size() const\n  \n  Returns the number of data points in the container.\n*/\n\n/*! \\fn bool QCPDataContainer<DataType>::isEmpty() const\n  \n  Returns whether this container holds no data points.\n*/\n\n/*! \\fn QCPDataContainer::const_iterator QCPDataContainer<DataType>::constBegin() const\n  \n  Returns a const iterator to the first data point in this container.\n*/\n\n/*! \\fn QCPDataContainer::const_iterator QCPDataContainer<DataType>::constEnd() const\n  \n  Returns a const iterator to the element past the last data point in this container.\n*/\n\n/*! \\fn QCPDataContainer::iterator QCPDataContainer<DataType>::begin() const\n  \n  Returns a non-const iterator to the first data point in this container.\n\n  You can manipulate the data points in-place through the non-const iterators, but great care must\n  be taken when manipulating the sort key of a data point, see \\ref sort, or the detailed\n  description of this class.\n*/\n\n/*! \\fn QCPDataContainer::iterator QCPDataContainer<DataType>::end() const\n  \n  Returns a non-const iterator to the element past the last data point in this container.\n  \n  You can manipulate the data points in-place through the non-const iterators, but great care must\n  be taken when manipulating the sort key of a data point, see \\ref sort, or the detailed\n  description of this class.\n*/\n\n/*! \\fn QCPDataContainer::const_iterator QCPDataContainer<DataType>::at(int index) const\n\n  Returns a const iterator to the element with the specified \\a index. If \\a index points beyond\n  the available elements in this container, returns \\ref constEnd, i.e. an iterator past the last\n  valid element.\n\n  You can use this method to easily obtain iterators from a \\ref QCPDataRange, see the \\ref\n  dataselection-accessing \"data selection page\" for an example.\n*/\n\n/*! \\fn QCPDataRange QCPDataContainer::dataRange() const\n\n  Returns a \\ref QCPDataRange encompassing the entire data set of this container. This means the\n  begin index of the returned range is 0, and the end index is \\ref size.\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Constructs a QCPDataContainer used for plottable classes that represent a series of key-sorted\n  data\n*/\ntemplate <class DataType>\nQCPDataContainer<DataType>::QCPDataContainer() :\n  mAutoSqueeze(true),\n  mPreallocSize(0),\n  mPreallocIteration(0)\n{\n}\n\n/*!\n  Sets whether the container automatically decides when to release memory from its post- and\n  preallocation pools when data points are removed. By default this is enabled and for typical\n  applications shouldn't be changed.\n  \n  If auto squeeze is disabled, you can manually decide when to release pre-/postallocation with\n  \\ref squeeze.\n*/\ntemplate <class DataType>\nvoid QCPDataContainer<DataType>::setAutoSqueeze(bool enabled)\n{\n  if (mAutoSqueeze != enabled)\n  {\n    mAutoSqueeze = enabled;\n    if (mAutoSqueeze)\n      performAutoSqueeze();\n  }\n}\n\n/*! \\overload\n  \n  Replaces the current data in this container with the provided \\a data.\n  \n  \\see add, remove\n*/\ntemplate <class DataType>\nvoid QCPDataContainer<DataType>::set(const QCPDataContainer<DataType> &data)\n{\n  clear();\n  add(data);\n}\n\n/*! \\overload\n  \n  Replaces the current data in this container with the provided \\a data\n\n  If you can guarantee that the data points in \\a data have ascending order with respect to the\n  DataType's sort key, set \\a alreadySorted to true to avoid an unnecessary sorting run.\n  \n  \\see add, remove\n*/\ntemplate <class DataType>\nvoid QCPDataContainer<DataType>::set(const QVector<DataType> &data, bool alreadySorted)\n{\n  mData = data;\n  mPreallocSize = 0;\n  mPreallocIteration = 0;\n  if (!alreadySorted)\n    sort();\n}\n\n/*! \\overload\n  \n  Adds the provided \\a data to the current data in this container.\n  \n  \\see set, remove\n*/\ntemplate <class DataType>\nvoid QCPDataContainer<DataType>::add(const QCPDataContainer<DataType> &data)\n{\n  if (data.isEmpty())\n    return;\n  \n  const int n = data.size();\n  const int oldSize = size();\n  \n  if (oldSize > 0 && !qcpLessThanSortKey<DataType>(*constBegin(), *(data.constEnd()-1))) // prepend if new data keys are all smaller than or equal to existing ones\n  {\n    if (mPreallocSize < n)\n      preallocateGrow(n);\n    mPreallocSize -= n;\n    std::copy(data.constBegin(), data.constEnd(), begin());\n  } else // don't need to prepend, so append and merge if necessary\n  {\n    mData.resize(mData.size()+n);\n    std::copy(data.constBegin(), data.constEnd(), end()-n);\n    if (oldSize > 0 && !qcpLessThanSortKey<DataType>(*(constEnd()-n-1), *(constEnd()-n))) // if appended range keys aren't all greater than existing ones, merge the two partitions\n      std::inplace_merge(begin(), end()-n, end(), qcpLessThanSortKey<DataType>);\n  }\n}\n\n/*!\n  Adds the provided data points in \\a data to the current data.\n  \n  If you can guarantee that the data points in \\a data have ascending order with respect to the\n  DataType's sort key, set \\a alreadySorted to true to avoid an unnecessary sorting run.\n  \n  \\see set, remove\n*/\ntemplate <class DataType>\nvoid QCPDataContainer<DataType>::add(const QVector<DataType> &data, bool alreadySorted)\n{\n  if (data.isEmpty())\n    return;\n  if (isEmpty())\n  {\n    set(data, alreadySorted);\n    return;\n  }\n  \n  const int n = data.size();\n  const int oldSize = size();\n  \n  if (alreadySorted && oldSize > 0 && !qcpLessThanSortKey<DataType>(*constBegin(), *(data.constEnd()-1))) // prepend if new data is sorted and keys are all smaller than or equal to existing ones\n  {\n    if (mPreallocSize < n)\n      preallocateGrow(n);\n    mPreallocSize -= n;\n    std::copy(data.constBegin(), data.constEnd(), begin());\n  } else // don't need to prepend, so append and then sort and merge if necessary\n  {\n    mData.resize(mData.size()+n);\n    std::copy(data.constBegin(), data.constEnd(), end()-n);\n    if (!alreadySorted) // sort appended subrange if it wasn't already sorted\n      std::sort(end()-n, end(), qcpLessThanSortKey<DataType>);\n    if (oldSize > 0 && !qcpLessThanSortKey<DataType>(*(constEnd()-n-1), *(constEnd()-n))) // if appended range keys aren't all greater than existing ones, merge the two partitions\n      std::inplace_merge(begin(), end()-n, end(), qcpLessThanSortKey<DataType>);\n  }\n}\n\n/*! \\overload\n  \n  Adds the provided single data point to the current data.\n  \n  \\see remove\n*/\ntemplate <class DataType>\nvoid QCPDataContainer<DataType>::add(const DataType &data)\n{\n  if (isEmpty() || !qcpLessThanSortKey<DataType>(data, *(constEnd()-1))) // quickly handle appends if new data key is greater or equal to existing ones\n  {\n    mData.append(data);\n  } else if (qcpLessThanSortKey<DataType>(data, *constBegin()))  // quickly handle prepends using preallocated space\n  {\n    if (mPreallocSize < 1)\n      preallocateGrow(1);\n    --mPreallocSize;\n    *begin() = data;\n  } else // handle inserts, maintaining sorted keys\n  {\n    QCPDataContainer<DataType>::iterator insertionPoint = std::lower_bound(begin(), end(), data, qcpLessThanSortKey<DataType>);\n    mData.insert(insertionPoint, data);\n  }\n}\n\n/*!\n  Removes all data points with (sort-)keys smaller than or equal to \\a sortKey.\n  \n  \\see removeAfter, remove, clear\n*/\ntemplate <class DataType>\nvoid QCPDataContainer<DataType>::removeBefore(double sortKey)\n{\n  QCPDataContainer<DataType>::iterator it = begin();\n  QCPDataContainer<DataType>::iterator itEnd = std::lower_bound(begin(), end(), DataType::fromSortKey(sortKey), qcpLessThanSortKey<DataType>);\n  mPreallocSize += int(itEnd-it); // don't actually delete, just add it to the preallocated block (if it gets too large, squeeze will take care of it)\n  if (mAutoSqueeze)\n    performAutoSqueeze();\n}\n\n/*!\n  Removes all data points with (sort-)keys greater than or equal to \\a sortKey.\n\n  \\see removeBefore, remove, clear\n*/\ntemplate <class DataType>\nvoid QCPDataContainer<DataType>::removeAfter(double sortKey)\n{\n  QCPDataContainer<DataType>::iterator it = std::upper_bound(begin(), end(), DataType::fromSortKey(sortKey), qcpLessThanSortKey<DataType>);\n  QCPDataContainer<DataType>::iterator itEnd = end();\n  mData.erase(it, itEnd); // typically adds it to the postallocated block\n  if (mAutoSqueeze)\n    performAutoSqueeze();\n}\n\n/*!\n  Removes all data points with (sort-)keys between \\a sortKeyFrom and \\a sortKeyTo. if \\a\n  sortKeyFrom is greater or equal to \\a sortKeyTo, the function does nothing. To remove a single\n  data point with known (sort-)key, use \\ref remove(double sortKey).\n  \n  \\see removeBefore, removeAfter, clear\n*/\ntemplate <class DataType>\nvoid QCPDataContainer<DataType>::remove(double sortKeyFrom, double sortKeyTo)\n{\n  if (sortKeyFrom >= sortKeyTo || isEmpty())\n    return;\n  \n  QCPDataContainer<DataType>::iterator it = std::lower_bound(begin(), end(), DataType::fromSortKey(sortKeyFrom), qcpLessThanSortKey<DataType>);\n  QCPDataContainer<DataType>::iterator itEnd = std::upper_bound(it, end(), DataType::fromSortKey(sortKeyTo), qcpLessThanSortKey<DataType>);\n  mData.erase(it, itEnd);\n  if (mAutoSqueeze)\n    performAutoSqueeze();\n}\n\n/*! \\overload\n  \n  Removes a single data point at \\a sortKey. If the position is not known with absolute (binary)\n  precision, consider using \\ref remove(double sortKeyFrom, double sortKeyTo) with a small\n  fuzziness interval around the suspected position, depeding on the precision with which the\n  (sort-)key is known.\n  \n  \\see removeBefore, removeAfter, clear\n*/\ntemplate <class DataType>\nvoid QCPDataContainer<DataType>::remove(double sortKey)\n{\n  QCPDataContainer::iterator it = std::lower_bound(begin(), end(), DataType::fromSortKey(sortKey), qcpLessThanSortKey<DataType>);\n  if (it != end() && it->sortKey() == sortKey)\n  {\n    if (it == begin())\n      ++mPreallocSize; // don't actually delete, just add it to the preallocated block (if it gets too large, squeeze will take care of it)\n    else\n      mData.erase(it);\n  }\n  if (mAutoSqueeze)\n    performAutoSqueeze();\n}\n\n/*!\n  Removes all data points.\n  \n  \\see remove, removeAfter, removeBefore\n*/\ntemplate <class DataType>\nvoid QCPDataContainer<DataType>::clear()\n{\n  mData.clear();\n  mPreallocIteration = 0;\n  mPreallocSize = 0;\n}\n\n/*!\n  Re-sorts all data points in the container by their sort key.\n\n  When setting, adding or removing points using the QCPDataContainer interface (\\ref set, \\ref add,\n  \\ref remove, etc.), the container makes sure to always stay in a sorted state such that a full\n  resort is never necessary. However, if you choose to directly manipulate the sort key on data\n  points by accessing and modifying it through the non-const iterators (\\ref begin, \\ref end), it\n  is your responsibility to bring the container back into a sorted state before any other methods\n  are called on it. This can be achieved by calling this method immediately after finishing the\n  sort key manipulation.\n*/\ntemplate <class DataType>\nvoid QCPDataContainer<DataType>::sort()\n{\n  std::sort(begin(), end(), qcpLessThanSortKey<DataType>);\n}\n\n/*!\n  Frees all unused memory that is currently in the preallocation and postallocation pools.\n  \n  Note that QCPDataContainer automatically decides whether squeezing is necessary, if \\ref\n  setAutoSqueeze is left enabled. It should thus not be necessary to use this method for typical\n  applications.\n  \n  The parameters \\a preAllocation and \\a postAllocation control whether pre- and/or post allocation\n  should be freed, respectively.\n*/\ntemplate <class DataType>\nvoid QCPDataContainer<DataType>::squeeze(bool preAllocation, bool postAllocation)\n{\n  if (preAllocation)\n  {\n    if (mPreallocSize > 0)\n    {\n      std::copy(begin(), end(), mData.begin());\n      mData.resize(size());\n      mPreallocSize = 0;\n    }\n    mPreallocIteration = 0;\n  }\n  if (postAllocation)\n    mData.squeeze();\n}\n\n/*!\n  Returns an iterator to the data point with a (sort-)key that is equal to, just below, or just\n  above \\a sortKey. If \\a expandedRange is true, the data point just below \\a sortKey will be\n  considered, otherwise the one just above.\n\n  This can be used in conjunction with \\ref findEnd to iterate over data points within a given key\n  range, including or excluding the bounding data points that are just beyond the specified range.\n\n  If \\a expandedRange is true but there are no data points below \\a sortKey, \\ref constBegin is\n  returned.\n\n  If the container is empty, returns \\ref constEnd.\n\n  \\see findEnd, QCPPlottableInterface1D::findBegin\n*/\ntemplate <class DataType>\ntypename QCPDataContainer<DataType>::const_iterator QCPDataContainer<DataType>::findBegin(double sortKey, bool expandedRange) const\n{\n  if (isEmpty())\n    return constEnd();\n  \n  QCPDataContainer<DataType>::const_iterator it = std::lower_bound(constBegin(), constEnd(), DataType::fromSortKey(sortKey), qcpLessThanSortKey<DataType>);\n  if (expandedRange && it != constBegin()) // also covers it == constEnd case, and we know --constEnd is valid because mData isn't empty\n    --it;\n  return it;\n}\n\n/*!\n  Returns an iterator to the element after the data point with a (sort-)key that is equal to, just\n  above or just below \\a sortKey. If \\a expandedRange is true, the data point just above \\a sortKey\n  will be considered, otherwise the one just below.\n\n  This can be used in conjunction with \\ref findBegin to iterate over data points within a given\n  key range, including the bounding data points that are just below and above the specified range.\n\n  If \\a expandedRange is true but there are no data points above \\a sortKey, \\ref constEnd is\n  returned.\n\n  If the container is empty, \\ref constEnd is returned.\n\n  \\see findBegin, QCPPlottableInterface1D::findEnd\n*/\ntemplate <class DataType>\ntypename QCPDataContainer<DataType>::const_iterator QCPDataContainer<DataType>::findEnd(double sortKey, bool expandedRange) const\n{\n  if (isEmpty())\n    return constEnd();\n  \n  QCPDataContainer<DataType>::const_iterator it = std::upper_bound(constBegin(), constEnd(), DataType::fromSortKey(sortKey), qcpLessThanSortKey<DataType>);\n  if (expandedRange && it != constEnd())\n    ++it;\n  return it;\n}\n\n/*!\n  Returns the range encompassed by the (main-)key coordinate of all data points. The output\n  parameter \\a foundRange indicates whether a sensible range was found. If this is false, you\n  should not use the returned QCPRange (e.g. the data container is empty or all points have the\n  same key).\n  \n  Use \\a signDomain to control which sign of the key coordinates should be considered. This is\n  relevant e.g. for logarithmic plots which can mathematically only display one sign domain at a\n  time.\n  \n  If the DataType reports that its main key is equal to the sort key (\\a sortKeyIsMainKey), as is\n  the case for most plottables, this method uses this fact and finds the range very quickly.\n  \n  \\see valueRange\n*/\ntemplate <class DataType>\nQCPRange QCPDataContainer<DataType>::keyRange(bool &foundRange, QCP::SignDomain signDomain)\n{\n  if (isEmpty())\n  {\n    foundRange = false;\n    return QCPRange();\n  }\n  QCPRange range;\n  bool haveLower = false;\n  bool haveUpper = false;\n  double current;\n  \n  QCPDataContainer<DataType>::const_iterator it = constBegin();\n  QCPDataContainer<DataType>::const_iterator itEnd = constEnd();\n  if (signDomain == QCP::sdBoth) // range may be anywhere\n  {\n    if (DataType::sortKeyIsMainKey()) // if DataType is sorted by main key (e.g. QCPGraph, but not QCPCurve), use faster algorithm by finding just first and last key with non-NaN value\n    {\n      while (it != itEnd) // find first non-nan going up from left\n      {\n        if (!qIsNaN(it->mainValue()))\n        {\n          range.lower = it->mainKey();\n          haveLower = true;\n          break;\n        }\n        ++it;\n      }\n      it = itEnd;\n      while (it != constBegin()) // find first non-nan going down from right\n      {\n        --it;\n        if (!qIsNaN(it->mainValue()))\n        {\n          range.upper = it->mainKey();\n          haveUpper = true;\n          break;\n        }\n      }\n    } else // DataType is not sorted by main key, go through all data points and accordingly expand range\n    {\n      while (it != itEnd)\n      {\n        if (!qIsNaN(it->mainValue()))\n        {\n          current = it->mainKey();\n          if (current < range.lower || !haveLower)\n          {\n            range.lower = current;\n            haveLower = true;\n          }\n          if (current > range.upper || !haveUpper)\n          {\n            range.upper = current;\n            haveUpper = true;\n          }\n        }\n        ++it;\n      }\n    }\n  } else if (signDomain == QCP::sdNegative) // range may only be in the negative sign domain\n  {\n    while (it != itEnd)\n    {\n      if (!qIsNaN(it->mainValue()))\n      {\n        current = it->mainKey();\n        if ((current < range.lower || !haveLower) && current < 0)\n        {\n          range.lower = current;\n          haveLower = true;\n        }\n        if ((current > range.upper || !haveUpper) && current < 0)\n        {\n          range.upper = current;\n          haveUpper = true;\n        }\n      }\n      ++it;\n    }\n  } else if (signDomain == QCP::sdPositive) // range may only be in the positive sign domain\n  {\n    while (it != itEnd)\n    {\n      if (!qIsNaN(it->mainValue()))\n      {\n        current = it->mainKey();\n        if ((current < range.lower || !haveLower) && current > 0)\n        {\n          range.lower = current;\n          haveLower = true;\n        }\n        if ((current > range.upper || !haveUpper) && current > 0)\n        {\n          range.upper = current;\n          haveUpper = true;\n        }\n      }\n      ++it;\n    }\n  }\n  \n  foundRange = haveLower && haveUpper;\n  return range;\n}\n\n/*!\n  Returns the range encompassed by the value coordinates of the data points in the specified key\n  range (\\a inKeyRange), using the full \\a DataType::valueRange reported by the data points. The\n  output parameter \\a foundRange indicates whether a sensible range was found. If this is false,\n  you should not use the returned QCPRange (e.g. the data container is empty or all points have the\n  same value).\n\n  If \\a inKeyRange has both lower and upper bound set to zero (is equal to <tt>QCPRange()</tt>),\n  all data points are considered, without any restriction on the keys.\n\n  Use \\a signDomain to control which sign of the value coordinates should be considered. This is\n  relevant e.g. for logarithmic plots which can mathematically only display one sign domain at a\n  time.\n\n  \\see keyRange\n*/\ntemplate <class DataType>\nQCPRange QCPDataContainer<DataType>::valueRange(bool &foundRange, QCP::SignDomain signDomain, const QCPRange &inKeyRange)\n{\n  if (isEmpty())\n  {\n    foundRange = false;\n    return QCPRange();\n  }\n  QCPRange range;\n  const bool restrictKeyRange = inKeyRange != QCPRange();\n  bool haveLower = false;\n  bool haveUpper = false;\n  QCPRange current;\n  QCPDataContainer<DataType>::const_iterator itBegin = constBegin();\n  QCPDataContainer<DataType>::const_iterator itEnd = constEnd();\n  if (DataType::sortKeyIsMainKey() && restrictKeyRange)\n  {\n    itBegin = findBegin(inKeyRange.lower, false);\n    itEnd = findEnd(inKeyRange.upper, false);\n  }\n  if (signDomain == QCP::sdBoth) // range may be anywhere\n  {\n    for (QCPDataContainer<DataType>::const_iterator it = itBegin; it != itEnd; ++it)\n    {\n      if (restrictKeyRange && (it->mainKey() < inKeyRange.lower || it->mainKey() > inKeyRange.upper))\n        continue;\n      current = it->valueRange();\n      if ((current.lower < range.lower || !haveLower) && !qIsNaN(current.lower))\n      {\n        range.lower = current.lower;\n        haveLower = true;\n      }\n      if ((current.upper > range.upper || !haveUpper) && !qIsNaN(current.upper))\n      {\n        range.upper = current.upper;\n        haveUpper = true;\n      }\n    }\n  } else if (signDomain == QCP::sdNegative) // range may only be in the negative sign domain\n  {\n    for (QCPDataContainer<DataType>::const_iterator it = itBegin; it != itEnd; ++it)\n    {\n      if (restrictKeyRange && (it->mainKey() < inKeyRange.lower || it->mainKey() > inKeyRange.upper))\n        continue;\n      current = it->valueRange();\n      if ((current.lower < range.lower || !haveLower) && current.lower < 0 && !qIsNaN(current.lower))\n      {\n        range.lower = current.lower;\n        haveLower = true;\n      }\n      if ((current.upper > range.upper || !haveUpper) && current.upper < 0 && !qIsNaN(current.upper))\n      {\n        range.upper = current.upper;\n        haveUpper = true;\n      }\n    }\n  } else if (signDomain == QCP::sdPositive) // range may only be in the positive sign domain\n  {\n    for (QCPDataContainer<DataType>::const_iterator it = itBegin; it != itEnd; ++it)\n    {\n      if (restrictKeyRange && (it->mainKey() < inKeyRange.lower || it->mainKey() > inKeyRange.upper))\n        continue;\n      current = it->valueRange();\n      if ((current.lower < range.lower || !haveLower) && current.lower > 0 && !qIsNaN(current.lower))\n      {\n        range.lower = current.lower;\n        haveLower = true;\n      }\n      if ((current.upper > range.upper || !haveUpper) && current.upper > 0 && !qIsNaN(current.upper))\n      {\n        range.upper = current.upper;\n        haveUpper = true;\n      }\n    }\n  }\n  \n  foundRange = haveLower && haveUpper;\n  return range;\n}\n\n/*!\n  Makes sure \\a begin and \\a end mark a data range that is both within the bounds of this data\n  container's data, as well as within the specified \\a dataRange. The initial range described by\n  the passed iterators \\a begin and \\a end is never expanded, only contracted if necessary.\n  \n  This function doesn't require for \\a dataRange to be within the bounds of this data container's\n  valid range.\n*/\ntemplate <class DataType>\nvoid QCPDataContainer<DataType>::limitIteratorsToDataRange(const_iterator &begin, const_iterator &end, const QCPDataRange &dataRange) const\n{\n  QCPDataRange iteratorRange(int(begin-constBegin()), int(end-constBegin()));\n  iteratorRange = iteratorRange.bounded(dataRange.bounded(this->dataRange()));\n  begin = constBegin()+iteratorRange.begin();\n  end = constBegin()+iteratorRange.end();\n}\n\n/*! \\internal\n  \n  Increases the preallocation pool to have a size of at least \\a minimumPreallocSize. Depending on\n  the preallocation history, the container will grow by more than requested, to speed up future\n  consecutive size increases.\n  \n  if \\a minimumPreallocSize is smaller than or equal to the current preallocation pool size, this\n  method does nothing.\n*/\ntemplate <class DataType>\nvoid QCPDataContainer<DataType>::preallocateGrow(int minimumPreallocSize)\n{\n  if (minimumPreallocSize <= mPreallocSize)\n    return;\n  \n  int newPreallocSize = minimumPreallocSize;\n  newPreallocSize += (1u<<qBound(4, mPreallocIteration+4, 15)) - 12; // do 4 up to 32768-12 preallocation, doubling in each intermediate iteration\n  ++mPreallocIteration;\n  \n  int sizeDifference = newPreallocSize-mPreallocSize;\n  mData.resize(mData.size()+sizeDifference);\n  std::copy_backward(mData.begin()+mPreallocSize, mData.end()-sizeDifference, mData.end());\n  mPreallocSize = newPreallocSize;\n}\n\n/*! \\internal\n  \n  This method decides, depending on the total allocation size and the size of the unused pre- and\n  postallocation pools, whether it is sensible to reduce the pools in order to free up unused\n  memory. It then possibly calls \\ref squeeze to do the deallocation.\n  \n  If \\ref setAutoSqueeze is enabled, this method is called automatically each time data points are\n  removed from the container (e.g. \\ref remove).\n  \n  \\note when changing the decision parameters, care must be taken not to cause a back-and-forth\n  between squeezing and reallocation due to the growth strategy of the internal QVector and \\ref\n  preallocateGrow. The hysteresis between allocation and deallocation should be made high enough\n  (at the expense of possibly larger unused memory from time to time).\n*/\ntemplate <class DataType>\nvoid QCPDataContainer<DataType>::performAutoSqueeze()\n{\n  const int totalAlloc = mData.capacity();\n  const int postAllocSize = totalAlloc-mData.size();\n  const int usedSize = size();\n  bool shrinkPostAllocation = false;\n  bool shrinkPreAllocation = false;\n  if (totalAlloc > 650000) // if allocation is larger, shrink earlier with respect to total used size\n  {\n    shrinkPostAllocation = postAllocSize > usedSize*1.5; // QVector grow strategy is 2^n for static data. Watch out not to oscillate!\n    shrinkPreAllocation = mPreallocSize*10 > usedSize;\n  } else if (totalAlloc > 1000) // below 10 MiB raw data be generous with preallocated memory, below 1k points don't even bother\n  {\n    shrinkPostAllocation = postAllocSize > usedSize*5;\n    shrinkPreAllocation = mPreallocSize > usedSize*1.5; // preallocation can grow into postallocation, so can be smaller\n  }\n  \n  if (shrinkPreAllocation || shrinkPostAllocation)\n    squeeze(shrinkPreAllocation, shrinkPostAllocation);\n}\n\n\n/* end of 'src/datacontainer.h' */\n\n\n/* including file 'src/plottable.h'        */\n/* modified 2021-03-29T02:30:44, size 8461 */\n\nclass QCP_LIB_DECL QCPSelectionDecorator\n{\n  Q_GADGET\npublic:\n  QCPSelectionDecorator();\n  virtual ~QCPSelectionDecorator();\n  \n  // getters:\n  QPen pen() const { return mPen; }\n  QBrush brush() const { return mBrush; }\n  QCPScatterStyle scatterStyle() const { return mScatterStyle; }\n  QCPScatterStyle::ScatterProperties usedScatterProperties() const { return mUsedScatterProperties; }\n  \n  // setters:\n  void setPen(const QPen &pen);\n  void setBrush(const QBrush &brush);\n  void setScatterStyle(const QCPScatterStyle &scatterStyle, QCPScatterStyle::ScatterProperties usedProperties=QCPScatterStyle::spPen);\n  void setUsedScatterProperties(const QCPScatterStyle::ScatterProperties &properties);\n  \n  // non-virtual methods:\n  void applyPen(QCPPainter *painter) const;\n  void applyBrush(QCPPainter *painter) const;\n  QCPScatterStyle getFinalScatterStyle(const QCPScatterStyle &unselectedStyle) const;\n  \n  // introduced virtual methods:\n  virtual void copyFrom(const QCPSelectionDecorator *other);\n  virtual void drawDecoration(QCPPainter *painter, QCPDataSelection selection);\n  \nprotected:\n  // property members:\n  QPen mPen;\n  QBrush mBrush;\n  QCPScatterStyle mScatterStyle;\n  QCPScatterStyle::ScatterProperties mUsedScatterProperties;\n  // non-property members:\n  QCPAbstractPlottable *mPlottable;\n  \n  // introduced virtual methods:\n  virtual bool registerWithPlottable(QCPAbstractPlottable *plottable);\n  \nprivate:\n  Q_DISABLE_COPY(QCPSelectionDecorator)\n  friend class QCPAbstractPlottable;\n};\nQ_DECLARE_METATYPE(QCPSelectionDecorator*)\n\n\nclass QCP_LIB_DECL QCPAbstractPlottable : public QCPLayerable\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QString name READ name WRITE setName)\n  Q_PROPERTY(bool antialiasedFill READ antialiasedFill WRITE setAntialiasedFill)\n  Q_PROPERTY(bool antialiasedScatters READ antialiasedScatters WRITE setAntialiasedScatters)\n  Q_PROPERTY(QPen pen READ pen WRITE setPen)\n  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)\n  Q_PROPERTY(QCPAxis* keyAxis READ keyAxis WRITE setKeyAxis)\n  Q_PROPERTY(QCPAxis* valueAxis READ valueAxis WRITE setValueAxis)\n  Q_PROPERTY(QCP::SelectionType selectable READ selectable WRITE setSelectable NOTIFY selectableChanged)\n  Q_PROPERTY(QCPDataSelection selection READ selection WRITE setSelection NOTIFY selectionChanged)\n  Q_PROPERTY(QCPSelectionDecorator* selectionDecorator READ selectionDecorator WRITE setSelectionDecorator)\n  /// \\endcond\npublic:\n  QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis);\n  virtual ~QCPAbstractPlottable() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QString name() const { return mName; }\n  bool antialiasedFill() const { return mAntialiasedFill; }\n  bool antialiasedScatters() const { return mAntialiasedScatters; }\n  QPen pen() const { return mPen; }\n  QBrush brush() const { return mBrush; }\n  QCPAxis *keyAxis() const { return mKeyAxis.data(); }\n  QCPAxis *valueAxis() const { return mValueAxis.data(); }\n  QCP::SelectionType selectable() const { return mSelectable; }\n  bool selected() const { return !mSelection.isEmpty(); }\n  QCPDataSelection selection() const { return mSelection; }\n  QCPSelectionDecorator *selectionDecorator() const { return mSelectionDecorator; }\n  \n  // setters:\n  void setName(const QString &name);\n  void setAntialiasedFill(bool enabled);\n  void setAntialiasedScatters(bool enabled);\n  void setPen(const QPen &pen);\n  void setBrush(const QBrush &brush);\n  void setKeyAxis(QCPAxis *axis);\n  void setValueAxis(QCPAxis *axis);\n  Q_SLOT void setSelectable(QCP::SelectionType selectable);\n  Q_SLOT void setSelection(QCPDataSelection selection);\n  void setSelectionDecorator(QCPSelectionDecorator *decorator);\n\n  // introduced virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE = 0; // actually introduced in QCPLayerable as non-pure, but we want to force reimplementation for plottables\n  virtual QCPPlottableInterface1D *interface1D() { return nullptr; }\n  virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const = 0;\n  virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const = 0;\n  \n  // non-property methods:\n  void coordsToPixels(double key, double value, double &x, double &y) const;\n  const QPointF coordsToPixels(double key, double value) const;\n  void pixelsToCoords(double x, double y, double &key, double &value) const;\n  void pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const;\n  void rescaleAxes(bool onlyEnlarge=false) const;\n  void rescaleKeyAxis(bool onlyEnlarge=false) const;\n  void rescaleValueAxis(bool onlyEnlarge=false, bool inKeyRange=false) const;\n  bool addToLegend(QCPLegend *legend);\n  bool addToLegend();\n  bool removeFromLegend(QCPLegend *legend) const;\n  bool removeFromLegend() const;\n  \nsignals:\n  void selectionChanged(bool selected);\n  void selectionChanged(const QCPDataSelection &selection);\n  void selectableChanged(QCP::SelectionType selectable);\n  \nprotected:\n  // property members:\n  QString mName;\n  bool mAntialiasedFill, mAntialiasedScatters;\n  QPen mPen;\n  QBrush mBrush;\n  QPointer<QCPAxis> mKeyAxis, mValueAxis;\n  QCP::SelectionType mSelectable;\n  QCPDataSelection mSelection;\n  QCPSelectionDecorator *mSelectionDecorator;\n  \n  // reimplemented virtual methods:\n  virtual QRect clipRect() const Q_DECL_OVERRIDE;\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE = 0;\n  virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE;\n  void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;\n  // events:\n  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE;\n  virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE;\n  \n  // introduced virtual methods:\n  virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const = 0;\n  \n  // non-virtual methods:\n  void applyFillAntialiasingHint(QCPPainter *painter) const;\n  void applyScattersAntialiasingHint(QCPPainter *painter) const;\n\nprivate:\n  Q_DISABLE_COPY(QCPAbstractPlottable)\n  \n  friend class QCustomPlot;\n  friend class QCPAxis;\n  friend class QCPPlottableLegendItem;\n};\n\n\n/* end of 'src/plottable.h' */\n\n\n/* including file 'src/item.h'             */\n/* modified 2021-03-29T02:30:44, size 9425 */\n\nclass QCP_LIB_DECL QCPItemAnchor\n{\n  Q_GADGET\npublic:\n  QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name, int anchorId=-1);\n  virtual ~QCPItemAnchor();\n  \n  // getters:\n  QString name() const { return mName; }\n  virtual QPointF pixelPosition() const;\n  \nprotected:\n  // property members:\n  QString mName;\n  \n  // non-property members:\n  QCustomPlot *mParentPlot;\n  QCPAbstractItem *mParentItem;\n  int mAnchorId;\n  QSet<QCPItemPosition*> mChildrenX, mChildrenY;\n  \n  // introduced virtual methods:\n  virtual QCPItemPosition *toQCPItemPosition() { return nullptr; }\n  \n  // non-virtual methods:\n  void addChildX(QCPItemPosition* pos); // called from pos when this anchor is set as parent\n  void removeChildX(QCPItemPosition *pos); // called from pos when its parent anchor is reset or pos deleted\n  void addChildY(QCPItemPosition* pos); // called from pos when this anchor is set as parent\n  void removeChildY(QCPItemPosition *pos); // called from pos when its parent anchor is reset or pos deleted\n  \nprivate:\n  Q_DISABLE_COPY(QCPItemAnchor)\n  \n  friend class QCPItemPosition;\n};\n\n\n\nclass QCP_LIB_DECL QCPItemPosition : public QCPItemAnchor\n{\n  Q_GADGET\npublic:\n  /*!\n    Defines the ways an item position can be specified. Thus it defines what the numbers passed to\n    \\ref setCoords actually mean.\n    \n    \\see setType\n  */\n  enum PositionType { ptAbsolute        ///< Static positioning in pixels, starting from the top left corner of the viewport/widget.\n                      ,ptViewportRatio  ///< Static positioning given by a fraction of the viewport size. For example, if you call setCoords(0, 0), the position will be at the top\n                                        ///< left corner of the viewport/widget. setCoords(1, 1) will be at the bottom right corner, setCoords(0.5, 0) will be horizontally centered and\n                                        ///< vertically at the top of the viewport/widget, etc.\n                      ,ptAxisRectRatio  ///< Static positioning given by a fraction of the axis rect size (see \\ref setAxisRect). For example, if you call setCoords(0, 0), the position will be at the top\n                                        ///< left corner of the axis rect. setCoords(1, 1) will be at the bottom right corner, setCoords(0.5, 0) will be horizontally centered and\n                                        ///< vertically at the top of the axis rect, etc. You can also go beyond the axis rect by providing negative coordinates or coordinates larger than 1.\n                      ,ptPlotCoords     ///< Dynamic positioning at a plot coordinate defined by two axes (see \\ref setAxes).\n                    };\n  Q_ENUMS(PositionType)\n  \n  QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name);\n  virtual ~QCPItemPosition() Q_DECL_OVERRIDE;\n  \n  // getters:\n  PositionType type() const { return typeX(); }\n  PositionType typeX() const { return mPositionTypeX; }\n  PositionType typeY() const { return mPositionTypeY; }\n  QCPItemAnchor *parentAnchor() const { return parentAnchorX(); }\n  QCPItemAnchor *parentAnchorX() const { return mParentAnchorX; }\n  QCPItemAnchor *parentAnchorY() const { return mParentAnchorY; }\n  double key() const { return mKey; }\n  double value() const { return mValue; }\n  QPointF coords() const { return QPointF(mKey, mValue); }\n  QCPAxis *keyAxis() const { return mKeyAxis.data(); }\n  QCPAxis *valueAxis() const { return mValueAxis.data(); }\n  QCPAxisRect *axisRect() const;\n  virtual QPointF pixelPosition() const Q_DECL_OVERRIDE;\n  \n  // setters:\n  void setType(PositionType type);\n  void setTypeX(PositionType type);\n  void setTypeY(PositionType type);\n  bool setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false);\n  bool setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false);\n  bool setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false);\n  void setCoords(double key, double value);\n  void setCoords(const QPointF &pos);\n  void setAxes(QCPAxis* keyAxis, QCPAxis* valueAxis);\n  void setAxisRect(QCPAxisRect *axisRect);\n  void setPixelPosition(const QPointF &pixelPosition);\n  \nprotected:\n  // property members:\n  PositionType mPositionTypeX, mPositionTypeY;\n  QPointer<QCPAxis> mKeyAxis, mValueAxis;\n  QPointer<QCPAxisRect> mAxisRect;\n  double mKey, mValue;\n  QCPItemAnchor *mParentAnchorX, *mParentAnchorY;\n  \n  // reimplemented virtual methods:\n  virtual QCPItemPosition *toQCPItemPosition() Q_DECL_OVERRIDE { return this; }\n  \nprivate:\n  Q_DISABLE_COPY(QCPItemPosition)\n  \n};\nQ_DECLARE_METATYPE(QCPItemPosition::PositionType)\n\n\nclass QCP_LIB_DECL QCPAbstractItem : public QCPLayerable\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(bool clipToAxisRect READ clipToAxisRect WRITE setClipToAxisRect)\n  Q_PROPERTY(QCPAxisRect* clipAxisRect READ clipAxisRect WRITE setClipAxisRect)\n  Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged)\n  Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged)\n  /// \\endcond\npublic:\n  explicit QCPAbstractItem(QCustomPlot *parentPlot);\n  virtual ~QCPAbstractItem() Q_DECL_OVERRIDE;\n  \n  // getters:\n  bool clipToAxisRect() const { return mClipToAxisRect; }\n  QCPAxisRect *clipAxisRect() const;\n  bool selectable() const { return mSelectable; }\n  bool selected() const { return mSelected; }\n  \n  // setters:\n  void setClipToAxisRect(bool clip);\n  void setClipAxisRect(QCPAxisRect *rect);\n  Q_SLOT void setSelectable(bool selectable);\n  Q_SLOT void setSelected(bool selected);\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE = 0;\n  \n  // non-virtual methods:\n  QList<QCPItemPosition*> positions() const { return mPositions; }\n  QList<QCPItemAnchor*> anchors() const { return mAnchors; }\n  QCPItemPosition *position(const QString &name) const;\n  QCPItemAnchor *anchor(const QString &name) const;\n  bool hasAnchor(const QString &name) const;\n  \nsignals:\n  void selectionChanged(bool selected);\n  void selectableChanged(bool selectable);\n  \nprotected:\n  // property members:\n  bool mClipToAxisRect;\n  QPointer<QCPAxisRect> mClipAxisRect;\n  QList<QCPItemPosition*> mPositions;\n  QList<QCPItemAnchor*> mAnchors;\n  bool mSelectable, mSelected;\n  \n  // reimplemented virtual methods:\n  virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE;\n  virtual QRect clipRect() const Q_DECL_OVERRIDE;\n  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE = 0;\n  // events:\n  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE;\n  virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE;\n  \n  // introduced virtual methods:\n  virtual QPointF anchorPixelPosition(int anchorId) const;\n  \n  // non-virtual methods:\n  double rectDistance(const QRectF &rect, const QPointF &pos, bool filledRect) const;\n  QCPItemPosition *createPosition(const QString &name);\n  QCPItemAnchor *createAnchor(const QString &name, int anchorId);\n  \nprivate:\n  Q_DISABLE_COPY(QCPAbstractItem)\n  \n  friend class QCustomPlot;\n  friend class QCPItemAnchor;\n};\n\n/* end of 'src/item.h' */\n\n\n/* including file 'src/core.h'              */\n/* modified 2021-03-29T02:30:44, size 19304 */\n\nclass QCP_LIB_DECL QCustomPlot : public QWidget\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QRect viewport READ viewport WRITE setViewport)\n  Q_PROPERTY(QPixmap background READ background WRITE setBackground)\n  Q_PROPERTY(bool backgroundScaled READ backgroundScaled WRITE setBackgroundScaled)\n  Q_PROPERTY(Qt::AspectRatioMode backgroundScaledMode READ backgroundScaledMode WRITE setBackgroundScaledMode)\n  Q_PROPERTY(QCPLayoutGrid* plotLayout READ plotLayout)\n  Q_PROPERTY(bool autoAddPlottableToLegend READ autoAddPlottableToLegend WRITE setAutoAddPlottableToLegend)\n  Q_PROPERTY(int selectionTolerance READ selectionTolerance WRITE setSelectionTolerance)\n  Q_PROPERTY(bool noAntialiasingOnDrag READ noAntialiasingOnDrag WRITE setNoAntialiasingOnDrag)\n  Q_PROPERTY(Qt::KeyboardModifier multiSelectModifier READ multiSelectModifier WRITE setMultiSelectModifier)\n  Q_PROPERTY(bool openGl READ openGl WRITE setOpenGl)\n  /// \\endcond\npublic:\n  /*!\n    Defines how a layer should be inserted relative to an other layer.\n\n    \\see addLayer, moveLayer\n  */\n  enum LayerInsertMode { limBelow  ///< Layer is inserted below other layer\n                         ,limAbove ///< Layer is inserted above other layer\n                       };\n  Q_ENUMS(LayerInsertMode)\n  \n  /*!\n    Defines with what timing the QCustomPlot surface is refreshed after a replot.\n\n    \\see replot\n  */\n  enum RefreshPriority { rpImmediateRefresh ///< Replots immediately and repaints the widget immediately by calling QWidget::repaint() after the replot\n                         ,rpQueuedRefresh   ///< Replots immediately, but queues the widget repaint, by calling QWidget::update() after the replot. This way multiple redundant widget repaints can be avoided.\n                         ,rpRefreshHint     ///< Whether to use immediate or queued refresh depends on whether the plotting hint \\ref QCP::phImmediateRefresh is set, see \\ref setPlottingHints.\n                         ,rpQueuedReplot    ///< Queues the entire replot for the next event loop iteration. This way multiple redundant replots can be avoided. The actual replot is then done with \\ref rpRefreshHint priority.\n                       };\n  Q_ENUMS(RefreshPriority)\n  \n  explicit QCustomPlot(QWidget *parent = nullptr);\n  virtual ~QCustomPlot() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QRect viewport() const { return mViewport; }\n  double bufferDevicePixelRatio() const { return mBufferDevicePixelRatio; }\n  QPixmap background() const { return mBackgroundPixmap; }\n  bool backgroundScaled() const { return mBackgroundScaled; }\n  Qt::AspectRatioMode backgroundScaledMode() const { return mBackgroundScaledMode; }\n  QCPLayoutGrid *plotLayout() const { return mPlotLayout; }\n  QCP::AntialiasedElements antialiasedElements() const { return mAntialiasedElements; }\n  QCP::AntialiasedElements notAntialiasedElements() const { return mNotAntialiasedElements; }\n  bool autoAddPlottableToLegend() const { return mAutoAddPlottableToLegend; }\n  const QCP::Interactions interactions() const { return mInteractions; }\n  int selectionTolerance() const { return mSelectionTolerance; }\n  bool noAntialiasingOnDrag() const { return mNoAntialiasingOnDrag; }\n  QCP::PlottingHints plottingHints() const { return mPlottingHints; }\n  Qt::KeyboardModifier multiSelectModifier() const { return mMultiSelectModifier; }\n  QCP::SelectionRectMode selectionRectMode() const { return mSelectionRectMode; }\n  QCPSelectionRect *selectionRect() const { return mSelectionRect; }\n  bool openGl() const { return mOpenGl; }\n  \n  // setters:\n  void setViewport(const QRect &rect);\n  void setBufferDevicePixelRatio(double ratio);\n  void setBackground(const QPixmap &pm);\n  void setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding);\n  void setBackground(const QBrush &brush);\n  void setBackgroundScaled(bool scaled);\n  void setBackgroundScaledMode(Qt::AspectRatioMode mode);\n  void setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements);\n  void setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled=true);\n  void setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements);\n  void setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled=true);\n  void setAutoAddPlottableToLegend(bool on);\n  void setInteractions(const QCP::Interactions &interactions);\n  void setInteraction(const QCP::Interaction &interaction, bool enabled=true);\n  void setSelectionTolerance(int pixels);\n  void setNoAntialiasingOnDrag(bool enabled);\n  void setPlottingHints(const QCP::PlottingHints &hints);\n  void setPlottingHint(QCP::PlottingHint hint, bool enabled=true);\n  void setMultiSelectModifier(Qt::KeyboardModifier modifier);\n  void setSelectionRectMode(QCP::SelectionRectMode mode);\n  void setSelectionRect(QCPSelectionRect *selectionRect);\n  void setOpenGl(bool enabled, int multisampling=16);\n  \n  // non-property methods:\n  // plottable interface:\n  QCPAbstractPlottable *plottable(int index);\n  QCPAbstractPlottable *plottable();\n  bool removePlottable(QCPAbstractPlottable *plottable);\n  bool removePlottable(int index);\n  int clearPlottables();\n  int plottableCount() const;\n  QList<QCPAbstractPlottable*> selectedPlottables() const;\n  template<class PlottableType>\n  PlottableType *plottableAt(const QPointF &pos, bool onlySelectable=false, int *dataIndex=nullptr) const;\n  QCPAbstractPlottable *plottableAt(const QPointF &pos, bool onlySelectable=false, int *dataIndex=nullptr) const;\n  bool hasPlottable(QCPAbstractPlottable *plottable) const;\n \n  // specialized interface for QCPGraph:\n  QCPGraph *graph(int index) const;\n  QCPGraph *graph() const;\n  QCPGraph *addGraph(QCPAxis *keyAxis=nullptr, QCPAxis *valueAxis=nullptr);\n  bool removeGraph(QCPGraph *graph);\n  bool removeGraph(int index);\n  int clearGraphs();\n  int graphCount() const;\n  QList<QCPGraph*> selectedGraphs() const;\n\n  // item interface:\n  QCPAbstractItem *item(int index) const;\n  QCPAbstractItem *item() const;\n  bool removeItem(QCPAbstractItem *item);\n  bool removeItem(int index);\n  int clearItems();\n  int itemCount() const;\n  QList<QCPAbstractItem*> selectedItems() const;\n  template<class ItemType>\n  ItemType *itemAt(const QPointF &pos, bool onlySelectable=false) const;\n  QCPAbstractItem *itemAt(const QPointF &pos, bool onlySelectable=false) const;\n  bool hasItem(QCPAbstractItem *item) const;\n  \n  // layer interface:\n  QCPLayer *layer(const QString &name) const;\n  QCPLayer *layer(int index) const;\n  QCPLayer *currentLayer() const;\n  bool setCurrentLayer(const QString &name);\n  bool setCurrentLayer(QCPLayer *layer);\n  int layerCount() const;\n  bool addLayer(const QString &name, QCPLayer *otherLayer=nullptr, LayerInsertMode insertMode=limAbove);\n  bool removeLayer(QCPLayer *layer);\n  bool moveLayer(QCPLayer *layer, QCPLayer *otherLayer, LayerInsertMode insertMode=limAbove);\n  \n  // axis rect/layout interface:\n  int axisRectCount() const;\n  QCPAxisRect* axisRect(int index=0) const;\n  QList<QCPAxisRect*> axisRects() const;\n  QCPLayoutElement* layoutElementAt(const QPointF &pos) const;\n  QCPAxisRect* axisRectAt(const QPointF &pos) const;\n  Q_SLOT void rescaleAxes(bool onlyVisiblePlottables=false);\n  \n  QList<QCPAxis*> selectedAxes() const;\n  QList<QCPLegend*> selectedLegends() const;\n  Q_SLOT void deselectAll();\n  \n  bool savePdf(const QString &fileName, int width=0, int height=0, QCP::ExportPen exportPen=QCP::epAllowCosmetic, const QString &pdfCreator=QString(), const QString &pdfTitle=QString());\n  bool savePng(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch);\n  bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch);\n  bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch);\n  bool saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality=-1, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch);\n  QPixmap toPixmap(int width=0, int height=0, double scale=1.0);\n  void toPainter(QCPPainter *painter, int width=0, int height=0);\n  Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpRefreshHint);\n  double replotTime(bool average=false) const;\n  \n  QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;\n  QCPLegend *legend;\n  \nsignals:\n  void mouseDoubleClick(QMouseEvent *event);\n  void mousePress(QMouseEvent *event);\n  void mouseMove(QMouseEvent *event);\n  void mouseRelease(QMouseEvent *event);\n  void mouseWheel(QWheelEvent *event);\n  \n  void plottableClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event);\n  void plottableDoubleClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event);\n  void itemClick(QCPAbstractItem *item, QMouseEvent *event);\n  void itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event);\n  void axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event);\n  void axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event);\n  void legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event);\n  void legendDoubleClick(QCPLegend *legend,  QCPAbstractLegendItem *item, QMouseEvent *event);\n  \n  void selectionChangedByUser();\n  void beforeReplot();\n  void afterLayout();\n  void afterReplot();\n  \nprotected:\n  // property members:\n  QRect mViewport;\n  double mBufferDevicePixelRatio;\n  QCPLayoutGrid *mPlotLayout;\n  bool mAutoAddPlottableToLegend;\n  QList<QCPAbstractPlottable*> mPlottables;\n  QList<QCPGraph*> mGraphs; // extra list of plottables also in mPlottables that are of type QCPGraph\n  QList<QCPAbstractItem*> mItems;\n  QList<QCPLayer*> mLayers;\n  QCP::AntialiasedElements mAntialiasedElements, mNotAntialiasedElements;\n  QCP::Interactions mInteractions;\n  int mSelectionTolerance;\n  bool mNoAntialiasingOnDrag;\n  QBrush mBackgroundBrush;\n  QPixmap mBackgroundPixmap;\n  QPixmap mScaledBackgroundPixmap;\n  bool mBackgroundScaled;\n  Qt::AspectRatioMode mBackgroundScaledMode;\n  QCPLayer *mCurrentLayer;\n  QCP::PlottingHints mPlottingHints;\n  Qt::KeyboardModifier mMultiSelectModifier;\n  QCP::SelectionRectMode mSelectionRectMode;\n  QCPSelectionRect *mSelectionRect;\n  bool mOpenGl;\n  \n  // non-property members:\n  QList<QSharedPointer<QCPAbstractPaintBuffer> > mPaintBuffers;\n  QPoint mMousePressPos;\n  bool mMouseHasMoved;\n  QPointer<QCPLayerable> mMouseEventLayerable;\n  QPointer<QCPLayerable> mMouseSignalLayerable;\n  QVariant mMouseEventLayerableDetails;\n  QVariant mMouseSignalLayerableDetails;\n  bool mReplotting;\n  bool mReplotQueued;\n  double mReplotTime, mReplotTimeAverage;\n  int mOpenGlMultisamples;\n  QCP::AntialiasedElements mOpenGlAntialiasedElementsBackup;\n  bool mOpenGlCacheLabelsBackup;\n#ifdef QCP_OPENGL_FBO\n  QSharedPointer<QOpenGLContext> mGlContext;\n  QSharedPointer<QSurface> mGlSurface;\n  QSharedPointer<QOpenGLPaintDevice> mGlPaintDevice;\n#endif\n  \n  // reimplemented virtual methods:\n  virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE;\n  virtual QSize sizeHint() const Q_DECL_OVERRIDE;\n  virtual void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;\n  virtual void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE;\n  virtual void mouseDoubleClickEvent(QMouseEvent *event) Q_DECL_OVERRIDE;\n  virtual void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;\n  virtual void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;\n  virtual void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;\n  virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;\n  \n  // introduced virtual methods:\n  virtual void draw(QCPPainter *painter);\n  virtual void updateLayout();\n  virtual void axisRemoved(QCPAxis *axis);\n  virtual void legendRemoved(QCPLegend *legend);\n  Q_SLOT virtual void processRectSelection(QRect rect, QMouseEvent *event);\n  Q_SLOT virtual void processRectZoom(QRect rect, QMouseEvent *event);\n  Q_SLOT virtual void processPointSelection(QMouseEvent *event);\n  \n  // non-virtual methods:\n  bool registerPlottable(QCPAbstractPlottable *plottable);\n  bool registerGraph(QCPGraph *graph);\n  bool registerItem(QCPAbstractItem* item);\n  void updateLayerIndices() const;\n  QCPLayerable *layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=nullptr) const;\n  QList<QCPLayerable*> layerableListAt(const QPointF &pos, bool onlySelectable, QList<QVariant> *selectionDetails=nullptr) const;\n  void drawBackground(QCPPainter *painter);\n  void setupPaintBuffers();\n  QCPAbstractPaintBuffer *createPaintBuffer();\n  bool hasInvalidatedPaintBuffers();\n  bool setupOpenGl();\n  void freeOpenGl();\n  \n  friend class QCPLegend;\n  friend class QCPAxis;\n  friend class QCPLayer;\n  friend class QCPAxisRect;\n  friend class QCPAbstractPlottable;\n  friend class QCPGraph;\n  friend class QCPAbstractItem;\n};\nQ_DECLARE_METATYPE(QCustomPlot::LayerInsertMode)\nQ_DECLARE_METATYPE(QCustomPlot::RefreshPriority)\n\n\n// implementation of template functions:\n\n/*!\n  Returns the plottable at the pixel position \\a pos. The plottable type (a QCPAbstractPlottable\n  subclass) that shall be taken into consideration can be specified via the template parameter.\n\n  Plottables that only consist of single lines (like graphs) have a tolerance band around them, see\n  \\ref setSelectionTolerance. If multiple plottables come into consideration, the one closest to \\a\n  pos is returned.\n  \n  If \\a onlySelectable is true, only plottables that are selectable\n  (QCPAbstractPlottable::setSelectable) are considered.\n  \n  if \\a dataIndex is non-null, it is set to the index of the plottable's data point that is closest\n  to \\a pos.\n\n  If there is no plottable of the specified type at \\a pos, returns \\c nullptr.\n  \n  \\see itemAt, layoutElementAt\n*/\ntemplate<class PlottableType>\nPlottableType *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable, int *dataIndex) const\n{\n  PlottableType *resultPlottable = 0;\n  QVariant resultDetails;\n  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value\n  \n  foreach (QCPAbstractPlottable *plottable, mPlottables)\n  {\n    PlottableType *currentPlottable = qobject_cast<PlottableType*>(plottable);\n    if (!currentPlottable || (onlySelectable && !currentPlottable->selectable())) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractPlottable::selectable\n      continue;\n    if (currentPlottable->clipRect().contains(pos.toPoint())) // only consider clicks where the plottable is actually visible\n    {\n      QVariant details;\n      double currentDistance = currentPlottable->selectTest(pos, false, dataIndex ? &details : nullptr);\n      if (currentDistance >= 0 && currentDistance < resultDistance)\n      {\n        resultPlottable = currentPlottable;\n        resultDetails = details;\n        resultDistance = currentDistance;\n      }\n    }\n  }\n  \n  if (resultPlottable && dataIndex)\n  {\n    QCPDataSelection sel = resultDetails.value<QCPDataSelection>();\n    if (!sel.isEmpty())\n      *dataIndex = sel.dataRange(0).begin();\n  }\n  return resultPlottable;\n}\n\n/*!\n  Returns the item at the pixel position \\a pos. The item type (a QCPAbstractItem subclass) that shall be\n  taken into consideration can be specified via the template parameter. Items that only consist of single\n  lines (e.g. \\ref QCPItemLine or \\ref QCPItemCurve) have a tolerance band around them, see \\ref\n  setSelectionTolerance. If multiple items come into consideration, the one closest to \\a pos is returned.\n  \n  If \\a onlySelectable is true, only items that are selectable (QCPAbstractItem::setSelectable) are\n  considered.\n  \n  If there is no item at \\a pos, returns \\c nullptr.\n  \n  \\see plottableAt, layoutElementAt\n*/\ntemplate<class ItemType>\nItemType *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const\n{\n  ItemType *resultItem = 0;\n  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value\n  \n  foreach (QCPAbstractItem *item, mItems)\n  {\n    ItemType *currentItem = qobject_cast<ItemType*>(item);\n    if (!currentItem || (onlySelectable && !currentItem->selectable())) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractItem::selectable\n      continue;\n    if (!currentItem->clipToAxisRect() || currentItem->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it\n    {\n      double currentDistance = currentItem->selectTest(pos, false);\n      if (currentDistance >= 0 && currentDistance < resultDistance)\n      {\n        resultItem = currentItem;\n        resultDistance = currentDistance;\n      }\n    }\n  }\n  \n  return resultItem;\n}\n\n\n\n/* end of 'src/core.h' */\n\n\n/* including file 'src/plottable1d.h'       */\n/* modified 2021-03-29T02:30:44, size 25638 */\n\nclass QCPPlottableInterface1D\n{\npublic:\n  virtual ~QCPPlottableInterface1D() = default;\n  // introduced pure virtual methods:\n  virtual int dataCount() const = 0;\n  virtual double dataMainKey(int index) const = 0;\n  virtual double dataSortKey(int index) const = 0;\n  virtual double dataMainValue(int index) const = 0;\n  virtual QCPRange dataValueRange(int index) const = 0;\n  virtual QPointF dataPixelPosition(int index) const = 0;\n  virtual bool sortKeyIsMainKey() const = 0;\n  virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const = 0;\n  virtual int findBegin(double sortKey, bool expandedRange=true) const = 0;\n  virtual int findEnd(double sortKey, bool expandedRange=true) const = 0;\n};\n\ntemplate <class DataType>\nclass QCPAbstractPlottable1D : public QCPAbstractPlottable, public QCPPlottableInterface1D // no QCP_LIB_DECL, template class ends up in header (cpp included below)\n{\n  // No Q_OBJECT macro due to template class\n  \npublic:\n  QCPAbstractPlottable1D(QCPAxis *keyAxis, QCPAxis *valueAxis);\n  virtual ~QCPAbstractPlottable1D() Q_DECL_OVERRIDE;\n  \n  // virtual methods of 1d plottable interface:\n  virtual int dataCount() const Q_DECL_OVERRIDE;\n  virtual double dataMainKey(int index) const Q_DECL_OVERRIDE;\n  virtual double dataSortKey(int index) const Q_DECL_OVERRIDE;\n  virtual double dataMainValue(int index) const Q_DECL_OVERRIDE;\n  virtual QCPRange dataValueRange(int index) const Q_DECL_OVERRIDE;\n  virtual QPointF dataPixelPosition(int index) const Q_DECL_OVERRIDE;\n  virtual bool sortKeyIsMainKey() const Q_DECL_OVERRIDE;\n  virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE;\n  virtual int findBegin(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE;\n  virtual int findEnd(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE;\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  virtual QCPPlottableInterface1D *interface1D() Q_DECL_OVERRIDE { return this; }\n  \nprotected:\n  // property members:\n  QSharedPointer<QCPDataContainer<DataType> > mDataContainer;\n  \n  // helpers for subclasses:\n  void getDataSegments(QList<QCPDataRange> &selectedSegments, QList<QCPDataRange> &unselectedSegments) const;\n  void drawPolyline(QCPPainter *painter, const QVector<QPointF> &lineData) const;\n\nprivate:\n  Q_DISABLE_COPY(QCPAbstractPlottable1D)\n  \n};\n\n\n\n// include implementation in header since it is a class template:\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPPlottableInterface1D\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPPlottableInterface1D\n  \\brief Defines an abstract interface for one-dimensional plottables\n\n  This class contains only pure virtual methods which define a common interface to the data\n  of one-dimensional plottables.\n\n  For example, it is implemented by the template class \\ref QCPAbstractPlottable1D (the preferred\n  base class for one-dimensional plottables). So if you use that template class as base class of\n  your one-dimensional plottable, you won't have to care about implementing the 1d interface\n  yourself.\n\n  If your plottable doesn't derive from \\ref QCPAbstractPlottable1D but still wants to provide a 1d\n  interface (e.g. like \\ref QCPErrorBars does), you should inherit from both \\ref\n  QCPAbstractPlottable and \\ref QCPPlottableInterface1D and accordingly reimplement the pure\n  virtual methods of the 1d interface, matching your data container. Also, reimplement \\ref\n  QCPAbstractPlottable::interface1D to return the \\c this pointer.\n\n  If you have a \\ref QCPAbstractPlottable pointer, you can check whether it implements this\n  interface by calling \\ref QCPAbstractPlottable::interface1D and testing it for a non-zero return\n  value. If it indeed implements this interface, you may use it to access the plottable's data\n  without needing to know the exact type of the plottable or its data point type.\n*/\n\n/* start documentation of pure virtual functions */\n\n/*! \\fn virtual int QCPPlottableInterface1D::dataCount() const = 0;\n  \n  Returns the number of data points of the plottable.\n*/\n\n/*! \\fn virtual QCPDataSelection QCPPlottableInterface1D::selectTestRect(const QRectF &rect, bool onlySelectable) const = 0;\n  \n  Returns a data selection containing all the data points of this plottable which are contained (or\n  hit by) \\a rect. This is used mainly in the selection rect interaction for data selection (\\ref\n  dataselection \"data selection mechanism\").\n  \n  If \\a onlySelectable is true, an empty QCPDataSelection is returned if this plottable is not\n  selectable (i.e. if \\ref QCPAbstractPlottable::setSelectable is \\ref QCP::stNone).\n  \n  \\note \\a rect must be a normalized rect (positive or zero width and height). This is especially\n  important when using the rect of \\ref QCPSelectionRect::accepted, which is not necessarily\n  normalized. Use <tt>QRect::normalized()</tt> when passing a rect which might not be normalized.\n*/\n\n/*! \\fn virtual double QCPPlottableInterface1D::dataMainKey(int index) const = 0\n  \n  Returns the main key of the data point at the given \\a index.\n  \n  What the main key is, is defined by the plottable's data type. See the \\ref\n  qcpdatacontainer-datatype \"QCPDataContainer DataType\" documentation for details about this naming\n  convention.\n*/\n\n/*! \\fn virtual double QCPPlottableInterface1D::dataSortKey(int index) const = 0\n  \n  Returns the sort key of the data point at the given \\a index.\n  \n  What the sort key is, is defined by the plottable's data type. See the \\ref\n  qcpdatacontainer-datatype \"QCPDataContainer DataType\" documentation for details about this naming\n  convention.\n*/\n\n/*! \\fn virtual double QCPPlottableInterface1D::dataMainValue(int index) const = 0\n  \n  Returns the main value of the data point at the given \\a index.\n  \n  What the main value is, is defined by the plottable's data type. See the \\ref\n  qcpdatacontainer-datatype \"QCPDataContainer DataType\" documentation for details about this naming\n  convention.\n*/\n\n/*! \\fn virtual QCPRange QCPPlottableInterface1D::dataValueRange(int index) const = 0\n  \n  Returns the value range of the data point at the given \\a index.\n  \n  What the value range is, is defined by the plottable's data type. See the \\ref\n  qcpdatacontainer-datatype \"QCPDataContainer DataType\" documentation for details about this naming\n  convention.\n*/\n\n/*! \\fn virtual QPointF QCPPlottableInterface1D::dataPixelPosition(int index) const = 0\n\n  Returns the pixel position on the widget surface at which the data point at the given \\a index\n  appears.\n\n  Usually this corresponds to the point of \\ref dataMainKey/\\ref dataMainValue, in pixel\n  coordinates. However, depending on the plottable, this might be a different apparent position\n  than just a coord-to-pixel transform of those values. For example, \\ref QCPBars apparent data\n  values can be shifted depending on their stacking, bar grouping or configured base value.\n*/\n\n/*! \\fn virtual bool QCPPlottableInterface1D::sortKeyIsMainKey() const = 0\n\n  Returns whether the sort key (\\ref dataSortKey) is identical to the main key (\\ref dataMainKey).\n\n  What the sort and main keys are, is defined by the plottable's data type. See the \\ref\n  qcpdatacontainer-datatype \"QCPDataContainer DataType\" documentation for details about this naming\n  convention.\n*/\n\n/*! \\fn virtual int QCPPlottableInterface1D::findBegin(double sortKey, bool expandedRange) const = 0\n\n  Returns the index of the data point with a (sort-)key that is equal to, just below, or just above\n  \\a sortKey. If \\a expandedRange is true, the data point just below \\a sortKey will be considered,\n  otherwise the one just above.\n\n  This can be used in conjunction with \\ref findEnd to iterate over data points within a given key\n  range, including or excluding the bounding data points that are just beyond the specified range.\n\n  If \\a expandedRange is true but there are no data points below \\a sortKey, 0 is returned.\n\n  If the container is empty, returns 0 (in that case, \\ref findEnd will also return 0, so a loop\n  using these methods will not iterate over the index 0).\n\n  \\see findEnd, QCPDataContainer::findBegin\n*/\n\n/*! \\fn virtual int QCPPlottableInterface1D::findEnd(double sortKey, bool expandedRange) const = 0\n\n  Returns the index one after the data point with a (sort-)key that is equal to, just above, or\n  just below \\a sortKey. If \\a expandedRange is true, the data point just above \\a sortKey will be\n  considered, otherwise the one just below.\n\n  This can be used in conjunction with \\ref findBegin to iterate over data points within a given\n  key range, including the bounding data points that are just below and above the specified range.\n\n  If \\a expandedRange is true but there are no data points above \\a sortKey, the index just above the\n  highest data point is returned.\n\n  If the container is empty, returns 0.\n\n  \\see findBegin, QCPDataContainer::findEnd\n*/\n\n/* end documentation of pure virtual functions */\n\n\n////////////////////////////////////////////////////////////////////////////////////////////////////\n//////////////////// QCPAbstractPlottable1D\n////////////////////////////////////////////////////////////////////////////////////////////////////\n\n/*! \\class QCPAbstractPlottable1D\n  \\brief A template base class for plottables with one-dimensional data\n\n  This template class derives from \\ref QCPAbstractPlottable and from the abstract interface \\ref\n  QCPPlottableInterface1D. It serves as a base class for all one-dimensional data (i.e. data with\n  one key dimension), such as \\ref QCPGraph and QCPCurve.\n\n  The template parameter \\a DataType is the type of the data points of this plottable (e.g. \\ref\n  QCPGraphData or \\ref QCPCurveData). The main purpose of this base class is to provide the member\n  \\a mDataContainer (a shared pointer to a \\ref QCPDataContainer \"QCPDataContainer<DataType>\") and\n  implement the according virtual methods of the \\ref QCPPlottableInterface1D, such that most\n  subclassed plottables don't need to worry about this anymore.\n\n  Further, it provides a convenience method for retrieving selected/unselected data segments via\n  \\ref getDataSegments. This is useful when subclasses implement their \\ref draw method and need to\n  draw selected segments with a different pen/brush than unselected segments (also see \\ref\n  QCPSelectionDecorator).\n\n  This class implements basic functionality of \\ref QCPAbstractPlottable::selectTest and \\ref\n  QCPPlottableInterface1D::selectTestRect, assuming point-like data points, based on the 1D data\n  interface. In spite of that, most plottable subclasses will want to reimplement those methods\n  again, to provide a more accurate hit test based on their specific data visualization geometry.\n*/\n\n/* start documentation of inline functions */\n\n/*! \\fn QCPPlottableInterface1D *QCPAbstractPlottable1D::interface1D()\n  \n  Returns a \\ref QCPPlottableInterface1D pointer to this plottable, providing access to its 1D\n  interface.\n  \n  \\seebaseclassmethod\n*/\n\n/* end documentation of inline functions */\n\n/*!\n  Forwards \\a keyAxis and \\a valueAxis to the \\ref QCPAbstractPlottable::QCPAbstractPlottable\n  \"QCPAbstractPlottable\" constructor and allocates the \\a mDataContainer.\n*/\ntemplate <class DataType>\nQCPAbstractPlottable1D<DataType>::QCPAbstractPlottable1D(QCPAxis *keyAxis, QCPAxis *valueAxis) :\n  QCPAbstractPlottable(keyAxis, valueAxis),\n  mDataContainer(new QCPDataContainer<DataType>)\n{\n}\n\ntemplate <class DataType>\nQCPAbstractPlottable1D<DataType>::~QCPAbstractPlottable1D()\n{\n}\n\n/*!\n  \\copydoc QCPPlottableInterface1D::dataCount\n*/\ntemplate <class DataType>\nint QCPAbstractPlottable1D<DataType>::dataCount() const\n{\n  return mDataContainer->size();\n}\n\n/*!\n  \\copydoc QCPPlottableInterface1D::dataMainKey\n*/\ntemplate <class DataType>\ndouble QCPAbstractPlottable1D<DataType>::dataMainKey(int index) const\n{\n  if (index >= 0 && index < mDataContainer->size())\n  {\n    return (mDataContainer->constBegin()+index)->mainKey();\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"Index out of bounds\" << index;\n    return 0;\n  }\n}\n\n/*!\n  \\copydoc QCPPlottableInterface1D::dataSortKey\n*/\ntemplate <class DataType>\ndouble QCPAbstractPlottable1D<DataType>::dataSortKey(int index) const\n{\n  if (index >= 0 && index < mDataContainer->size())\n  {\n    return (mDataContainer->constBegin()+index)->sortKey();\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"Index out of bounds\" << index;\n    return 0;\n  }\n}\n\n/*!\n  \\copydoc QCPPlottableInterface1D::dataMainValue\n*/\ntemplate <class DataType>\ndouble QCPAbstractPlottable1D<DataType>::dataMainValue(int index) const\n{\n  if (index >= 0 && index < mDataContainer->size())\n  {\n    return (mDataContainer->constBegin()+index)->mainValue();\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"Index out of bounds\" << index;\n    return 0;\n  }\n}\n\n/*!\n  \\copydoc QCPPlottableInterface1D::dataValueRange\n*/\ntemplate <class DataType>\nQCPRange QCPAbstractPlottable1D<DataType>::dataValueRange(int index) const\n{\n  if (index >= 0 && index < mDataContainer->size())\n  {\n    return (mDataContainer->constBegin()+index)->valueRange();\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"Index out of bounds\" << index;\n    return QCPRange(0, 0);\n  }\n}\n\n/*!\n  \\copydoc QCPPlottableInterface1D::dataPixelPosition\n*/\ntemplate <class DataType>\nQPointF QCPAbstractPlottable1D<DataType>::dataPixelPosition(int index) const\n{\n  if (index >= 0 && index < mDataContainer->size())\n  {\n    const typename QCPDataContainer<DataType>::const_iterator it = mDataContainer->constBegin()+index;\n    return coordsToPixels(it->mainKey(), it->mainValue());\n  } else\n  {\n    qDebug() << Q_FUNC_INFO << \"Index out of bounds\" << index;\n    return QPointF();\n  }\n}\n\n/*!\n  \\copydoc QCPPlottableInterface1D::sortKeyIsMainKey\n*/\ntemplate <class DataType>\nbool QCPAbstractPlottable1D<DataType>::sortKeyIsMainKey() const\n{\n  return DataType::sortKeyIsMainKey();\n}\n\n/*!\n  Implements a rect-selection algorithm assuming the data (accessed via the 1D data interface) is\n  point-like. Most subclasses will want to reimplement this method again, to provide a more\n  accurate hit test based on the true data visualization geometry.\n\n  \\seebaseclassmethod\n*/\ntemplate <class DataType>\nQCPDataSelection QCPAbstractPlottable1D<DataType>::selectTestRect(const QRectF &rect, bool onlySelectable) const\n{\n  QCPDataSelection result;\n  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())\n    return result;\n  if (!mKeyAxis || !mValueAxis)\n    return result;\n  \n  // convert rect given in pixels to ranges given in plot coordinates:\n  double key1, value1, key2, value2;\n  pixelsToCoords(rect.topLeft(), key1, value1);\n  pixelsToCoords(rect.bottomRight(), key2, value2);\n  QCPRange keyRange(key1, key2); // QCPRange normalizes internally so we don't have to care about whether key1 < key2\n  QCPRange valueRange(value1, value2);\n  typename QCPDataContainer<DataType>::const_iterator begin = mDataContainer->constBegin();\n  typename QCPDataContainer<DataType>::const_iterator end = mDataContainer->constEnd();\n  if (DataType::sortKeyIsMainKey()) // we can assume that data is sorted by main key, so can reduce the searched key interval:\n  {\n    begin = mDataContainer->findBegin(keyRange.lower, false);\n    end = mDataContainer->findEnd(keyRange.upper, false);\n  }\n  if (begin == end)\n    return result;\n  \n  int currentSegmentBegin = -1; // -1 means we're currently not in a segment that's contained in rect\n  for (typename QCPDataContainer<DataType>::const_iterator it=begin; it!=end; ++it)\n  {\n    if (currentSegmentBegin == -1)\n    {\n      if (valueRange.contains(it->mainValue()) && keyRange.contains(it->mainKey())) // start segment\n        currentSegmentBegin = int(it-mDataContainer->constBegin());\n    } else if (!valueRange.contains(it->mainValue()) || !keyRange.contains(it->mainKey())) // segment just ended\n    {\n      result.addDataRange(QCPDataRange(currentSegmentBegin, int(it-mDataContainer->constBegin())), false);\n      currentSegmentBegin = -1;\n    }\n  }\n  // process potential last segment:\n  if (currentSegmentBegin != -1)\n    result.addDataRange(QCPDataRange(currentSegmentBegin, int(end-mDataContainer->constBegin())), false);\n  \n  result.simplify();\n  return result;\n}\n\n/*!\n  \\copydoc QCPPlottableInterface1D::findBegin\n*/\ntemplate <class DataType>\nint QCPAbstractPlottable1D<DataType>::findBegin(double sortKey, bool expandedRange) const\n{\n  return int(mDataContainer->findBegin(sortKey, expandedRange)-mDataContainer->constBegin());\n}\n\n/*!\n  \\copydoc QCPPlottableInterface1D::findEnd\n*/\ntemplate <class DataType>\nint QCPAbstractPlottable1D<DataType>::findEnd(double sortKey, bool expandedRange) const\n{\n  return int(mDataContainer->findEnd(sortKey, expandedRange)-mDataContainer->constBegin());\n}\n\n/*!\n  Implements a point-selection algorithm assuming the data (accessed via the 1D data interface) is\n  point-like. Most subclasses will want to reimplement this method again, to provide a more\n  accurate hit test based on the true data visualization geometry.\n\n  If \\a details is not 0, it will be set to a \\ref QCPDataSelection, describing the closest data point\n  to \\a pos.\n  \n  \\seebaseclassmethod\n*/\ntemplate <class DataType>\ndouble QCPAbstractPlottable1D<DataType>::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const\n{\n  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())\n    return -1;\n  if (!mKeyAxis || !mValueAxis)\n    return -1;\n  \n  QCPDataSelection selectionResult;\n  double minDistSqr = (std::numeric_limits<double>::max)();\n  int minDistIndex = mDataContainer->size();\n  \n  typename QCPDataContainer<DataType>::const_iterator begin = mDataContainer->constBegin();\n  typename QCPDataContainer<DataType>::const_iterator end = mDataContainer->constEnd();\n  if (DataType::sortKeyIsMainKey()) // we can assume that data is sorted by main key, so can reduce the searched key interval:\n  {\n    // determine which key range comes into question, taking selection tolerance around pos into account:\n    double posKeyMin, posKeyMax, dummy;\n    pixelsToCoords(pos-QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMin, dummy);\n    pixelsToCoords(pos+QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMax, dummy);\n    if (posKeyMin > posKeyMax)\n      qSwap(posKeyMin, posKeyMax);\n    begin = mDataContainer->findBegin(posKeyMin, true);\n    end = mDataContainer->findEnd(posKeyMax, true);\n  }\n  if (begin == end)\n    return -1;\n  QCPRange keyRange(mKeyAxis->range());\n  QCPRange valueRange(mValueAxis->range());\n  for (typename QCPDataContainer<DataType>::const_iterator it=begin; it!=end; ++it)\n  {\n    const double mainKey = it->mainKey();\n    const double mainValue = it->mainValue();\n    if (keyRange.contains(mainKey) && valueRange.contains(mainValue)) // make sure data point is inside visible range, for speedup in cases where sort key isn't main key and we iterate over all points\n    {\n      const double currentDistSqr = QCPVector2D(coordsToPixels(mainKey, mainValue)-pos).lengthSquared();\n      if (currentDistSqr < minDistSqr)\n      {\n        minDistSqr = currentDistSqr;\n        minDistIndex = int(it-mDataContainer->constBegin());\n      }\n    }\n  }\n  if (minDistIndex != mDataContainer->size())\n    selectionResult.addDataRange(QCPDataRange(minDistIndex, minDistIndex+1), false);\n  \n  selectionResult.simplify();\n  if (details)\n    details->setValue(selectionResult);\n  return qSqrt(minDistSqr);\n}\n\n/*!\n  Splits all data into selected and unselected segments and outputs them via \\a selectedSegments\n  and \\a unselectedSegments, respectively.\n\n  This is useful when subclasses implement their \\ref draw method and need to draw selected\n  segments with a different pen/brush than unselected segments (also see \\ref\n  QCPSelectionDecorator).\n\n  \\see setSelection\n*/\ntemplate <class DataType>\nvoid QCPAbstractPlottable1D<DataType>::getDataSegments(QList<QCPDataRange> &selectedSegments, QList<QCPDataRange> &unselectedSegments) const\n{\n  selectedSegments.clear();\n  unselectedSegments.clear();\n  if (mSelectable == QCP::stWhole) // stWhole selection type draws the entire plottable with selected style if mSelection isn't empty\n  {\n    if (selected())\n      selectedSegments << QCPDataRange(0, dataCount());\n    else\n      unselectedSegments << QCPDataRange(0, dataCount());\n  } else\n  {\n    QCPDataSelection sel(selection());\n    sel.simplify();\n    selectedSegments = sel.dataRanges();\n    unselectedSegments = sel.inverse(QCPDataRange(0, dataCount())).dataRanges();\n  }\n}\n\n/*!\n  A helper method which draws a line with the passed \\a painter, according to the pixel data in \\a\n  lineData. NaN points create gaps in the line, as expected from QCustomPlot's plottables (this is\n  the main difference to QPainter's regular drawPolyline, which handles NaNs by lagging or\n  crashing).\n\n  Further it uses a faster line drawing technique based on \\ref QCPPainter::drawLine rather than \\c\n  QPainter::drawPolyline if the configured \\ref QCustomPlot::setPlottingHints() and \\a painter\n  style allows.\n*/\ntemplate <class DataType>\nvoid QCPAbstractPlottable1D<DataType>::drawPolyline(QCPPainter *painter, const QVector<QPointF> &lineData) const\n{\n  // if drawing lines in plot (instead of PDF), reduce 1px lines to cosmetic, because at least in\n  // Qt6 drawing of \"1px\" width lines is much slower even though it has same appearance apart from\n  // High-DPI. In High-DPI cases people must set a pen width slightly larger than 1.0 to get\n  // correct DPI scaling of width, but of course with performance penalty.\n  if (!painter->modes().testFlag(QCPPainter::pmVectorized) &&\n      qFuzzyCompare(painter->pen().widthF(), 1.0))\n  {\n    QPen newPen = painter->pen();\n    newPen.setWidth(0);\n    painter->setPen(newPen);\n  }\n\n  // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:\n  if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&\n      painter->pen().style() == Qt::SolidLine &&\n      !painter->modes().testFlag(QCPPainter::pmVectorized) &&\n      !painter->modes().testFlag(QCPPainter::pmNoCaching))\n  {\n    int i = 0;\n    bool lastIsNan = false;\n    const int lineDataSize = lineData.size();\n    while (i < lineDataSize && (qIsNaN(lineData.at(i).y()) || qIsNaN(lineData.at(i).x()))) // make sure first point is not NaN\n      ++i;\n    ++i; // because drawing works in 1 point retrospect\n    while (i < lineDataSize)\n    {\n      if (!qIsNaN(lineData.at(i).y()) && !qIsNaN(lineData.at(i).x())) // NaNs create a gap in the line\n      {\n        if (!lastIsNan)\n          painter->drawLine(lineData.at(i-1), lineData.at(i));\n        else\n          lastIsNan = false;\n      } else\n        lastIsNan = true;\n      ++i;\n    }\n  } else\n  {\n    int segmentStart = 0;\n    int i = 0;\n    const int lineDataSize = lineData.size();\n    while (i < lineDataSize)\n    {\n      if (qIsNaN(lineData.at(i).y()) || qIsNaN(lineData.at(i).x()) || qIsInf(lineData.at(i).y())) // NaNs create a gap in the line. Also filter Infs which make drawPolyline block\n      {\n        painter->drawPolyline(lineData.constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point\n        segmentStart = i+1;\n      }\n      ++i;\n    }\n    // draw last segment:\n    painter->drawPolyline(lineData.constData()+segmentStart, lineDataSize-segmentStart);\n  }\n}\n\n\n/* end of 'src/plottable1d.h' */\n\n\n/* including file 'src/colorgradient.h'    */\n/* modified 2021-03-29T02:30:44, size 7262 */\n\nclass QCP_LIB_DECL QCPColorGradient\n{\n  Q_GADGET\npublic:\n  /*!\n    Defines the color spaces in which color interpolation between gradient stops can be performed.\n    \n    \\see setColorInterpolation\n  */\n  enum ColorInterpolation { ciRGB  ///< Color channels red, green and blue are linearly interpolated\n                            ,ciHSV ///< Color channels hue, saturation and value are linearly interpolated (The hue is interpolated over the shortest angle distance)\n                          };\n  Q_ENUMS(ColorInterpolation)\n  \n  /*!\n    Defines how NaN data points shall appear in the plot.\n    \n    \\see setNanHandling, setNanColor\n  */\n  enum NanHandling { nhNone ///< NaN data points are not explicitly handled and shouldn't occur in the data (this gives slight performance improvement)\n                     ,nhLowestColor  ///< NaN data points appear as the lowest color defined in this QCPColorGradient\n                     ,nhHighestColor ///< NaN data points appear as the highest color defined in this QCPColorGradient\n                     ,nhTransparent ///< NaN data points appear transparent\n                     ,nhNanColor ///< NaN data points appear as the color defined with \\ref setNanColor\n                   };\n  Q_ENUMS(NanHandling)\n  \n  /*!\n    Defines the available presets that can be loaded with \\ref loadPreset. See the documentation\n    there for an image of the presets.\n  */\n  enum GradientPreset { gpGrayscale  ///< Continuous lightness from black to white (suited for non-biased data representation)\n                        ,gpHot       ///< Continuous lightness from black over firey colors to white (suited for non-biased data representation)\n                        ,gpCold      ///< Continuous lightness from black over icey colors to white (suited for non-biased data representation)\n                        ,gpNight     ///< Continuous lightness from black over weak blueish colors to white (suited for non-biased data representation)\n                        ,gpCandy     ///< Blue over pink to white\n                        ,gpGeography ///< Colors suitable to represent different elevations on geographical maps\n                        ,gpIon       ///< Half hue spectrum from black over purple to blue and finally green (creates banding illusion but allows more precise magnitude estimates)\n                        ,gpThermal   ///< Colors suitable for thermal imaging, ranging from dark blue over purple to orange, yellow and white\n                        ,gpPolar     ///< Colors suitable to emphasize polarity around the center, with blue for negative, black in the middle and red for positive values\n                        ,gpSpectrum  ///< An approximation of the visible light spectrum (creates banding illusion but allows more precise magnitude estimates)\n                        ,gpJet       ///< Hue variation similar to a spectrum, often used in numerical visualization (creates banding illusion but allows more precise magnitude estimates)\n                        ,gpHues      ///< Full hue cycle, with highest and lowest color red (suitable for periodic data, such as angles and phases, see \\ref setPeriodic)\n                      };\n  Q_ENUMS(GradientPreset)\n  \n  QCPColorGradient();\n  QCPColorGradient(GradientPreset preset);\n  bool operator==(const QCPColorGradient &other) const;\n  bool operator!=(const QCPColorGradient &other) const { return !(*this == other); }\n  \n  // getters:\n  int levelCount() const { return mLevelCount; }\n  QMap<double, QColor> colorStops() const { return mColorStops; }\n  ColorInterpolation colorInterpolation() const { return mColorInterpolation; }\n  NanHandling nanHandling() const { return mNanHandling; }\n  QColor nanColor() const { return mNanColor; }\n  bool periodic() const { return mPeriodic; }\n  \n  // setters:\n  void setLevelCount(int n);\n  void setColorStops(const QMap<double, QColor> &colorStops);\n  void setColorStopAt(double position, const QColor &color);\n  void setColorInterpolation(ColorInterpolation interpolation);\n  void setNanHandling(NanHandling handling);\n  void setNanColor(const QColor &color);\n  void setPeriodic(bool enabled);\n  \n  // non-property methods:\n  void colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor=1, bool logarithmic=false);\n  void colorize(const double *data, const unsigned char *alpha, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor=1, bool logarithmic=false);\n  QRgb color(double position, const QCPRange &range, bool logarithmic=false);\n  void loadPreset(GradientPreset preset);\n  void clearColorStops();\n  QCPColorGradient inverted() const;\n  \nprotected:\n  // property members:\n  int mLevelCount;\n  QMap<double, QColor> mColorStops;\n  ColorInterpolation mColorInterpolation;\n  NanHandling mNanHandling;\n  QColor mNanColor;\n  bool mPeriodic;\n  \n  // non-property members:\n  QVector<QRgb> mColorBuffer; // have colors premultiplied with alpha (for usage with QImage::Format_ARGB32_Premultiplied)\n  bool mColorBufferInvalidated;\n  \n  // non-virtual methods:\n  bool stopsUseAlpha() const;\n  void updateColorBuffer();\n};\nQ_DECLARE_METATYPE(QCPColorGradient::ColorInterpolation)\nQ_DECLARE_METATYPE(QCPColorGradient::NanHandling)\nQ_DECLARE_METATYPE(QCPColorGradient::GradientPreset)\n\n/* end of 'src/colorgradient.h' */\n\n\n/* including file 'src/selectiondecorator-bracket.h' */\n/* modified 2021-03-29T02:30:44, size 4458           */\n\nclass QCP_LIB_DECL QCPSelectionDecoratorBracket : public QCPSelectionDecorator\n{\n  Q_GADGET\npublic:\n  \n  /*!\n    Defines which shape is drawn at the boundaries of selected data ranges.\n    \n    Some of the bracket styles further allow specifying a height and/or width, see \\ref\n    setBracketHeight and \\ref setBracketWidth.\n  */\n  enum BracketStyle { bsSquareBracket ///< A square bracket is drawn.\n                      ,bsHalfEllipse   ///< A half ellipse is drawn. The size of the ellipse is given by the bracket width/height properties.\n                      ,bsEllipse       ///< An ellipse is drawn. The size of the ellipse is given by the bracket width/height properties.\n                      ,bsPlus         ///< A plus is drawn.\n                      ,bsUserStyle    ///< Start custom bracket styles at this index when subclassing and reimplementing \\ref drawBracket.\n  };\n  Q_ENUMS(BracketStyle)\n  \n  QCPSelectionDecoratorBracket();\n  virtual ~QCPSelectionDecoratorBracket() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QPen bracketPen() const { return mBracketPen; }\n  QBrush bracketBrush() const { return mBracketBrush; }\n  int bracketWidth() const { return mBracketWidth; }\n  int bracketHeight() const { return mBracketHeight; }\n  BracketStyle bracketStyle() const { return mBracketStyle; }\n  bool tangentToData() const { return mTangentToData; }\n  int tangentAverage() const { return mTangentAverage; }\n  \n  // setters:\n  void setBracketPen(const QPen &pen);\n  void setBracketBrush(const QBrush &brush);\n  void setBracketWidth(int width);\n  void setBracketHeight(int height);\n  void setBracketStyle(BracketStyle style);\n  void setTangentToData(bool enabled);\n  void setTangentAverage(int pointCount);\n  \n  // introduced virtual methods:\n  virtual void drawBracket(QCPPainter *painter, int direction) const;\n  \n  // virtual methods:\n  virtual void drawDecoration(QCPPainter *painter, QCPDataSelection selection) Q_DECL_OVERRIDE;\n  \nprotected:\n  // property members:\n  QPen mBracketPen;\n  QBrush mBracketBrush;\n  int mBracketWidth;\n  int mBracketHeight;\n  BracketStyle mBracketStyle;\n  bool mTangentToData;\n  int mTangentAverage;\n  \n  // non-virtual methods:\n  double getTangentAngle(const QCPPlottableInterface1D *interface1d, int dataIndex, int direction) const;\n  QPointF getPixelCoordinates(const QCPPlottableInterface1D *interface1d, int dataIndex) const;\n  \n};\nQ_DECLARE_METATYPE(QCPSelectionDecoratorBracket::BracketStyle)\n\n/* end of 'src/selectiondecorator-bracket.h' */\n\n\n/* including file 'src/layoutelements/layoutelement-axisrect.h' */\n/* modified 2021-03-29T02:30:44, size 7529                      */\n\nclass QCP_LIB_DECL QCPAxisRect : public QCPLayoutElement\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QPixmap background READ background WRITE setBackground)\n  Q_PROPERTY(bool backgroundScaled READ backgroundScaled WRITE setBackgroundScaled)\n  Q_PROPERTY(Qt::AspectRatioMode backgroundScaledMode READ backgroundScaledMode WRITE setBackgroundScaledMode)\n  Q_PROPERTY(Qt::Orientations rangeDrag READ rangeDrag WRITE setRangeDrag)\n  Q_PROPERTY(Qt::Orientations rangeZoom READ rangeZoom WRITE setRangeZoom)\n  /// \\endcond\npublic:\n  explicit QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes=true);\n  virtual ~QCPAxisRect() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QPixmap background() const { return mBackgroundPixmap; }\n  QBrush backgroundBrush() const { return mBackgroundBrush; }\n  bool backgroundScaled() const { return mBackgroundScaled; }\n  Qt::AspectRatioMode backgroundScaledMode() const { return mBackgroundScaledMode; }\n  Qt::Orientations rangeDrag() const { return mRangeDrag; }\n  Qt::Orientations rangeZoom() const { return mRangeZoom; }\n  QCPAxis *rangeDragAxis(Qt::Orientation orientation);\n  QCPAxis *rangeZoomAxis(Qt::Orientation orientation);\n  QList<QCPAxis*> rangeDragAxes(Qt::Orientation orientation);\n  QList<QCPAxis*> rangeZoomAxes(Qt::Orientation orientation);\n  double rangeZoomFactor(Qt::Orientation orientation);\n  \n  // setters:\n  void setBackground(const QPixmap &pm);\n  void setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding);\n  void setBackground(const QBrush &brush);\n  void setBackgroundScaled(bool scaled);\n  void setBackgroundScaledMode(Qt::AspectRatioMode mode);\n  void setRangeDrag(Qt::Orientations orientations);\n  void setRangeZoom(Qt::Orientations orientations);\n  void setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical);\n  void setRangeDragAxes(QList<QCPAxis*> axes);\n  void setRangeDragAxes(QList<QCPAxis*> horizontal, QList<QCPAxis*> vertical);\n  void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical);\n  void setRangeZoomAxes(QList<QCPAxis*> axes);\n  void setRangeZoomAxes(QList<QCPAxis*> horizontal, QList<QCPAxis*> vertical);\n  void setRangeZoomFactor(double horizontalFactor, double verticalFactor);\n  void setRangeZoomFactor(double factor);\n  \n  // non-property methods:\n  int axisCount(QCPAxis::AxisType type) const;\n  QCPAxis *axis(QCPAxis::AxisType type, int index=0) const;\n  QList<QCPAxis*> axes(QCPAxis::AxisTypes types) const;\n  QList<QCPAxis*> axes() const;\n  QCPAxis *addAxis(QCPAxis::AxisType type, QCPAxis *axis=nullptr);\n  QList<QCPAxis*> addAxes(QCPAxis::AxisTypes types);\n  bool removeAxis(QCPAxis *axis);\n  QCPLayoutInset *insetLayout() const { return mInsetLayout; }\n  \n  void zoom(const QRectF &pixelRect);\n  void zoom(const QRectF &pixelRect, const QList<QCPAxis*> &affectedAxes);\n  void setupFullAxesBox(bool connectRanges=false);\n  QList<QCPAbstractPlottable*> plottables() const;\n  QList<QCPGraph*> graphs() const;\n  QList<QCPAbstractItem*> items() const;\n  \n  // read-only interface imitating a QRect:\n  int left() const { return mRect.left(); }\n  int right() const { return mRect.right(); }\n  int top() const { return mRect.top(); }\n  int bottom() const { return mRect.bottom(); }\n  int width() const { return mRect.width(); }\n  int height() const { return mRect.height(); }\n  QSize size() const { return mRect.size(); }\n  QPoint topLeft() const { return mRect.topLeft(); }\n  QPoint topRight() const { return mRect.topRight(); }\n  QPoint bottomLeft() const { return mRect.bottomLeft(); }\n  QPoint bottomRight() const { return mRect.bottomRight(); }\n  QPoint center() const { return mRect.center(); }\n  \n  // reimplemented virtual methods:\n  virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE;\n  virtual QList<QCPLayoutElement*> elements(bool recursive) const Q_DECL_OVERRIDE;\n\nprotected:\n  // property members:\n  QBrush mBackgroundBrush;\n  QPixmap mBackgroundPixmap;\n  QPixmap mScaledBackgroundPixmap;\n  bool mBackgroundScaled;\n  Qt::AspectRatioMode mBackgroundScaledMode;\n  QCPLayoutInset *mInsetLayout;\n  Qt::Orientations mRangeDrag, mRangeZoom;\n  QList<QPointer<QCPAxis> > mRangeDragHorzAxis, mRangeDragVertAxis;\n  QList<QPointer<QCPAxis> > mRangeZoomHorzAxis, mRangeZoomVertAxis;\n  double mRangeZoomFactorHorz, mRangeZoomFactorVert;\n  \n  // non-property members:\n  QList<QCPRange> mDragStartHorzRange, mDragStartVertRange;\n  QCP::AntialiasedElements mAADragBackup, mNotAADragBackup;\n  bool mDragging;\n  QHash<QCPAxis::AxisType, QList<QCPAxis*> > mAxes;\n  \n  // reimplemented virtual methods:\n  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual int calculateAutoMargin(QCP::MarginSide side) Q_DECL_OVERRIDE;\n  virtual void layoutChanged() Q_DECL_OVERRIDE;\n  // events:\n  virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE;\n  virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;\n  virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;\n  virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;\n  \n  // non-property methods:\n  void drawBackground(QCPPainter *painter);\n  void updateAxesOffset(QCPAxis::AxisType type);\n  \nprivate:\n  Q_DISABLE_COPY(QCPAxisRect)\n  \n  friend class QCustomPlot;\n};\n\n\n/* end of 'src/layoutelements/layoutelement-axisrect.h' */\n\n\n/* including file 'src/layoutelements/layoutelement-legend.h' */\n/* modified 2021-03-29T02:30:44, size 10425                   */\n\nclass QCP_LIB_DECL QCPAbstractLegendItem : public QCPLayoutElement\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QCPLegend* parentLegend READ parentLegend)\n  Q_PROPERTY(QFont font READ font WRITE setFont)\n  Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)\n  Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)\n  Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor)\n  Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectionChanged)\n  Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectableChanged)\n  /// \\endcond\npublic:\n  explicit QCPAbstractLegendItem(QCPLegend *parent);\n  \n  // getters:\n  QCPLegend *parentLegend() const { return mParentLegend; }\n  QFont font() const { return mFont; }\n  QColor textColor() const { return mTextColor; }\n  QFont selectedFont() const { return mSelectedFont; }\n  QColor selectedTextColor() const { return mSelectedTextColor; }\n  bool selectable() const { return mSelectable; }\n  bool selected() const { return mSelected; }\n  \n  // setters:\n  void setFont(const QFont &font);\n  void setTextColor(const QColor &color);\n  void setSelectedFont(const QFont &font);\n  void setSelectedTextColor(const QColor &color);\n  Q_SLOT void setSelectable(bool selectable);\n  Q_SLOT void setSelected(bool selected);\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  \nsignals:\n  void selectionChanged(bool selected);\n  void selectableChanged(bool selectable);\n  \nprotected:\n  // property members:\n  QCPLegend *mParentLegend;\n  QFont mFont;\n  QColor mTextColor;\n  QFont mSelectedFont;\n  QColor mSelectedTextColor;\n  bool mSelectable, mSelected;\n  \n  // reimplemented virtual methods:\n  virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE;\n  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;\n  virtual QRect clipRect() const Q_DECL_OVERRIDE;\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE = 0;\n  // events:\n  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE;\n  virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE;\n  \nprivate:\n  Q_DISABLE_COPY(QCPAbstractLegendItem)\n  \n  friend class QCPLegend;\n};\n\n\nclass QCP_LIB_DECL QCPPlottableLegendItem : public QCPAbstractLegendItem\n{\n  Q_OBJECT\npublic:\n  QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable);\n  \n  // getters:\n  QCPAbstractPlottable *plottable() { return mPlottable; }\n  \nprotected:\n  // property members:\n  QCPAbstractPlottable *mPlottable;\n  \n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  QPen getIconBorderPen() const;\n  QColor getTextColor() const;\n  QFont getFont() const;\n};\n\n\nclass QCP_LIB_DECL QCPLegend : public QCPLayoutGrid\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QPen borderPen READ borderPen WRITE setBorderPen)\n  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)\n  Q_PROPERTY(QFont font READ font WRITE setFont)\n  Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)\n  Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize)\n  Q_PROPERTY(int iconTextPadding READ iconTextPadding WRITE setIconTextPadding)\n  Q_PROPERTY(QPen iconBorderPen READ iconBorderPen WRITE setIconBorderPen)\n  Q_PROPERTY(SelectableParts selectableParts READ selectableParts WRITE setSelectableParts NOTIFY selectionChanged)\n  Q_PROPERTY(SelectableParts selectedParts READ selectedParts WRITE setSelectedParts NOTIFY selectableChanged)\n  Q_PROPERTY(QPen selectedBorderPen READ selectedBorderPen WRITE setSelectedBorderPen)\n  Q_PROPERTY(QPen selectedIconBorderPen READ selectedIconBorderPen WRITE setSelectedIconBorderPen)\n  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)\n  Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)\n  Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor)\n  /// \\endcond\npublic:\n  /*!\n    Defines the selectable parts of a legend\n    \n    \\see setSelectedParts, setSelectableParts\n  */\n  enum SelectablePart { spNone        = 0x000 ///< <tt>0x000</tt> None\n                        ,spLegendBox  = 0x001 ///< <tt>0x001</tt> The legend box (frame)\n                        ,spItems      = 0x002 ///< <tt>0x002</tt> Legend items individually (see \\ref selectedItems)\n                      };\n  Q_ENUMS(SelectablePart)\n  Q_FLAGS(SelectableParts)\n  Q_DECLARE_FLAGS(SelectableParts, SelectablePart)\n  \n  explicit QCPLegend();\n  virtual ~QCPLegend() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QPen borderPen() const { return mBorderPen; }\n  QBrush brush() const { return mBrush; }\n  QFont font() const { return mFont; }\n  QColor textColor() const { return mTextColor; }\n  QSize iconSize() const { return mIconSize; }\n  int iconTextPadding() const { return mIconTextPadding; }\n  QPen iconBorderPen() const { return mIconBorderPen; }\n  SelectableParts selectableParts() const { return mSelectableParts; }\n  SelectableParts selectedParts() const;\n  QPen selectedBorderPen() const { return mSelectedBorderPen; }\n  QPen selectedIconBorderPen() const { return mSelectedIconBorderPen; }\n  QBrush selectedBrush() const { return mSelectedBrush; }\n  QFont selectedFont() const { return mSelectedFont; }\n  QColor selectedTextColor() const { return mSelectedTextColor; }\n  \n  // setters:\n  void setBorderPen(const QPen &pen);\n  void setBrush(const QBrush &brush);\n  void setFont(const QFont &font);\n  void setTextColor(const QColor &color);\n  void setIconSize(const QSize &size);\n  void setIconSize(int width, int height);\n  void setIconTextPadding(int padding);\n  void setIconBorderPen(const QPen &pen);\n  Q_SLOT void setSelectableParts(const SelectableParts &selectableParts);\n  Q_SLOT void setSelectedParts(const SelectableParts &selectedParts);\n  void setSelectedBorderPen(const QPen &pen);\n  void setSelectedIconBorderPen(const QPen &pen);\n  void setSelectedBrush(const QBrush &brush);\n  void setSelectedFont(const QFont &font);\n  void setSelectedTextColor(const QColor &color);\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  QCPAbstractLegendItem *item(int index) const;\n  QCPPlottableLegendItem *itemWithPlottable(const QCPAbstractPlottable *plottable) const;\n  int itemCount() const;\n  bool hasItem(QCPAbstractLegendItem *item) const;\n  bool hasItemWithPlottable(const QCPAbstractPlottable *plottable) const;\n  bool addItem(QCPAbstractLegendItem *item);\n  bool removeItem(int index);\n  bool removeItem(QCPAbstractLegendItem *item);\n  void clearItems();\n  QList<QCPAbstractLegendItem*> selectedItems() const;\n  \nsignals:\n  void selectionChanged(QCPLegend::SelectableParts parts);\n  void selectableChanged(QCPLegend::SelectableParts parts);\n  \nprotected:\n  // property members:\n  QPen mBorderPen, mIconBorderPen;\n  QBrush mBrush;\n  QFont mFont;\n  QColor mTextColor;\n  QSize mIconSize;\n  int mIconTextPadding;\n  SelectableParts mSelectedParts, mSelectableParts;\n  QPen mSelectedBorderPen, mSelectedIconBorderPen;\n  QBrush mSelectedBrush;\n  QFont mSelectedFont;\n  QColor mSelectedTextColor;\n  \n  // reimplemented virtual methods:\n  virtual void parentPlotInitialized(QCustomPlot *parentPlot) Q_DECL_OVERRIDE;\n  virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE;\n  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  // events:\n  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE;\n  virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  QPen getBorderPen() const;\n  QBrush getBrush() const;\n  \nprivate:\n  Q_DISABLE_COPY(QCPLegend)\n  \n  friend class QCustomPlot;\n  friend class QCPAbstractLegendItem;\n};\nQ_DECLARE_OPERATORS_FOR_FLAGS(QCPLegend::SelectableParts)\nQ_DECLARE_METATYPE(QCPLegend::SelectablePart)\n\n/* end of 'src/layoutelements/layoutelement-legend.h' */\n\n\n/* including file 'src/layoutelements/layoutelement-textelement.h' */\n/* modified 2021-03-29T02:30:44, size 5359                         */\n\nclass QCP_LIB_DECL QCPTextElement : public QCPLayoutElement\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QString text READ text WRITE setText)\n  Q_PROPERTY(QFont font READ font WRITE setFont)\n  Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)\n  Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)\n  Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor)\n  Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged)\n  Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged)\n  /// \\endcond\npublic:\n  explicit QCPTextElement(QCustomPlot *parentPlot);\n  QCPTextElement(QCustomPlot *parentPlot, const QString &text);\n  QCPTextElement(QCustomPlot *parentPlot, const QString &text, double pointSize);\n  QCPTextElement(QCustomPlot *parentPlot, const QString &text, const QString &fontFamily, double pointSize);\n  QCPTextElement(QCustomPlot *parentPlot, const QString &text, const QFont &font);\n  \n  // getters:\n  QString text() const { return mText; }\n  int textFlags() const { return mTextFlags; }\n  QFont font() const { return mFont; }\n  QColor textColor() const { return mTextColor; }\n  QFont selectedFont() const { return mSelectedFont; }\n  QColor selectedTextColor() const { return mSelectedTextColor; }\n  bool selectable() const { return mSelectable; }\n  bool selected() const { return mSelected; }\n  \n  // setters:\n  void setText(const QString &text);\n  void setTextFlags(int flags);\n  void setFont(const QFont &font);\n  void setTextColor(const QColor &color);\n  void setSelectedFont(const QFont &font);\n  void setSelectedTextColor(const QColor &color);\n  Q_SLOT void setSelectable(bool selectable);\n  Q_SLOT void setSelected(bool selected);\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE;\n  virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;\n  virtual void mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE;\n  \nsignals:\n  void selectionChanged(bool selected);\n  void selectableChanged(bool selectable);\n  void clicked(QMouseEvent *event);\n  void doubleClicked(QMouseEvent *event);\n  \nprotected:\n  // property members:\n  QString mText;\n  int mTextFlags;\n  QFont mFont;\n  QColor mTextColor;\n  QFont mSelectedFont;\n  QColor mSelectedTextColor;\n  QRect mTextBoundingRect;\n  bool mSelectable, mSelected;\n  \n  // reimplemented virtual methods:\n  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE;\n  virtual QSize maximumOuterSizeHint() const Q_DECL_OVERRIDE;\n  // events:\n  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE;\n  virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  QFont mainFont() const;\n  QColor mainTextColor() const;\n  \nprivate:\n  Q_DISABLE_COPY(QCPTextElement)\n};\n\n\n\n/* end of 'src/layoutelements/layoutelement-textelement.h' */\n\n\n/* including file 'src/layoutelements/layoutelement-colorscale.h' */\n/* modified 2021-03-29T02:30:44, size 5939                        */\n\n\nclass QCPColorScaleAxisRectPrivate : public QCPAxisRect\n{\n  Q_OBJECT\npublic:\n  explicit QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale);\nprotected:\n  QCPColorScale *mParentColorScale;\n  QImage mGradientImage;\n  bool mGradientImageInvalidated;\n  // re-using some methods of QCPAxisRect to make them available to friend class QCPColorScale\n  using QCPAxisRect::calculateAutoMargin;\n  using QCPAxisRect::mousePressEvent;\n  using QCPAxisRect::mouseMoveEvent;\n  using QCPAxisRect::mouseReleaseEvent;\n  using QCPAxisRect::wheelEvent;\n  using QCPAxisRect::update;\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  void updateGradientImage();\n  Q_SLOT void axisSelectionChanged(QCPAxis::SelectableParts selectedParts);\n  Q_SLOT void axisSelectableChanged(QCPAxis::SelectableParts selectableParts);\n  friend class QCPColorScale;\n};\n\n\nclass QCP_LIB_DECL QCPColorScale : public QCPLayoutElement\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QCPAxis::AxisType type READ type WRITE setType)\n  Q_PROPERTY(QCPRange dataRange READ dataRange WRITE setDataRange NOTIFY dataRangeChanged)\n  Q_PROPERTY(QCPAxis::ScaleType dataScaleType READ dataScaleType WRITE setDataScaleType NOTIFY dataScaleTypeChanged)\n  Q_PROPERTY(QCPColorGradient gradient READ gradient WRITE setGradient NOTIFY gradientChanged)\n  Q_PROPERTY(QString label READ label WRITE setLabel)\n  Q_PROPERTY(int barWidth READ barWidth WRITE setBarWidth)\n  Q_PROPERTY(bool rangeDrag READ rangeDrag WRITE setRangeDrag)\n  Q_PROPERTY(bool rangeZoom READ rangeZoom WRITE setRangeZoom)\n  /// \\endcond\npublic:\n  explicit QCPColorScale(QCustomPlot *parentPlot);\n  virtual ~QCPColorScale() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QCPAxis *axis() const { return mColorAxis.data(); }\n  QCPAxis::AxisType type() const { return mType; }\n  QCPRange dataRange() const { return mDataRange; }\n  QCPAxis::ScaleType dataScaleType() const { return mDataScaleType; }\n  QCPColorGradient gradient() const { return mGradient; }\n  QString label() const;\n  int barWidth () const { return mBarWidth; }\n  bool rangeDrag() const;\n  bool rangeZoom() const;\n  \n  // setters:\n  void setType(QCPAxis::AxisType type);\n  Q_SLOT void setDataRange(const QCPRange &dataRange);\n  Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType);\n  Q_SLOT void setGradient(const QCPColorGradient &gradient);\n  void setLabel(const QString &str);\n  void setBarWidth(int width);\n  void setRangeDrag(bool enabled);\n  void setRangeZoom(bool enabled);\n  \n  // non-property methods:\n  QList<QCPColorMap*> colorMaps() const;\n  void rescaleDataRange(bool onlyVisibleMaps);\n  \n  // reimplemented virtual methods:\n  virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE;\n  \nsignals:\n  void dataRangeChanged(const QCPRange &newRange);\n  void dataScaleTypeChanged(QCPAxis::ScaleType scaleType);\n  void gradientChanged(const QCPColorGradient &newGradient);\n\nprotected:\n  // property members:\n  QCPAxis::AxisType mType;\n  QCPRange mDataRange;\n  QCPAxis::ScaleType mDataScaleType;\n  QCPColorGradient mGradient;\n  int mBarWidth;\n  \n  // non-property members:\n  QPointer<QCPColorScaleAxisRectPrivate> mAxisRect;\n  QPointer<QCPAxis> mColorAxis;\n  \n  // reimplemented virtual methods:\n  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;\n  // events:\n  virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE;\n  virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;\n  virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;\n  virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;\n  \nprivate:\n  Q_DISABLE_COPY(QCPColorScale)\n  \n  friend class QCPColorScaleAxisRectPrivate;\n};\n\n\n/* end of 'src/layoutelements/layoutelement-colorscale.h' */\n\n\n/* including file 'src/plottables/plottable-graph.h' */\n/* modified 2021-03-29T02:30:44, size 9316           */\n\nclass QCP_LIB_DECL QCPGraphData\n{\npublic:\n  QCPGraphData();\n  QCPGraphData(double key, double value);\n  \n  inline double sortKey() const { return key; }\n  inline static QCPGraphData fromSortKey(double sortKey) { return QCPGraphData(sortKey, 0); }\n  inline static bool sortKeyIsMainKey() { return true; }\n  \n  inline double mainKey() const { return key; }\n  inline double mainValue() const { return value; }\n  \n  inline QCPRange valueRange() const { return QCPRange(value, value); }\n  \n  double key, value;\n};\nQ_DECLARE_TYPEINFO(QCPGraphData, Q_PRIMITIVE_TYPE);\n\n\n/*! \\typedef QCPGraphDataContainer\n  \n  Container for storing \\ref QCPGraphData points. The data is stored sorted by \\a key.\n  \n  This template instantiation is the container in which QCPGraph holds its data. For details about\n  the generic container, see the documentation of the class template \\ref QCPDataContainer.\n  \n  \\see QCPGraphData, QCPGraph::setData\n*/\ntypedef QCPDataContainer<QCPGraphData> QCPGraphDataContainer;\n\nclass QCP_LIB_DECL QCPGraph : public QCPAbstractPlottable1D<QCPGraphData>\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(LineStyle lineStyle READ lineStyle WRITE setLineStyle)\n  Q_PROPERTY(QCPScatterStyle scatterStyle READ scatterStyle WRITE setScatterStyle)\n  Q_PROPERTY(int scatterSkip READ scatterSkip WRITE setScatterSkip)\n  Q_PROPERTY(QCPGraph* channelFillGraph READ channelFillGraph WRITE setChannelFillGraph)\n  Q_PROPERTY(bool adaptiveSampling READ adaptiveSampling WRITE setAdaptiveSampling)\n  /// \\endcond\npublic:\n  /*!\n    Defines how the graph's line is represented visually in the plot. The line is drawn with the\n    current pen of the graph (\\ref setPen).\n    \\see setLineStyle\n  */\n  enum LineStyle { lsNone        ///< data points are not connected with any lines (e.g. data only represented\n                                 ///< with symbols according to the scatter style, see \\ref setScatterStyle)\n                   ,lsLine       ///< data points are connected by a straight line\n                   ,lsStepLeft   ///< line is drawn as steps where the step height is the value of the left data point\n                   ,lsStepRight  ///< line is drawn as steps where the step height is the value of the right data point\n                   ,lsStepCenter ///< line is drawn as steps where the step is in between two data points\n                   ,lsImpulse    ///< each data point is represented by a line parallel to the value axis, which reaches from the data point to the zero-value-line\n                 };\n  Q_ENUMS(LineStyle)\n  \n  explicit QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis);\n  virtual ~QCPGraph() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QSharedPointer<QCPGraphDataContainer> data() const { return mDataContainer; }\n  LineStyle lineStyle() const { return mLineStyle; }\n  QCPScatterStyle scatterStyle() const { return mScatterStyle; }\n  int scatterSkip() const { return mScatterSkip; }\n  QCPGraph *channelFillGraph() const { return mChannelFillGraph.data(); }\n  bool adaptiveSampling() const { return mAdaptiveSampling; }\n  \n  // setters:\n  void setData(QSharedPointer<QCPGraphDataContainer> data);\n  void setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted=false);\n  void setLineStyle(LineStyle ls);\n  void setScatterStyle(const QCPScatterStyle &style);\n  void setScatterSkip(int skip);\n  void setChannelFillGraph(QCPGraph *targetGraph);\n  void setAdaptiveSampling(bool enabled);\n  \n  // non-property methods:\n  void addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted=false);\n  void addData(double key, double value);\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE;\n  virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE;\n  \nprotected:\n  // property members:\n  LineStyle mLineStyle;\n  QCPScatterStyle mScatterStyle;\n  int mScatterSkip;\n  QPointer<QCPGraph> mChannelFillGraph;\n  bool mAdaptiveSampling;\n  \n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE;\n  \n  // introduced virtual methods:\n  virtual void drawFill(QCPPainter *painter, QVector<QPointF> *lines) const;\n  virtual void drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &scatters, const QCPScatterStyle &style) const;\n  virtual void drawLinePlot(QCPPainter *painter, const QVector<QPointF> &lines) const;\n  virtual void drawImpulsePlot(QCPPainter *painter, const QVector<QPointF> &lines) const;\n  \n  virtual void getOptimizedLineData(QVector<QCPGraphData> *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const;\n  virtual void getOptimizedScatterData(QVector<QCPGraphData> *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const;\n  \n  // non-virtual methods:\n  void getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const;\n  void getLines(QVector<QPointF> *lines, const QCPDataRange &dataRange) const;\n  void getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange) const;\n  QVector<QPointF> dataToLines(const QVector<QCPGraphData> &data) const;\n  QVector<QPointF> dataToStepLeftLines(const QVector<QCPGraphData> &data) const;\n  QVector<QPointF> dataToStepRightLines(const QVector<QCPGraphData> &data) const;\n  QVector<QPointF> dataToStepCenterLines(const QVector<QCPGraphData> &data) const;\n  QVector<QPointF> dataToImpulseLines(const QVector<QCPGraphData> &data) const;\n  QVector<QCPDataRange> getNonNanSegments(const QVector<QPointF> *lineData, Qt::Orientation keyOrientation) const;\n  QVector<QPair<QCPDataRange, QCPDataRange> > getOverlappingSegments(QVector<QCPDataRange> thisSegments, const QVector<QPointF> *thisData, QVector<QCPDataRange> otherSegments, const QVector<QPointF> *otherData) const;\n  bool segmentsIntersect(double aLower, double aUpper, double bLower, double bUpper, int &bPrecedence) const;\n  QPointF getFillBasePoint(QPointF matchingDataPoint) const;\n  const QPolygonF getFillPolygon(const QVector<QPointF> *lineData, QCPDataRange segment) const;\n  const QPolygonF getChannelFillPolygon(const QVector<QPointF> *thisData, QCPDataRange thisSegment, const QVector<QPointF> *otherData, QCPDataRange otherSegment) const;\n  int findIndexBelowX(const QVector<QPointF> *data, double x) const;\n  int findIndexAboveX(const QVector<QPointF> *data, double x) const;\n  int findIndexBelowY(const QVector<QPointF> *data, double y) const;\n  int findIndexAboveY(const QVector<QPointF> *data, double y) const;\n  double pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const;\n  \n  friend class QCustomPlot;\n  friend class QCPLegend;\n};\nQ_DECLARE_METATYPE(QCPGraph::LineStyle)\n\n/* end of 'src/plottables/plottable-graph.h' */\n\n\n/* including file 'src/plottables/plottable-curve.h' */\n/* modified 2021-03-29T02:30:44, size 7434           */\n\nclass QCP_LIB_DECL QCPCurveData\n{\npublic:\n  QCPCurveData();\n  QCPCurveData(double t, double key, double value);\n  \n  inline double sortKey() const { return t; }\n  inline static QCPCurveData fromSortKey(double sortKey) { return QCPCurveData(sortKey, 0, 0); }\n  inline static bool sortKeyIsMainKey() { return false; }\n  \n  inline double mainKey() const { return key; }\n  inline double mainValue() const { return value; }\n  \n  inline QCPRange valueRange() const { return QCPRange(value, value); }\n  \n  double t, key, value;\n};\nQ_DECLARE_TYPEINFO(QCPCurveData, Q_PRIMITIVE_TYPE);\n\n\n/*! \\typedef QCPCurveDataContainer\n  \n  Container for storing \\ref QCPCurveData points. The data is stored sorted by \\a t, so the \\a\n  sortKey() (returning \\a t) is different from \\a mainKey() (returning \\a key).\n  \n  This template instantiation is the container in which QCPCurve holds its data. For details about\n  the generic container, see the documentation of the class template \\ref QCPDataContainer.\n  \n  \\see QCPCurveData, QCPCurve::setData\n*/\ntypedef QCPDataContainer<QCPCurveData> QCPCurveDataContainer;\n\nclass QCP_LIB_DECL QCPCurve : public QCPAbstractPlottable1D<QCPCurveData>\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QCPScatterStyle scatterStyle READ scatterStyle WRITE setScatterStyle)\n  Q_PROPERTY(int scatterSkip READ scatterSkip WRITE setScatterSkip)\n  Q_PROPERTY(LineStyle lineStyle READ lineStyle WRITE setLineStyle)\n  /// \\endcond\npublic:\n  /*!\n    Defines how the curve's line is represented visually in the plot. The line is drawn with the\n    current pen of the curve (\\ref setPen).\n    \\see setLineStyle\n  */\n  enum LineStyle { lsNone  ///< No line is drawn between data points (e.g. only scatters)\n                   ,lsLine ///< Data points are connected with a straight line\n                 };\n  Q_ENUMS(LineStyle)\n  \n  explicit QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis);\n  virtual ~QCPCurve() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QSharedPointer<QCPCurveDataContainer> data() const { return mDataContainer; }\n  QCPScatterStyle scatterStyle() const { return mScatterStyle; }\n  int scatterSkip() const { return mScatterSkip; }\n  LineStyle lineStyle() const { return mLineStyle; }\n  \n  // setters:\n  void setData(QSharedPointer<QCPCurveDataContainer> data);\n  void setData(const QVector<double> &t, const QVector<double> &keys, const QVector<double> &values, bool alreadySorted=false);\n  void setData(const QVector<double> &keys, const QVector<double> &values);\n  void setScatterStyle(const QCPScatterStyle &style);\n  void setScatterSkip(int skip);\n  void setLineStyle(LineStyle style);\n  \n  // non-property methods:\n  void addData(const QVector<double> &t, const QVector<double> &keys, const QVector<double> &values, bool alreadySorted=false);\n  void addData(const QVector<double> &keys, const QVector<double> &values);\n  void addData(double t, double key, double value);\n  void addData(double key, double value);\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE;\n  virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE;\n  \nprotected:\n  // property members:\n  QCPScatterStyle mScatterStyle;\n  int mScatterSkip;\n  LineStyle mLineStyle;\n  \n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE;\n  \n  // introduced virtual methods:\n  virtual void drawCurveLine(QCPPainter *painter, const QVector<QPointF> &lines) const;\n  virtual void drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &points, const QCPScatterStyle &style) const;\n  \n  // non-virtual methods:\n  void getCurveLines(QVector<QPointF> *lines, const QCPDataRange &dataRange, double penWidth) const;\n  void getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange, double scatterWidth) const;\n  int getRegion(double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const;\n  QPointF getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const;\n  QVector<QPointF> getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const;\n  bool mayTraverse(int prevRegion, int currentRegion) const;\n  bool getTraverse(double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin, QPointF &crossA, QPointF &crossB) const;\n  void getTraverseCornerPoints(int prevRegion, int currentRegion, double keyMin, double valueMax, double keyMax, double valueMin, QVector<QPointF> &beforeTraverse, QVector<QPointF> &afterTraverse) const;\n  double pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer::const_iterator &closestData) const;\n  \n  friend class QCustomPlot;\n  friend class QCPLegend;\n};\nQ_DECLARE_METATYPE(QCPCurve::LineStyle)\n\n/* end of 'src/plottables/plottable-curve.h' */\n\n\n/* including file 'src/plottables/plottable-bars.h' */\n/* modified 2021-03-29T02:30:44, size 8955          */\n\nclass QCP_LIB_DECL QCPBarsGroup : public QObject\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(SpacingType spacingType READ spacingType WRITE setSpacingType)\n  Q_PROPERTY(double spacing READ spacing WRITE setSpacing)\n  /// \\endcond\npublic:\n  /*!\n    Defines the ways the spacing between bars in the group can be specified. Thus it defines what\n    the number passed to \\ref setSpacing actually means.\n    \n    \\see setSpacingType, setSpacing\n  */\n  enum SpacingType { stAbsolute       ///< Bar spacing is in absolute pixels\n                     ,stAxisRectRatio ///< Bar spacing is given by a fraction of the axis rect size\n                     ,stPlotCoords    ///< Bar spacing is in key coordinates and thus scales with the key axis range\n                   };\n  Q_ENUMS(SpacingType)\n  \n  explicit QCPBarsGroup(QCustomPlot *parentPlot);\n  virtual ~QCPBarsGroup();\n  \n  // getters:\n  SpacingType spacingType() const { return mSpacingType; }\n  double spacing() const { return mSpacing; }\n  \n  // setters:\n  void setSpacingType(SpacingType spacingType);\n  void setSpacing(double spacing);\n  \n  // non-virtual methods:\n  QList<QCPBars*> bars() const { return mBars; }\n  QCPBars* bars(int index) const;\n  int size() const { return mBars.size(); }\n  bool isEmpty() const { return mBars.isEmpty(); }\n  void clear();\n  bool contains(QCPBars *bars) const { return mBars.contains(bars); }\n  void append(QCPBars *bars);\n  void insert(int i, QCPBars *bars);\n  void remove(QCPBars *bars);\n  \nprotected:\n  // non-property members:\n  QCustomPlot *mParentPlot;\n  SpacingType mSpacingType;\n  double mSpacing;\n  QList<QCPBars*> mBars;\n  \n  // non-virtual methods:\n  void registerBars(QCPBars *bars);\n  void unregisterBars(QCPBars *bars);\n  \n  // virtual methods:\n  double keyPixelOffset(const QCPBars *bars, double keyCoord);\n  double getPixelSpacing(const QCPBars *bars, double keyCoord);\n  \nprivate:\n  Q_DISABLE_COPY(QCPBarsGroup)\n  \n  friend class QCPBars;\n};\nQ_DECLARE_METATYPE(QCPBarsGroup::SpacingType)\n\n\nclass QCP_LIB_DECL QCPBarsData\n{\npublic:\n  QCPBarsData();\n  QCPBarsData(double key, double value);\n  \n  inline double sortKey() const { return key; }\n  inline static QCPBarsData fromSortKey(double sortKey) { return QCPBarsData(sortKey, 0); }\n  inline static bool sortKeyIsMainKey() { return true; } \n  \n  inline double mainKey() const { return key; }\n  inline double mainValue() const { return value; }\n  \n  inline QCPRange valueRange() const { return QCPRange(value, value); } // note that bar base value isn't held in each QCPBarsData and thus can't/shouldn't be returned here\n  \n  double key, value;\n};\nQ_DECLARE_TYPEINFO(QCPBarsData, Q_PRIMITIVE_TYPE);\n\n\n/*! \\typedef QCPBarsDataContainer\n  \n  Container for storing \\ref QCPBarsData points. The data is stored sorted by \\a key.\n  \n  This template instantiation is the container in which QCPBars holds its data. For details about\n  the generic container, see the documentation of the class template \\ref QCPDataContainer.\n  \n  \\see QCPBarsData, QCPBars::setData\n*/\ntypedef QCPDataContainer<QCPBarsData> QCPBarsDataContainer;\n\nclass QCP_LIB_DECL QCPBars : public QCPAbstractPlottable1D<QCPBarsData>\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(double width READ width WRITE setWidth)\n  Q_PROPERTY(WidthType widthType READ widthType WRITE setWidthType)\n  Q_PROPERTY(QCPBarsGroup* barsGroup READ barsGroup WRITE setBarsGroup)\n  Q_PROPERTY(double baseValue READ baseValue WRITE setBaseValue)\n  Q_PROPERTY(double stackingGap READ stackingGap WRITE setStackingGap)\n  Q_PROPERTY(QCPBars* barBelow READ barBelow)\n  Q_PROPERTY(QCPBars* barAbove READ barAbove)\n  /// \\endcond\npublic:\n  /*!\n    Defines the ways the width of the bar can be specified. Thus it defines what the number passed\n    to \\ref setWidth actually means.\n    \n    \\see setWidthType, setWidth\n  */\n  enum WidthType { wtAbsolute       ///< Bar width is in absolute pixels\n                   ,wtAxisRectRatio ///< Bar width is given by a fraction of the axis rect size\n                   ,wtPlotCoords    ///< Bar width is in key coordinates and thus scales with the key axis range\n                 };\n  Q_ENUMS(WidthType)\n  \n  explicit QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis);\n  virtual ~QCPBars() Q_DECL_OVERRIDE;\n  \n  // getters:\n  double width() const { return mWidth; }\n  WidthType widthType() const { return mWidthType; }\n  QCPBarsGroup *barsGroup() const { return mBarsGroup; }\n  double baseValue() const { return mBaseValue; }\n  double stackingGap() const { return mStackingGap; }\n  QCPBars *barBelow() const { return mBarBelow.data(); }\n  QCPBars *barAbove() const { return mBarAbove.data(); }\n  QSharedPointer<QCPBarsDataContainer> data() const { return mDataContainer; }\n  \n  // setters:\n  void setData(QSharedPointer<QCPBarsDataContainer> data);\n  void setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted=false);\n  void setWidth(double width);\n  void setWidthType(WidthType widthType);\n  void setBarsGroup(QCPBarsGroup *barsGroup);\n  void setBaseValue(double baseValue);\n  void setStackingGap(double pixels);\n  \n  // non-property methods:\n  void addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted=false);\n  void addData(double key, double value);\n  void moveBelow(QCPBars *bars);\n  void moveAbove(QCPBars *bars);\n  \n  // reimplemented virtual methods:\n  virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE;\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE;\n  virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE;\n  virtual QPointF dataPixelPosition(int index) const Q_DECL_OVERRIDE;\n  \nprotected:\n  // property members:\n  double mWidth;\n  WidthType mWidthType;\n  QCPBarsGroup *mBarsGroup;\n  double mBaseValue;\n  double mStackingGap;\n  QPointer<QCPBars> mBarBelow, mBarAbove;\n  \n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  void getVisibleDataBounds(QCPBarsDataContainer::const_iterator &begin, QCPBarsDataContainer::const_iterator &end) const;\n  QRectF getBarRect(double key, double value) const;\n  void getPixelWidth(double key, double &lower, double &upper) const;\n  double getStackedBaseValue(double key, bool positive) const;\n  static void connectBars(QCPBars* lower, QCPBars* upper);\n  \n  friend class QCustomPlot;\n  friend class QCPLegend;\n  friend class QCPBarsGroup;\n};\nQ_DECLARE_METATYPE(QCPBars::WidthType)\n\n/* end of 'src/plottables/plottable-bars.h' */\n\n\n/* including file 'src/plottables/plottable-statisticalbox.h' */\n/* modified 2021-03-29T02:30:44, size 7522                    */\n\nclass QCP_LIB_DECL QCPStatisticalBoxData\n{\npublic:\n  QCPStatisticalBoxData();\n  QCPStatisticalBoxData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector<double>& outliers=QVector<double>());\n  \n  inline double sortKey() const { return key; }\n  inline static QCPStatisticalBoxData fromSortKey(double sortKey) { return QCPStatisticalBoxData(sortKey, 0, 0, 0, 0, 0); }\n  inline static bool sortKeyIsMainKey() { return true; }\n  \n  inline double mainKey() const { return key; }\n  inline double mainValue() const { return median; }\n  \n  inline QCPRange valueRange() const\n  {\n    QCPRange result(minimum, maximum);\n    for (QVector<double>::const_iterator it = outliers.constBegin(); it != outliers.constEnd(); ++it)\n      result.expand(*it);\n    return result;\n  }\n  \n  double key, minimum, lowerQuartile, median, upperQuartile, maximum;\n  QVector<double> outliers;\n};\nQ_DECLARE_TYPEINFO(QCPStatisticalBoxData, Q_MOVABLE_TYPE);\n\n\n/*! \\typedef QCPStatisticalBoxDataContainer\n  \n  Container for storing \\ref QCPStatisticalBoxData points. The data is stored sorted by \\a key.\n  \n  This template instantiation is the container in which QCPStatisticalBox holds its data. For\n  details about the generic container, see the documentation of the class template \\ref\n  QCPDataContainer.\n  \n  \\see QCPStatisticalBoxData, QCPStatisticalBox::setData\n*/\ntypedef QCPDataContainer<QCPStatisticalBoxData> QCPStatisticalBoxDataContainer;\n\nclass QCP_LIB_DECL QCPStatisticalBox : public QCPAbstractPlottable1D<QCPStatisticalBoxData>\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(double width READ width WRITE setWidth)\n  Q_PROPERTY(double whiskerWidth READ whiskerWidth WRITE setWhiskerWidth)\n  Q_PROPERTY(QPen whiskerPen READ whiskerPen WRITE setWhiskerPen)\n  Q_PROPERTY(QPen whiskerBarPen READ whiskerBarPen WRITE setWhiskerBarPen)\n  Q_PROPERTY(bool whiskerAntialiased READ whiskerAntialiased WRITE setWhiskerAntialiased)\n  Q_PROPERTY(QPen medianPen READ medianPen WRITE setMedianPen)\n  Q_PROPERTY(QCPScatterStyle outlierStyle READ outlierStyle WRITE setOutlierStyle)\n  /// \\endcond\npublic:\n  explicit QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis);\n  \n  // getters:\n  QSharedPointer<QCPStatisticalBoxDataContainer> data() const { return mDataContainer; }\n  double width() const { return mWidth; }\n  double whiskerWidth() const { return mWhiskerWidth; }\n  QPen whiskerPen() const { return mWhiskerPen; }\n  QPen whiskerBarPen() const { return mWhiskerBarPen; }\n  bool whiskerAntialiased() const { return mWhiskerAntialiased; }\n  QPen medianPen() const { return mMedianPen; }\n  QCPScatterStyle outlierStyle() const { return mOutlierStyle; }\n\n  // setters:\n  void setData(QSharedPointer<QCPStatisticalBoxDataContainer> data);\n  void setData(const QVector<double> &keys, const QVector<double> &minimum, const QVector<double> &lowerQuartile, const QVector<double> &median, const QVector<double> &upperQuartile, const QVector<double> &maximum, bool alreadySorted=false);\n  void setWidth(double width);\n  void setWhiskerWidth(double width);\n  void setWhiskerPen(const QPen &pen);\n  void setWhiskerBarPen(const QPen &pen);\n  void setWhiskerAntialiased(bool enabled);\n  void setMedianPen(const QPen &pen);\n  void setOutlierStyle(const QCPScatterStyle &style);\n  \n  // non-property methods:\n  void addData(const QVector<double> &keys, const QVector<double> &minimum, const QVector<double> &lowerQuartile, const QVector<double> &median, const QVector<double> &upperQuartile, const QVector<double> &maximum, bool alreadySorted=false);\n  void addData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector<double> &outliers=QVector<double>());\n  \n  // reimplemented virtual methods:\n  virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE;\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE;\n  virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE;\n  \nprotected:\n  // property members:\n  double mWidth;\n  double mWhiskerWidth;\n  QPen mWhiskerPen, mWhiskerBarPen;\n  bool mWhiskerAntialiased;\n  QPen mMedianPen;\n  QCPScatterStyle mOutlierStyle;\n  \n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE;\n  \n  // introduced virtual methods:\n  virtual void drawStatisticalBox(QCPPainter *painter, QCPStatisticalBoxDataContainer::const_iterator it, const QCPScatterStyle &outlierStyle) const;\n  \n  // non-virtual methods:\n  void getVisibleDataBounds(QCPStatisticalBoxDataContainer::const_iterator &begin, QCPStatisticalBoxDataContainer::const_iterator &end) const;\n  QRectF getQuartileBox(QCPStatisticalBoxDataContainer::const_iterator it) const;\n  QVector<QLineF> getWhiskerBackboneLines(QCPStatisticalBoxDataContainer::const_iterator it) const;\n  QVector<QLineF> getWhiskerBarLines(QCPStatisticalBoxDataContainer::const_iterator it) const;\n  \n  friend class QCustomPlot;\n  friend class QCPLegend;\n};\n\n/* end of 'src/plottables/plottable-statisticalbox.h' */\n\n\n/* including file 'src/plottables/plottable-colormap.h' */\n/* modified 2021-03-29T02:30:44, size 7092              */\n\nclass QCP_LIB_DECL QCPColorMapData\n{\npublic:\n  QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange);\n  ~QCPColorMapData();\n  QCPColorMapData(const QCPColorMapData &other);\n  QCPColorMapData &operator=(const QCPColorMapData &other);\n  \n  // getters:\n  int keySize() const { return mKeySize; }\n  int valueSize() const { return mValueSize; }\n  QCPRange keyRange() const { return mKeyRange; }\n  QCPRange valueRange() const { return mValueRange; }\n  QCPRange dataBounds() const { return mDataBounds; }\n  double data(double key, double value);\n  double cell(int keyIndex, int valueIndex);\n  unsigned char alpha(int keyIndex, int valueIndex);\n  \n  // setters:\n  void setSize(int keySize, int valueSize);\n  void setKeySize(int keySize);\n  void setValueSize(int valueSize);\n  void setRange(const QCPRange &keyRange, const QCPRange &valueRange);\n  void setKeyRange(const QCPRange &keyRange);\n  void setValueRange(const QCPRange &valueRange);\n  void setData(double key, double value, double z);\n  void setCell(int keyIndex, int valueIndex, double z);\n  void setAlpha(int keyIndex, int valueIndex, unsigned char alpha);\n  \n  // non-property methods:\n  void recalculateDataBounds();\n  void clear();\n  void clearAlpha();\n  void fill(double z);\n  void fillAlpha(unsigned char alpha);\n  bool isEmpty() const { return mIsEmpty; }\n  void coordToCell(double key, double value, int *keyIndex, int *valueIndex) const;\n  void cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const;\n  \nprotected:\n  // property members:\n  int mKeySize, mValueSize;\n  QCPRange mKeyRange, mValueRange;\n  bool mIsEmpty;\n  \n  // non-property members:\n  double *mData;\n  unsigned char *mAlpha;\n  QCPRange mDataBounds;\n  bool mDataModified;\n  \n  bool createAlpha(bool initializeOpaque=true);\n  \n  friend class QCPColorMap;\n};\n\n\nclass QCP_LIB_DECL QCPColorMap : public QCPAbstractPlottable\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QCPRange dataRange READ dataRange WRITE setDataRange NOTIFY dataRangeChanged)\n  Q_PROPERTY(QCPAxis::ScaleType dataScaleType READ dataScaleType WRITE setDataScaleType NOTIFY dataScaleTypeChanged)\n  Q_PROPERTY(QCPColorGradient gradient READ gradient WRITE setGradient NOTIFY gradientChanged)\n  Q_PROPERTY(bool interpolate READ interpolate WRITE setInterpolate)\n  Q_PROPERTY(bool tightBoundary READ tightBoundary WRITE setTightBoundary)\n  Q_PROPERTY(QCPColorScale* colorScale READ colorScale WRITE setColorScale)\n  /// \\endcond\npublic:\n  explicit QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis);\n  virtual ~QCPColorMap() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QCPColorMapData *data() const { return mMapData; }\n  QCPRange dataRange() const { return mDataRange; }\n  QCPAxis::ScaleType dataScaleType() const { return mDataScaleType; }\n  bool interpolate() const { return mInterpolate; }\n  bool tightBoundary() const { return mTightBoundary; }\n  QCPColorGradient gradient() const { return mGradient; }\n  QCPColorScale *colorScale() const { return mColorScale.data(); }\n  \n  // setters:\n  void setData(QCPColorMapData *data, bool copy=false);\n  Q_SLOT void setDataRange(const QCPRange &dataRange);\n  Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType);\n  Q_SLOT void setGradient(const QCPColorGradient &gradient);\n  void setInterpolate(bool enabled);\n  void setTightBoundary(bool enabled);\n  void setColorScale(QCPColorScale *colorScale);\n  \n  // non-property methods:\n  void rescaleDataRange(bool recalculateDataBounds=false);\n  Q_SLOT void updateLegendIcon(Qt::TransformationMode transformMode=Qt::SmoothTransformation, const QSize &thumbSize=QSize(32, 18));\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE;\n  virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE;\n  \nsignals:\n  void dataRangeChanged(const QCPRange &newRange);\n  void dataScaleTypeChanged(QCPAxis::ScaleType scaleType);\n  void gradientChanged(const QCPColorGradient &newGradient);\n  \nprotected:\n  // property members:\n  QCPRange mDataRange;\n  QCPAxis::ScaleType mDataScaleType;\n  QCPColorMapData *mMapData;\n  QCPColorGradient mGradient;\n  bool mInterpolate;\n  bool mTightBoundary;\n  QPointer<QCPColorScale> mColorScale;\n  \n  // non-property members:\n  QImage mMapImage, mUndersampledMapImage;\n  QPixmap mLegendIcon;\n  bool mMapImageInvalidated;\n  \n  // introduced virtual methods:\n  virtual void updateMapImage();\n  \n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE;\n  \n  friend class QCustomPlot;\n  friend class QCPLegend;\n};\n\n/* end of 'src/plottables/plottable-colormap.h' */\n\n\n/* including file 'src/plottables/plottable-financial.h' */\n/* modified 2021-03-29T02:30:44, size 8644               */\n\nclass QCP_LIB_DECL QCPFinancialData\n{\npublic:\n  QCPFinancialData();\n  QCPFinancialData(double key, double open, double high, double low, double close);\n  \n  inline double sortKey() const { return key; }\n  inline static QCPFinancialData fromSortKey(double sortKey) { return QCPFinancialData(sortKey, 0, 0, 0, 0); }\n  inline static bool sortKeyIsMainKey() { return true; } \n  \n  inline double mainKey() const { return key; }\n  inline double mainValue() const { return open; }\n  \n  inline QCPRange valueRange() const { return QCPRange(low, high); } // open and close must lie between low and high, so we don't need to check them\n  \n  double key, open, high, low, close;\n};\nQ_DECLARE_TYPEINFO(QCPFinancialData, Q_PRIMITIVE_TYPE);\n\n\n/*! \\typedef QCPFinancialDataContainer\n  \n  Container for storing \\ref QCPFinancialData points. The data is stored sorted by \\a key.\n  \n  This template instantiation is the container in which QCPFinancial holds its data. For details\n  about the generic container, see the documentation of the class template \\ref QCPDataContainer.\n  \n  \\see QCPFinancialData, QCPFinancial::setData\n*/\ntypedef QCPDataContainer<QCPFinancialData> QCPFinancialDataContainer;\n\nclass QCP_LIB_DECL QCPFinancial : public QCPAbstractPlottable1D<QCPFinancialData>\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(ChartStyle chartStyle READ chartStyle WRITE setChartStyle)\n  Q_PROPERTY(double width READ width WRITE setWidth)\n  Q_PROPERTY(WidthType widthType READ widthType WRITE setWidthType)\n  Q_PROPERTY(bool twoColored READ twoColored WRITE setTwoColored)\n  Q_PROPERTY(QBrush brushPositive READ brushPositive WRITE setBrushPositive)\n  Q_PROPERTY(QBrush brushNegative READ brushNegative WRITE setBrushNegative)\n  Q_PROPERTY(QPen penPositive READ penPositive WRITE setPenPositive)\n  Q_PROPERTY(QPen penNegative READ penNegative WRITE setPenNegative)\n  /// \\endcond\npublic:\n  /*!\n    Defines the ways the width of the financial bar can be specified. Thus it defines what the\n    number passed to \\ref setWidth actually means.\n\n    \\see setWidthType, setWidth\n  */\n  enum WidthType { wtAbsolute       ///< width is in absolute pixels\n                   ,wtAxisRectRatio ///< width is given by a fraction of the axis rect size\n                   ,wtPlotCoords    ///< width is in key coordinates and thus scales with the key axis range\n                 };\n  Q_ENUMS(WidthType)\n  \n  /*!\n    Defines the possible representations of OHLC data in the plot.\n    \n    \\see setChartStyle\n  */\n  enum ChartStyle { csOhlc         ///< Open-High-Low-Close bar representation\n                   ,csCandlestick  ///< Candlestick representation\n                  };\n  Q_ENUMS(ChartStyle)\n  \n  explicit QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis);\n  virtual ~QCPFinancial() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QSharedPointer<QCPFinancialDataContainer> data() const { return mDataContainer; }\n  ChartStyle chartStyle() const { return mChartStyle; }\n  double width() const { return mWidth; }\n  WidthType widthType() const { return mWidthType; }\n  bool twoColored() const { return mTwoColored; }\n  QBrush brushPositive() const { return mBrushPositive; }\n  QBrush brushNegative() const { return mBrushNegative; }\n  QPen penPositive() const { return mPenPositive; }\n  QPen penNegative() const { return mPenNegative; }\n  \n  // setters:\n  void setData(QSharedPointer<QCPFinancialDataContainer> data);\n  void setData(const QVector<double> &keys, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close, bool alreadySorted=false);\n  void setChartStyle(ChartStyle style);\n  void setWidth(double width);\n  void setWidthType(WidthType widthType);\n  void setTwoColored(bool twoColored);\n  void setBrushPositive(const QBrush &brush);\n  void setBrushNegative(const QBrush &brush);\n  void setPenPositive(const QPen &pen);\n  void setPenNegative(const QPen &pen);\n  \n  // non-property methods:\n  void addData(const QVector<double> &keys, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close, bool alreadySorted=false);\n  void addData(double key, double open, double high, double low, double close);\n  \n  // reimplemented virtual methods:\n  virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE;\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE;\n  virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE;\n  \n  // static methods:\n  static QCPFinancialDataContainer timeSeriesToOhlc(const QVector<double> &time, const QVector<double> &value, double timeBinSize, double timeBinOffset = 0);\n  \nprotected:\n  // property members:\n  ChartStyle mChartStyle;\n  double mWidth;\n  WidthType mWidthType;\n  bool mTwoColored;\n  QBrush mBrushPositive, mBrushNegative;\n  QPen mPenPositive, mPenNegative;\n  \n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  void drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected);\n  void drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected);\n  double getPixelWidth(double key, double keyPixel) const;\n  double ohlcSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const;\n  double candlestickSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const;\n  void getVisibleDataBounds(QCPFinancialDataContainer::const_iterator &begin, QCPFinancialDataContainer::const_iterator &end) const;\n  QRectF selectionHitBox(QCPFinancialDataContainer::const_iterator it) const;\n  \n  friend class QCustomPlot;\n  friend class QCPLegend;\n};\nQ_DECLARE_METATYPE(QCPFinancial::ChartStyle)\n\n/* end of 'src/plottables/plottable-financial.h' */\n\n\n/* including file 'src/plottables/plottable-errorbar.h' */\n/* modified 2021-03-29T02:30:44, size 7749              */\n\nclass QCP_LIB_DECL QCPErrorBarsData\n{\npublic:\n  QCPErrorBarsData();\n  explicit QCPErrorBarsData(double error);\n  QCPErrorBarsData(double errorMinus, double errorPlus);\n  \n  double errorMinus, errorPlus;\n};\nQ_DECLARE_TYPEINFO(QCPErrorBarsData, Q_PRIMITIVE_TYPE);\n\n\n/*! \\typedef QCPErrorBarsDataContainer\n\n  Container for storing \\ref QCPErrorBarsData points. It is a typedef for <tt>QVector<\\ref\n  QCPErrorBarsData></tt>.\n\n  This is the container in which \\ref QCPErrorBars holds its data. Unlike most other data\n  containers for plottables, it is not based on \\ref QCPDataContainer. This is because the error\n  bars plottable is special in that it doesn't store its own key and value coordinate per error\n  bar. It adopts the key and value from the plottable to which the error bars shall be applied\n  (\\ref QCPErrorBars::setDataPlottable). So the stored \\ref QCPErrorBarsData doesn't need a\n  sortable key, but merely an index (as \\c QVector provides), which maps one-to-one to the indices\n  of the other plottable's data.\n\n  \\see QCPErrorBarsData, QCPErrorBars::setData\n*/\ntypedef QVector<QCPErrorBarsData> QCPErrorBarsDataContainer;\n\nclass QCP_LIB_DECL QCPErrorBars : public QCPAbstractPlottable, public QCPPlottableInterface1D\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QSharedPointer<QCPErrorBarsDataContainer> data READ data WRITE setData)\n  Q_PROPERTY(QCPAbstractPlottable* dataPlottable READ dataPlottable WRITE setDataPlottable)\n  Q_PROPERTY(ErrorType errorType READ errorType WRITE setErrorType)\n  Q_PROPERTY(double whiskerWidth READ whiskerWidth WRITE setWhiskerWidth)\n  Q_PROPERTY(double symbolGap READ symbolGap WRITE setSymbolGap)\n  /// \\endcond\npublic:\n  \n  /*!\n    Defines in which orientation the error bars shall appear. If your data needs both error\n    dimensions, create two \\ref QCPErrorBars with different \\ref ErrorType.\n\n    \\see setErrorType\n  */\n  enum ErrorType { etKeyError    ///< The errors are for the key dimension (bars appear parallel to the key axis)\n                   ,etValueError ///< The errors are for the value dimension (bars appear parallel to the value axis)\n  };\n  Q_ENUMS(ErrorType)\n  \n  explicit QCPErrorBars(QCPAxis *keyAxis, QCPAxis *valueAxis);\n  virtual ~QCPErrorBars() Q_DECL_OVERRIDE;\n  // getters:\n  QSharedPointer<QCPErrorBarsDataContainer> data() const { return mDataContainer; }\n  QCPAbstractPlottable *dataPlottable() const { return mDataPlottable.data(); }\n  ErrorType errorType() const { return mErrorType; }\n  double whiskerWidth() const { return mWhiskerWidth; }\n  double symbolGap() const { return mSymbolGap; }\n  \n  // setters:\n  void setData(QSharedPointer<QCPErrorBarsDataContainer> data);\n  void setData(const QVector<double> &error);\n  void setData(const QVector<double> &errorMinus, const QVector<double> &errorPlus);\n  void setDataPlottable(QCPAbstractPlottable* plottable);\n  void setErrorType(ErrorType type);\n  void setWhiskerWidth(double pixels);\n  void setSymbolGap(double pixels);\n  \n  // non-property methods:\n  void addData(const QVector<double> &error);\n  void addData(const QVector<double> &errorMinus, const QVector<double> &errorPlus);\n  void addData(double error);\n  void addData(double errorMinus, double errorPlus);\n  \n  // virtual methods of 1d plottable interface:\n  virtual int dataCount() const Q_DECL_OVERRIDE;\n  virtual double dataMainKey(int index) const Q_DECL_OVERRIDE;\n  virtual double dataSortKey(int index) const Q_DECL_OVERRIDE;\n  virtual double dataMainValue(int index) const Q_DECL_OVERRIDE;\n  virtual QCPRange dataValueRange(int index) const Q_DECL_OVERRIDE;\n  virtual QPointF dataPixelPosition(int index) const Q_DECL_OVERRIDE;\n  virtual bool sortKeyIsMainKey() const Q_DECL_OVERRIDE;\n  virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE;\n  virtual int findBegin(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE;\n  virtual int findEnd(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE;\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  virtual QCPPlottableInterface1D *interface1D() Q_DECL_OVERRIDE { return this; }\n  \nprotected:\n  // property members:\n  QSharedPointer<QCPErrorBarsDataContainer> mDataContainer;\n  QPointer<QCPAbstractPlottable> mDataPlottable;\n  ErrorType mErrorType;\n  double mWhiskerWidth;\n  double mSymbolGap;\n  \n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE;\n  virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE;\n  virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  void getErrorBarLines(QCPErrorBarsDataContainer::const_iterator it, QVector<QLineF> &backbones, QVector<QLineF> &whiskers) const;\n  void getVisibleDataBounds(QCPErrorBarsDataContainer::const_iterator &begin, QCPErrorBarsDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const;\n  double pointDistance(const QPointF &pixelPoint, QCPErrorBarsDataContainer::const_iterator &closestData) const;\n  // helpers:\n  void getDataSegments(QList<QCPDataRange> &selectedSegments, QList<QCPDataRange> &unselectedSegments) const;\n  bool errorBarVisible(int index) const;\n  bool rectIntersectsLine(const QRectF &pixelRect, const QLineF &line) const;\n  \n  friend class QCustomPlot;\n  friend class QCPLegend;\n};\n\n/* end of 'src/plottables/plottable-errorbar.h' */\n\n\n/* including file 'src/items/item-straightline.h' */\n/* modified 2021-03-29T02:30:44, size 3137        */\n\nclass QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QPen pen READ pen WRITE setPen)\n  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)\n  /// \\endcond\npublic:\n  explicit QCPItemStraightLine(QCustomPlot *parentPlot);\n  virtual ~QCPItemStraightLine() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QPen pen() const { return mPen; }\n  QPen selectedPen() const { return mSelectedPen; }\n  \n  // setters;\n  void setPen(const QPen &pen);\n  void setSelectedPen(const QPen &pen);\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  \n  QCPItemPosition * const point1;\n  QCPItemPosition * const point2;\n  \nprotected:\n  // property members:\n  QPen mPen, mSelectedPen;\n  \n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  QLineF getRectClippedStraightLine(const QCPVector2D &base, const QCPVector2D &vec, const QRect &rect) const;\n  QPen mainPen() const;\n};\n\n/* end of 'src/items/item-straightline.h' */\n\n\n/* including file 'src/items/item-line.h'  */\n/* modified 2021-03-29T02:30:44, size 3429 */\n\nclass QCP_LIB_DECL QCPItemLine : public QCPAbstractItem\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QPen pen READ pen WRITE setPen)\n  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)\n  Q_PROPERTY(QCPLineEnding head READ head WRITE setHead)\n  Q_PROPERTY(QCPLineEnding tail READ tail WRITE setTail)\n  /// \\endcond\npublic:\n  explicit QCPItemLine(QCustomPlot *parentPlot);\n  virtual ~QCPItemLine() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QPen pen() const { return mPen; }\n  QPen selectedPen() const { return mSelectedPen; }\n  QCPLineEnding head() const { return mHead; }\n  QCPLineEnding tail() const { return mTail; }\n  \n  // setters;\n  void setPen(const QPen &pen);\n  void setSelectedPen(const QPen &pen);\n  void setHead(const QCPLineEnding &head);\n  void setTail(const QCPLineEnding &tail);\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  \n  QCPItemPosition * const start;\n  QCPItemPosition * const end;\n  \nprotected:\n  // property members:\n  QPen mPen, mSelectedPen;\n  QCPLineEnding mHead, mTail;\n  \n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  QLineF getRectClippedLine(const QCPVector2D &start, const QCPVector2D &end, const QRect &rect) const;\n  QPen mainPen() const;\n};\n\n/* end of 'src/items/item-line.h' */\n\n\n/* including file 'src/items/item-curve.h' */\n/* modified 2021-03-29T02:30:44, size 3401 */\n\nclass QCP_LIB_DECL QCPItemCurve : public QCPAbstractItem\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QPen pen READ pen WRITE setPen)\n  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)\n  Q_PROPERTY(QCPLineEnding head READ head WRITE setHead)\n  Q_PROPERTY(QCPLineEnding tail READ tail WRITE setTail)\n  /// \\endcond\npublic:\n  explicit QCPItemCurve(QCustomPlot *parentPlot);\n  virtual ~QCPItemCurve() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QPen pen() const { return mPen; }\n  QPen selectedPen() const { return mSelectedPen; }\n  QCPLineEnding head() const { return mHead; }\n  QCPLineEnding tail() const { return mTail; }\n  \n  // setters;\n  void setPen(const QPen &pen);\n  void setSelectedPen(const QPen &pen);\n  void setHead(const QCPLineEnding &head);\n  void setTail(const QCPLineEnding &tail);\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  \n  QCPItemPosition * const start;\n  QCPItemPosition * const startDir;\n  QCPItemPosition * const endDir;\n  QCPItemPosition * const end;\n  \nprotected:\n  // property members:\n  QPen mPen, mSelectedPen;\n  QCPLineEnding mHead, mTail;\n  \n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  QPen mainPen() const;\n};\n\n/* end of 'src/items/item-curve.h' */\n\n\n/* including file 'src/items/item-rect.h'  */\n/* modified 2021-03-29T02:30:44, size 3710 */\n\nclass QCP_LIB_DECL QCPItemRect : public QCPAbstractItem\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QPen pen READ pen WRITE setPen)\n  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)\n  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)\n  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)\n  /// \\endcond\npublic:\n  explicit QCPItemRect(QCustomPlot *parentPlot);\n  virtual ~QCPItemRect() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QPen pen() const { return mPen; }\n  QPen selectedPen() const { return mSelectedPen; }\n  QBrush brush() const { return mBrush; }\n  QBrush selectedBrush() const { return mSelectedBrush; }\n  \n  // setters;\n  void setPen(const QPen &pen);\n  void setSelectedPen(const QPen &pen);\n  void setBrush(const QBrush &brush);\n  void setSelectedBrush(const QBrush &brush);\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  \n  QCPItemPosition * const topLeft;\n  QCPItemPosition * const bottomRight;\n  QCPItemAnchor * const top;\n  QCPItemAnchor * const topRight;\n  QCPItemAnchor * const right;\n  QCPItemAnchor * const bottom;\n  QCPItemAnchor * const bottomLeft;\n  QCPItemAnchor * const left;\n  \nprotected:\n  enum AnchorIndex {aiTop, aiTopRight, aiRight, aiBottom, aiBottomLeft, aiLeft};\n  \n  // property members:\n  QPen mPen, mSelectedPen;\n  QBrush mBrush, mSelectedBrush;\n  \n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  QPen mainPen() const;\n  QBrush mainBrush() const;\n};\n\n/* end of 'src/items/item-rect.h' */\n\n\n/* including file 'src/items/item-text.h'  */\n/* modified 2021-03-29T02:30:44, size 5576 */\n\nclass QCP_LIB_DECL QCPItemText : public QCPAbstractItem\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QColor color READ color WRITE setColor)\n  Q_PROPERTY(QColor selectedColor READ selectedColor WRITE setSelectedColor)\n  Q_PROPERTY(QPen pen READ pen WRITE setPen)\n  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)\n  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)\n  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)\n  Q_PROPERTY(QFont font READ font WRITE setFont)\n  Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)\n  Q_PROPERTY(QString text READ text WRITE setText)\n  Q_PROPERTY(Qt::Alignment positionAlignment READ positionAlignment WRITE setPositionAlignment)\n  Q_PROPERTY(Qt::Alignment textAlignment READ textAlignment WRITE setTextAlignment)\n  Q_PROPERTY(double rotation READ rotation WRITE setRotation)\n  Q_PROPERTY(QMargins padding READ padding WRITE setPadding)\n  /// \\endcond\npublic:\n  explicit QCPItemText(QCustomPlot *parentPlot);\n  virtual ~QCPItemText() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QColor color() const { return mColor; }\n  QColor selectedColor() const { return mSelectedColor; }\n  QPen pen() const { return mPen; }\n  QPen selectedPen() const { return mSelectedPen; }\n  QBrush brush() const { return mBrush; }\n  QBrush selectedBrush() const { return mSelectedBrush; }\n  QFont font() const { return mFont; }\n  QFont selectedFont() const { return mSelectedFont; }\n  QString text() const { return mText; }\n  Qt::Alignment positionAlignment() const { return mPositionAlignment; }\n  Qt::Alignment textAlignment() const { return mTextAlignment; }\n  double rotation() const { return mRotation; }\n  QMargins padding() const { return mPadding; }\n  \n  // setters;\n  void setColor(const QColor &color);\n  void setSelectedColor(const QColor &color);\n  void setPen(const QPen &pen);\n  void setSelectedPen(const QPen &pen);\n  void setBrush(const QBrush &brush);\n  void setSelectedBrush(const QBrush &brush);\n  void setFont(const QFont &font);\n  void setSelectedFont(const QFont &font);\n  void setText(const QString &text);\n  void setPositionAlignment(Qt::Alignment alignment);\n  void setTextAlignment(Qt::Alignment alignment);\n  void setRotation(double degrees);\n  void setPadding(const QMargins &padding);\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  \n  QCPItemPosition * const position;\n  QCPItemAnchor * const topLeft;\n  QCPItemAnchor * const top;\n  QCPItemAnchor * const topRight;\n  QCPItemAnchor * const right;\n  QCPItemAnchor * const bottomRight;\n  QCPItemAnchor * const bottom;\n  QCPItemAnchor * const bottomLeft;\n  QCPItemAnchor * const left;\n  \nprotected:\n  enum AnchorIndex {aiTopLeft, aiTop, aiTopRight, aiRight, aiBottomRight, aiBottom, aiBottomLeft, aiLeft};\n  \n  // property members:\n  QColor mColor, mSelectedColor;\n  QPen mPen, mSelectedPen;\n  QBrush mBrush, mSelectedBrush;\n  QFont mFont, mSelectedFont;\n  QString mText;\n  Qt::Alignment mPositionAlignment;\n  Qt::Alignment mTextAlignment;\n  double mRotation;\n  QMargins mPadding;\n  \n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  QPointF getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const;\n  QFont mainFont() const;\n  QColor mainColor() const;\n  QPen mainPen() const;\n  QBrush mainBrush() const;\n};\n\n/* end of 'src/items/item-text.h' */\n\n\n/* including file 'src/items/item-ellipse.h' */\n/* modified 2021-03-29T02:30:44, size 3890   */\n\nclass QCP_LIB_DECL QCPItemEllipse : public QCPAbstractItem\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QPen pen READ pen WRITE setPen)\n  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)\n  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)\n  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)\n  /// \\endcond\npublic:\n  explicit QCPItemEllipse(QCustomPlot *parentPlot);\n  virtual ~QCPItemEllipse() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QPen pen() const { return mPen; }\n  QPen selectedPen() const { return mSelectedPen; }\n  QBrush brush() const { return mBrush; }\n  QBrush selectedBrush() const { return mSelectedBrush; }\n  \n  // setters;\n  void setPen(const QPen &pen);\n  void setSelectedPen(const QPen &pen);\n  void setBrush(const QBrush &brush);\n  void setSelectedBrush(const QBrush &brush);\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  \n  QCPItemPosition * const topLeft;\n  QCPItemPosition * const bottomRight;\n  QCPItemAnchor * const topLeftRim;\n  QCPItemAnchor * const top;\n  QCPItemAnchor * const topRightRim;\n  QCPItemAnchor * const right;\n  QCPItemAnchor * const bottomRightRim;\n  QCPItemAnchor * const bottom;\n  QCPItemAnchor * const bottomLeftRim;\n  QCPItemAnchor * const left;\n  QCPItemAnchor * const center;\n  \nprotected:\n  enum AnchorIndex {aiTopLeftRim, aiTop, aiTopRightRim, aiRight, aiBottomRightRim, aiBottom, aiBottomLeftRim, aiLeft, aiCenter};\n  \n  // property members:\n  QPen mPen, mSelectedPen;\n  QBrush mBrush, mSelectedBrush;\n  \n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  QPen mainPen() const;\n  QBrush mainBrush() const;\n};\n\n/* end of 'src/items/item-ellipse.h' */\n\n\n/* including file 'src/items/item-pixmap.h' */\n/* modified 2021-03-29T02:30:44, size 4407  */\n\nclass QCP_LIB_DECL QCPItemPixmap : public QCPAbstractItem\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap)\n  Q_PROPERTY(bool scaled READ scaled WRITE setScaled)\n  Q_PROPERTY(Qt::AspectRatioMode aspectRatioMode READ aspectRatioMode)\n  Q_PROPERTY(Qt::TransformationMode transformationMode READ transformationMode)\n  Q_PROPERTY(QPen pen READ pen WRITE setPen)\n  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)\n  /// \\endcond\npublic:\n  explicit QCPItemPixmap(QCustomPlot *parentPlot);\n  virtual ~QCPItemPixmap() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QPixmap pixmap() const { return mPixmap; }\n  bool scaled() const { return mScaled; }\n  Qt::AspectRatioMode aspectRatioMode() const { return mAspectRatioMode; }\n  Qt::TransformationMode transformationMode() const { return mTransformationMode; }\n  QPen pen() const { return mPen; }\n  QPen selectedPen() const { return mSelectedPen; }\n  \n  // setters;\n  void setPixmap(const QPixmap &pixmap);\n  void setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio, Qt::TransformationMode transformationMode=Qt::SmoothTransformation);\n  void setPen(const QPen &pen);\n  void setSelectedPen(const QPen &pen);\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  \n  QCPItemPosition * const topLeft;\n  QCPItemPosition * const bottomRight;\n  QCPItemAnchor * const top;\n  QCPItemAnchor * const topRight;\n  QCPItemAnchor * const right;\n  QCPItemAnchor * const bottom;\n  QCPItemAnchor * const bottomLeft;\n  QCPItemAnchor * const left;\n  \nprotected:\n  enum AnchorIndex {aiTop, aiTopRight, aiRight, aiBottom, aiBottomLeft, aiLeft};\n  \n  // property members:\n  QPixmap mPixmap;\n  QPixmap mScaledPixmap;\n  bool mScaled;\n  bool mScaledPixmapInvalidated;\n  Qt::AspectRatioMode mAspectRatioMode;\n  Qt::TransformationMode mTransformationMode;\n  QPen mPen, mSelectedPen;\n  \n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  void updateScaledPixmap(QRect finalRect=QRect(), bool flipHorz=false, bool flipVert=false);\n  QRect getFinalRect(bool *flippedHorz=nullptr, bool *flippedVert=nullptr) const;\n  QPen mainPen() const;\n};\n\n/* end of 'src/items/item-pixmap.h' */\n\n\n/* including file 'src/items/item-tracer.h' */\n/* modified 2021-03-29T02:30:44, size 4811  */\n\nclass QCP_LIB_DECL QCPItemTracer : public QCPAbstractItem\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QPen pen READ pen WRITE setPen)\n  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)\n  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)\n  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)\n  Q_PROPERTY(double size READ size WRITE setSize)\n  Q_PROPERTY(TracerStyle style READ style WRITE setStyle)\n  Q_PROPERTY(QCPGraph* graph READ graph WRITE setGraph)\n  Q_PROPERTY(double graphKey READ graphKey WRITE setGraphKey)\n  Q_PROPERTY(bool interpolating READ interpolating WRITE setInterpolating)\n  /// \\endcond\npublic:\n  /*!\n    The different visual appearances a tracer item can have. Some styles size may be controlled with \\ref setSize.\n    \n    \\see setStyle\n  */\n  enum TracerStyle { tsNone        ///< The tracer is not visible\n                     ,tsPlus       ///< A plus shaped crosshair with limited size\n                     ,tsCrosshair  ///< A plus shaped crosshair which spans the complete axis rect\n                     ,tsCircle     ///< A circle\n                     ,tsSquare     ///< A square\n                   };\n  Q_ENUMS(TracerStyle)\n\n  explicit QCPItemTracer(QCustomPlot *parentPlot);\n  virtual ~QCPItemTracer() Q_DECL_OVERRIDE;\n\n  // getters:\n  QPen pen() const { return mPen; }\n  QPen selectedPen() const { return mSelectedPen; }\n  QBrush brush() const { return mBrush; }\n  QBrush selectedBrush() const { return mSelectedBrush; }\n  double size() const { return mSize; }\n  TracerStyle style() const { return mStyle; }\n  QCPGraph *graph() const { return mGraph; }\n  double graphKey() const { return mGraphKey; }\n  bool interpolating() const { return mInterpolating; }\n\n  // setters;\n  void setPen(const QPen &pen);\n  void setSelectedPen(const QPen &pen);\n  void setBrush(const QBrush &brush);\n  void setSelectedBrush(const QBrush &brush);\n  void setSize(double size);\n  void setStyle(TracerStyle style);\n  void setGraph(QCPGraph *graph);\n  void setGraphKey(double key);\n  void setInterpolating(bool enabled);\n\n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  void updatePosition();\n\n  QCPItemPosition * const position;\n\nprotected:\n  // property members:\n  QPen mPen, mSelectedPen;\n  QBrush mBrush, mSelectedBrush;\n  double mSize;\n  TracerStyle mStyle;\n  QCPGraph *mGraph;\n  double mGraphKey;\n  bool mInterpolating;\n\n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n\n  // non-virtual methods:\n  QPen mainPen() const;\n  QBrush mainBrush() const;\n};\nQ_DECLARE_METATYPE(QCPItemTracer::TracerStyle)\n\n/* end of 'src/items/item-tracer.h' */\n\n\n/* including file 'src/items/item-bracket.h' */\n/* modified 2021-03-29T02:30:44, size 3991   */\n\nclass QCP_LIB_DECL QCPItemBracket : public QCPAbstractItem\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  Q_PROPERTY(QPen pen READ pen WRITE setPen)\n  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)\n  Q_PROPERTY(double length READ length WRITE setLength)\n  Q_PROPERTY(BracketStyle style READ style WRITE setStyle)\n  /// \\endcond\npublic:\n  /*!\n    Defines the various visual shapes of the bracket item. The appearance can be further modified\n    by \\ref setLength and \\ref setPen.\n    \n    \\see setStyle\n  */\n  enum BracketStyle { bsSquare  ///< A brace with angled edges\n                      ,bsRound  ///< A brace with round edges\n                      ,bsCurly  ///< A curly brace\n                      ,bsCalligraphic ///< A curly brace with varying stroke width giving a calligraphic impression\n  };\n  Q_ENUMS(BracketStyle)\n\n  explicit QCPItemBracket(QCustomPlot *parentPlot);\n  virtual ~QCPItemBracket() Q_DECL_OVERRIDE;\n  \n  // getters:\n  QPen pen() const { return mPen; }\n  QPen selectedPen() const { return mSelectedPen; }\n  double length() const { return mLength; }\n  BracketStyle style() const { return mStyle; }\n  \n  // setters;\n  void setPen(const QPen &pen);\n  void setSelectedPen(const QPen &pen);\n  void setLength(double length);\n  void setStyle(BracketStyle style);\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=nullptr) const Q_DECL_OVERRIDE;\n  \n  QCPItemPosition * const left;\n  QCPItemPosition * const right;\n  QCPItemAnchor * const center;\n  \nprotected:\n  // property members:\n  enum AnchorIndex {aiCenter};\n  QPen mPen, mSelectedPen;\n  double mLength;\n  BracketStyle mStyle;\n  \n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  QPen mainPen() const;\n};\nQ_DECLARE_METATYPE(QCPItemBracket::BracketStyle)\n\n/* end of 'src/items/item-bracket.h' */\n\n\n/* including file 'src/polar/radialaxis.h'  */\n/* modified 2021-03-29T02:30:44, size 12227 */\n\n\nclass QCP_LIB_DECL QCPPolarAxisRadial : public QCPLayerable\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  \n  /// \\endcond\npublic:\n  /*!\n    Defines the reference of the angle at which a radial axis is tilted (\\ref setAngle).\n  */\n  enum AngleReference { arAbsolute    ///< The axis tilt is given in absolute degrees. The zero is to the right and positive angles are measured counter-clockwise.\n                       ,arAngularAxis ///< The axis tilt is measured in the angular coordinate system given by the parent angular axis.\n                      };\n  Q_ENUMS(AngleReference)\n  /*!\n    Defines the scale of an axis.\n    \\see setScaleType\n  */\n  enum ScaleType { stLinear       ///< Linear scaling\n                   ,stLogarithmic ///< Logarithmic scaling with correspondingly transformed axis coordinates (possibly also \\ref setTicker to a \\ref QCPAxisTickerLog instance).\n                 };\n  Q_ENUMS(ScaleType)\n  /*!\n    Defines the selectable parts of an axis.\n    \\see setSelectableParts, setSelectedParts\n  */\n  enum SelectablePart { spNone        = 0      ///< None of the selectable parts\n                        ,spAxis       = 0x001  ///< The axis backbone and tick marks\n                        ,spTickLabels = 0x002  ///< Tick labels (numbers) of this axis (as a whole, not individually)\n                        ,spAxisLabel  = 0x004  ///< The axis label\n                      };\n  Q_ENUMS(SelectablePart)\n  Q_FLAGS(SelectableParts)\n  Q_DECLARE_FLAGS(SelectableParts, SelectablePart)\n  \n  enum LabelMode { lmUpright   ///< \n                   ,lmRotated ///< \n                 };\n  Q_ENUMS(LabelMode)\n  \n  explicit QCPPolarAxisRadial(QCPPolarAxisAngular *parent);\n  virtual ~QCPPolarAxisRadial();\n  \n  // getters:\n  bool rangeDrag() const { return mRangeDrag; }\n  bool rangeZoom() const { return mRangeZoom; }\n  double rangeZoomFactor() const { return mRangeZoomFactor; }\n  \n  QCPPolarAxisAngular *angularAxis() const { return mAngularAxis; }\n  ScaleType scaleType() const { return mScaleType; }\n  const QCPRange range() const { return mRange; }\n  bool rangeReversed() const { return mRangeReversed; }\n  double angle() const { return mAngle; }\n  AngleReference angleReference() const { return mAngleReference; }\n  QSharedPointer<QCPAxisTicker> ticker() const { return mTicker; }\n  bool ticks() const { return mTicks; }\n  bool tickLabels() const { return mTickLabels; }\n  int tickLabelPadding() const { return mLabelPainter.padding(); }\n  QFont tickLabelFont() const { return mTickLabelFont; }\n  QColor tickLabelColor() const { return mTickLabelColor; }\n  double tickLabelRotation() const { return mLabelPainter.rotation(); }\n  LabelMode tickLabelMode() const;\n  QString numberFormat() const;\n  int numberPrecision() const { return mNumberPrecision; }\n  QVector<double> tickVector() const { return mTickVector; }\n  QVector<double> subTickVector() const { return mSubTickVector; }\n  QVector<QString> tickVectorLabels() const { return mTickVectorLabels; }\n  int tickLengthIn() const;\n  int tickLengthOut() const;\n  bool subTicks() const { return mSubTicks; }\n  int subTickLengthIn() const;\n  int subTickLengthOut() const;\n  QPen basePen() const { return mBasePen; }\n  QPen tickPen() const { return mTickPen; }\n  QPen subTickPen() const { return mSubTickPen; }\n  QFont labelFont() const { return mLabelFont; }\n  QColor labelColor() const { return mLabelColor; }\n  QString label() const { return mLabel; }\n  int labelPadding() const;\n  SelectableParts selectedParts() const { return mSelectedParts; }\n  SelectableParts selectableParts() const { return mSelectableParts; }\n  QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; }\n  QFont selectedLabelFont() const { return mSelectedLabelFont; }\n  QColor selectedTickLabelColor() const { return mSelectedTickLabelColor; }\n  QColor selectedLabelColor() const { return mSelectedLabelColor; }\n  QPen selectedBasePen() const { return mSelectedBasePen; }\n  QPen selectedTickPen() const { return mSelectedTickPen; }\n  QPen selectedSubTickPen() const { return mSelectedSubTickPen; }\n  \n  // setters:\n  void setRangeDrag(bool enabled);\n  void setRangeZoom(bool enabled);\n  void setRangeZoomFactor(double factor);\n  \n  Q_SLOT void setScaleType(QCPPolarAxisRadial::ScaleType type);\n  Q_SLOT void setRange(const QCPRange &range);\n  void setRange(double lower, double upper);\n  void setRange(double position, double size, Qt::AlignmentFlag alignment);\n  void setRangeLower(double lower);\n  void setRangeUpper(double upper);\n  void setRangeReversed(bool reversed);\n  void setAngle(double degrees);\n  void setAngleReference(AngleReference reference);\n  void setTicker(QSharedPointer<QCPAxisTicker> ticker);\n  void setTicks(bool show);\n  void setTickLabels(bool show);\n  void setTickLabelPadding(int padding);\n  void setTickLabelFont(const QFont &font);\n  void setTickLabelColor(const QColor &color);\n  void setTickLabelRotation(double degrees);\n  void setTickLabelMode(LabelMode mode);\n  void setNumberFormat(const QString &formatCode);\n  void setNumberPrecision(int precision);\n  void setTickLength(int inside, int outside=0);\n  void setTickLengthIn(int inside);\n  void setTickLengthOut(int outside);\n  void setSubTicks(bool show);\n  void setSubTickLength(int inside, int outside=0);\n  void setSubTickLengthIn(int inside);\n  void setSubTickLengthOut(int outside);\n  void setBasePen(const QPen &pen);\n  void setTickPen(const QPen &pen);\n  void setSubTickPen(const QPen &pen);\n  void setLabelFont(const QFont &font);\n  void setLabelColor(const QColor &color);\n  void setLabel(const QString &str);\n  void setLabelPadding(int padding);\n  void setSelectedTickLabelFont(const QFont &font);\n  void setSelectedLabelFont(const QFont &font);\n  void setSelectedTickLabelColor(const QColor &color);\n  void setSelectedLabelColor(const QColor &color);\n  void setSelectedBasePen(const QPen &pen);\n  void setSelectedTickPen(const QPen &pen);\n  void setSelectedSubTickPen(const QPen &pen);\n  Q_SLOT void setSelectableParts(const QCPPolarAxisRadial::SelectableParts &selectableParts);\n  Q_SLOT void setSelectedParts(const QCPPolarAxisRadial::SelectableParts &selectedParts);\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;\n  \n  // non-property methods:\n  void moveRange(double diff);\n  void scaleRange(double factor);\n  void scaleRange(double factor, double center);\n  void rescale(bool onlyVisiblePlottables=false);\n  void pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const;\n  QPointF coordToPixel(double angleCoord, double radiusCoord) const;\n  double coordToRadius(double coord) const;\n  double radiusToCoord(double radius) const;\n  SelectablePart getPartAt(const QPointF &pos) const;\n  \nsignals:\n  void rangeChanged(const QCPRange &newRange);\n  void rangeChanged(const QCPRange &newRange, const QCPRange &oldRange);\n  void scaleTypeChanged(QCPPolarAxisRadial::ScaleType scaleType);\n  void selectionChanged(const QCPPolarAxisRadial::SelectableParts &parts);\n  void selectableChanged(const QCPPolarAxisRadial::SelectableParts &parts);\n\nprotected:\n  // property members:\n  bool mRangeDrag;\n  bool mRangeZoom;\n  double mRangeZoomFactor;\n  \n  // axis base:\n  QCPPolarAxisAngular *mAngularAxis;\n  double mAngle;\n  AngleReference mAngleReference;\n  SelectableParts mSelectableParts, mSelectedParts;\n  QPen mBasePen, mSelectedBasePen;\n  // axis label:\n  int mLabelPadding;\n  QString mLabel;\n  QFont mLabelFont, mSelectedLabelFont;\n  QColor mLabelColor, mSelectedLabelColor;\n  // tick labels:\n  //int mTickLabelPadding; in label painter\n  bool mTickLabels;\n  //double mTickLabelRotation; in label painter\n  QFont mTickLabelFont, mSelectedTickLabelFont;\n  QColor mTickLabelColor, mSelectedTickLabelColor;\n  int mNumberPrecision;\n  QLatin1Char mNumberFormatChar;\n  bool mNumberBeautifulPowers;\n  bool mNumberMultiplyCross;\n  // ticks and subticks:\n  bool mTicks;\n  bool mSubTicks;\n  int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut;\n  QPen mTickPen, mSelectedTickPen;\n  QPen mSubTickPen, mSelectedSubTickPen;\n  // scale and range:\n  QCPRange mRange;\n  bool mRangeReversed;\n  ScaleType mScaleType;\n  \n  // non-property members:\n  QPointF mCenter;\n  double mRadius;\n  QSharedPointer<QCPAxisTicker> mTicker;\n  QVector<double> mTickVector;\n  QVector<QString> mTickVectorLabels;\n  QVector<double> mSubTickVector;\n  bool mDragging;\n  QCPRange mDragStartRange;\n  QCP::AntialiasedElements mAADragBackup, mNotAADragBackup;\n  QCPLabelPainterPrivate mLabelPainter;\n  \n  // reimplemented virtual methods:\n  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE;\n  // events:\n  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE;\n  virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE;\n  // mouse events:\n  virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE;\n  virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;\n  virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;\n  virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  void updateGeometry(const QPointF &center, double radius);\n  void setupTickVectors();\n  QPen getBasePen() const;\n  QPen getTickPen() const;\n  QPen getSubTickPen() const;\n  QFont getTickLabelFont() const;\n  QFont getLabelFont() const;\n  QColor getTickLabelColor() const;\n  QColor getLabelColor() const;\n  \nprivate:\n  Q_DISABLE_COPY(QCPPolarAxisRadial)\n  \n  friend class QCustomPlot;\n  friend class QCPPolarAxisAngular;\n};\nQ_DECLARE_OPERATORS_FOR_FLAGS(QCPPolarAxisRadial::SelectableParts)\nQ_DECLARE_METATYPE(QCPPolarAxisRadial::AngleReference)\nQ_DECLARE_METATYPE(QCPPolarAxisRadial::ScaleType)\nQ_DECLARE_METATYPE(QCPPolarAxisRadial::SelectablePart)\n\n\n\n/* end of 'src/polar/radialaxis.h' */\n\n\n/* including file 'src/polar/layoutelement-angularaxis.h' */\n/* modified 2021-03-29T02:30:44, size 13461               */\n\nclass QCP_LIB_DECL QCPPolarAxisAngular : public QCPLayoutElement\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  \n  /// \\endcond\npublic:\n  /*!\n    Defines the selectable parts of an axis.\n    \\see setSelectableParts, setSelectedParts\n  */\n  enum SelectablePart { spNone        = 0      ///< None of the selectable parts\n                        ,spAxis       = 0x001  ///< The axis backbone and tick marks\n                        ,spTickLabels = 0x002  ///< Tick labels (numbers) of this axis (as a whole, not individually)\n                        ,spAxisLabel  = 0x004  ///< The axis label\n                      };\n  Q_ENUMS(SelectablePart)\n  Q_FLAGS(SelectableParts)\n  Q_DECLARE_FLAGS(SelectableParts, SelectablePart)\n  \n  /*!\n    TODO\n  */\n  enum LabelMode { lmUpright   ///< \n                   ,lmRotated ///< \n                 };\n  Q_ENUMS(LabelMode)\n  \n  explicit QCPPolarAxisAngular(QCustomPlot *parentPlot);\n  virtual ~QCPPolarAxisAngular();\n  \n  // getters:\n  QPixmap background() const { return mBackgroundPixmap; }\n  QBrush backgroundBrush() const { return mBackgroundBrush; }\n  bool backgroundScaled() const { return mBackgroundScaled; }\n  Qt::AspectRatioMode backgroundScaledMode() const { return mBackgroundScaledMode; }\n  bool rangeDrag() const { return mRangeDrag; }\n  bool rangeZoom() const { return mRangeZoom; }\n  double rangeZoomFactor() const { return mRangeZoomFactor; }\n  \n  const QCPRange range() const { return mRange; }\n  bool rangeReversed() const { return mRangeReversed; }\n  double angle() const { return mAngle; }\n  QSharedPointer<QCPAxisTicker> ticker() const { return mTicker; }\n  bool ticks() const { return mTicks; }\n  bool tickLabels() const { return mTickLabels; }\n  int tickLabelPadding() const { return mLabelPainter.padding(); }\n  QFont tickLabelFont() const { return mTickLabelFont; }\n  QColor tickLabelColor() const { return mTickLabelColor; }\n  double tickLabelRotation() const { return mLabelPainter.rotation(); }\n  LabelMode tickLabelMode() const;\n  QString numberFormat() const;\n  int numberPrecision() const { return mNumberPrecision; }\n  QVector<double> tickVector() const { return mTickVector; }\n  QVector<QString> tickVectorLabels() const { return mTickVectorLabels; }\n  int tickLengthIn() const { return mTickLengthIn; }\n  int tickLengthOut() const { return mTickLengthOut; }\n  bool subTicks() const { return mSubTicks; }\n  int subTickLengthIn() const { return mSubTickLengthIn; }\n  int subTickLengthOut() const { return mSubTickLengthOut; }\n  QPen basePen() const { return mBasePen; }\n  QPen tickPen() const { return mTickPen; }\n  QPen subTickPen() const { return mSubTickPen; }\n  QFont labelFont() const { return mLabelFont; }\n  QColor labelColor() const { return mLabelColor; }\n  QString label() const { return mLabel; }\n  int labelPadding() const { return mLabelPadding; }\n  SelectableParts selectedParts() const { return mSelectedParts; }\n  SelectableParts selectableParts() const { return mSelectableParts; }\n  QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; }\n  QFont selectedLabelFont() const { return mSelectedLabelFont; }\n  QColor selectedTickLabelColor() const { return mSelectedTickLabelColor; }\n  QColor selectedLabelColor() const { return mSelectedLabelColor; }\n  QPen selectedBasePen() const { return mSelectedBasePen; }\n  QPen selectedTickPen() const { return mSelectedTickPen; }\n  QPen selectedSubTickPen() const { return mSelectedSubTickPen; }\n  QCPPolarGrid *grid() const { return mGrid; }\n  \n  // setters:\n  void setBackground(const QPixmap &pm);\n  void setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding);\n  void setBackground(const QBrush &brush);\n  void setBackgroundScaled(bool scaled);\n  void setBackgroundScaledMode(Qt::AspectRatioMode mode);\n  void setRangeDrag(bool enabled);\n  void setRangeZoom(bool enabled);\n  void setRangeZoomFactor(double factor);\n  \n  Q_SLOT void setRange(const QCPRange &range);\n  void setRange(double lower, double upper);\n  void setRange(double position, double size, Qt::AlignmentFlag alignment);\n  void setRangeLower(double lower);\n  void setRangeUpper(double upper);\n  void setRangeReversed(bool reversed);\n  void setAngle(double degrees);\n  void setTicker(QSharedPointer<QCPAxisTicker> ticker);\n  void setTicks(bool show);\n  void setTickLabels(bool show);\n  void setTickLabelPadding(int padding);\n  void setTickLabelFont(const QFont &font);\n  void setTickLabelColor(const QColor &color);\n  void setTickLabelRotation(double degrees);\n  void setTickLabelMode(LabelMode mode);\n  void setNumberFormat(const QString &formatCode);\n  void setNumberPrecision(int precision);\n  void setTickLength(int inside, int outside=0);\n  void setTickLengthIn(int inside);\n  void setTickLengthOut(int outside);\n  void setSubTicks(bool show);\n  void setSubTickLength(int inside, int outside=0);\n  void setSubTickLengthIn(int inside);\n  void setSubTickLengthOut(int outside);\n  void setBasePen(const QPen &pen);\n  void setTickPen(const QPen &pen);\n  void setSubTickPen(const QPen &pen);\n  void setLabelFont(const QFont &font);\n  void setLabelColor(const QColor &color);\n  void setLabel(const QString &str);\n  void setLabelPadding(int padding);\n  void setLabelPosition(Qt::AlignmentFlag position);\n  void setSelectedTickLabelFont(const QFont &font);\n  void setSelectedLabelFont(const QFont &font);\n  void setSelectedTickLabelColor(const QColor &color);\n  void setSelectedLabelColor(const QColor &color);\n  void setSelectedBasePen(const QPen &pen);\n  void setSelectedTickPen(const QPen &pen);\n  void setSelectedSubTickPen(const QPen &pen);\n  Q_SLOT void setSelectableParts(const QCPPolarAxisAngular::SelectableParts &selectableParts);\n  Q_SLOT void setSelectedParts(const QCPPolarAxisAngular::SelectableParts &selectedParts);\n  \n  // reimplemented virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE;\n  virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE;\n  virtual QList<QCPLayoutElement*> elements(bool recursive) const Q_DECL_OVERRIDE;\n  \n  // non-property methods:\n  bool removeGraph(QCPPolarGraph *graph);\n  int radialAxisCount() const;\n  QCPPolarAxisRadial *radialAxis(int index=0) const;\n  QList<QCPPolarAxisRadial*> radialAxes() const;\n  QCPPolarAxisRadial *addRadialAxis(QCPPolarAxisRadial *axis=0);\n  bool removeRadialAxis(QCPPolarAxisRadial *axis);\n  QCPLayoutInset *insetLayout() const { return mInsetLayout; }\n  QRegion exactClipRegion() const;\n  \n  void moveRange(double diff);\n  void scaleRange(double factor);\n  void scaleRange(double factor, double center);\n  void rescale(bool onlyVisiblePlottables=false);\n  double coordToAngleRad(double coord) const { return mAngleRad+(coord-mRange.lower)/mRange.size()*(mRangeReversed ? -2.0*M_PI : 2.0*M_PI); } // mention in doc that return doesn't wrap\n  double angleRadToCoord(double angleRad) const { return mRange.lower+(angleRad-mAngleRad)/(mRangeReversed ? -2.0*M_PI : 2.0*M_PI)*mRange.size(); }\n  void pixelToCoord(QPointF pixelPos, double &angleCoord, double &radiusCoord) const;\n  QPointF coordToPixel(double angleCoord, double radiusCoord) const;\n  SelectablePart getPartAt(const QPointF &pos) const;\n  \n  // read-only interface imitating a QRect:\n  int left() const { return mRect.left(); }\n  int right() const { return mRect.right(); }\n  int top() const { return mRect.top(); }\n  int bottom() const { return mRect.bottom(); }\n  int width() const { return mRect.width(); }\n  int height() const { return mRect.height(); }\n  QSize size() const { return mRect.size(); }\n  QPoint topLeft() const { return mRect.topLeft(); }\n  QPoint topRight() const { return mRect.topRight(); }\n  QPoint bottomLeft() const { return mRect.bottomLeft(); }\n  QPoint bottomRight() const { return mRect.bottomRight(); }\n  QPointF center() const { return mCenter; }\n  double radius() const { return mRadius; }\n  \nsignals:\n  void rangeChanged(const QCPRange &newRange);\n  void rangeChanged(const QCPRange &newRange, const QCPRange &oldRange);\n  void selectionChanged(const QCPPolarAxisAngular::SelectableParts &parts);\n  void selectableChanged(const QCPPolarAxisAngular::SelectableParts &parts);\n  \nprotected:\n  // property members:\n  QBrush mBackgroundBrush;\n  QPixmap mBackgroundPixmap;\n  QPixmap mScaledBackgroundPixmap;\n  bool mBackgroundScaled;\n  Qt::AspectRatioMode mBackgroundScaledMode;\n  QCPLayoutInset *mInsetLayout;\n  bool mRangeDrag;\n  bool mRangeZoom;\n  double mRangeZoomFactor;\n  \n  // axis base:\n  double mAngle, mAngleRad;\n  SelectableParts mSelectableParts, mSelectedParts;\n  QPen mBasePen, mSelectedBasePen;\n  // axis label:\n  int mLabelPadding;\n  QString mLabel;\n  QFont mLabelFont, mSelectedLabelFont;\n  QColor mLabelColor, mSelectedLabelColor;\n  // tick labels:\n  //int mTickLabelPadding; in label painter\n  bool mTickLabels;\n  //double mTickLabelRotation; in label painter\n  QFont mTickLabelFont, mSelectedTickLabelFont;\n  QColor mTickLabelColor, mSelectedTickLabelColor;\n  int mNumberPrecision;\n  QLatin1Char mNumberFormatChar;\n  bool mNumberBeautifulPowers;\n  bool mNumberMultiplyCross;\n  // ticks and subticks:\n  bool mTicks;\n  bool mSubTicks;\n  int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut;\n  QPen mTickPen, mSelectedTickPen;\n  QPen mSubTickPen, mSelectedSubTickPen;\n  // scale and range:\n  QCPRange mRange;\n  bool mRangeReversed;\n  \n  // non-property members:\n  QPointF mCenter;\n  double mRadius;\n  QList<QCPPolarAxisRadial*> mRadialAxes;\n  QCPPolarGrid *mGrid;\n  QList<QCPPolarGraph*> mGraphs;\n  QSharedPointer<QCPAxisTicker> mTicker;\n  QVector<double> mTickVector;\n  QVector<QString> mTickVectorLabels;\n  QVector<QPointF> mTickVectorCosSin;\n  QVector<double> mSubTickVector;\n  QVector<QPointF> mSubTickVectorCosSin;\n  bool mDragging;\n  QCPRange mDragAngularStart;\n  QList<QCPRange> mDragRadialStart;\n  QCP::AntialiasedElements mAADragBackup, mNotAADragBackup;\n  QCPLabelPainterPrivate mLabelPainter;\n  \n  // reimplemented virtual methods:\n  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE;\n  // events:\n  virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE;\n  virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;\n  virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE;\n  virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  bool registerPolarGraph(QCPPolarGraph *graph);\n  void drawBackground(QCPPainter *painter, const QPointF &center, double radius);\n  void setupTickVectors();\n  QPen getBasePen() const;\n  QPen getTickPen() const;\n  QPen getSubTickPen() const;\n  QFont getTickLabelFont() const;\n  QFont getLabelFont() const;\n  QColor getTickLabelColor() const;\n  QColor getLabelColor() const;\n  \nprivate:\n  Q_DISABLE_COPY(QCPPolarAxisAngular)\n  \n  friend class QCustomPlot;\n  friend class QCPPolarGrid;\n  friend class QCPPolarGraph;\n};\nQ_DECLARE_OPERATORS_FOR_FLAGS(QCPPolarAxisAngular::SelectableParts)\nQ_DECLARE_METATYPE(QCPPolarAxisAngular::SelectablePart)\n\n/* end of 'src/polar/layoutelement-angularaxis.h' */\n\n\n/* including file 'src/polar/polargrid.h'  */\n/* modified 2021-03-29T02:30:44, size 4506 */\n\nclass QCP_LIB_DECL QCPPolarGrid :public QCPLayerable\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  \n  /// \\endcond\npublic:\n  /*!\n    TODO\n  */\n  enum GridType { gtAngular = 0x01 ///< \n                  ,gtRadial = 0x02 ///< \n                  ,gtAll    = 0xFF ///< \n                  ,gtNone   = 0x00 ///< \n                };\n  Q_ENUMS(GridType)\n  Q_FLAGS(GridTypes)\n  Q_DECLARE_FLAGS(GridTypes, GridType)\n  \n  explicit QCPPolarGrid(QCPPolarAxisAngular *parentAxis);\n  \n  // getters:\n  QCPPolarAxisRadial *radialAxis() const { return mRadialAxis.data(); }\n  GridTypes type() const { return mType; }\n  GridTypes subGridType() const { return mSubGridType; }\n  bool antialiasedSubGrid() const { return mAntialiasedSubGrid; }\n  bool antialiasedZeroLine() const { return mAntialiasedZeroLine; }\n  QPen angularPen() const { return mAngularPen; }\n  QPen angularSubGridPen() const { return mAngularSubGridPen; }\n  QPen radialPen() const { return mRadialPen; }\n  QPen radialSubGridPen() const { return mRadialSubGridPen; }\n  QPen radialZeroLinePen() const { return mRadialZeroLinePen; }\n  \n  // setters:\n  void setRadialAxis(QCPPolarAxisRadial *axis);\n  void setType(GridTypes type);\n  void setSubGridType(GridTypes type);\n  void setAntialiasedSubGrid(bool enabled);\n  void setAntialiasedZeroLine(bool enabled);\n  void setAngularPen(const QPen &pen);\n  void setAngularSubGridPen(const QPen &pen);\n  void setRadialPen(const QPen &pen);\n  void setRadialSubGridPen(const QPen &pen);\n  void setRadialZeroLinePen(const QPen &pen);\n  \nprotected:\n  // property members:\n  GridTypes mType;\n  GridTypes mSubGridType;\n  bool mAntialiasedSubGrid, mAntialiasedZeroLine;\n  QPen mAngularPen, mAngularSubGridPen;\n  QPen mRadialPen, mRadialSubGridPen, mRadialZeroLinePen;\n  \n  // non-property members:\n  QCPPolarAxisAngular *mParentAxis;\n  QPointer<QCPPolarAxisRadial> mRadialAxis;\n  \n  // reimplemented virtual methods:\n  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE;\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  void drawRadialGrid(QCPPainter *painter, const QPointF &center, const QVector<double> &coords, const QPen &pen, const QPen &zeroPen=Qt::NoPen);\n  void drawAngularGrid(QCPPainter *painter, const QPointF &center, double radius, const QVector<QPointF> &ticksCosSin, const QPen &pen);\n  \nprivate:\n  Q_DISABLE_COPY(QCPPolarGrid)\n  \n};\n\nQ_DECLARE_OPERATORS_FOR_FLAGS(QCPPolarGrid::GridTypes)\nQ_DECLARE_METATYPE(QCPPolarGrid::GridType)\n\n\n/* end of 'src/polar/polargrid.h' */\n\n\n/* including file 'src/polar/polargraph.h' */\n/* modified 2021-03-29T02:30:44, size 9606 */\n\n\nclass QCP_LIB_DECL QCPPolarLegendItem : public QCPAbstractLegendItem\n{\n  Q_OBJECT\npublic:\n  QCPPolarLegendItem(QCPLegend *parent, QCPPolarGraph *graph);\n  \n  // getters:\n  QCPPolarGraph *polarGraph() { return mPolarGraph; }\n  \nprotected:\n  // property members:\n  QCPPolarGraph *mPolarGraph;\n  \n  // reimplemented virtual methods:\n  virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;\n  virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE;\n  \n  // non-virtual methods:\n  QPen getIconBorderPen() const;\n  QColor getTextColor() const;\n  QFont getFont() const;\n};\n\n\nclass QCP_LIB_DECL QCPPolarGraph : public QCPLayerable\n{\n  Q_OBJECT\n  /// \\cond INCLUDE_QPROPERTIES\n  \n  /// \\endcond\npublic:\n  /*!\n    Defines how the graph's line is represented visually in the plot. The line is drawn with the\n    current pen of the graph (\\ref setPen).\n    \\see setLineStyle\n  */\n  enum LineStyle { lsNone        ///< data points are not connected with any lines (e.g. data only represented\n                                 ///< with symbols according to the scatter style, see \\ref setScatterStyle)\n                   ,lsLine       ///< data points are connected by a straight line\n                 };\n  Q_ENUMS(LineStyle)\n  \n  QCPPolarGraph(QCPPolarAxisAngular *keyAxis, QCPPolarAxisRadial *valueAxis);\n  virtual ~QCPPolarGraph();\n  \n  // getters:\n  QString name() const { return mName; }\n  bool antialiasedFill() const { return mAntialiasedFill; }\n  bool antialiasedScatters() const { return mAntialiasedScatters; }\n  QPen pen() const { return mPen; }\n  QBrush brush() const { return mBrush; }\n  bool periodic() const { return mPeriodic; }\n  QCPPolarAxisAngular *keyAxis() const { return mKeyAxis.data(); }\n  QCPPolarAxisRadial *valueAxis() const { return mValueAxis.data(); }\n  QCP::SelectionType selectable() const { return mSelectable; }\n  bool selected() const { return !mSelection.isEmpty(); }\n  QCPDataSelection selection() const { return mSelection; }\n  //QCPSelectionDecorator *selectionDecorator() const { return mSelectionDecorator; }\n  QSharedPointer<QCPGraphDataContainer> data() const { return mDataContainer; }\n  LineStyle lineStyle() const { return mLineStyle; }\n  QCPScatterStyle scatterStyle() const { return mScatterStyle; }\n  \n  // setters:\n  void setName(const QString &name);\n  void setAntialiasedFill(bool enabled);\n  void setAntialiasedScatters(bool enabled);\n  void setPen(const QPen &pen);\n  void setBrush(const QBrush &brush);\n  void setPeriodic(bool enabled);\n  void setKeyAxis(QCPPolarAxisAngular *axis);\n  void setValueAxis(QCPPolarAxisRadial *axis);\n  Q_SLOT void setSelectable(QCP::SelectionType selectable);\n  Q_SLOT void setSelection(QCPDataSelection selection);\n  //void setSelectionDecorator(QCPSelectionDecorator *decorator);\n  void setData(QSharedPointer<QCPGraphDataContainer> data);\n  void setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted=false);\n  void setLineStyle(LineStyle ls);\n  void setScatterStyle(const QCPScatterStyle &style);\n\n  // non-property methods:\n  void addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted=false);\n  void addData(double key, double value);\n  void coordsToPixels(double key, double value, double &x, double &y) const;\n  const QPointF coordsToPixels(double key, double value) const;\n  void pixelsToCoords(double x, double y, double &key, double &value) const;\n  void pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const;\n  void rescaleAxes(bool onlyEnlarge=false) const;\n  void rescaleKeyAxis(bool onlyEnlarge=false) const;\n  void rescaleValueAxis(bool onlyEnlarge=false, bool inKeyRange=false) const;\n  bool addToLegend(QCPLegend *legend);\n  bool addToLegend();\n  bool removeFromLegend(QCPLegend *legend) const;\n  bool removeFromLegend() const;\n  \n  // introduced virtual methods:\n  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; // actually introduced in QCPLayerable as non-pure, but we want to force reimplementation for plottables\n  virtual QCPPlottableInterface1D *interface1D() { return 0; } // TODO: return this later, when QCPAbstractPolarPlottable is created\n  virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const;\n  virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const;\n  \nsignals:\n  void selectionChanged(bool selected);\n  void selectionChanged(const QCPDataSelection &selection);\n  void selectableChanged(QCP::SelectionType selectable);\n  \nprotected:\n  // property members:\n  QSharedPointer<QCPGraphDataContainer> mDataContainer;\n  LineStyle mLineStyle;\n  QCPScatterStyle mScatterStyle;\n  QString mName;\n  bool mAntialiasedFill, mAntialiasedScatters;\n  QPen mPen;\n  QBrush mBrush;\n  bool mPeriodic;\n  QPointer<QCPPolarAxisAngular> mKeyAxis;\n  QPointer<QCPPolarAxisRadial> mValueAxis;\n  QCP::SelectionType mSelectable;\n  QCPDataSelection mSelection;\n  //QCPSelectionDecorator *mSelectionDecorator;\n  \n  // introduced virtual methods (later reimplemented TODO from QCPAbstractPolarPlottable):\n  virtual QRect clipRect() const;\n  virtual void draw(QCPPainter *painter);\n  virtual QCP::Interaction selectionCategory() const;\n  void applyDefaultAntialiasingHint(QCPPainter *painter) const;\n  // events:\n  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);\n  virtual void deselectEvent(bool *selectionStateChanged);\n  // virtual drawing helpers:\n  virtual void drawLinePlot(QCPPainter *painter, const QVector<QPointF> &lines) const;\n  virtual void drawFill(QCPPainter *painter, QVector<QPointF> *lines) const;\n  virtual void drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &scatters, const QCPScatterStyle &style) const;\n  \n  // introduced virtual methods:\n  virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;\n  \n  // non-virtual methods:\n  void applyFillAntialiasingHint(QCPPainter *painter) const;\n  void applyScattersAntialiasingHint(QCPPainter *painter) const;\n  double pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const;\n  // drawing helpers:\n  virtual int dataCount() const;\n  void getDataSegments(QList<QCPDataRange> &selectedSegments, QList<QCPDataRange> &unselectedSegments) const;\n  void drawPolyline(QCPPainter *painter, const QVector<QPointF> &lineData) const;\n  void getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const;\n  void getLines(QVector<QPointF> *lines, const QCPDataRange &dataRange) const;\n  void getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange) const;\n  void getOptimizedLineData(QVector<QCPGraphData> *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const;\n  void getOptimizedScatterData(QVector<QCPGraphData> *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const;\n  QVector<QPointF> dataToLines(const QVector<QCPGraphData> &data) const;\n\nprivate:\n  Q_DISABLE_COPY(QCPPolarGraph)\n  \n  friend class QCPPolarLegendItem;\n};\n\n/* end of 'src/polar/polargraph.h' */\n\n\n#endif // QCUSTOMPLOT_H\n\n"
  },
  {
    "path": "g2p_train/README.md",
    "content": "﻿#  G2P for TensorVox\nTensorVox utilizes an RNN-based G2P model implemented in Tensorflow to convert text to phonemes before feeding the text2speech models.\n\n## Training\nIn order to train a model, you need to prepare two things:\n1. A dictionary in format `WORD \\t PHONETIC SPELLING` as the dataset\n2. A config file (optional, there is already one in `config/default.yaml`)\n\nTensorflow 2.0 or greater, is of course, required.\n\nSince the training is very quick on GPU (Tesla T4), it's just one script that does preprocessing, training, and exporting. If you don't have one, just use Google Colab.\n\nYou can download my English dictionary (converted to tab-based from the LibriSpeech lexicon) [here](https://drive.google.com/file/d/19cnHM3-Zsc7uRJ2scUPNMNoSlyXuaGNe/view?usp=sharing).\nRename it from dict.d to dict.txt\n\nThe command to run it is as follows: \n\n    python3 train_and_export.py --dict-path dict.txt --config-path config/default.yaml --out-path English\n\nArguments should be self-explanatory. \n### Important note\nIf your phoneme format does not separate phonemes by space (like IPA), pass `--char-tok-phn` as an argument, because the script assumes that all phoneme texts are like ARPA (example: G R IY1 N) and tokenizes separated by spaces. One sign that it may be doing this could be very slow training on a decent GPU.\n\n## Structure\n\nOnce finished, the script will output all files required to use the model to the folder determined by the `--out-path` argument  (will be created if it doesn't exist). \n\nNo further action is necessary, just drag it so that all the files in the folder are in the (executable file path)/g2p/`language name` folder and it will be used by the program to do phoneme conversion for all models it loads in that language. Make sure language name folder is capitalized.\n\nThe output consists of three things:\n\n4. **char2id.txt, phn2id.txt**: Two text files in format `TOKEN \\t ID` that indicate the IDs that first go into the model (char) and are returned (phn). Automatically generated by the script.\n5. **dict.txt**: Dictionary in format `WORD \\t PHONETIC-SPELLING` that is used to find phonetic spellings in. Automatically re-exported (words forced to lowercase) by the script.\n6. **model**: The actual G2P model, saved in Tensorflow SavedModel format.\n\nDue to the unreliability of the network, we only want to use it to guess novel words, so first it does a dictionary lookup (semi-optimized with bucketed string search) then if not found, uses the model.\n\nAn example English model is zipped in the `models/` directory.\n"
  },
  {
    "path": "g2p_train/config/default.yaml",
    "content": "# Config file for G2P-English model. This one does 15 epochs and trains very quickly on GPU.\n\ngru_dims: 128 # Size of GRU (and embedding layer)\nbatch_size: 4096 # Batch size\nval_per: 0.1 # % of dataset to be used for validation. Floating point, 1.0 = 100%; 0.5 = 50%, etc...\nepochs: 15 # Amount of epochs\nlearning_rate: 0.006 # Learning rate\n"
  },
  {
    "path": "g2p_train/config/longer.yaml",
    "content": "# Config file for G2P model that trains for longer with softer learning, recommended\n\ngru_dims: 128 # Size of GRU (and embedding layer)\nbatch_size: 4096 # Batch size\nval_per: 0.02 # % of dataset to be used for validation. Floating point, 1.0 = 100%; 0.5 = 50%, etc...\nepochs: 35 # Amount of epochs\nlearning_rate: 0.001 # Learning rate\n"
  },
  {
    "path": "g2p_train/train_and_export.py",
    "content": "from tqdm import tqdm\nimport os\nimport argparse\nimport tensorflow as tf\nimport yaml\nimport shutil\nglobal_max = 0\ncumodel = None\n\ndef safemkdir(dirn):\n  if not os.path.isdir(dirn):\n    os.mkdir(dirn)\n\n\ndef preprocess(in_fname,char_phn_tok):\n  words = list()\n  phn = list()\n  print(\"Opening file...\")\n  with open(in_fname,\"r\",encoding=\"utf-8\") as f:\n    for li in tqdm(f.readlines()):\n      spl = li.strip().split(\"\\t\")\n      if len(spl) > 1:\n        words.append(spl[0].lower()) #convert to lowercase for re-exporting later\n        phn.append(spl[1])\n\n  if char_phn_tok:\n    print(\"Tokenizing phoneme strings in char level too\")\n    \n  phntok = tf.keras.preprocessing.text.Tokenizer(lower=False,filters='\"\\t\\n',char_level=char_phn_tok)\n  txttok = tf.keras.preprocessing.text.Tokenizer(char_level=True)\n  \n  print(\"Fitting on texts...\")\n  phntok.fit_on_texts(phn)\n  txttok.fit_on_texts(words)\n\n  print(\"Converting to sequences\")\n  txtseqs = txttok.texts_to_sequences(words)\n  phnseqs = phntok.texts_to_sequences(phn)\n\n  txt_max = len(max(txtseqs, key=len))\n  phn_max = len(max(phnseqs,key=len))\n\n  global global_max\n  global_max = max(txt_max,phn_max)\n  print(\"Common padding index is \" + str(global_max))\n\n  txtpadded = tf.keras.preprocessing.sequence.pad_sequences(txtseqs,padding=\"post\",maxlen=global_max)\n  phnpadded =  tf.keras.preprocessing.sequence.pad_sequences(phnseqs,padding=\"post\",maxlen=global_max)\n  \n  txtsize = len(txttok.word_index)\n  phnsize = len(phntok.word_index)\n\n  return txtpadded, phnpadded, txtsize, phnsize, phntok.word_index, txttok.word_index, words, phn\n\n\ndef getmodel(input_shape, in_vocab_size, out_vocab_size,gru_size,in_lr):\n    model = tf.keras.models.Sequential([tf.keras.layers.Embedding(in_vocab_size, gru_size, input_length=input_shape[1], input_shape=input_shape[1:]),\n                                        tf.keras.layers.Bidirectional(tf.keras.layers.GRU(gru_size,input_shape=input_shape[1:],return_sequences=True)),\n                                        tf.keras.layers.TimeDistributed(tf.keras.layers.Dense(1024,activation=\"relu\")),\n                                        tf.keras.layers.Dropout(0.5),\n                                        tf.keras.layers.TimeDistributed(tf.keras.layers.Dense(out_vocab_size,activation=\"softmax\"))])\n    \n    model.compile(loss='sparse_categorical_crossentropy',\n                  optimizer=tf.keras.optimizers.Adam(in_lr),\n                  metrics=['accuracy'])\n    return model\n\n@tf.function(\n    experimental_relax_shapes=True,\n    input_signature=[\n        tf.TensorSpec([None], dtype=tf.int32, name=\"input_ids\"),\n        tf.TensorSpec([1,], dtype=tf.int32, name=\"input_len\"),\n        tf.TensorSpec([1,], dtype=tf.float32, name=\"input_temperature\"),\n    ],\n)\ndef callg2p(input_ids,input_len,input_temperature):\n  #Generate padding\n  pad = tf.zeros([global_max - input_len[0]],dtype=tf.int32)\n  #Add padding to input_ids and reshape\n  input_ids = tf.concat([input_ids,pad],0)\n  input_ids = tf.reshape(input_ids,[-1,global_max])\n  #Predict\n  pred = cumodel(input_ids)\n  #Apply temperature\n  predx = tf.squeeze(pred, 0)\n  predx /= input_temperature\n\n  #Select IDs\n  retids = tf.random.categorical(predx, 1)\n\n  #Remove padding\n  bool_mask = tf.not_equal(retids, 0)\n  phn_ids = tf.boolean_mask(retids, bool_mask)\n\n  return tf.cast(phn_ids,tf.int32)\n\ndef exportdict(indict,outf):\n  f = open(outf,\"w\")\n  for de in indict:\n    f.write(de + \"\\t\" + str(indict[de]) + \"\\n\")\n  \n  f.close()\n\n\ndef export_model(folname,in_model,in_phnwi,in_charwi):\n  safemkdir(folname)\n\n\n  exportdict(in_phnwi,os.path.join(folname,\"phn2id.txt\"))\n  exportdict(in_charwi,os.path.join(folname,\"char2id.txt\"))\n  \n  print(\"Exporting model...\")\n  in_model.save(os.path.join(folname,\"model\"),save_format=\"tf\",signatures=callg2p)\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Train and export a G2P model\")\n    parser.add_argument(\n        \"--config-path\",\n        default=\"config/default.yaml\",\n        type=str,\n        help=\"Path of config\",\n    )    \n    parser.add_argument(\n        \"--dict-path\",\n        default=\"dict.txt\",\n        type=str,\n        help=\"Path of dictionary\",\n    )\n    parser.add_argument(\n        \"--out-path\",\n        default=\"model1\",\n        type=str,\n        help=\"Output path of model\",\n    )\n    parser.add_argument(\n      \"--char-tok-phn\",\n      action=\"store_true\",\n      help=\"Whether to tokenize phoneme strings by char. Turn this on if using IPA or some other phoneme with no spaces inbetween\",\n    )\n\n\n    args = parser.parse_args()\n    \n    txtpadded, phnpadded, txtsize, phnsize, phn_wi, txt_wi, words, phns = preprocess(args.dict_path,args.char_tok_phn)\n    \n    yf = open(args.config_path,\"r\")\n    config = yaml.load(yf,Loader=yaml.FullLoader)\n    yf.close()\n\n    print(\"Finished preprocessing. Getting model\")\n    global cumodel\n    cumodel = getmodel(txtpadded.shape,txtsize + 1,phnsize + 1,config[\"gru_dims\"],config[\"learning_rate\"])\n\n    x_train = txtpadded\n    y_train = phnpadded\n\n    print(\"Starting training...\")\n    cumodel.fit(x_train, y_train, batch_size=config[\"batch_size\"], epochs=config[\"epochs\"],validation_split=config[\"val_per\"])\n    \n    print(\"Starting export...\")\n    export_model(args.out_path,cumodel,phn_wi,txt_wi)\n\n    print(\"Re-exporting dict...\")\n    outdict = open(os.path.join(args.out_path,\"dict.txt\"),\"w\",encoding=\"utf-8\")\n\n    for idx, w in enumerate(words):\n      outdict.write(w + \"\\t\" + phns[idx] + \"\\n\")\n    \n    outdict.close()\n\n\n\n\n\n    \n    print(\"Done!\")\n\n\n\n\n\n\n        \n\nif __name__ == \"__main__\":\n    main()\n\n"
  },
  {
    "path": "istftnettorch.cpp",
    "content": "#include \"istftnettorch.h\"\n#include <windows.h>\nbool iSTFTNetTorch::Initialize(const std::string &VocoderPath)\n{\n    torch::Device device(torch::kCPU);\n\n    try {\n        // Deserialize the ScriptModule from a file using torch::jit::load().\n\n        std::string VCP = VocoderPath + \".pt\";\n\n        Model = torch::jit::load(VCP,device);\n\n    }\n    catch (const c10::Error& e) {\n        QMessageBox::critical(nullptr,\"r\",e.what_without_backtrace());\n        return false;\n\n    }\n    try{\n        std::string PostPath = VocoderPath + \"-post.pt\";\n        Post = torch::jit::load(PostPath,device);\n        PostLoaded = true;\n    }\n    catch (const c10::Error& e){\n        PostLoaded = false;\n\n    }\n\n\n\n    return true;\n\n}\n\nTFTensor<float> iSTFTNetTorch::DoInference(const TFTensor<float> &InMel)\n{\n    // without this memory consumption is 4x\n    torch::NoGradGuard no_grad;\n    torch::Device device(torch::kCPU);\n    auto TorchMel = torch::tensor(InMel.Data,device).reshape(InMel.Shape).transpose(1,2); // [1, frames, n_mels] -> [1, n_mels, frames]\n\n\n\n    try{\n        at::Tensor Output = Model({TorchMel}).toTensor().squeeze(); // (audio frames)\n        if (PostLoaded)\n            Output = Post({Output.unsqueeze(0).toType(at::ScalarType::Float)}).toTensor();\n\n\n        TFTensor<float> Tens = VoxUtil::CopyTensor<float>(Output);\n\n\n        return Tens;\n    }\n    catch (const std::exception& e) {\n        int msgboxID = MessageBox(\n            NULL,\n            (LPCWSTR)QString::fromStdString(e.what()).toStdWString().c_str(),\n            (LPCWSTR)L\"Account Details\",\n            MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2\n        );\n\n\n        return TFTensor<float>();\n\n    }\n\n\n\n\n\n}\n\niSTFTNetTorch::iSTFTNetTorch()\n{\n\n}\n"
  },
  {
    "path": "istftnettorch.h",
    "content": "#ifndef ISTFTNETTORCH_H\n#define ISTFTNETTORCH_H\n#include \"MultiBandMelGAN.h\"\n\nclass iSTFTNetTorch : public MultiBandMelGAN\n{\nprivate:\n   torch::jit::script::Module Model;\n   torch::jit::script::Module Post;\n\n   bool PostLoaded;\n\npublic:\n    bool Initialize(const std::string& VocoderPath);\n\n\n\n    // Do MultiBand MelGAN inference including PQMF\n    // -> InMel:  Mel spectrogram (shape [1, xx, 80])\n    // <- Returns: Tensor data  [frames]\n    virtual TFTensor<float> DoInference(const TFTensor<float>& InMel);\n    iSTFTNetTorch();\n};\n\n#endif // ISTFTNETTORCH_H\n"
  },
  {
    "path": "main.cpp",
    "content": "#include \"mainwindow.h\"\n\n#include \"ext/Qt-Frameless-Window-DarkStyle-master/DarkStyle.h\"\n#include \"framelesswindow.h\"\n\n#include <QApplication>\n\nint main(int argc, char *argv[])\n{\n    QCoreApplication::addLibraryPath(\"./\");\n    QApplication a(argc, argv);\n    a.setStyle(new DarkStyle);\n\n    FramelessWindow framelessWindow;\n    framelessWindow.setWindowTitle(\"TensorVox\");\n    framelessWindow.setWindowIcon(QIcon(\"://res/stdico.png\"));\n\n    MainWindow *mainWindow = new MainWindow;\n    mainWindow->pDarkFw = &framelessWindow;\n\n    framelessWindow.setContent(mainWindow);\n    framelessWindow.show();\n\n\n    return a.exec();\n}\n"
  },
  {
    "path": "mainwindow.cpp",
    "content": "#include \"mainwindow.h\"\n#include \"ui_mainwindow.h\"\n#include <QDir>\n#include <QDirIterator>\n#include <QSplitter>\n\n#include <QFileDialog>\n\n#include \"voxer.h\"\n\n#include \"ext/ByteArr.h\"\n#include \"ext/ZFile.h\"\n\n#include \"phddialog.h\"\n#include \"framelesswindow.h\"\n#include <QMessageBox>\n#include \"modelinfodlg.h\"\n#include <LogitechLEDLib.h>\n#include \"track.h\"\n#define FwParent ((FramelessWindow*)pDarkFw)\n#include <psapi.h>\n#include <QTextStream>\n#include <random>\n#include <windows.h>\n\n\n\n// maybe hardcoding the sample sentences is a bad idea?\n// ZDisket: I'm lazy\nstatic const QString RandomTexts[] = {\"Drink the water fishy\",\n                                      \"She turned herself into a thorn. It was the funniest shit I've ever seen\",\n                                      \"That was an order! Steiner's attack was an order!\",\n                                      \"President Trump met with other leaders at the Group of twenty conference\",\n                                      \"There’s a way to measure the acute emotional intelligence that has never gone out of style\",\n                                      \"Peter Piper picked a peck of pickled peppers. How many pickled peppers did Peter Piper pick?\",\n                                      \"It needs to be about 20 percent cooler\",\n                                      \"Sneed's feed and seed\",\n                                      \"The right to repair electronics refers to proposed government legislation that would allow consumers the ability to repair and modify their own consumer electronic devices\",\n                                      \"Eternal chaos come with chocolate rain, you guys!\",\n                                      \"What fun is there in making sense?\",\n                                      \"And then what she said was scandalous\",\n                                      \"Think of tuna fish as a very fishy fish\",\n                                      \"Still don't trust me\",\n                                      \"Do not compare yourself to others. If you do so, you are insulting yourself\",\n                                      \"Let that child alone\",\n                                      \"If I don't eat rice, the power won't come!\",\n                                      \"Their power is really extraordinary. One thing is sure, they're from another world\",\n                                      \"Now I see. Black human beings dislike the sound of rubbing glass probably the soundwave of the whistle\",\n                                      \"Cats and tomatoes don't mix\"};\n\nMainWindow::MainWindow(QWidget *parent)\n    : QMainWindow(parent)\n    , ui(new Ui::MainWindow)\n{\n    DoingBatchDenoising = false;\n\n    ui->setupUi(this);\n    PopulateComboBox();\n\n    DoUpdateSplitAuto = true;\n    qRegisterMetaType< std::vector<float> >( \"std::vector<float>\" );\n\n    qRegisterMetaType< QVector<int> >( \"QVector<int>\" );\n    qRegisterMetaType<std::chrono::duration<double>>(\"std::chrono::duration<double>\");\n    qRegisterMetaType<uint32_t>(\"uint32_t\");\n    qRegisterMetaType< TFTensor<float> >( \"TFTensor<float>\" );\n\n    StatusLbl = new QLabel(ui->statusbar);\n    ui->statusbar->addPermanentWidget(StatusLbl);\n\n\n\n    ui->splitter->setSizes(QList<int>() << width() * 0.8  << width() * 0.2 );\n\n    StdFmt.setSampleSize(sizeof(float) * 8);\n    StdFmt.setSampleType(QAudioFormat::Float);\n\n    // This is already set in the constructor, but just to be extra, extra sure...\n    StdFmt.setByteOrder(QAudioFormat::Endian(QSysInfo::ByteOrder));\n\n    StdFmt.setSampleRate(CommonSampleRate);\n    StdFmt.setChannelCount(1);\n    StdFmt.setCodec(\"audio/pcm\");\n\n    CanPlayAudio = true;\n\n    StdOutput = new QAudioOutput(StdFmt,this);\n\n    CurrentBuffIndex = 0;\n\n     connect(StdOutput,&QAudioOutput::stateChanged, this, &MainWindow::OnAudioStateChange);\n\n    RecPerfLines = false;\n\n    pHigh = new PhoneticHighlighter(ui->edtInput->document());\n\n    ui->cbSpeaker->setVisible(false);\n    ui->lblSpeaker->setVisible(false);\n\n    ui->cbEmotions->setVisible(false);\n    ui->lblEmotions->setVisible(false);\n\n    ui->lblEmotionOvr->setVisible(false);\n    ui->edtEmotionOvr->setVisible(false);\n\n\n\n\n    CurrentInferIndex = 0;\n\n    PhonDict.Import(QCoreApplication::applicationDirPath() + \"/dict.phd\");\n    pTaskBtn = new QWinTaskbarButton(this);\n    pTskProg = pTaskBtn->progress();\n\n    SetDict();\n\n    ui->spbThreads->setValue(QThread::idealThreadCount());\n\n    NumDone = 0;\n    CurrentAmtThreads = 0;\n\n    LogiLedAvailable = LogiLedInitWithName(\"Vox\");\n    if (LogiLedAvailable)\n        LogiLedSetTargetDevice(LOGI_DEVICETYPE_RGB);\n\n    UpdateLogiLed();\n\n    ClipBrd = QGuiApplication::clipboard();\n    connect(ClipBrd,&QClipboard::dataChanged,this,&MainWindow::OnClipboardDataChanged);\n\n\n    QTimer* timMemUp = new QTimer(this);\n    timMemUp->setSingleShot(false);\n    timMemUp->setInterval(5000);\n\n\n    connect(timMemUp,&QTimer::timeout,this,&MainWindow::OnMemoryUpdate);\n    LastInferBatchSize = 0;\n\n    timMemUp->start();\n    //ui->widAudioPlot->hide();\n\n    QCPColorGradient Viridis;\n    // Build Viridis color gradient.\n    Viridis.clearColorStops();\n    Viridis.setColorStops({{0.0,QColor(54, 1, 81)},\n                           {0.25,QColor(45, 62, 120)},\n                           {0.5,QColor(30, 131, 121)},\n                           {0.75,QColor(94, 200, 71)},\n                           {1.0,QColor(245, 228 ,27)}\n                          });\n\n    QCPColorMap* ColMap = new QCPColorMap(ui->widAttention->xAxis, ui->widAttention->yAxis);\n    ColMap->setName(\"Alignment\");\n    ui->widAttention->Map = ColMap;\n    ColMap->setGradient(Viridis);\n\n    ui->tabMetrics->hide();\n    ui->tabMetrics->setTabEnabled(2,false);\n\n\n\n    // The spectrogram in Python is shown with np.rot90, otherwise it looks wrong\n    // So I flip the axes to make it look right, instead of bothering to do the math (I lost an evening trying this)\n    QCPColorMap* SpecMap = new QCPColorMap(ui->widSpec->yAxis, ui->widSpec->xAxis);\n    SpecMap->setName(\"Spectrogram\");\n    ui->widSpec->Map = SpecMap;\n    SpecMap->setGradient(Viridis);\n\n\n    ui->lstUtts->addActions({ui->actExWAVSe, ui->actCopySel});\n\n    DenBatchSize = 0;\n    DenDone = 0;\n    LastExportDir = QCoreApplication::applicationDirPath() + \"/Utt.wav\";\n    ui->grpFs2Params->hide();\n\n\n\n\n\n}\n\nInferIDTrueID* MainWindow::FindByFirst(uint32_t inGetID)\n{\n    for (auto& ID : IdVec)\n    {\n        if (inGetID == ID.first)\n            return &ID;\n\n    }\n    return nullptr;\n\n\n}\n\nvoid MainWindow::PushToInfers(InferDetails &InDets)\n{\n\n    if (!InDets.pItem)\n        InDets.pItem = new QListWidgetItem(InDets.ExportFileName.split('/').last(),ui->lstUtts);\n\n\n\n\n    Infers.push(InDets);\n    pTskProg->show();\n\n    IterateQueue();\n\n\n}\n\nint32_t MainWindow::GetCountItems()\n{\n    return ui->lstUtts->count();\n}\n\n\n\nvoid MainWindow::showEvent(QShowEvent *e)\n{\n\n#ifdef Q_OS_WIN\n    pTaskBtn->setWindow(((FramelessWindow*)pDarkFw)->windowHandle());\n    pTaskBtn->progress()->show();\n\n\n\n#endif\n    //FwParent->setWindowTitle(\"TensorVox\");\n\n   e->accept();\n}\n\nMainWindow::~MainWindow()\n{\n    on_btnClear_clicked();\n    LogiLedShutdown();\n    delete ui;\n}\n\nvoid MainWindow::OnAudioRecv(std::vector<float> InDat, TFTensor<float> InMel, std::chrono::duration<double> infer_span, uint32_t inID)\n{\n    if (inID == UINT32_MAX)\n    {\n        DoingBatchDenoising = true;\n        ++DenDone;\n\n        --CurrentAmtThreads;\n\n\n        ui->chkMultiThreaded->setChecked(ui->lstUtts->count() > ui->spbThreads->value());\n\n\n\n\n\n        if (DenDone == DenBatchSize){\n            on_btnClear_clicked();\n            return;\n\n        }\n\n\n\n        IterateQueue();\n\n        return;\n\n    }\n    DoingBatchDenoising = false;\n\n\n\n    QBuffer* Buf = new QBuffer(this);\n    Buf->setData((const char*)InDat.data(),sizeof(float) * InDat.size());\n\n\n    // We push to the mel and audbuff always at the same time, so the IDs will be the same.\n    Mels.push_back(InMel);\n    AudBuffs.push_back(Buf);\n\n    IdVec.push_back(InferIDTrueID{inID,AudBuffs.size() - 1,-1});\n    if (AllowedToPlayAudio())\n    {\n        PlayBuffer(Buf,false,inID);\n\n    }\n\n\n    if (RecPerfLines){\n\n        double InferSecs = (double)InDat.size() / CommonSampleRate;\n        double InferTime = infer_span.count();\n\n        // https://enacademic.com/dic.nsf/enwiki/3796485\n        double RealTimeFactor = InferTime / InferSecs;\n\n        QString Pline = \"Inferred \" + QString::number(InferSecs,'f',3) + \" seconds of audio in \" + QString::number(InferTime,'f',3) + \" seconds; \" + \"RTF: \" + QString::number(RealTimeFactor,'f',3);\n        PerfReportLines.push_back(Pline);\n\n    }\n\n\n\n    NumDone += 1;\n\n\n    pTskProg->setRange(0,ui->lstUtts->count());\n    pTskProg->setValue(NumDone);\n\n\n    bool NoInfers = NumDone == ui->lstUtts->count();\n\n    ui->btnExportSel->setEnabled(NoInfers);\n    ui->btnExReport->setEnabled(NoInfers);\n\n    if (NoInfers)\n    {\n        LogiLedSetLighting(0,100,50);\n\n\n        // Prevent lighting from being flashed if it's just one or a few utterances.\n        if (LastInferBatchSize > 3)\n            LogiLedFlashLighting(0,100,50,6000,500);\n\n\n\n\n    }\n    else\n    {\n        float NDonef = (float)NumDone;\n        float Cnt = (float)ui->lstUtts->count();\n\n        float BlueLevel = NDonef / Cnt;\n        BlueLevel *= 100.f;\n\n        LogiLedSetLighting(20,0,(int)BlueLevel);\n\n\n    }\n\n\n\n\n    --CurrentAmtThreads;\n\n    IterateQueue();\n    CountBlues();\n\n\n\n}\n\nvoid MainWindow::OnAudioStateChange(QAudio::State newState)\n{\n\n    if (newState == QAudio::IdleState || newState == QAudio::StoppedState)\n    {\n        InferIDTrueID* FoundItm = FindBySecond(CurrentBuffIndex);\n\n        if (!FoundItm)\n            return;\n\n        if ((int32_t)FoundItm->first > ui->lstUtts->count() - 1)\n            return;\n\n        ui->lstUtts->item(FoundItm->first)->setBackgroundColor(DoneColor);\n\n\n        // If the user queues up multiple utterances then due to the fast inference speed we can't play them all at once\n        // It then becomes necessary that we advance on the event that audio finishes playing.\n        CurrentBuffIndex += 1;\n        if (CurrentBuffIndex < AudBuffs.size())\n            AdvanceBuffer();\n        else\n            CanPlayAudio = true;\n\n\n        if (StdOutput->error() != QAudio::NoError && StdOutput->error() != QAudio::UnderrunError) {\n            QMessageBox::critical(this,\"Error\",\"Audio stopped playing due to error ID \" + QString::number(StdOutput->error()));\n\n        }\n\n    }\n}\n\nvoid MainWindow::OnClipboardDataChanged()\n{\n\n    if (ui->actAutoInferClipBrd->isChecked() && !ui->edtInput->hasFocus() && !ClipBrd->text().isEmpty())\n    {\n        ui->edtInput->setText(ClipBrd->text());\n        on_btnInfer_clicked();\n\n    }\n\n}\n\nvoid MainWindow::OnAttentionRecv(TFTensor<float> InAtt, uint32_t inID)\n{\n    // The audio recv event should have added the thing to the vec\n    InferIDTrueID* pInferEntry = FindByFirst(inID);\n\n    if (pInferEntry)\n    {\n        Alignments.push_back(InAtt);\n\n        pInferEntry->Align = Alignments.size() - 1;\n\n    }\n\n\n\n    if (ui->chkAutoPlay->isChecked() && ui->lstUtts->item((int32_t)inID)->backgroundColor() == PlayingColor)\n        PlotAttention(InAtt);\n\n\n\n\n\n}\n\n\n\n\nvoid MainWindow::on_btnInfer_clicked()\n{\n    // While the Voice class can already autoload, we explicitly do so here so we can handle everything (note text, disabling)\n\n    if (VoMan.FindVoice(ui->cbModels->currentText(),false) == -1)\n        on_btnLoad_clicked();\n\n\n    // Convert to lowercase here before we add phonemes\n    QString BeforeInput = ui->edtInput->toPlainText();\n    QString RawInput = BeforeInput;\n    QString Input = RawInput.replace(\"\\n\",\" \");\n    const int MaxShowInputLen = ui->lstUtts->size().width() / 6;\n\n\n\n    QStringList InputSplits;\n    QStringList BeforeSplits;\n\n    if (ui->rbSplitWord->isChecked())\n    {\n        BeforeSplits.push_back(Input);\n\n    }\n    else{\n        BeforeSplits = ui->edtInput->toPlainText().split(QRegExp(\"[\\r\\n]\"),QString::SkipEmptyParts);\n\n\n\n    }\n    if (ui->chkPonctAware->isChecked())\n    {\n        QStringList TempBeforeSplits;\n        for (const QString& CuSplit: BeforeSplits)\n        {\n            TempBeforeSplits.append(CuSplit.split(\".\",QString::SkipEmptyParts));\n\n\n        }\n        BeforeSplits = TempBeforeSplits;\n\n    }\n\n    for (const QString& LiSplit : BeforeSplits)\n    {\n        InputSplits.append(SuperWordSplit(LiSplit,ui->spbSeqLen->value()));\n    }\n    LastInferBatchSize = InputSplits.size();\n\n    for (QString& idvInput : InputSplits)\n    {\n        if (idvInput.isEmpty())\n            continue;\n\n        InferDetails Dets;\n        ProcessCurlies(idvInput);\n\n        // If it ends in a period remove it since it will destabilize both Tacotron and FastSpeech\n        if (idvInput[idvInput.size() - 1] == '.')\n            idvInput = idvInput.left(idvInput.size() - 1);\n\n        QString InputForShow = idvInput;\n\n        if (idvInput.length() > MaxShowInputLen)\n            InputForShow = idvInput.mid(0,MaxShowInputLen) + \"(...)\";\n\n\n\n        QListWidgetItem* widItm = new QListWidgetItem(InputForShow,ui->lstUtts);\n\n\n        if (ui->chkBiPad->isChecked())\n            idvInput = \"@SIL \" + idvInput;\n\n        Dets.F0 = RangeToFloat(ui->sliF0->value());\n        Dets.Speed = RangeToFloat(ui->sliSpeed->value());\n        Dets.Energy = RangeToFloat(ui->sliEnergy->value());\n        Dets.pItem = widItm;\n        Dets.Prompt = idvInput;\n        Dets.SpeakerID = -1;\n        Dets.EmotionID = -1;\n        Dets.Denoise = ui->chkDenoise->isChecked();\n        Dets.Amplification = (float)ui->sliVolBoost->value() / 1000.f;\n        Dets.ExportFileName = \"\";\n        size_t VoiceID = (size_t)VoMan.FindVoice(ui->cbModels->currentText(),true);\n\n        Dets.SampleRate = VoMan[VoiceID]->GetInfo().SampleRate;\n\n        if (ui->cbSpeaker->isVisible())\n            Dets.SpeakerID = ui->cbSpeaker->currentIndex();\n\n        if (ui->cbEmotions->isVisible())\n            Dets.EmotionID = ui->cbEmotions->currentIndex();\n\n        if (ui->edtEmotionOvr->isVisible()){\n\n            if (ui->edtEmotionOvr->text().isEmpty())\n                Dets.EmotionOvr = ui->edtInput->toPlainText().toLower();\n            else\n                Dets.EmotionOvr = ui->edtEmotionOvr->text().toLower();\n\n\n        }\n\n\n        Dets.VoiceName = ui->cbModels->currentText();\n\n        Infers.push(Dets);\n\n    }\n\n\n    pTskProg->show();\n\n    // If this is the first iteration (indicated by an empty list), or all are done (no list widget items are in process color),\n    // we explicitly iterate it. Otherwise, we let the active triggers (after audio data is received) iterate it for us.\n    // after adding it.\n    if (MustExplicitlyIterateQueue())\n        IterateQueue();\n\n\n\n\n\n\n\n\n\n\n}\n\nQStringList MainWindow::ListDirs(const QString &ParentDir)\n{\n    QStringList all_dirs;\n    QDirIterator directories(ParentDir, QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot);\n\n    while(directories.hasNext()){\n        directories.next();\n        all_dirs << directories.filePath().replace(\"models/\",\"\");\n    }\n\n    return all_dirs;\n}\n\nfloat MainWindow::RangeToFloat(int val, bool invert)\n{\n\n    int procval = invert ? 200 - val : val;\n    if (procval == 0)\n        procval = 1;\n\n    return (float)procval / 100.f;\n\n}\n\nvoid MainWindow::PlayBuffer(QBuffer *pBuff,bool ByUser, int32_t RowID)\n{\n\n\n\n\n\n    if (!ui->chkAutoPlay->isChecked() && !ByUser)\n        return;\n\n\n\n    pBuff->open(QBuffer::ReadWrite);\n\n    QAudioBuffer BuffAud(pBuff->buffer(),StdFmt);\n\n    uint64_t NumSamples = pBuff->size() / sizeof (float);\n    const TFTensor<float>& MelSpec = Mels[FindByFirst(RowID)->second];\n\n    if (MelSpec.Shape[0] != -1)\n        PlotSpec(MelSpec,( ((float)NumSamples) / ((float)CommonSampleRate)));\n    else\n        ui->tabMetrics->setTabEnabled(1,false);\n\n\n\n    ui->widAudioPlot->setSource(BuffAud);\n    ui->widAudioPlot->plot();\n    if (ui->actShowWaveform->isChecked())\n        ui->tabMetrics->show();\n\n\n\n    StdOutput->start(pBuff);\n    CanPlayAudio = false;\n\n    ui->lstUtts->item(RowID)->setBackgroundColor(PlayingColor);\n\n\n    UpdateIfDoSlides();\n\n\n\n}\n\nvoid MainWindow::AdvanceBuffer()\n{\n    InferIDTrueID* Infer = FindBySecond(CurrentBuffIndex);\n    if (Infer->Align != -1)\n    {\n        PlotAttention(Alignments[(size_t)Infer->Align]);\n\n    }else{\n        ui->tabMetrics->setTabEnabled(2,false);\n    }\n\n    PlayBuffer(AudBuffs[CurrentBuffIndex],false,FindBySecond(CurrentBuffIndex)->first);\n}\n\nvoid MainWindow::AdvanceQueue()\n{\n    if (!Infers.size())\n        return;\n\n\n     DoInference(Infers.front());\n     Infers.pop();\n\n     ++CurrentInferIndex;\n\n\n\n}\n\nint32_t MainWindow::CountBlues()\n{\n\n    if (DoingBatchDenoising)\n        return 0;\n\n\n    int32_t NumBlues = 0;\n    int32_t NumGreen = 0;\n\n    for (int i = 0; i < ui->lstUtts->count();i++)\n    {\n        if (!ui->lstUtts->item(i))\n            continue;\n\n        if (ui->lstUtts->item(i)->backgroundColor() == InProcessColor)\n            NumBlues += 1;\n        else if (ui->lstUtts->item(i)->backgroundColor() == DoneColor || ui->lstUtts->item(i)->backgroundColor() == PlayingColor)\n            NumGreen += 1;\n\n\n\n\n    }\n\n\n\n    // Letting the user click clear when there is an in process utterance will make a crash\n    ui->btnClear->setEnabled(NumBlues == 0 && NumGreen == ui->lstUtts->count());\n\n\n\n    return NumBlues;\n\n}\n\nint32_t MainWindow::GetNumThreads()\n{\n    int32_t NThreads = 1;\n\n    if (ui->chkMultiThreaded->isChecked())\n        NThreads = ui->spbThreads->value();\n\n    return NThreads;\n}\n\nbool MainWindow::MustExplicitlyIterateQueue()\n{\n    if (!ui->lstUtts->count())\n        return true;\n\n\n    if (std::max(GetNumThreads(),1) < CountBlues())\n        return false;\n\n    return true;\n\n}\n\nvoid MainWindow::PopulateComboBox()\n{\n\n    ui->cbModels->clear();\n    QStringList CuModels = ListDirs(\"models\");\n\n    if (!CuModels.empty())\n        ui->cbModels->insertItems(0,CuModels);\n\n    // This will either disable or enable depending on whether it found models or not.\n    ui->cbModels->setEnabled(!CuModels.empty());\n    ui->btnLoad->setEnabled(!CuModels.empty());\n\n\n\n\n}\n\nQStringList MainWindow::SuperWordSplit(const QString &InStr, int MaxLen)\n{\n    QStringList RawWords = InStr.split(\" \",QString::SplitBehavior::SkipEmptyParts,Qt::CaseInsensitive);\n    int AmtWords = RawWords.size();\n\n    int Idx = 0;\n    QString CurrentStr = \"\";\n\n    QStringList SplitStrs;\n\n    while (Idx < AmtWords)\n    {\n        if (CurrentStr.size() > 0)\n            CurrentStr.append(\" \");\n\n        QString CuWord = RawWords[Idx];\n\n        if (!CuWord.contains(\"@\")) // phonetic input has to be uppercase\n            CuWord = CuWord.toLower();\n\n        CurrentStr.append(CuWord);\n\n        if (CurrentStr.length() > MaxLen){\n            SplitStrs.append(CurrentStr);\n            CurrentStr = \"\";\n\n        }\n\n\n        Idx += 1;\n\n        // Add the last string\n        if (Idx == AmtWords)\n            SplitStrs.append(CurrentStr);\n\n\n\n\n\n\n    }\n\n    return SplitStrs;\n\n}\n\nvoid MainWindow::ProcessCurlies(QString &ModTxt)\n{\n\n\n    QRegularExpression& PhonemeExp = pHigh->PhonemeExp;\n    QRegularExpressionMatchIterator MatchIter = PhonemeExp.globalMatch(ModTxt);\n\n    while (MatchIter.hasNext()) {\n        QRegularExpressionMatch match = MatchIter.next();\n        QString ToProcess = ModTxt.mid(match.capturedStart(),match.capturedLength());\n\n\n        // Curlie processing not supported in IPA\n        if (GetCurrentVoice()->GetInfo().LangType == ETTSLanguageType::IPA)\n        {\n            QMessageBox::critical((QWidget*)pDarkFw,\"Warning\",\"Curly brace phonetic text input processing not supported in IPA\");\n\n            return;\n\n\n        }\n\n        QString SepStr = \" \";\n\n        QStringList Toks = ToProcess.split(SepStr,QString::SplitBehavior::SkipEmptyParts,Qt::CaseInsensitive);\n\n        QStringList NewTokens;\n        for (QString& Tk : Toks){\n            Tk = Tk.replace(\"{\",\"\").replace(\"}\",\"\");\n            if (Tk.isEmpty())\n                continue;\n\n\n\n\n            // Only ARPA requires all phn input to be uppercase\n\n            if (GetCurrentVoice()->GetInfo().LangType == ETTSLanguageType::ARPA)\n                Tk = Tk.toUpper();\n\n            NewTokens.push_back(\"@\" + Tk);\n\n        }\n\n        QString Assembled = NewTokens.join(\" \");\n        QString BeforeTxt = ModTxt.mid(0,match.capturedStart());\n        QString AfterTxt = ModTxt.mid(match.capturedEnd());\n\n        ModTxt = BeforeTxt + Assembled + AfterTxt;\n        // After every process we take the string and rebuild it, which makes the old indices outdated and dangerous to use.\n        // We match again.\n        MatchIter = PhonemeExp.globalMatch(ModTxt);\n    }\n\n}\n\n\nvoid MainWindow::IterateQueue()\n{\n    if (!Infers.size())\n        return;\n\n    int32_t NumInfer = GetNumThreads();\n\n    // Count how many we must infer\n    // This is the amount of threads minus the amount that are already being done now\n    // We ensure this is not negative by forcing it to 0\n    NumInfer = std::max(NumInfer - CountBlues(),0);\n\n\n\n    if (!NumInfer)\n        return;\n\n    for (int32_t t = 0; t < NumInfer;t++)\n    {\n        if (CurrentAmtThreads >= (uint32_t)GetNumThreads())\n            break;\n\n        AdvanceQueue();\n\n\n\n    }\n\n    if (!DoingBatchDenoising)\n         CountBlues();\n\n\n\n\n\n}\n\nvoid MainWindow::DoInference(InferDetails &Dets)\n{\n\n    Voxer* VoxThread = new Voxer;\n    VoxThread->F0 = Dets.F0;\n    VoxThread->Energy = Dets.Energy;\n    VoxThread->Speed = Dets.Speed;\n    VoxThread->Prompt = Dets.Prompt;\n    VoxThread->pAttItem = Dets.pItem;\n    VoxThread->SpeakerID = Dets.SpeakerID;\n    VoxThread->ForcedAudio = Dets.ForcedAudio;\n\n    if (!VoxThread->ForcedAudio.size())\n    {\n        size_t VoiceID = (size_t)VoMan.FindVoice(Dets.VoiceName,true);\n        //Auto-load is true, so we will always get a good pointer.\n        VoxThread->pAttVoice = VoMan[VoiceID];\n    }else\n    {\n        // Denoising threads don't need voices.\n        VoxThread->pAttVoice = nullptr;\n\n    }\n\n    VoxThread->SampleRate = Dets.SampleRate;\n    VoxThread->EmotionID = Dets.EmotionID;\n    VoxThread->EmotionOverride = Dets.EmotionOvr;\n\n    VoxThread->CurrentID = (uint32_t)CurrentInferIndex;\n    VoxThread->Denoise = Dets.Denoise;\n    VoxThread->Amplify = Dets.Amplification;\n    VoxThread->ExportFileName = Dets.ExportFileName;\n\n    connect(VoxThread,&Voxer::Done,this,&MainWindow::OnAudioRecv);\n    connect(VoxThread,&Voxer::AttentionReady,this,&MainWindow::OnAttentionRecv);\n\n    // Otherwise the thread lingers and causes a memory leak\n    connect(VoxThread, &Voxer::finished, VoxThread, &QObject::deleteLater);\n\n\n    VoxThread->start();\n    ++CurrentAmtThreads;\n\n    ui->btnClear->setEnabled(false);\n\n}\n\n\n\nvoid MainWindow::on_btnLoad_clicked()\n{\n    LogiLedFlashLighting(55,0,100,LOGI_LED_DURATION_INFINITE,500);\n    size_t VoID = VoMan.LoadVoice(ui->cbModels->currentText());\n    ui->btnLoad->setEnabled(false);\n    std::string VoNote = VoMan[VoID]->GetInfo().Note;\n\n    ui->lblModelNote->setText(QString::fromStdString(VoNote));\n    HandleIsMultiSpeaker(VoID);\n\n    LogiLedStopEffects();\n\n    LogiLedFlashLighting(0,100,100,5000,500);\n\n\n    if (VoMan[VoID]->GetInfo().Architecture.Text2Mel == EText2MelModel::FastSpeech2)\n        ui->tabMetrics->setTabEnabled(2,false); // FS2 has no attention\n\n    if (VoMan[VoID]->GetInfo().Architecture.Text2Mel == EText2MelModel::VITS)\n        ui->tabMetrics->setTabEnabled(1,false); // VITS has no mel\n\n\n\n\n\n\n\n}\n\nvoid MainWindow::on_cbModels_currentIndexChanged(const QString &arg1)\n{\n\n    ui->btnLoad->setEnabled(VoMan.FindVoice(arg1,false) == -1);\n\n\n}\n\nvoid MainWindow::on_sliEnergy_sliderMoved(int position)\n{\n    ui->lblEnergyShow->setText(QString::number(position) + \"%\");\n\n}\n\nvoid MainWindow::on_sliSpeed_sliderMoved(int position)\n{\n    ui->lblSpeedShow->setText(QString::number(position) + \"%\");\n}\n\nvoid MainWindow::on_lstUtts_itemDoubleClicked(QListWidgetItem *item)\n{\n\n\n    if (item->backgroundColor() == DoneColor || item->backgroundColor() == PlayingColor){\n\n\n        InferIDTrueID* pInfer = FindByFirst(ui->lstUtts->row(item));\n        if (!pInfer)\n        {\n            QMessageBox::critical(this,\"Error!!!!\",\"Could not find the inference ID to play, but it's apparently done???? (You shouldn't be seeing this error!!!!) (panik)\");\n            return;\n\n        }\n\n        InferIDTrueID* PreviousInfer = FindBySecond(CurrentBuffIndex);\n        if (PreviousInfer)\n        {\n            if (ui->lstUtts->item(PreviousInfer->first)->backgroundColor() == PlayingColor)\n            {\n                ui->lstUtts->item(PreviousInfer->first)->setBackgroundColor(DoneColor);\n\n\n            }\n\n\n        }\n\n        QBuffer* pBuff = AudBuffs[(uint64_t)pInfer->second];\n        CurrentBuffIndex = pInfer->second;\n\n        PlayBuffer(pBuff,true,pInfer->first);\n        if (pInfer->Align != -1)\n        {\n            PlotAttention(Alignments[(size_t)pInfer->Align]);\n\n        }else{\n            ui->tabMetrics->setTabEnabled(2,false);\n        }\n\n\n\n        int32_t MSecsShow = (pBuff->size() / sizeof (float)) / (int32_t)(CommonSampleRate / 1000);\n        LogiLedFlashLighting(100,100,100,MSecsShow,200);\n\n        UpdateIfDoSlides();\n\n\n\n\n\n\n\n    }\n\n\n}\n\nvoid MainWindow::on_btnClear_clicked()\n{\n    ui->lstUtts->clear();\n\n    AudBuffs.clear();\n    Alignments.clear();\n    Mels.clear();\n\n    CurrentBuffIndex = 0;\n    PerfReportLines.clear();\n    CurrentInferIndex = 0;\n\n    pTskProg->hide();\n\n    IdVec.clear();\n    NumDone = 0;\n    CurrentAmtThreads = 0;\n\n    UpdateLogiLed();\n\n\n\n\n}\n\nvoid MainWindow::on_btnExportSel_clicked()\n{\n    if (ui->lstUtts->selectedItems().size() == 0){\n        QMessageBox::critical(FwParent,\"Error\",\"You have to select an item to export selection.\");\n        return;\n\n    }\n\n\n    QString ofname = QFileDialog::getSaveFileName(FwParent, tr(\"Export WAV file\"), LastExportDir, tr(\"WAV, float32 PCM (*.wav)\"));\n    if (!ofname.size())\n        return;\n\n    std::vector<float> Audat;\n    QByteArray& AuBuff = AudBuffs[(size_t)ui->lstUtts->currentRow()]->buffer();\n    ExportAudBuffer(ofname,AuBuff,CommonSampleRate);\n\n    LastExportDir = ofname;\n\n\n}\n\nvoid MainWindow::on_actionExport_performance_report_triggered()\n{\n    QString ofname = QFileDialog::getSaveFileName(FwParent, tr(\"Export performance report TXT file\"), \"log\", tr(\"Text file (*.txt)\"));\n    if (!ofname.size())\n        return;\n\n    ZFile zfOut;\n    zfOut.Open(ofname.toStdString(),EZFOpenMode::BinaryWrite);\n    for (const QString& PerfLi : PerfReportLines)\n        zfOut.WriteLine(PerfLi.toStdString());\n\n    zfOut.Close();\n\n\n\n\n}\n\nvoid MainWindow::on_chkRecPerfLog_clicked(bool checked)\n{\n    RecPerfLines = checked;\n\n}\n\nvoid MainWindow::on_btnExReport_clicked()\n{\n\n    bool AltExport = GetAsyncKeyState(VK_LSHIFT);\n\n\n    QString ExTitle = \"Export WAV file\";\n\n    if (AltExport)\n        ExTitle += \"s (separately)\";\n\n    QString ofname = QFileDialog::getSaveFileName(FwParent,ExTitle,LastExportDir, tr(\"WAV, float32 PCM (*.wav)\"));\n    if (!ofname.size())\n        return;\n\n\n    LogiLedSetLighting(100,100,100);\n\n    if (AltExport)\n    {\n        for (int32_t i = 0; i < ui->lstUtts->count();i++)\n        {\n\n\n            QString FnNoFex = ofname.split(\".\")[0];\n            FnNoFex += QString::number(i);\n            FnNoFex += \".wav\";\n\n            ExportAudBuffer(FnNoFex,AudBuffs[(uint64_t)GetID(i)]->buffer(),CommonSampleRate);\n\n        }\n\n\n        LogiLedFlashLighting(0,50,100,5000,500);\n\n\n        ResetLogiLedIn(8);\n        LastExportDir = ofname;\n\n        return;\n\n\n    }\n\n    QByteArray CuBuff;\n\n    for (int32_t i = 0; i < ui->lstUtts->count();i++)\n    {\n        CuBuff.append(AudBuffs[(uint64_t)GetID(i)]->buffer());\n\n    }\n    ExportAudBuffer(ofname,CuBuff,CommonSampleRate);\n\n\n\n    LogiLedFlashLighting(0,50,100,5000,500);\n\n    LastExportDir = ofname;\n\n\n    ResetLogiLedIn(8);\n\n\n}\n\nvoid MainWindow::on_btnRefreshList_clicked()\n{\n    PopulateComboBox();\n\n}\n\nvoid MainWindow::on_sliF0_sliderMoved(int position)\n{\n    ui->lblF0Show->setText(QString::number(position) + \"%\");\n\n\n}\n\nvoid MainWindow::on_cbModels_currentTextChanged(const QString &arg1)\n{\n    int32_t CurrentIndex = VoMan.FindVoice(arg1,false);\n\n    if (CurrentIndex != -1){\n\n\n        ui->lblModelNote->setText(QString::fromStdString(VoMan[(size_t)CurrentIndex]->GetInfo().Note));\n        HandleIsMultiSpeaker((size_t)CurrentIndex);\n\n\n    }\n\n\n}\n\nvoid MainWindow::on_hkInfer_triggered()\n{\n    on_btnInfer_clicked();\n}\n\nvoid MainWindow::HandleIsMultiSpeaker(size_t inVid)\n{\n    HandleIsMultiEmotion(inVid);\n\n    Voice& CurrentVoice = *VoMan[inVid];\n\n    AutoUpdateSplit();\n\n\n    ArchitectureInfo Inf = CurrentVoice.GetInfo().Architecture;\n    if (Inf.Text2Mel == EText2MelModel::FastSpeech2 || Inf.Text2Mel == EText2MelModel::VITS || Inf.Text2Mel == EText2MelModel::DEVITS || Inf.Text2Mel == EText2MelModel::Supertonic)\n    {\n        ui->grpFs2Params->show();\n\n        if (Inf.Text2Mel == EText2MelModel::Supertonic)\n            ui->sliSpeed->setValue(105);\n\n\n        bool IsFs2 = Inf.Text2Mel == EText2MelModel::FastSpeech2;\n\n        ui->SubEnergy_2->setVisible(IsFs2);\n        ui->SubF0_2->setVisible(IsFs2);\n\n        ui->chkBiPad->setEnabled(IsFs2);\n\n\n    }\n    else\n    {\n        ui->grpFs2Params->hide();\n        ui->chkBiPad->setEnabled(false);\n\n\n\n    }\n\n\n    if (!CurrentVoice.GetSpeakers().size()){\n        ui->lblSpeaker->setVisible(false);\n        ui->cbSpeaker->setVisible(false);\n\n        return;\n    }\n\n\n    ui->cbSpeaker->clear();\n\n    for (const auto& SpkName : CurrentVoice.GetSpeakers())\n    {\n        ui->cbSpeaker->addItem(QString::fromStdString(SpkName));\n\n\n    }\n\n    ui->lblSpeaker->setVisible(true);\n    ui->cbSpeaker->setVisible(true);\n\n\n    if (!CurrentVoice.GetEmotions().size()){\n        ui->lblEmotions->setVisible(false);\n        ui->cbEmotions->setVisible(false);\n        return;\n    }\n\n\n\n\n\n\n}\n\nvoid MainWindow::HandleIsMultiEmotion(size_t inVid)\n{\n    Voice& CurrentVoice = *VoMan[inVid];\n\n\n    const bool TorchMojiEnabled = CurrentVoice.GetInfo().Architecture.Text2Mel == EText2MelModel::DEVITS;\n\n    ui->lblEmotionOvr->setVisible(TorchMojiEnabled);\n    ui->edtEmotionOvr->setVisible(TorchMojiEnabled);\n\n    if (!CurrentVoice.GetEmotions().size()){\n        ui->lblEmotions->setVisible(false);\n        ui->cbEmotions->setVisible(false);\n        return;\n    }\n\n\n    ui->cbEmotions->clear();\n\n    for (const auto& EmName : CurrentVoice.GetEmotions())\n    {\n        ui->cbEmotions->addItem(QString::fromStdString(EmName));\n\n\n    }\n\n    ui->lblEmotions->setVisible(true);\n    ui->cbEmotions->setVisible(true);\n\n\n\n\n}\n\nvoid MainWindow::on_actionOverrides_triggered()\n{\n    int32_t CurrentIndex = VoMan.FindVoice(ui->cbModels->currentText(),false);\n\n    if (CurrentIndex == -1){\n        QMessageBox::critical(FwParent,\"Error\",\"You gotta have a model loaded before accessing the phonetic dictionary. (We need to know which language you want to use)\");\n        return;\n\n    }\n\n    if (VoMan[CurrentIndex]->GetInfo().LangType == ETTSLanguageType::Char){\n        QMessageBox::critical(FwParent,\"Error\",\"Phonetic overrides dictionary is not available for character-based models. Please use a phoneme-based model.\");\n        return;\n\n\n    }\n\n    FramelessWindow FDlg(FwParent);\n    FDlg.setWindowIcon(QIcon(\":/res/phoneticdico.png\"));\n    FDlg.setWindowTitle(\"Phonetic Overrides\");\n    FDlg.SetTitleBarBtns(false,false,true);\n    FDlg.resize(640,480);\n\n    PhdDialog Dlg(FwParent);\n    Dlg.Entrs = PhonDict.Entries;\n    Dlg.CurrentLang = VoMan[CurrentIndex]->GetInfo().s_Language_Fullname;\n\n    FDlg.setContent(&Dlg);\n    FDlg.ContentDlg(&Dlg);\n\n    FDlg.show();\n\n    Dlg.setModal(true);\n\n    int code = Dlg.exec();\n    if (code == QDialog::Accepted)\n    {\n        PhonDict.Entries = Dlg.Entrs;\n        PhonDict.Export(QCoreApplication::applicationDirPath() + \"/dict.phd\");\n        SetDict();\n\n\n\n    }\n\n}\n\nvoid MainWindow::SetDict()\n{\n    VoMan.SetDict(PhonDict.Entries);\n    for (Voice*& Vo : VoMan.GetVoices())\n    {\n\n        Vo->SetDictEntries(PhonDict.Entries);\n\n    }\n\n}\n\nunsigned MainWindow::OptCountWords(const QString &InText)\n{\n    unsigned RetNum = 0;\n    bool InSpaceChain = false;\n    for (const QChar& ch : InText){\n        if (ch == ' '){\n            if (!InSpaceChain)\n                RetNum++;\n\n            InSpaceChain = true;\n\n        }else{\n            InSpaceChain = false;\n        }\n\n\n    }\n\n    return RetNum;\n\n}\n\nvoid MainWindow::on_actionRefresh_model_listing_triggered()\n{\n    PopulateComboBox();\n}\n\nvoid MainWindow::on_btnLoadInfo_clicked()\n{\n    int32_t CurrentIndex = VoMan.FindVoice(ui->cbModels->currentText(),false);\n\n    if (CurrentIndex == -1){\n        QMessageBox::critical(FwParent,\"Error\",\"You have to load the model before accessing its info\");\n        return;\n\n    }\n    VoiceInfo Voi = VoMan[(size_t)CurrentIndex]->GetInfo();\n\n    FramelessWindow FDlg(FwParent);\n    FDlg.setWindowIcon(QIcon(\":/res/infico.png\"));\n    FDlg.setWindowTitle(\"Model Info\");\n    FDlg.SetTitleBarBtns(false,false,true);\n    FDlg.resize(600,550);\n\n    ModelInfoDlg Dlg(FwParent);\n\n    FDlg.setContent(&Dlg);\n    FDlg.ContentDlg(&Dlg);\n\n    QString MdlDesc = QString::fromStdString(Voi.Description);\n    std::string MiExpanded = VoMan[(size_t)CurrentIndex]->GetModelInfo();\n\n    if (MiExpanded.size() > 1)\n        MdlDesc = QString::fromStdString(MiExpanded);\n\n\n    FDlg.show();\n    Dlg.SetInfo(QString::fromStdString(Voi.Name),MdlDesc,Voi.Version,QString::fromStdString(Voi.Author),\n                QString::fromStdString(Voi.Architecture.s_Repo),QString::fromStdString(Voi.Architecture.s_Text2Mel),\n                QString::fromStdString(Voi.Architecture.s_Vocoder),Voi.SampleRate);\n\n\n    Dlg.setModal(true);\n\n    Dlg.exec();\n\n}\n\nvoid MainWindow::on_chkMultiThreaded_stateChanged(int arg1)\n{\n    if (arg1 == Qt::Checked)\n    {\n        ui->chkAutoPlay->setChecked(false);\n        ui->chkAutoPlay->setEnabled(false);\n\n    }else{\n        ui->chkAutoPlay->setEnabled(true);\n    }\n\n}\n\nvoid MainWindow::OnFireLogiLed()\n{\n    UpdateLogiLed();\n}\n\nvoid MainWindow::ExportAudBuffer(const QString &InFilename, const QByteArray &CurrentBuff, uint32_t InSampleRate)\n{\n\n\n    std::vector<float> Audat;\n\n    Audat.resize((size_t)CurrentBuff.size() / sizeof(float));\n\n\n    smemcpy(Audat.data(),Audat.size() * sizeof(float),CurrentBuff.data(),(size_t)CurrentBuff.size());\n\n\n    VoxUtil::ExportWAV(InFilename.toStdString(),Audat,InSampleRate);\n\n}\n\nvoid MainWindow::ResetLogiLedIn(unsigned secs)\n{\n   QTimer::singleShot(secs * 1000,this,&MainWindow::OnFireLogiLed);\n\n}\n\nint32_t MainWindow::GetID(int32_t InID)\n{\n    for (const InferIDTrueID& CuId : IdVec)\n    {\n        if (CuId.first == (uint32_t)InID)\n            return (int32_t)CuId.second;\n\n\n    }\n\n    return -1;\n}\n\nvoid MainWindow::UpdateLogiLed()\n{\n    if (!LogiLedAvailable)\n        return;\n\n\n    if (ui->lstUtts->count() == 0)\n        LogiLedSetLighting(100, 0, 95);\n    else\n        LogiLedSetLighting(0,100,50);\n\n\n}\n\nvoid MainWindow::on_actDenWAV_triggered()\n{\n\n    QString fnamei = QFileDialog::getOpenFileName(this, tr(\"Open WAV to denoise\"), QString(), tr(\"Wave files (*.wav)\"));\n\n    if (fnamei == \"\")\n        return;\n\n    AudioFile<float> AudFile;\n    AudFile.load(fnamei.toStdString());\n\n\n    InferDetails Dets;\n\n\n    QString RawFn = fnamei.split(\"/\").last();\n    QListWidgetItem* widItm = new QListWidgetItem(\"Denoise \" + RawFn,ui->lstUtts);\n\n\n\n\n    Dets.F0 = RangeToFloat(ui->sliF0->value());\n    Dets.Speed = RangeToFloat(ui->sliSpeed->value());\n    Dets.Energy = RangeToFloat(ui->sliEnergy->value());\n    Dets.pItem = widItm;\n    Dets.Prompt = \"\";\n    Dets.SpeakerID = 0;\n    Dets.EmotionID = -1;\n    Dets.Denoise = true;\n    Dets.Amplification = (float)ui->sliVolBoost->value() / 1000.f;\n    Dets.ExportFileName = \"\";\n\n\n    Dets.VoiceName = ui->cbModels->currentText();\n    Dets.ForcedAudio = AudFile.samples[0];\n    Dets.SampleRate = AudFile.getSampleRate();\n\n    Infers.push(Dets);\n    if (MustExplicitlyIterateQueue())\n        IterateQueue();\n\n}\n\nvoid MainWindow::on_actShowWaveform_triggered()\n{\n\n}\n\nvoid MainWindow::on_actShowWaveform_toggled(bool arg1)\n{\n    if (!arg1)\n        ui->tabMetrics->hide();\n\n}\n\nvoid MainWindow::on_tabMetrics_currentChanged(int index)\n{\n    if (index == 0)\n    {\n        ui->tabMetrics->setSizePolicy(QSizePolicy::Policy::Expanding,QSizePolicy::Policy::Preferred);\n        ui->tabMetrics->setMinimumHeight(70);\n\n\n\n    }\n    else\n    {\n        ui->tabMetrics->setSizePolicy(QSizePolicy::Policy::Expanding,QSizePolicy::Policy::Expanding);\n        ui->tabMetrics->setMinimumHeight(150);\n\n\n    }\n    if (index == 2)\n    {\n        ui->tabMetrics->setSizePolicy(QSizePolicy::Policy::Expanding,QSizePolicy::Policy::Expanding);\n        ui->tabMetrics->setMinimumHeight(200);\n\n    }\n\n    update();\n\n    UpdateIfDoSlides();\n\n\n\n}\n\nInferIDTrueID* MainWindow::FindBySecond(int32_t BuffID)\n{\n    for (auto& ID : IdVec)\n    {\n        if ((size_t)BuffID == ID.second)\n            return &ID;\n\n    }\n    return nullptr;\n}\n\nvoid MainWindow::UpdateIfDoSlides()\n{\n    // Why do we do this instead of letting slides happen regardless?\n    // Because due to constant replotting this operation is quite expensive and can cause a noticeable performance drop\n    // if we let two of them go around at the same time\n\n    ui->widSpec->DoSlide = ui->tabMetrics->currentIndex() == 1 && StdOutput->state() == QAudio::State::ActiveState;\n\n    ui->widAudioPlot->DoSlide = ui->tabMetrics->currentIndex() == 0 && StdOutput->state() == QAudio::State::ActiveState;\n\n}\n\nvoid MainWindow::PlotSpec(const TFTensor<float> &InMel,float TimeInSecs)\n{\n    UpdateIfDoSlides();\n    ui->widSpec->DoPlot(InMel,TimeInSecs);\n    ui->tabMetrics->setTabEnabled(1,true);\n\n}\n\nvoid MainWindow::PlotAttention(const TFTensor<float>& TacAtt)\n{\n    UpdateIfDoSlides();\n\n    ui->tabMetrics->setTabEnabled(2,true);\n\n\n    ui->widAttention->DoPlot(TacAtt);\n\n\n}\n\nvoid MainWindow::on_actExAtt_triggered()\n{\n    if (!ui->tabMetrics->isTabEnabled(2))\n    {\n        QMessageBox::critical(FwParent,\"Error\",\"There is no attention map to export. Only Tacotron 2 or VITS models generate alignment.\");\n        return;\n\n\n    }\n\n\n    QString ofname = QFileDialog::getSaveFileName(FwParent, tr(\"Export PNG file\"), \"AttentionMap\", tr(\"PNG image (*.png)\"));\n    if (!ofname.size())\n        return;\n\n    ui->widAttention->savePng(ofname,ui->widAttention->width() * 2,ui->widAttention->height() * 2);\n\n\n}\n\nvoid MainWindow::on_actExSpec_triggered()\n{\n    if (!ui->tabMetrics->isTabEnabled(1))\n    {\n        QMessageBox::critical(FwParent,\"Error\",\"There is no spectrogram to export.\");\n        return;\n\n\n    }\n\n    QString ofname = QFileDialog::getSaveFileName(FwParent, tr(\"Export PNG file\"), \"Spect\", tr(\"PNG image (*.png)\"));\n    if (!ofname.size())\n        return;\n\n    ui->widSpec->savePng(ofname,ui->widSpec->width(),ui->widSpec->height());\n\n\n}\n\nvoid MainWindow::OnMemoryUpdate()\n{\n    size_t MemUsg = GetMemoryUsage();\n\n    QString MemStr = QString::number((size_t)(MemUsg / 1e+6)) + \" MB\";\n    if (MemUsg > 1e+9)\n        MemStr = QString::number(MemUsg / 1e+9,'f',1) + \" GB\";\n\n    if (auto* CurrentVoice = GetCurrentVoice()) {\n        static QString cachedCpuName;\n        QString deviceName = QString::fromStdWString(CurrentVoice->GetMelPredictorDeviceName());\n        QString deviceLabel = deviceName;\n        if (CurrentVoice->IsMelPredictorGPU()) {\n            deviceLabel = \"GPU (\" + deviceName + \")\";\n        } else {\n            if (cachedCpuName.isEmpty()) {\n                wchar_t buffer[256] = {};\n                DWORD bufferSize = sizeof(buffer);\n                if (RegGetValueW(HKEY_LOCAL_MACHINE,\n                                 L\"HARDWARE\\\\DESCRIPTION\\\\System\\\\CentralProcessor\\\\0\",\n                                 L\"ProcessorNameString\",\n                                 RRF_RT_REG_SZ,\n                                 nullptr,\n                                 buffer,\n                                 &bufferSize) == ERROR_SUCCESS) {\n                    cachedCpuName = QString::fromWCharArray(buffer).trimmed();\n                } else {\n                    cachedCpuName = \"CPU\";\n                }\n            }\n\n            deviceLabel = cachedCpuName == \"CPU\" ? \"CPU\" : (\"CPU (\" + cachedCpuName + \")\");\n        }\n\n        StatusLbl->setText(\"Device: \" + deviceLabel + \" | RAM usage: \" + MemStr);\n        return;\n    }\n\n    StatusLbl->setText(\"Memory usage: \" + MemStr);\n\n}\n\n\n\nsize_t MainWindow::GetMemoryUsage()\n{\n    PROCESS_MEMORY_COUNTERS MemCtr;\n\n    GetProcessMemoryInfo( GetCurrentProcess(), &MemCtr, sizeof(MemCtr));\n\n    return MemCtr.WorkingSetSize;\n\n\n\n\n}\n\nvoid MainWindow::on_actionPhonemize_filelist_triggered()\n{\n    QString fnamei = QFileDialog::getOpenFileName(this, tr(\"Open TXT to phonemize\"), QString(), tr(\"TXT filelist files (*.txt)\"));\n\n    if (fnamei == \"\")\n        return;\n\n    int32_t CurrentIndex = VoMan.FindVoice(ui->cbModels->currentText(),false);\n\n    if (CurrentIndex == -1){\n        QMessageBox::critical(FwParent,\"Error\",\"You have to load the model before phonemizing\");\n        return;\n\n    }\n\n    Voice& CurrentVoice = *VoMan[CurrentIndex];\n\n    QFile InputFile(fnamei);\n    QFile OutputFile(fnamei.replace(\".txt\",\"_p.txt\"));\n\n    OutputFile.open(QIODevice::WriteOnly | QIODevice::Text);\n\n    QTextCodec *codec = QTextCodec::codecForName(\"UTF-8\");\n\n    if (InputFile.open(QIODevice::ReadOnly))\n    {\n       QTextStream InStream(&InputFile);\n       QTextStream OutStream(&OutputFile);\n       InStream.setCodec(codec);\n       OutStream.setCodec(codec);\n\n\n\n       while (!InStream.atEnd())\n       {\n          QString Line = InStream.readLine().replace(\"\\n\",\"\");\n\n          QStringList Splitty = Line.split(\"|\");\n\n          if (Splitty.isEmpty())\n              continue;\n\n          QString Transcript = Splitty[1];\n          QString Filename = Splitty[0];\n\n\n          QString NewTranscript = PhonemizeStr(Transcript,CurrentVoice);\n\n          OutStream << QStringList{Filename, NewTranscript}.join(\"|\") << \"\\n\";\n\n\n\n       }\n\n       InputFile.close();\n       OutputFile.close();\n    }\n\n\n\n\n\n}\n\nQString MainWindow::PhonemizeStr(QString &Text, Voice &VoxIn)\n{\n    const QString punctuation = \",.;¡!¿?':- \";\n\n\n\n\n    QString PhonemedTxt = QString::fromStdString(VoxIn.PhonemizeStr(Text.toLower().toStdString()));\n    if (VoxIn.GetInfo().LangType == ETTSLanguageType::IPA)\n        return PhonemedTxt;\n\n\n\n    bool InCurlies = false;\n\n    QString NewPhonemed = \"\";\n\n\n\n\n\n    QStringList SplitTrans =  PhonemedTxt.split(\" \");\n\n\n    for (int32_t i = 0; i < SplitTrans.size();i++)\n    {\n\n        // Add curly braces to phonemes and exclude them for punctuation.\n\n        if (!punctuation.contains(SplitTrans[i])  && !InCurlies){\n            NewPhonemed += \" { \";\n            InCurlies = true;\n        }\n\n        if (punctuation.contains(SplitTrans[i]) && InCurlies){\n            NewPhonemed += \" } \";\n            InCurlies = false;\n        }\n\n\n\n\n\n\n        NewPhonemed += SplitTrans[i].replace(\"@\",\"\") + \" \";\n\n\n\n\n\n    }\n    if (InCurlies)\n         NewPhonemed += \" } \";\n\n    return NewPhonemed.simplified();\n\n}\n\nvoid MainWindow::on_actPhnSel_triggered()\n{\n    int32_t CurrentIndex = VoMan.FindVoice(ui->cbModels->currentText(),false);\n\n    if (CurrentIndex == -1){\n        QMessageBox::critical(FwParent,\"Error\",\"You have to load the model before phonemizing\");\n        return;\n\n    }\n\n    Voice& CurrentVoice = *VoMan[CurrentIndex];\n\n    QTextCursor TCursor = ui->edtInput->textCursor();\n    if (!TCursor.hasSelection())\n        return;\n\n    QString SelTxt = TCursor.selectedText();\n    TCursor.insertText(PhonemizeStr(SelTxt,CurrentVoice));\n\n\n\n\n}\n\nvoid MainWindow::AutoUpdateSplit()\n{\n    if (!DoUpdateSplitAuto)\n        return;\n\n    int32_t CurrentIndex = VoMan.FindVoice(ui->cbModels->currentText(),false);\n\n    if (CurrentIndex == -1)\n        return;\n\n    Voice& CurrentVoice = *VoMan[CurrentIndex];\n\n    if (CurrentVoice.GetInfo().Architecture.Text2Mel == EText2MelModel::Tacotron2)\n        ui->spbSeqLen->setValue(500);\n    else\n        ui->spbSeqLen->setValue(300);\n\n\n\n\n\n}\n\nvoid MainWindow::on_spbSeqLen_editingFinished()\n{\n    DoUpdateSplitAuto = false;\n}\n\nVoice *MainWindow::GetCurrentVoice()\n{\n    int32_t CurrentIndex = VoMan.FindVoice(ui->cbModels->currentText(),false);\n    if (CurrentIndex == -1)\n        return nullptr;\n\n    return VoMan[(size_t)CurrentIndex];\n\n\n}\n\nvoid MainWindow::on_actBatchDen_triggered()\n{\n\n    FramelessWindow FDlg(FwParent);\n    FDlg.setWindowIcon(QIcon(\":/res/infico.png\"));\n    FDlg.setWindowTitle(\"Batch Denoiser\");\n    FDlg.SetTitleBarBtns(false,false,true);\n    FDlg.resize(750,450);\n\n    BatchDenoiseDlg Dlg(FwParent);\n\n    FDlg.setContent(&Dlg);\n    FDlg.ContentDlg(&Dlg);\n\n    Dlg.pMainWindow = this;\n\n    ui->chkAutoPlay->setChecked(false);\n    ui->chkMultiThreaded->setChecked(true);\n\n    on_btnClear_clicked();\n\n\n    FDlg.show();\n\n    Dlg.setModal(true);\n\n    Dlg.exec();\n\n}\n\nvoid MainWindow::on_actStopPlaying_triggered()\n{\n    StdOutput->stop();\n    UpdateIfDoSlides();\n\n}\n\nbool MainWindow::AllowedToPlayAudio()\n{\n    return CanPlayAudio && (StdOutput->state() == QAudio::State::IdleState || StdOutput->state() == QAudio::State::StoppedState);\n\n}\n\nvoid MainWindow::on_actOpenLastExDir_triggered()\n{\n    QProcess::startDetached(QString(\"explorer /select, \\\"%1\\\"\").arg(QDir::toNativeSeparators(LastExportDir)));\n\n}\n\nvoid MainWindow::on_edtInput_textChanged()\n{\n    const QString Plain = ui->edtInput->toPlainText();\n\n    const int NumSplits = Plain.length() / ui->spbSeqLen->value();\n\n    ui->lblCharCount->setText(QString::number(Plain.length()) + \" C \" +\n                              QString::number(OptCountWords(Plain + \" \"))  + \" W \" +\n                              QString::number(NumSplits) + \" S \");\n\n}\n\nvoid MainWindow::on_actCopySel_triggered()\n{\n    QClipboard* Clip = QGuiApplication::clipboard();\n\n    Clip->setText(ui->lstUtts->currentItem()->text());\n\n\n\n}\n\nvoid MainWindow::on_actExWAVSe_triggered()\n{\n    on_btnExportSel_clicked();\n\n\n}\n\nvoid MainWindow::on_btnClearTxt_clicked()\n{\n    ui->edtInput->clear();\n}\n\nvoid MainWindow::on_btnRandom_clicked()\n{\n    if (!GetCurrentVoice())\n        return;\n\n    if (GetCurrentVoice()->GetInfo().s_Language.find(\"English\") == std::string::npos)\n        return; // No random sent for other langs\n\n    std::random_device dev;\n    std::mt19937 rng(dev());\n    std::uniform_int_distribution<std::mt19937::result_type> dist6(0,RandomTexts->size() - 1);\n\n    ui->edtInput->setText(RandomTexts[dist6(rng)]);\n\n\n\n\n}\n"
  },
  {
    "path": "mainwindow.h",
    "content": "#ifndef MAINWINDOW_H\n#define MAINWINDOW_H\n\n#include <QMainWindow>\n#include \"Voice.h\"\n#include <QStringList>\n#include <QAudioFormat>\n#include \"voicemanager.h\"\n#include <QBuffer>\n#include <QListWidgetItem>\n#include <QAudio>\n#include <QAudioOutput>\n#include <queue>\n\n#include \"phonetichighlighter.h\"\n\n#include <QWinTaskbarButton>\n#include <QWinTaskbarProgress>\n\n#include \"phoneticdict.h\"\n#include \"batchdenoisedlg.h\"\n#include \"rnnoise.h\"\n#include <QClipboard>\n\nQT_BEGIN_NAMESPACE\nnamespace Ui { class MainWindow; }\nQT_END_NAMESPACE\n\nstruct InferDetails{\n  QString Prompt;\n  float Energy;\n  float Speed;\n  float F0;\n  float Amplification;\n  QListWidgetItem* pItem;\n  int32_t SpeakerID;\n  int32_t EmotionID;\n  QString VoiceName;\n  QString EmotionOvr;\n  bool Denoise;\n\n  uint32_t SampleRate;\n\n  std::vector<float> ForcedAudio;\n  QString ExportFileName;\n\n\n\n\n\n};\nstruct InferIDTrueID{\n  uint32_t first;\n  size_t second;\n\n  int32_t Align;\n\n};\n\n\n\n\n\n\nclass MainWindow : public QMainWindow\n{\n    Q_OBJECT\n\nprivate:\n\n    std::vector<InferIDTrueID> IdVec;\n\n    std::vector<TFTensor<float>> Alignments;\n    std::vector<TFTensor<float>> Mels;\n\n    VoiceManager VoMan;\n    QAudioFormat StdFmt;\n    QAudioOutput* StdOutput;\n\n    QWinTaskbarButton* pTaskBtn;\n\n    QWinTaskbarProgress* pTskProg;\n\n\n    int32_t CurrentInferIndex;\n    uint32_t CurrentAmtThreads;\n    PhoneticDict PhonDict;\n\n    QClipboard* ClipBrd;\n\n    uint32_t LastInferBatchSize;\n\n    bool DoUpdateSplitAuto;\n\n    InferIDTrueID* FindByFirst(uint32_t inGetID);\n    QString LastExportDir;\n\n\n\nprotected:\n    friend class BatchDenoiseDlg;\n\n    void PushToInfers(InferDetails& InDets);\n    int32_t GetCountItems();\n    bool DoingBatchDenoising;\n\n    int32_t DenBatchSize;\n    int32_t DenDone;\n\npublic:\n    void* pDarkFw;\n    MainWindow(QWidget *parent = nullptr);\n\n    ~MainWindow();\nprotected:\n   void showEvent(QShowEvent *e) override;\npublic slots:\n    void OnAudioRecv(std::vector<float> InDat,TFTensor<float> InMel,std::chrono::duration<double> infer_span,uint32_t inID);\n    void OnAudioStateChange(QAudio::State newState);\n    void OnClipboardDataChanged();\n\n    void OnAttentionRecv(TFTensor<float> InAtt,uint32_t inID);\nprivate slots:\n\n    void on_btnInfer_clicked();\n\n    void on_btnLoad_clicked();\n\n    void on_cbModels_currentIndexChanged(const QString &arg1);\n\n    void on_sliEnergy_sliderMoved(int position);\n\n    void on_sliSpeed_sliderMoved(int position);\n\n    void on_lstUtts_itemDoubleClicked(QListWidgetItem *item);\n\n    void on_btnClear_clicked();\n\n    void on_btnExportSel_clicked();\n\n    void on_actionExport_performance_report_triggered();\n\n    void on_chkRecPerfLog_clicked(bool checked);\n\n    void on_btnExReport_clicked();\n\n    void on_btnRefreshList_clicked();\n\n    void on_sliF0_sliderMoved(int position);\n\n    void on_cbModels_currentTextChanged(const QString &arg1);\n\n    void on_hkInfer_triggered();\n\n    void on_actionOverrides_triggered();\n\n    void on_actionRefresh_model_listing_triggered();\n\n    void on_btnLoadInfo_clicked();\n\n    void on_chkMultiThreaded_stateChanged(int arg1);\n\n    void OnFireLogiLed();\n\n    void on_actDenWAV_triggered();\n\n    void on_actShowWaveform_triggered();\n\n    void on_actShowWaveform_toggled(bool arg1);\n\n    void on_tabMetrics_currentChanged(int index);\n\n    void on_actExAtt_triggered();\n\n    void on_actExSpec_triggered();\n\n    void OnMemoryUpdate();\n\n    void on_actionPhonemize_filelist_triggered();\n\n    void on_actPhnSel_triggered();\n\n    void on_spbSeqLen_editingFinished();\n\n    void on_actBatchDen_triggered();\n\n    void on_actStopPlaying_triggered();\n\n    void on_actOpenLastExDir_triggered();\n\n    void on_edtInput_textChanged();\n\n    void on_actCopySel_triggered();\n\n    void on_actExWAVSe_triggered();\n\n    void on_btnClearTxt_clicked();\n\n    void on_btnRandom_clicked();\n\nprivate:\n\n    bool AllowedToPlayAudio();\n\n\n    Voice* GetCurrentVoice();\n\n\n    void AutoUpdateSplit();\n    QString PhonemizeStr(QString &Text, Voice& VoxIn);\n    QLabel* StatusLbl;\n\n    size_t GetMemoryUsage();\n    InferIDTrueID *FindBySecond(int32_t BuffID);\n\n    void UpdateIfDoSlides();\n    void PlotSpec(const TFTensor<float>& InMel, float TimeInSecs);\n    void PlotAttention(const TFTensor<float> &TacAtt);\n\n    void ExportAudBuffer(const QString& InFilename,const QByteArray& CurrentBuff,uint32_t InSampleRate);\n\n    bool LogiLedAvailable;\n\n    void ResetLogiLedIn(unsigned secs);\n\n    int32_t NumDone;\n    int32_t GetID(int32_t InID);\n    void UpdateLogiLed();\n    void SetDict();\n\n    unsigned OptCountWords(const QString& InText);\n    void HandleIsMultiSpeaker(size_t inVid);\n    void HandleIsMultiEmotion(size_t inVid);\n    bool CanPlayAudio;\n    QStringList ListDirs(const QString& ParentDir);\n    float RangeToFloat(int val, bool invert = true);\n    void PlayBuffer(QBuffer* pBuff, bool ByUser, int32_t RowID);\n    bool RecPerfLines;\n\n    void AdvanceBuffer();\n    void AdvanceQueue();\n\n    int32_t CountBlues();\n\n    int32_t GetNumThreads();\n\n    bool MustExplicitlyIterateQueue();\n    void PopulateComboBox();\n\n    std::vector<QBuffer*> AudBuffs;\n\n    size_t CurrentBuffIndex;\n\n    QStringList SuperWordSplit(const QString& InStr,int MaxLen);\n\n    void ProcessCurlies(QString& ModTxt);\n\n\n    std::queue<InferDetails> Infers;\n\n    QStringList PerfReportLines;\n    void IterateQueue();\n\n    void DoInference(InferDetails &Dets);\n\n    PhoneticHighlighter* pHigh;\n\n    Ui::MainWindow *ui;\n};\n#endif // MAINWINDOW_H\n"
  },
  {
    "path": "mainwindow.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>MainWindow</class>\n <widget class=\"QMainWindow\" name=\"MainWindow\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>1047</width>\n    <height>538</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>MainWindow</string>\n  </property>\n  <widget class=\"QWidget\" name=\"centralwidget\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n    <item>\n     <widget class=\"QSplitter\" name=\"splitter\">\n      <property name=\"orientation\">\n       <enum>Qt::Horizontal</enum>\n      </property>\n      <widget class=\"QWidget\" name=\"layoutWidget\">\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout\" stretch=\"0,0\">\n        <item>\n         <layout class=\"QVBoxLayout\" name=\"verticalLayout_7\">\n          <item>\n           <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n            <item>\n             <widget class=\"QLabel\" name=\"label_3\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Preferred\">\n                <horstretch>0</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"text\">\n               <string>Voice:</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QComboBox\" name=\"cbModels\"/>\n            </item>\n            <item>\n             <widget class=\"QPushButton\" name=\"btnLoad\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n                <horstretch>0</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"text\">\n               <string>Load</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QPushButton\" name=\"btnLoadInfo\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n                <horstretch>0</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"toolTip\">\n               <string>See the information of this model, including description and characteristics</string>\n              </property>\n              <property name=\"text\">\n               <string>Info</string>\n              </property>\n             </widget>\n            </item>\n           </layout>\n          </item>\n          <item>\n           <layout class=\"QHBoxLayout\" name=\"horizontalLayout_9\">\n            <item>\n             <widget class=\"QLabel\" name=\"lblSpeaker\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Preferred\">\n                <horstretch>0</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"text\">\n               <string>Speaker: </string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QComboBox\" name=\"cbSpeaker\"/>\n            </item>\n           </layout>\n          </item>\n          <item>\n           <layout class=\"QHBoxLayout\" name=\"horizontalLayout_10\">\n            <item>\n             <widget class=\"QLabel\" name=\"lblEmotions\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Preferred\">\n                <horstretch>0</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"text\">\n               <string>Emotion: </string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QComboBox\" name=\"cbEmotions\">\n              <property name=\"editable\">\n               <bool>false</bool>\n              </property>\n             </widget>\n            </item>\n           </layout>\n          </item>\n          <item>\n           <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n            <item>\n             <widget class=\"QLabel\" name=\"lblEmotionOvr\">\n              <property name=\"text\">\n               <string>Emotion override:</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QLineEdit\" name=\"edtEmotionOvr\"/>\n            </item>\n           </layout>\n          </item>\n          <item>\n           <widget class=\"QGroupBox\" name=\"grpFs2Params\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Minimum\">\n              <horstretch>0</horstretch>\n              <verstretch>4</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"title\">\n             <string>Control Parameters</string>\n            </property>\n            <layout class=\"QVBoxLayout\" name=\"verticalLayout_5\">\n             <property name=\"spacing\">\n              <number>1</number>\n             </property>\n             <property name=\"sizeConstraint\">\n              <enum>QLayout::SetMinimumSize</enum>\n             </property>\n             <item>\n              <widget class=\"QWidget\" name=\"SubEnergy_2\" native=\"true\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Minimum\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <layout class=\"QHBoxLayout\" name=\"SubEnergy\">\n                <item>\n                 <widget class=\"QLabel\" name=\"label\">\n                  <property name=\"text\">\n                   <string>Energy</string>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QSlider\" name=\"sliEnergy\">\n                  <property name=\"maximum\">\n                   <number>200</number>\n                  </property>\n                  <property name=\"value\">\n                   <number>100</number>\n                  </property>\n                  <property name=\"orientation\">\n                   <enum>Qt::Horizontal</enum>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QLabel\" name=\"lblEnergyShow\">\n                  <property name=\"text\">\n                   <string>100%</string>\n                  </property>\n                 </widget>\n                </item>\n               </layout>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QWidget\" name=\"widget\" native=\"true\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Minimum\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n                <item>\n                 <widget class=\"QLabel\" name=\"label_2\">\n                  <property name=\"text\">\n                   <string>Speed </string>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QSlider\" name=\"sliSpeed\">\n                  <property name=\"maximum\">\n                   <number>200</number>\n                  </property>\n                  <property name=\"value\">\n                   <number>100</number>\n                  </property>\n                  <property name=\"orientation\">\n                   <enum>Qt::Horizontal</enum>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QLabel\" name=\"lblSpeedShow\">\n                  <property name=\"text\">\n                   <string>100%</string>\n                  </property>\n                 </widget>\n                </item>\n               </layout>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QWidget\" name=\"SubF0_2\" native=\"true\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Minimum\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <layout class=\"QHBoxLayout\" name=\"SubF0\">\n                <item>\n                 <widget class=\"QLabel\" name=\"label_6\">\n                  <property name=\"sizePolicy\">\n                   <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Preferred\">\n                    <horstretch>0</horstretch>\n                    <verstretch>0</verstretch>\n                   </sizepolicy>\n                  </property>\n                  <property name=\"text\">\n                   <string>F0       </string>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QSlider\" name=\"sliF0\">\n                  <property name=\"maximum\">\n                   <number>200</number>\n                  </property>\n                  <property name=\"value\">\n                   <number>100</number>\n                  </property>\n                  <property name=\"orientation\">\n                   <enum>Qt::Horizontal</enum>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QLabel\" name=\"lblF0Show\">\n                  <property name=\"text\">\n                   <string>100%</string>\n                  </property>\n                 </widget>\n                </item>\n               </layout>\n              </widget>\n             </item>\n            </layout>\n           </widget>\n          </item>\n          <item>\n           <layout class=\"QHBoxLayout\" name=\"horizontalLayout_5\">\n            <item>\n             <widget class=\"QLabel\" name=\"label_5\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Preferred\">\n                <horstretch>0</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"text\">\n               <string>Split mode:</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QRadioButton\" name=\"rbSplitWord\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n                <horstretch>0</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"toolTip\">\n               <string>Word-aware splitting auto-splits by character limit taking into account words</string>\n              </property>\n              <property name=\"text\">\n               <string>Word-aware</string>\n              </property>\n              <property name=\"checked\">\n               <bool>true</bool>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QSpinBox\" name=\"spbSeqLen\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n                <horstretch>0</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"toolTip\">\n               <string>Max length (in characters) of every split</string>\n              </property>\n              <property name=\"minimum\">\n               <number>20</number>\n              </property>\n              <property name=\"maximum\">\n               <number>5000</number>\n              </property>\n              <property name=\"singleStep\">\n               <number>10</number>\n              </property>\n              <property name=\"value\">\n               <number>180</number>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QRadioButton\" name=\"rbSplitNewlines\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n                <horstretch>0</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"toolTip\">\n               <string>Splits by lines</string>\n              </property>\n              <property name=\"text\">\n               <string>By lines</string>\n              </property>\n              <property name=\"checked\">\n               <bool>false</bool>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QCheckBox\" name=\"chkPonctAware\">\n              <property name=\"toolTip\">\n               <string>Takes into account punctuation when splitting, ending the split early when encountering a period</string>\n              </property>\n              <property name=\"text\">\n               <string>Punctuation-aware</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <spacer name=\"horizontalSpacer\">\n              <property name=\"orientation\">\n               <enum>Qt::Horizontal</enum>\n              </property>\n              <property name=\"sizeHint\" stdset=\"0\">\n               <size>\n                <width>40</width>\n                <height>20</height>\n               </size>\n              </property>\n             </spacer>\n            </item>\n            <item>\n             <widget class=\"QCheckBox\" name=\"chkDenoise\">\n              <property name=\"toolTip\">\n               <string>Denoise the output audio with a smart recurrent neural network (RNN)-based denoiser. Effective against background noise</string>\n              </property>\n              <property name=\"text\">\n               <string>Denoise</string>\n              </property>\n             </widget>\n            </item>\n           </layout>\n          </item>\n          <item>\n           <widget class=\"QTextEdit\" name=\"edtInput\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n              <horstretch>4</horstretch>\n              <verstretch>14</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"acceptRichText\">\n             <bool>false</bool>\n            </property>\n            <property name=\"placeholderText\">\n             <string>Type something here...</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <layout class=\"QHBoxLayout\" name=\"horizontalLayout_14\">\n            <item>\n             <widget class=\"QPushButton\" name=\"btnInfer\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n                <horstretch>2</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"font\">\n               <font>\n                <weight>75</weight>\n                <bold>true</bold>\n               </font>\n              </property>\n              <property name=\"text\">\n               <string>Infer (CTRL + Enter)</string>\n              </property>\n              <property name=\"iconSize\">\n               <size>\n                <width>32</width>\n                <height>32</height>\n               </size>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QPushButton\" name=\"btnClearTxt\">\n              <property name=\"toolTip\">\n               <string>Clear current text</string>\n              </property>\n              <property name=\"text\">\n               <string/>\n              </property>\n              <property name=\"icon\">\n               <iconset resource=\"stdres.qrc\">\n                <normaloff>:/res/clear64.png</normaloff>:/res/clear64.png</iconset>\n              </property>\n              <property name=\"iconSize\">\n               <size>\n                <width>16</width>\n                <height>16</height>\n               </size>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QPushButton\" name=\"btnRandom\">\n              <property name=\"toolTip\">\n               <string>Random text</string>\n              </property>\n              <property name=\"text\">\n               <string/>\n              </property>\n              <property name=\"icon\">\n               <iconset resource=\"stdres.qrc\">\n                <normaloff>:/res/random64.png</normaloff>:/res/random64.png</iconset>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QLabel\" name=\"lblCharCount\">\n              <property name=\"text\">\n               <string>0 C 0 W 0 S</string>\n              </property>\n              <property name=\"alignment\">\n               <set>Qt::AlignCenter</set>\n              </property>\n             </widget>\n            </item>\n           </layout>\n          </item>\n          <item>\n           <layout class=\"QHBoxLayout\" name=\"horizontalLayout_11\"/>\n          </item>\n         </layout>\n        </item>\n        <item>\n         <layout class=\"QVBoxLayout\" name=\"verticalLayout_8\" stretch=\"0\">\n          <item>\n           <widget class=\"QTabWidget\" name=\"tabMetrics\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n              <horstretch>0</horstretch>\n              <verstretch>4</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"currentIndex\">\n             <number>0</number>\n            </property>\n            <widget class=\"QWidget\" name=\"tbWaveform\">\n             <attribute name=\"title\">\n              <string>Waveform</string>\n             </attribute>\n             <layout class=\"QVBoxLayout\" name=\"verticalLayout_9\">\n              <item>\n               <widget class=\"Track\" name=\"widAudioPlot\" native=\"true\"/>\n              </item>\n             </layout>\n            </widget>\n            <widget class=\"QWidget\" name=\"tbMel\">\n             <attribute name=\"title\">\n              <string>Spectrogram</string>\n             </attribute>\n             <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n              <item>\n               <widget class=\"Spectrogram\" name=\"widSpec\" native=\"true\"/>\n              </item>\n             </layout>\n            </widget>\n            <widget class=\"QWidget\" name=\"tbAttention\">\n             <attribute name=\"title\">\n              <string>Attention</string>\n             </attribute>\n             <layout class=\"QVBoxLayout\" name=\"verticalLayout_10\">\n              <item>\n               <widget class=\"Attention\" name=\"widAttention\" native=\"true\"/>\n              </item>\n             </layout>\n            </widget>\n           </widget>\n          </item>\n         </layout>\n        </item>\n       </layout>\n      </widget>\n      <widget class=\"QWidget\" name=\"verticalLayoutWidget\">\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n        <item>\n         <layout class=\"QHBoxLayout\" name=\"horizontalLayout_12\">\n          <item>\n           <widget class=\"QLabel\" name=\"label_4\">\n            <property name=\"text\">\n             <string>Double click on an entry to replay</string>\n            </property>\n            <property name=\"alignment\">\n             <set>Qt::AlignCenter</set>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </item>\n        <item>\n         <widget class=\"QLabel\" name=\"lblModelNote\">\n          <property name=\"font\">\n           <font>\n            <italic>true</italic>\n           </font>\n          </property>\n          <property name=\"text\">\n           <string/>\n          </property>\n          <property name=\"alignment\">\n           <set>Qt::AlignCenter</set>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <layout class=\"QHBoxLayout\" name=\"horizontalLayout_7\">\n          <item>\n           <widget class=\"QCheckBox\" name=\"chkBiPad\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"toolTip\">\n             <string>Adds starting silence token as well as ending</string>\n            </property>\n            <property name=\"text\">\n             <string>Bidirectional padding</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QCheckBox\" name=\"chkRecPerfLog\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"toolTip\">\n             <string>Records performance log with every inference which you can access in the File menu</string>\n            </property>\n            <property name=\"text\">\n             <string>Record Performance Log</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </item>\n        <item>\n         <layout class=\"QHBoxLayout\" name=\"horizontalLayout_13\">\n          <item>\n           <spacer name=\"horizontalSpacer_3\">\n            <property name=\"orientation\">\n             <enum>Qt::Horizontal</enum>\n            </property>\n            <property name=\"sizeHint\" stdset=\"0\">\n             <size>\n              <width>40</width>\n              <height>20</height>\n             </size>\n            </property>\n           </spacer>\n          </item>\n          <item>\n           <widget class=\"QCheckBox\" name=\"chkAutoPlay\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"toolTip\">\n             <string>Whether to auto-play utterances as their inference is completed. Not possible with multi-threaded generation</string>\n            </property>\n            <property name=\"text\">\n             <string>Auto-Play</string>\n            </property>\n            <property name=\"checked\">\n             <bool>true</bool>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <spacer name=\"horizontalSpacer_2\">\n            <property name=\"orientation\">\n             <enum>Qt::Horizontal</enum>\n            </property>\n            <property name=\"sizeHint\" stdset=\"0\">\n             <size>\n              <width>40</width>\n              <height>20</height>\n             </size>\n            </property>\n           </spacer>\n          </item>\n          <item>\n           <widget class=\"QCheckBox\" name=\"chkMultiThreaded\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"toolTip\">\n             <string>Whether to process multiple utterances at the same time in threads. Auto-play is not supported with multi-threaded generation</string>\n            </property>\n            <property name=\"text\">\n             <string>Multi-Threaded</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QSpinBox\" name=\"spbThreads\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"toolTip\">\n             <string>Number of threads. Recommended to set it to the number of threads that your CPU has, which is the default value.</string>\n            </property>\n            <property name=\"minimum\">\n             <number>1</number>\n            </property>\n            <property name=\"maximum\">\n             <number>1024</number>\n            </property>\n            <property name=\"value\">\n             <number>2</number>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </item>\n        <item>\n         <layout class=\"QHBoxLayout\" name=\"horizontalLayout_8\">\n          <item>\n           <widget class=\"QLabel\" name=\"label_7\">\n            <property name=\"text\">\n             <string>Volume boost:</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QSlider\" name=\"sliVolBoost\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"toolTip\">\n             <string>Amplify volume of inference output</string>\n            </property>\n            <property name=\"maximum\">\n             <number>4000</number>\n            </property>\n            <property name=\"value\">\n             <number>1000</number>\n            </property>\n            <property name=\"orientation\">\n             <enum>Qt::Horizontal</enum>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </item>\n        <item>\n         <widget class=\"QListWidget\" name=\"lstUtts\">\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Expanding\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"contextMenuPolicy\">\n           <enum>Qt::ActionsContextMenu</enum>\n          </property>\n          <property name=\"editTriggers\">\n           <set>QAbstractItemView::NoEditTriggers</set>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n          <item>\n           <widget class=\"QPushButton\" name=\"btnClear\">\n            <property name=\"toolTip\">\n             <string>Clear the inference queue. Note that this will result in you losing all your current results, so export them if needed.</string>\n            </property>\n            <property name=\"text\">\n             <string>Clear</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QPushButton\" name=\"btnExportSel\">\n            <property name=\"enabled\">\n             <bool>false</bool>\n            </property>\n            <property name=\"toolTip\">\n             <string>Export the selected inference result to WAV</string>\n            </property>\n            <property name=\"text\">\n             <string>Export selection to WAV</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QPushButton\" name=\"btnExReport\">\n            <property name=\"enabled\">\n             <bool>false</bool>\n            </property>\n            <property name=\"toolTip\">\n             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Auto-join all lines together into one single WAV file and export. Shift + Click to export every line separately, with their order numbers appended.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n            </property>\n            <property name=\"text\">\n             <string>Export all to WAV</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </item>\n       </layout>\n      </widget>\n     </widget>\n    </item>\n   </layout>\n  </widget>\n  <widget class=\"QMenuBar\" name=\"menubar\">\n   <property name=\"geometry\">\n    <rect>\n     <x>0</x>\n     <y>0</y>\n     <width>1047</width>\n     <height>20</height>\n    </rect>\n   </property>\n   <widget class=\"QMenu\" name=\"menuFile\">\n    <property name=\"title\">\n     <string>File</string>\n    </property>\n    <widget class=\"QMenu\" name=\"menuExport_metric\">\n     <property name=\"title\">\n      <string>Export metric</string>\n     </property>\n     <addaction name=\"actExSpec\"/>\n     <addaction name=\"actExAtt\"/>\n    </widget>\n    <addaction name=\"actionExport_performance_report\"/>\n    <addaction name=\"menuExport_metric\"/>\n    <addaction name=\"actOpenLastExDir\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuTriggers\">\n    <property name=\"title\">\n     <string>Triggers</string>\n    </property>\n    <addaction name=\"hkInfer\"/>\n    <addaction name=\"actPhnSel\"/>\n    <addaction name=\"actStopPlaying\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuTools\">\n    <property name=\"title\">\n     <string>Tools</string>\n    </property>\n    <addaction name=\"actionOverrides\"/>\n    <addaction name=\"actionRefresh_model_listing\"/>\n    <addaction name=\"actAutoInferClipBrd\"/>\n    <addaction name=\"actShowWaveform\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuExtras\">\n    <property name=\"title\">\n     <string>Extras</string>\n    </property>\n    <addaction name=\"actDenWAV\"/>\n    <addaction name=\"actionPhonemize_filelist\"/>\n    <addaction name=\"actBatchDen\"/>\n   </widget>\n   <addaction name=\"menuFile\"/>\n   <addaction name=\"menuTools\"/>\n   <addaction name=\"menuTriggers\"/>\n   <addaction name=\"menuExtras\"/>\n  </widget>\n  <widget class=\"QStatusBar\" name=\"statusbar\"/>\n  <action name=\"actionExport_performance_report\">\n   <property name=\"text\">\n    <string>Export performance report</string>\n   </property>\n  </action>\n  <action name=\"hkInfer\">\n   <property name=\"text\">\n    <string>Infer</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Return</string>\n   </property>\n  </action>\n  <action name=\"actionOverrides\">\n   <property name=\"icon\">\n    <iconset resource=\"stdres.qrc\">\n     <normaloff>:/res/phoneticdico.png</normaloff>:/res/phoneticdico.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Overrides</string>\n   </property>\n  </action>\n  <action name=\"actionRefresh_model_listing\">\n   <property name=\"icon\">\n    <iconset resource=\"stdres.qrc\">\n     <normaloff>:/res/refresh.png</normaloff>:/res/refresh.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Refresh model listing</string>\n   </property>\n  </action>\n  <action name=\"actAutoInferClipBrd\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Auto-infer clipboard</string>\n   </property>\n  </action>\n  <action name=\"actDenWAV\">\n   <property name=\"text\">\n    <string>Denoise WAV with RNNoise</string>\n   </property>\n  </action>\n  <action name=\"actShowWaveform\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"checked\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Show metrics</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Show things like waveform and alignment if using Tacotron2</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+W</string>\n   </property>\n  </action>\n  <action name=\"actExSpec\">\n   <property name=\"text\">\n    <string>Spectrogram</string>\n   </property>\n  </action>\n  <action name=\"actExAtt\">\n   <property name=\"enabled\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Attention</string>\n   </property>\n  </action>\n  <action name=\"actionPhonemize_filelist\">\n   <property name=\"text\">\n    <string>Phonemize filelist</string>\n   </property>\n  </action>\n  <action name=\"actPhnSel\">\n   <property name=\"text\">\n    <string>Phonemize selection</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+P</string>\n   </property>\n  </action>\n  <action name=\"actBatchDen\">\n   <property name=\"text\">\n    <string>Batch Denoising </string>\n   </property>\n  </action>\n  <action name=\"actStopPlaying\">\n   <property name=\"text\">\n    <string>Stop playing</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Esc</string>\n   </property>\n  </action>\n  <action name=\"actOpenLastExDir\">\n   <property name=\"text\">\n    <string>Open last export directory</string>\n   </property>\n  </action>\n  <action name=\"actCopySel\">\n   <property name=\"text\">\n    <string>Copy text</string>\n   </property>\n  </action>\n  <action name=\"actExWAVSe\">\n   <property name=\"icon\">\n    <iconset resource=\"stdres.qrc\">\n     <normaloff>:/res/wav.png</normaloff>:/res/wav.png</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Export to WAV</string>\n   </property>\n  </action>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>Track</class>\n   <extends>QWidget</extends>\n   <header>track.h</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>Attention</class>\n   <extends>QWidget</extends>\n   <header>attention.h</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>Spectrogram</class>\n   <extends>QWidget</extends>\n   <header>spectrogram.h</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources>\n  <include location=\"stdres.qrc\"/>\n </resources>\n <connections/>\n</ui>\n"
  },
  {
    "path": "melgen.cpp",
    "content": "#include \"melgen.h\"\n\nMelGen::MelGen()\n{\n\n}\n\nMelGen::MelGen(const std::string &SavedModelFolder, ETTSRepo::Enum InTTSRepo)\n{\n   Initialize(SavedModelFolder,InTTSRepo);\n\n}\n\nbool MelGen::Initialize(const std::string &SavedModelFolder,  ETTSRepo::Enum InTTSRepo)\n{\n    try {\n        CurrentMdl = std::make_unique<cppflow::model>(SavedModelFolder);\n    }\n    catch (...) {\n        return false;\n\n    }\n    CurrentRepo = InTTSRepo;\n    return true;\n\n}\n"
  },
  {
    "path": "melgen.h",
    "content": "#ifndef MELGEN_H\n#define MELGEN_H\n\n\n\n#include \"ext/CppFlow/ops.h\"\n#include \"ext/CppFlow/model.h\"\n#include \"VoxCommon.hpp\"\n\n#include <memory>\n\n// MelGen: base virtual class for mel generators\nclass MelGen\n{\nprivate:\n\npublic:\n    ETTSRepo::Enum CurrentRepo;\n    MelGen();\n    MelGen(const std::string& SavedModelFolder,ETTSRepo::Enum InTTSRepo);\n\n\n    // Generic inference function\n    // Utilize ArgsFloat and ArgsInt for additional arguments for certain models\n    virtual TFTensor<float> DoInference(const std::vector<int32_t>& InputIDs,const std::vector<float>& ArgsFloat,const std::vector<int32_t> ArgsInt, int32_t SpeakerID = 0, int32_t EmotionID = -1) = 0;\n\n    /*\n    Initialize and load the model\n\n    -> SavedModelFolder: Folder where the .pb, variables, and other characteristics of the exported SavedModel\n    <- Returns: (bool)Success\n    */\n    virtual bool Initialize(const std::string& SavedModelFolder, ETTSRepo::Enum InTTSRepo);\n\n\n    std::unique_ptr<cppflow::model> CurrentMdl;\n\n    inline ETTSRepo::Enum GetCurrentRepo() {return CurrentRepo;}\n\n};\n\n#endif // MELGEN_H\n"
  },
  {
    "path": "modelinfodlg.cpp",
    "content": "#include \"modelinfodlg.h\"\n#include \"ui_modelinfodlg.h\"\n#include <QFile>\n\nModelInfoDlg::ModelInfoDlg(QWidget *parent) :\n    QDialog(parent),\n    ui(new Ui::ModelInfoDlg)\n{\n    ui->setupUi(this);\n}\n\nModelInfoDlg::~ModelInfoDlg()\n{\n    delete ui;\n}\n\nvoid ModelInfoDlg::SetInfo(const QString &ModelName, const QString &Info, int32_t InVersion, const QString &Author, const QString &Repo, const QString &MelGen, const QString &Vocoder, uint32_t SampleRate)\n{\n    ui->lblAuthor->setText(\"Author: \" + Author);\n    ui->lblVersion->setText(\"Version: \" + QString::number(InVersion) + \"  \");\n    ui->redtModelInfo->setText(QString(Info).replace(\"(/NL)\",\"\\n\"));\n\n\n    ui->lblModelTitle->setText(ModelName);\n\n    QString ArchShow = \"Architecture: \" + Repo + \" \" + MelGen;\n\n    if (Vocoder.size())\n        ArchShow += \" & \" + Vocoder;\n\n    ui->lblModelArchitecture->setText(ArchShow);\n    ui->lblSampleRate->setText(\"Sampling rate: \" + QString::number(SampleRate / 1000) + \"KHz\");\n\n    QString ImgPath = QApplication::applicationDirPath() + \"/models/\" + ModelName + \"/image.png\";\n    if (QFile::exists(ImgPath))\n    {\n        ui->lblImg->setPixmap(QPixmap::fromImage(QImage(ImgPath)));\n\n    }\n}\n"
  },
  {
    "path": "modelinfodlg.h",
    "content": "#ifndef MODELINFODLG_H\n#define MODELINFODLG_H\n\n#include <QDialog>\n\nnamespace Ui {\nclass ModelInfoDlg;\n}\n\nclass ModelInfoDlg : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit ModelInfoDlg(QWidget *parent = nullptr);\n    ~ModelInfoDlg();\n\n    void SetInfo(const QString& ModelName,const QString& Info,int32_t InVersion,const QString& Author,const QString& Repo,const QString& MelGen,const QString& Vocoder,uint32_t SampleRate);\n\nprivate:\n    Ui::ModelInfoDlg *ui;\n};\n\n#endif // MODELINFODLG_H\n"
  },
  {
    "path": "modelinfodlg.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ModelInfoDlg</class>\n <widget class=\"QDialog\" name=\"ModelInfoDlg\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>576</width>\n    <height>531</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Dialog</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"lblModelTitle\">\n     <property name=\"font\">\n      <font>\n       <family>Verdana</family>\n       <pointsize>16</pointsize>\n      </font>\n     </property>\n     <property name=\"text\">\n      <string>Model Name</string>\n     </property>\n     <property name=\"alignment\">\n      <set>Qt::AlignCenter</set>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n     <item>\n      <widget class=\"QLabel\" name=\"lblImg\">\n       <property name=\"maximumSize\">\n        <size>\n         <width>600</width>\n         <height>256</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"pixmap\">\n        <pixmap resource=\"stdres.qrc\">:/res/noim.png</pixmap>\n       </property>\n       <property name=\"scaledContents\">\n        <bool>true</bool>\n       </property>\n       <property name=\"alignment\">\n        <set>Qt::AlignCenter</set>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QTextEdit\" name=\"redtModelInfo\">\n     <property name=\"font\">\n      <font>\n       <family>Verdana</family>\n       <pointsize>10</pointsize>\n      </font>\n     </property>\n     <property name=\"readOnly\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <widget class=\"QLabel\" name=\"lblAuthor\">\n       <property name=\"text\">\n        <string>Author: Anonymous</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"lblSampleRate\">\n       <property name=\"text\">\n        <string>Sampling rate: 22KHz</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"horizontalSpacer_3\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"lblVersion\">\n       <property name=\"text\">\n        <string>Version: 1 </string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n     <item>\n      <widget class=\"QLabel\" name=\"lblModelArchitecture\">\n       <property name=\"text\">\n        <string>Architecture: TensorflowTTS FastSpeech2 &amp; Multi-Band MelGAN</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"horizontalSpacer_2\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources>\n  <include location=\"stdres.qrc\"/>\n </resources>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>ModelInfoDlg</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>ModelInfoDlg</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "phddialog.cpp",
    "content": "#include \"phddialog.h\"\n#include \"ui_phddialog.h\"\n#include <QTableWidget>\n#include <QTableWidgetItem>\n#include <QFileDialog>\n#include <QMessageBox>\nPhdDialog::PhdDialog(QWidget *parent) :\n    QDialog(parent),\n    ui(new Ui::PhdDialog)\n{\n    ui->setupUi(this);\n    ui->tbDict->horizontalHeader()->setStretchLastSection(true);\n\n}\n\nPhdDialog::~PhdDialog()\n{\n    delete ui;\n}\n\nint PhdDialog::exec()\n{\n    // Populate the list\n\n    PopulateWithEntries();\n\n\n  //  ui->tbDict->setColumnWidth(0,ui->tbDict->width() / 2);\n   // ui->tbDict->setColumnWidth(1,ui->tbDict->width() / 2);\n    return QDialog::exec();\n}\n\nvoid PhdDialog::accept()\n{\n    // Validate input\n    for (int i = 0; i < ui->tbDict->rowCount();++i)\n    {\n        if (ui->tbDict->item(i,0)->text().isEmpty()\n            || ui->tbDict->item(i,0)->text() == \" \" ||\n            ui->tbDict->item(i,1)->text().isEmpty())\n        {\n            QMessageBox::critical(this,\"Invalid input\",\"None of the cells can be empty, and words cannot be spaces. Check your input and try again.\");\n            return;\n\n        }\n\n\n    }\n\n    // Now clear and run second loop\n\n    Entrs.clear();\n    Entrs.reserve((size_t)ui->tbDict->rowCount());\n    // Second loop\n    for (int i = 0; i < ui->tbDict->rowCount();++i)\n    {\n        DictEntry de;\n        de.Word = ui->tbDict->item(i,0)->text().toStdString();\n        de.PhSpelling = ui->tbDict->item(i,1)->text().toStdString();\n        de.Language = ui->tbDict->item(i,2)->text().toStdString();\n        Entrs.push_back(de);\n\n\n\n    }\n\n    QDialog::accept();\n}\n\nvoid PhdDialog::on_btnAdd_clicked()\n{\n    ui->tbDict->insertRow(ui->tbDict->rowCount());\n    ui->tbDict->scrollToItem(ui->tbDict->item(ui->tbDict->rowCount() - 1,0));\n\n    QTableWidgetItem* LangItem = new QTableWidgetItem(QString::fromStdString(CurrentLang));\n    LangItem->setFlags(LangItem->flags() ^ Qt::ItemIsEditable);\n\n    ui->tbDict->setItem(ui->tbDict->rowCount() - 1,2,LangItem);\n\n}\n\nvoid PhdDialog::PopulateWithEntries()\n{\n    ui->tbDict->clearContents();\n    ui->tbDict->setRowCount((int)Entrs.size());\n    for (size_t i = 0;i < Entrs.size();++i)\n    {\n        ui->tbDict->setItem((int)i,0,new QTableWidgetItem(QString::fromStdString(Entrs[i].Word)));\n        ui->tbDict->setItem((int)i,1,new QTableWidgetItem(QString::fromStdString(Entrs[i].PhSpelling)));\n\n        QTableWidgetItem* LangItem = new QTableWidgetItem(QString::fromStdString(Entrs[i].Language));\n        LangItem->setFlags(LangItem->flags() ^ Qt::ItemIsEditable);\n\n        ui->tbDict->setItem((int)i,2,LangItem);\n\n\n    }\n\n}\n\nvoid PhdDialog::on_btnRemove_clicked()\n{\n    QList<QTableWidgetItem*> seli = ui->tbDict->selectedItems();\n    QList<QTableWidgetItem*>::iterator It = seli.begin();\n    while (It != seli.end())\n    {\n        QTableWidgetItem* item = *It;\n        ui->tbDict->removeRow(item->row());\n\n        ++It;\n    }\n}\n\nvoid PhdDialog::on_btnImport_clicked()\n{\n    QString fnamei = QFileDialog::getOpenFileName(this, tr(\"Open dictionary to import\"), QString(), tr(\"DeltaVox Phonetic Dictionary Files (*.phd)\"));\n\n    if (fnamei == \"\")\n        return;\n\n    PhoneticDict Pd;\n    if (!Pd.Import(fnamei)){\n        QMessageBox::critical(this,\"Error\",\"Failed to import this file.\");\n        return;\n    }\n\n    Entrs.reserve(Entrs.size() + Pd.Entries.size());\n    for (DictEntry& De : Pd.Entries )\n    {\n        Entrs.push_back(De);\n\n\n    }\n    PopulateWithEntries();\n\n\n\n}\n\nvoid PhdDialog::on_tbDict_cellChanged(int row, int column)\n{\n    if (row != 0)\n        return;\n\n}\n"
  },
  {
    "path": "phddialog.h",
    "content": "#ifndef PHDDIALOG_H\n#define PHDDIALOG_H\n\n#include <QDialog>\n#include \"phoneticdict.h\"\nnamespace Ui {\nclass PhdDialog;\n}\n\nclass PhdDialog : public QDialog\n{\n    Q_OBJECT\n\npublic:\n    explicit PhdDialog(QWidget *parent = nullptr);\n    ~PhdDialog();\n\n    int exec() override;\n    void accept() override;\n\n    std::vector<DictEntry> Entrs;\n\n\n    std::string CurrentLang;\nprivate slots:\n    void on_btnAdd_clicked();\n\n    void on_btnRemove_clicked();\n\n    void on_btnImport_clicked();\n\n    void on_tbDict_cellChanged(int row, int column);\n\nprivate:\n    void PopulateWithEntries();\n    Ui::PhdDialog *ui;\n};\n\n#endif // PHDDIALOG_H\n"
  },
  {
    "path": "phddialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>PhdDialog</class>\n <widget class=\"QDialog\" name=\"PhdDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>640</width>\n    <height>480</height>\n   </rect>\n  </property>\n  <property name=\"font\">\n   <font>\n    <family>Verdana</family>\n    <pointsize>9</pointsize>\n   </font>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Phonetic Dictionary</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <widget class=\"QTableWidget\" name=\"tbDict\">\n       <property name=\"font\">\n        <font>\n         <family>Verdana</family>\n         <pointsize>8</pointsize>\n        </font>\n       </property>\n       <column>\n        <property name=\"text\">\n         <string>Word</string>\n        </property>\n       </column>\n       <column>\n        <property name=\"text\">\n         <string>Phonetic spelling</string>\n        </property>\n       </column>\n       <column>\n        <property name=\"text\">\n         <string>Language</string>\n        </property>\n       </column>\n      </widget>\n     </item>\n     <item>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n       <item>\n        <widget class=\"QPushButton\" name=\"btnAdd\">\n         <property name=\"text\">\n          <string>Add</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QPushButton\" name=\"btnImport\">\n         <property name=\"text\">\n          <string>Import</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QPushButton\" name=\"btnRemove\">\n         <property name=\"text\">\n          <string>Remove</string>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>PhdDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>PhdDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "phonemizer.cpp",
    "content": "#include \"phonemizer.h\"\n#include <fstream>\n#include \"ext/ZCharScanner.h\"\n\n#include <QString>\nint32_t GetID(const std::vector<IdStr>& In, const std::string &InStr)\n{\n    for (const IdStr& It : In)\n        if (It.STR == InStr)\n            return It.ID;\n\n    return -1;\n}\n\nstd::string GetSTR(const std::vector<IdStr>& In, int32_t InID)\n{\n    for (const IdStr& It : In)\n        if (It.ID == InID)\n            return It.STR;\n\n    return \"\";\n\n}\n\n\n\nstd::vector<IdStr> Phonemizer::GetDelimitedFile(const std::string &InFname)\n{\n\n\n    std::ifstream InFile (InFname);\n\n    int32_t CuID;\n    std::string Tok;\n    std::vector<IdStr> RetVec;\n\n\n    std::string Line;\n    while (std::getline(InFile, Line)) {\n\n        if (Line.find(\"\\t\") == std::string::npos)\n            continue;\n\n\n        ZStringDelimiter Deline(Line);\n        Deline.AddDelimiter(\"\\t\");\n\n        CuID = stoi(Deline[1]);\n        Tok = Deline[0];\n\n\n        RetVec.push_back(IdStr{CuID,Tok});\n\n    }\n\n    return RetVec;\n\n\n}\n\nvoid Phonemizer::LoadDictionary(const std::string &InDictFn)\n{\n\n    std::ifstream InFile (InDictFn);\n\n    std::string Word;\n    std::string Phn;\n\n\n    if (MapDict.size())\n        MapDict.clear();\n\n\n    std::string Line;\n    while (std::getline(InFile, Line)) {\n\n        if (Line.find(\"\\t\") == std::string::npos)\n            continue;\n\n\n        ZStringDelimiter Deline(Line);\n        Deline.AddDelimiter(\"\\t\");\n\n        Word = Deline[0];\n        Phn = Deline[1];\n\n        MapDict.insert({Word,Phn});\n\n\n    }\n\n\n}\n\nstd::string Phonemizer::DictLookup(const std::string &InWord)\n{\n    auto It = MapDict.find(InWord);\n\n    if (It == MapDict.end())\n        return \"\";\n\n    return It->second;\n\n\n}\n// To remove from the string before dicting\nconst std::u32string StripPonct = U\",.;!?\";\n\n\nstd::string Phonemizer::CleanWord(const std::string &InW)\n{\n    // U32string = guaranteed 1 char = 1 value\n    std::u32string Word = VoxUtil::StrToU32(InW);\n\n\n    std::u32string RetWord;\n    RetWord.reserve(Word.size());\n\n    for (auto Ch : Word){\n        if (StripPonct.find(Ch) == std::u32string::npos)\n            RetWord.push_back(Ch);\n    }\n\n    return VoxUtil::U32ToStr(RetWord);\n}\n\n\nPhonemizer::Phonemizer()\n{\n    IsMinimal = true;\n\n}\n\nbool Phonemizer::Initialize(const std::string InPath, bool Minimal)\n{\n    IsMinimal = Minimal;\n\n\n\n    // Load char indices\n    CharId = GetDelimitedFile(InPath + \"/char2id.txt\");\n\n    // If we're doing minimal loading then stop here\n    if (IsMinimal)\n        return true;\n\n\n    PhnId = GetDelimitedFile(InPath + \"/phn2id.txt\");\n\n    // Load model\n    G2pModel.Initialize(InPath + \"/model\");\n\n    LoadDictionary(InPath + \"/dict.txt\");\n\n\n\n\n    IsMinimal = false;\n    return true;\n\n\n}\n\n\n\n\nstd::string Phonemizer::ProcessWord(const std::string &InWord,float Temperature)\n{\n    if (IsMinimal)\n        return InWord;\n\n\n    // First we try dictionary lookup\n    // This is because the g2p model can be unreliable, we only want to use it for novel sentences\n\n    std::string PhnDict = DictLookup(CleanWord(InWord));\n    if (!PhnDict.empty())\n        return PhnDict;\n\n    std::vector<int32_t> InIndexes;\n    std::u32string IterStr = VoxUtil::StrToU32(InWord);\n\n    InIndexes.reserve(IterStr.size());\n\n\n    // Turn word into indices\n    for (const char32_t ch : IterStr)\n    {\n        std::u32string Single(1,ch);\n        int32_t Idx = GetID(CharId,VoxUtil::U32ToStr(Single));\n\n        if (Idx != -1)\n            InIndexes.push_back(Idx);\n\n\n    }\n\n    TFTensor<int32_t> PhnPrediction = G2pModel.DoInference(InIndexes,Temperature);\n\n\n    std::string RetStr = \"\";\n    bool FirstIter = true;\n\n    for (int32_t PhnIdx : PhnPrediction.Data)\n    {\n        std::string PhnTxt = GetSTR(PhnId,PhnIdx);\n        if (!PhnTxt.empty())\n        {\n            if (!FirstIter)\n                RetStr.append(\" \");\n\n            RetStr.append(PhnTxt);\n\n        }\n\n        FirstIter = false;\n    }\n\n\n\n    return  RetStr;\n\n}\n\nstd::string Phonemizer::GetPhnLanguage() const\n{\n    return PhnLanguage;\n}\n\nvoid Phonemizer::SetPhnLanguage(const std::string &value)\n{\n\n    PhnLanguage = value;\n}\n\nstd::string Phonemizer::GetGraphemeChars()\n{\n\n    std::string RetAllowed = \"\";\n    for (const IdStr& Idx : CharId)\n        RetAllowed.append(Idx.STR);\n\n    return RetAllowed;\n\n}\n\nPhonemizer::~Phonemizer()\n{\n\n}\n\n\n\n\nbool operator<(const StrStr &right, const StrStr &left)\n{\n  return right.Word.length() < left.Word.length();\n}\n"
  },
  {
    "path": "phonemizer.h",
    "content": "#ifndef PHONEMIZER_H\n#define PHONEMIZER_H\n#include \"tfg2p.h\"\n#include <tuple>\n#include <set>\n#include <algorithm>\n\nstruct IdStr{\n    int32_t ID;\n    std::string STR;\n};\n\n\nstruct StrStr{\n    std::string Word;\n    std::string Phn;\n};\n\n// Length, start index in vec\ntypedef std::pair<size_t,size_t> VBucket;\n\nclass Phonemizer\n{\nprivate:\n    TFG2P G2pModel;\n\n    std::vector<IdStr> CharId;\n    std::vector<IdStr> PhnId;\n\n    std::unordered_map<std::string,std::string> MapDict;\n\n\n    std::string NumTxtLang;\n\n    bool IsMinimal;\n\n\n\n\n    std::vector<IdStr> GetDelimitedFile(const std::string& InFname);\n\n    void LoadDictionary(const std::string& InDictFn);\n\n    std::string DictLookup(const std::string& InWord);\n\n    std::string CleanWord(const std::string& InW);\n\n\n\n    std::string PhnLanguage;\npublic:\n    std::string PhnLangID;\npublic:\n    Phonemizer();\n    /*\n     * Initialize a phonemizer\n     * Expects: (if Minimal == false)\n     * - Two files consisting in TOKEN \\t ID:\n     * -- char2id.txt: Translation from input character to ID the model can accept\n     * -- phn2id.txt: Translation from output ID from the model to phoneme\n     * - A model/ folder where a G2P-Tensorflow model was saved as SavedModel\n     * - dict.txt: Phonetic dictionary. First it searches the word there and if it can't be found then it uses the model.\n     *\n     *\n     * If Minimal == true, it only requires the .sor and char2id (for determining allowed graphemes only,\n     * the IDs can be arbitrary in this case)\n     * A Minimal phonemizer only serves to hold values useful to the processor and tokenizer, for char-based models.\n\n    */\n    bool Initialize(const std::string InPath, bool Minimal);\n\n\n    std::string ProcessWord(const std::string& InWord, float Temperature = 0.1f);\n    std::string GetPhnLanguage() const;\n    void SetPhnLanguage(const std::string &value);\n\n    std::string GetGraphemeChars();\n\n    ~Phonemizer();\n\n    inline const std::string& GetNumTxtLang() {return NumTxtLang;}\n};\n\n\nbool operator<(const StrStr& right,const StrStr& left);\n#endif // PHONEMIZER_H\n"
  },
  {
    "path": "phoneticdict.cpp",
    "content": "#include \"phoneticdict.h\"\n#include \"ext/ZFile.h\"\n#include <map>\n\nconst std::map<std::string,std::string> LegToV1{\n  {\"English\",\"English-ARPA\"},\n  {\"Spanish\",\"Spanish-GlobalPhone\"}\n};\n\nvoid AutoConvertToV1(std::string& LangStr){\n   auto It = LegToV1.find(LangStr);\n   if (It != LegToV1.end())\n       LangStr = It->second;\n\n}\n\nZFILE_IOVR(DictEntry,inentr){\n    right << inentr.Word;\n    right << inentr.PhSpelling;\n    right << inentr.Language;\n    return right;\n}\n\nZFILE_OOVR(DictEntry,entr){\n    right >> entr.Word;\n    right >> entr.PhSpelling;\n    right >> entr.Language;\n\n    AutoConvertToV1(entr.Language);\n\n    return right;\n\n}\nPhoneticDict::PhoneticDict()\n{\n\n}\n\nvoid PhoneticDict::Export(const QString &exfn)\n{\n    ZFile ofi;\n    ofi.Open(exfn.toStdString(),EZFOpenMode::BinaryWrite);\n\n    ofi << Entries;\n    ofi.Close();\n\n\n}\n\nbool PhoneticDict::Import(const QString &infn)\n{\n    ZFile fi;\n    if (!fi.Open(infn.toStdString(),EZFOpenMode::BinaryRead))\n        return false;\n\n\n    if (fi.GetFileLength() == 0){\n        fi.Close();\n        return true;\n\n    }\n\n    fi >> Entries;\n\n    fi.Close();\n\n\n\n    return true;\n\n\n\n}\n\n\nbool operator==(const DictEntry &left, const std::string &right)\n{\n    return left.Word == right;\n\n\n}\n"
  },
  {
    "path": "phoneticdict.h",
    "content": "#ifndef PHONETICDICT_H\n#define PHONETICDICT_H\n#include \"ext/ZFile.h\"\n#include <string>\n#include <QString>\nstruct DictEntry{\n    std::string Word;\n    std::string PhSpelling;\n    std::string Language;\n};\n\n\n// Check if the base word is equal to this string\nbool operator==(const DictEntry& left,const std::string& right);\n\nZFILE_OOVR(DictEntry,entr);\n\nZFILE_IOVR(DictEntry,inentr);\nclass PhoneticDict\n{\npublic:\n    PhoneticDict();\n\n    void Export(const QString& exfn);\n    bool Import(const QString &infn);\n\n    std::vector<DictEntry> Entries;\n\nprivate:\n\n};\n\n#endif // PHONETICDICT_H\n"
  },
  {
    "path": "phonetichighlighter.cpp",
    "content": "#include \"phonetichighlighter.h\"\n\n\nPhoneticHighlighter::PhoneticHighlighter(QTextDocument *parent) : QSyntaxHighlighter(parent)\n{\n\n    QString MatchExp = \"\\\\{(\\\\s*?.*?)*?\\\\}\";\n    PhonemeFormat.setForeground(Qt::magenta);\n    PhonemeFormat.setFontWeight(QFont::Bold);\n    PhonemeExp = QRegularExpression(MatchExp);\n\n    QString SingleExp = \"@.\\\\S*\";\n    SinglePhonemeExp = QRegularExpression(SingleExp);\n\n    QString LongExp = \"\\\\b\\\\w{23,}\";\n    TooLongExp = QRegularExpression(LongExp);\n\n    ErrorFormat = PhonemeFormat;\n    ErrorFormat.setForeground(Qt::red);\n    ErrorFormat.setBackground(Qt::black);\n\n\n\n\n\n\n}\n\nvoid PhoneticHighlighter::highlightBlock(const QString &text)\n{\n\n    // Phoneme\n    HighlightRegex(text,PhonemeExp,PhonemeFormat);\n    HighlightRegex(text,SinglePhonemeExp,PhonemeFormat);\n\n    // Error\n    HighlightRegex(text,TooLongExp,ErrorFormat);\n\n}\n\nvoid PhoneticHighlighter::HighlightRegex(const QString& Text,const QRegularExpression &Reg, const QTextCharFormat &Fmt)\n{\n    QRegularExpressionMatchIterator MatchIter = Reg.globalMatch(Text);\n    while (MatchIter.hasNext()) {\n        QRegularExpressionMatch match = MatchIter.next();\n        setFormat(match.capturedStart(), match.capturedLength(), Fmt);\n    }\n\n}\n"
  },
  {
    "path": "phonetichighlighter.h",
    "content": "#ifndef PHONETICHIGHLIGHTER_H\n#define PHONETICHIGHLIGHTER_H\n#include <QSyntaxHighlighter>\n#include <QRegularExpression>\nclass PhoneticHighlighter : public QSyntaxHighlighter\n{\npublic:\n    PhoneticHighlighter(QTextDocument *parent = 0);\n\n    // This is public because the main window uses it\n    QRegularExpression PhonemeExp;\n\n\nprotected:\n    void highlightBlock(const QString &text) override;\nprivate:\n\n    void HighlightRegex(const QString &Text, const QRegularExpression& Reg, const QTextCharFormat& Fmt);\n    QRegularExpression SinglePhonemeExp;\n    QRegularExpression TooLongExp;\n    QTextCharFormat PhonemeFormat;\n    QTextCharFormat ErrorFormat;\n\n};\n\n#endif // PHONETICHIGHLIGHTER_H\n"
  },
  {
    "path": "spectrogram.cpp",
    "content": "#include \"spectrogram.h\"\n\n\n\nvoid Spectrogram::TimerTick()\n{\n    if (!DoSlide)\n        return;\n\n    float RemSecs = ((float)timEndTick->remainingTime()) / 1000.f;\n    float CurrentPos = TotSecs - RemSecs;\n    float TickSet = CurrentPos/TotSecs;\n\n    PlayRect->topLeft->setCoords(TickSet,0);\n\n    layer(\"Lay2\")->replot();\n\n\n\n}\n\nvoid Spectrogram::EndSlide()\n{\n    timGenericTick->stop();\n    timEndTick->stop();\n    PlayRect->topLeft->setCoords(1,0);\n\n    layer(\"Lay2\")->replot();\n\n}\n\nsize_t Spectrogram::Get2DIndex(size_t x, size_t y, size_t xSize)\n{\n      return x + xSize*y;\n}\n\n\nSpectrogram::Spectrogram(QWidget *parent) : QCustomPlot(parent)\n{\n\n    QBrush FillBrush(QColor(100,100,100));\n    this->setBackground(FillBrush);\n    QColor White(255,255,255);\n    QPen AxisPen(QColor(150,150,150));\n    xAxis->setTickLabelColor(White);\n    yAxis->setTickLabelColor(White);\n\n    xAxis->setBasePen(AxisPen);\n    yAxis->setBasePen(AxisPen);\n\n    yAxis->setLabel(\"Frequency\");\n    xAxis->setLabel(\"Time\");\n\n\n    // They show the wrong info\n\n    xAxis->setTickLabels(false);\n    yAxis->setTickLabels(false);\n\n\n    xAxis->setTicks(false);\n    yAxis->setTicks(false);\n    xAxis->setLabelColor(White);\n    yAxis->setLabelColor(White);\n    QFont Fnt = QFont(font().family(), 10);\n\n    xAxis->setLabelFont(Fnt);\n    yAxis->setLabelFont(Fnt);\n\n\n\n    PlayRect = new QCPItemRect(this);\n    PlayRect->topLeft->setType(QCPItemPosition::ptViewportRatio);\n    PlayRect->bottomRight->setType(QCPItemPosition::ptViewportRatio);\n\n\n\n    // The rect is not visible without adding a layer, probably because we are using a more unusual type of plot\n    addLayer(\"Lay2\");\n\n    QPen RectPen(QColor(255,255,255,150));\n    QBrush RectBrush(QColor(200,200,200,75));\n\n    RectPen.setWidth(3);\n    PlayRect->topLeft->setCoords(0,0);\n    PlayRect->bottomRight->setCoords(1,1);\n    PlayRect->setPen(RectPen);\n    PlayRect->setBrush(RectBrush);\n    PlayRect->setLayer(\"Lay2\");\n\n\n\n    timGenericTick = new QTimer(this);\n    timGenericTick->setInterval(10);\n    timGenericTick->setSingleShot(false);\n\n    timEndTick = new QTimer(this);\n    timEndTick->setInterval(1000);\n    timEndTick->setSingleShot(false);\n\n    connect(timGenericTick,&QTimer::timeout,this,&Spectrogram::TimerTick);\n    connect(timEndTick,&QTimer::timeout,this,&Spectrogram::EndSlide);\n\n    DoSlide = false;\n\n\n}\n\nvoid Spectrogram::DoPlot(const TFTensor<float> &InMel, float TimeInSeconds)\n{\n\n    const TFTensor<float>& Mel = InMel;\n\n\n    const auto& Shp = Mel.Shape;\n\n\n    Map->data()->setSize((int32_t)Shp[2],(int32_t)Shp[1]);\n\n    Map->data()->setRange(QCPRange(0.0,(double)Shp[1]),QCPRange(0.0,(double)Shp[2]));\n    for (int64_t x = 0; x < Shp[2];x++)\n    {\n        for (int64_t y = 0;y < Shp[1];y++)\n        {\n            size_t i = Get2DIndex(x,y,Shp[2]);\n            Map->data()->setCell(x,y,(double)Mel.Data[i]);\n\n        }\n\n\n    }\n    Map->rescaleDataRange(true);\n\n\n\n\n\n    rescaleAxes();\n\n    replot();\n\n    TotSecs = TimeInSeconds;\n\n\n    PlayRect->setVisible(true);\n\n    PlayRect->topLeft->setCoords(1,0);\n\n    timGenericTick->start();\n\n    timEndTick->start((int)(TimeInSeconds * 1000));\n\n\n\n\n\n}\n"
  },
  {
    "path": "spectrogram.h",
    "content": "#ifndef SPECTROGRAM_H\n#define SPECTROGRAM_H\n\n#include \"ext/qcustomplot.h\"\n#include \"VoxCommon.hpp\"\n\nclass Spectrogram : public QCustomPlot\n{\npublic slots:\n    void TimerTick();\n    void EndSlide();\nprivate:\n    inline size_t Get2DIndex(size_t x,size_t y,size_t xSize);\n\n    QCPItemRect* PlayRect;\n\n    QTimer* timGenericTick;\n    QTimer* timEndTick;\n\n    float TotSecs;\n\n\npublic:\n    bool DoSlide;\n    Spectrogram(QWidget *parent = nullptr);\n\n    void DoPlot(const TFTensor<float>& InMel,float TimeInSeconds);\n\n    QCPColorMap* Map;\n};\n\n#endif // SPECTROGRAM_H\n"
  },
  {
    "path": "stdres.qrc",
    "content": "<RCC>\n    <qresource prefix=\"/\">\n        <file>res/stdico.png</file>\n        <file>res/phoneticdico.png</file>\n        <file>res/infico.png</file>\n        <file>res/refresh.png</file>\n        <file>res/noim.png</file>\n        <file>res/clear64.png</file>\n        <file>res/multiwav.png</file>\n        <file>res/random64.png</file>\n        <file>res/wav.png</file>\n        <file>res/speak64.png</file>\n    </qresource>\n</RCC>\n"
  },
  {
    "path": "tacotron2.cpp",
    "content": "#include \"tacotron2.h\"\n\n\n\nTFTensor<float> Tacotron2::DoInferenceTFTTS(const std::vector<int32_t> &InputIDs, int32_t SpeakerID, int32_t EmotionID)\n{\n    if (!CurrentMdl)\n          throw std::exception(\"Tried to do inference on unloaded or invalid model!\");\n\n\n\n      // Convenience reference so that we don't have to constantly derefer pointers.\n      cppflow::model& Mdl = *CurrentMdl;\n\n\n      // Define the tensors\n\n      // This is the shape of the input IDs, our equivalent to tf.expand_dims.\n      std::vector<int64_t> InputIDShape = { 1, (int64_t)InputIDs.size() };\n\n\n\n      cppflow::tensor input_ids{ InputIDs, InputIDShape };\n      cppflow::tensor speaker_ids{SpeakerID };\n      cppflow::tensor input_lengths{(int32_t)InputIDs.size() };\n      cppflow::tensor* emotion_ids = nullptr;\n\n\n      // This is a multi-emotion model\n      if (EmotionID != -1)\n      {\n          emotion_ids = new cppflow::tensor{std::vector<int32_t>{EmotionID}};\n\n      }\n\n      TensorVec Inputs = {{\"serving_default_input_ids:0\",input_ids},\n                          {\"serving_default_input_lengths:0\",input_lengths},\n                          {\"serving_default_speaker_ids:0\",speaker_ids}};\n\n\n\n      // Define output tensor\n      if (EmotionID != -1)\n          Inputs.push_back({\"serving_default_emotion_ids:0\",*emotion_ids});\n\n\n      // Do inference\n\n      // We only care about the after mel-after [1] and alignment history [3]\n      auto Outputs = Mdl(Inputs,{\"StatefulPartitionedCall:0\",\"StatefulPartitionedCall:1\",\"StatefulPartitionedCall:2\",\"StatefulPartitionedCall:3\"});\n\n      // Define output and return it\n      TFTensor<float> MelOut = VoxUtil::CopyTensor<float>(Outputs[1]);\n      Attention = VoxUtil::CopyTensor<float>(Outputs[3]);\n\n\n      // We allocated the emotion_ids cppflow::tensor dynamically, delete it\n      if (emotion_ids)\n          delete emotion_ids;\n\n      // We could just straight out define it in the return statement, but I like it more this way\n\n      return MelOut;\n}\n\nTFTensor<float> Tacotron2::DoInferenceCoqui(const std::vector<int32_t> &InputIDs)\n{\n    // Convenience reference so that we don't have to constantly derefer pointers.\n    cppflow::model& Mdl = *CurrentMdl;\n\n\n    // Define the tensors\n\n    // This is the shape of the input IDs, our equivalent to tf.expand_dims.\n\n    std::vector<int64_t> InputIDShape = { 1, (int64_t)InputIDs.size() };\n    cppflow::tensor input_ids{ InputIDs, InputIDShape };\n\n\n    TensorVec Inputs = {{\"serving_default_characters:0\",input_ids}};\n\n\n    // We only care about the after mel-after [1] and alignment history [2]\n    auto Outputs = Mdl(Inputs,{\"StatefulPartitionedCall:0\",\"StatefulPartitionedCall:1\",\"StatefulPartitionedCall:2\",\"StatefulPartitionedCall:3\"});\n\n    // Define output and return it\n    TFTensor<float> MelOut = VoxUtil::CopyTensor<float>(Outputs[1]);\n\n\n    // Coqui TT2 attention output is inverse of what our attention plotter expects, so we transpose it.\n    cppflow::tensor AttTransposed = cppflow::transpose(Outputs[2],cppflow::tensor{0,2,1});\n    Attention = VoxUtil::CopyTensor<float>(AttTransposed);\n\n\n    return MelOut;\n}\n\nTacotron2::Tacotron2()\n{\n\n}\n\nTFTensor<float> Tacotron2::DoInference(const std::vector<int32_t> &InputIDs, const std::vector<float> &ArgsFloat, const std::vector<int32_t> ArgsInt, int32_t SpeakerID, int32_t EmotionID)\n{\n\n\n    if (!CurrentMdl)\n        throw std::runtime_error(\"Tried to do inference on unloaded or invalid model!\");\n\n    if (GetCurrentRepo() == ETTSRepo::TensorflowTTS)\n        return DoInferenceTFTTS(InputIDs,SpeakerID,EmotionID);\n    else if (GetCurrentRepo() == ETTSRepo::CoquiTTS)\n        return DoInferenceCoqui(InputIDs);\n    else\n        throw std::runtime_error(\"Unknown/unset/unimplemented TTS repo!!!\");\n\n}\n"
  },
  {
    "path": "tacotron2.h",
    "content": "#ifndef TACOTRON2_H\n#define TACOTRON2_H\n\n#include \"melgen.h\"\n\nclass Tacotron2 : public MelGen\n{\nprivate:\n\n    TFTensor<float> DoInferenceTFTTS(const std::vector<int32_t>& InputIDs,int32_t SpeakerID = 0, int32_t EmotionID = -1);\n    TFTensor<float> DoInferenceCoqui(const std::vector<int32_t>& InputIDs);\n\n\n\npublic:\n    Tacotron2();\n    TFTensor<float> Attention;\n\n    /*\n    Do inference on a Tacotron2 model.\n\n    -> InputIDs: Input IDs of tokens for inference\n    -> SpeakerID: ID of the speaker in the model to do inference on. If single speaker, always leave at 0. If multispeaker, refer to your model.\n\n    <- Returns: TFTensor<float> with shape {1,<len of mel in frames>,80} containing contents of mel spectrogram.\n    */\n    TFTensor<float> DoInference(const std::vector<int32_t>& InputIDs,const std::vector<float>& ArgsFloat,const std::vector<int32_t> ArgsInt, int32_t SpeakerID = 0, int32_t EmotionID = -1);\n\n};\n\n#endif // TACOTRON2_H\n"
  },
  {
    "path": "tacotron2torch.cpp",
    "content": "#include \"tacotron2torch.h\"\n\nTacotron2Torch::Tacotron2Torch()\n{\n\n}\n\nbool Tacotron2Torch::Initialize(const std::string &SavedModelFolder, ETTSRepo::Enum InTTSRepo)\n{\n    try {\n        // Deserialize the ScriptModule from a file using torch::jit::load().\n\n        Model = torch::jit::load(SavedModelFolder);\n\n    }\n    catch (const c10::Error& e) {\n        return false;\n\n    }\n\n    CurrentRepo = InTTSRepo;\n    return true;\n\n}\n\nTFTensor<float> Tacotron2Torch::DoInference(const std::vector<int32_t> &InputIDs, const std::vector<float> &ArgsFloat, const std::vector<int32_t> ArgsInt, int32_t SpeakerID, int32_t EmotionID)\n{\n    // without this memory consumption is 4x\n    torch::NoGradGuard no_grad;\n\n\n    std::vector<int64_t> IInputIDs;\n    IInputIDs.reserve(InputIDs.size());\n    for (const int32_t& Id : InputIDs){\n        int64_t casted = (int64_t)Id;\n        IInputIDs.push_back(casted);\n\n    }\n\n\n\n    torch::TensorOptions Opts = torch::TensorOptions().requires_grad(false);\n\n    // This Tacotron2 always takes in speaker IDs\n    if (SpeakerID == -1)\n        SpeakerID = 0;\n\n    auto InSpkid = torch::tensor({SpeakerID},Opts);\n    auto InIDS = torch::tensor(IInputIDs, Opts).unsqueeze(0);\n\n\n\n    std::vector<torch::jit::IValue> inputs{ InSpkid,InIDS};\n\n\n\n    // Infer\n    c10::IValue Output = Model(inputs);\n\n\n    // Output = list (mel_outputs, mel_outputs_postnet, gate_outputs, alignments)\n\n    auto OutputL = Output.toList();\n\n    auto MelTens = OutputL[1].get().toTensor();\n    auto AttTens = OutputL[3].get().toTensor();//.transpose(1,2); // [1, dec_t, enc_t ] -> [1, enc_t, dec_t]\n\n\n    Attention = VoxUtil::CopyTensor<float>(AttTens);\n\n\n    return VoxUtil::CopyTensor<float>(MelTens);\n\n\n\n}\n"
  },
  {
    "path": "tacotron2torch.h",
    "content": "#ifndef TACOTRON2TORCH_H\n#define TACOTRON2TORCH_H\n#include \"melgen.h\"\n\nclass Tacotron2Torch : public MelGen\n{\nprivate:\n   torch::jit::script::Module Model;\n\npublic:\n\n    TFTensor<float> Attention;\n\n\n    Tacotron2Torch();\n    /*\n    Initialize and load the model\n\n    -> SavedModelFolder: Folder where the TorchScript models are exported\n    <- Returns: (bool)Success\n    */\n    bool Initialize(const std::string& SavedModelFolder, ETTSRepo::Enum InTTSRepo);\n\n\n    /*\n    Do inference on a Tacotron2 model.\n\n    -> InputIDs: Input IDs of tokens for inference\n    -> SpeakerID: ID of the speaker in the model to do inference on. If single speaker, always leave at 0. If multispeaker, refer to your model.\n\n    <- Returns: TFTensor<float> with shape {1,<len of mel in frames>,80} containing contents of mel spectrogram.\n    */\n    TFTensor<float> DoInference(const std::vector<int32_t>& InputIDs,const std::vector<float>& ArgsFloat,const std::vector<int32_t> ArgsInt, int32_t SpeakerID = 0, int32_t EmotionID = -1);\n\n};\n\n#endif // TACOTRON2TORCH_H\n"
  },
  {
    "path": "tfg2p.cpp",
    "content": "#include \"tfg2p.h\"\nTFG2P::TFG2P()\n{\n    G2P = nullptr;\n\n}\n\nTFG2P::TFG2P(const std::string &SavedModelFolder)\n{\n    G2P = nullptr;\n\n    Initialize(SavedModelFolder);\n}\n\nbool TFG2P::Initialize(const std::string &SavedModelFolder)\n{\n    try {\n\n        G2P = new cppflow::model(SavedModelFolder);\n\n    }\n    catch (...) {\n        G2P = nullptr;\n        return false;\n\n    }\n    return true;\n}\n\nTFTensor<int32_t> TFG2P::DoInference(const std::vector<int32_t> &InputIDs, float Temperature)\n{\n    if (!G2P)\n        throw std::exception(\"Tried to do inference on unloaded or invalid model!\");\n\n    // Convenience reference so that we don't have to constantly derefer pointers.\n    cppflow::model& Mdl = *G2P;\n\n\n    // Convenience reference so that we don't have to constantly derefer pointers.\n\n    cppflow::tensor input_ids{ InputIDs, std::vector<int64_t>{(int64_t)InputIDs.size()}};\n    cppflow::tensor input_len{(int32_t)InputIDs.size()};\n    cppflow::tensor input_temp{Temperature};\n\n\n\n\n\n    auto Outs = Mdl({{\"serving_default_input_ids:0\",input_ids},\n         {\"serving_default_input_len:0\",input_len},\n         {\"serving_default_input_temperature:0\",input_temp}},{\"StatefulPartitionedCall:0\"});\n\n    TFTensor<int32_t> RetTensor = VoxUtil::CopyTensor<int32_t>(Outs[0]);\n\n    return RetTensor;\n\n\n}\n\nTFG2P::~TFG2P()\n{\n    if (G2P)\n        delete G2P;\n\n}\n"
  },
  {
    "path": "tfg2p.h",
    "content": "#ifndef TFG2P_H\n#define TFG2P_H\n\n#include \"VoxCommon.hpp\"\n\n\nclass TFG2P\n{\nprivate:\n    cppflow::model* G2P;\n\npublic:\n    TFG2P();\n    TFG2P(const std::string& SavedModelFolder);\n\n    /*\n    Initialize and load the model\n\n    -> SavedModelFolder: Folder where the .pb, variables, and other characteristics of the exported SavedModel\n    <- Returns: (bool)Success\n    */\n    bool Initialize(const std::string& SavedModelFolder);\n\n    /*\n    Do inference on a G2P-TF-RNN model.\n\n    -> InputIDs: Input IDs of tokens for inference\n    -> Temperature: Temperature of the RNN, values higher than 0.1 cause instability.\n\n    <- Returns: TFTensor<int32_t> containing phoneme IDs\n    */\n    TFTensor<int32_t> DoInference(const std::vector<int32_t>& InputIDs, float Temperature = 0.1f);\n\n    ~TFG2P();\n\n};\n\n#endif // TFG2P_H\n"
  },
  {
    "path": "torchmoji.cpp",
    "content": "#include \"torchmoji.h\"\n#include \"ext/ZCharScanner.h\"\n\nvoid TorchMoji::LoadDict(const std::string& Path)\n{\n   if (Dictionary.size())\n       Dictionary.clear();\n\n   std::vector<std::string> Lined = VoxUtil::GetLinedFile(Path);\n\n   ZStringDelimiter Delim;\n   Delim.AddDelimiter(\"\\t\");\n\n   for (const auto& Li : Lined){\n       Delim.SetText(Li);\n\n       if (Delim.szTokens() < 2)\n           continue;\n\n       Dictionary.insert({Delim[0], std::stoi(Delim[1])});\n   }\n}\n\nstd::vector<int32_t> TorchMoji::WordsToIDs(const std::vector<std::string>& Words)\n{\n    std::vector<int32_t> IDs(VoxCommon::TorchMojiLen,0);\n\n    for (size_t i = 0; i < Words.size();i++)\n    {\n        if (i + 1 > VoxCommon::TorchMojiLen)\n            break;\n\n        auto Iter = Dictionary.find(Words[i]);\n\n        if (Iter == Dictionary.end())\n            IDs[i] = 1; // unknown\n        else\n            IDs[i] = Iter->second;\n\n\n\n    }\n\n    return IDs;\n\n\n\n}\n\nTorchMoji::TorchMoji()\n{\n\n}\n\nTorchMoji::TorchMoji(const std::string &InitPath, const std::string &DPath)\n{\n    Initialize(InitPath,DPath);\n\n}\n\nvoid TorchMoji::Initialize(const std::string &Path, const std::string &DictPath)\n{\n\n    Model = torch::jit::load(Path);\n    LoadDict(DictPath);\n}\n\nTFTensor<float> TorchMoji::Infer(const std::vector<std::string> &Seq)\n{\n    torch::NoGradGuard no_grad;\n    std::vector<int32_t> Input = WordsToIDs(Seq);\n\n    auto InIDS = torch::tensor(Input).unsqueeze(0); // (1, TMLen)\n\n    at::Tensor Output = Model({InIDS}).toTensor(); // (1, VoxCommon::TorchMojiEmbSize)\n\n    Output = Output.squeeze(); // (TorchMojiEmbSize)\n\n    TFTensor<float> Tens = VoxUtil::CopyTensor<float>(Output);\n\n\n    return Tens;\n\n\n\n}\n"
  },
  {
    "path": "torchmoji.h",
    "content": "#ifndef TORCHMOJI_H\n#define TORCHMOJI_H\n#include \"VoxCommon.hpp\"\n\n\n// TorchMoji: Emotion contextualizer model (Cookie design: skipping last layer and using hidden states to feed TTS model)\n// Allows for manipulation of emotion at inference time\nclass TorchMoji\n{\nprivate:\n        // Word, ID\n     std::map<std::string,int32_t> Dictionary;\n\n     torch::jit::script::Module Model;\n\n     void LoadDict(const std::string& Path);\n\n     std::vector<int32_t> WordsToIDs(const std::vector<std::string> &Words);\npublic:\n    TorchMoji();\n\n    TorchMoji(const std::string& InitPath,const std::string& DPath);\n\n    void Initialize(const std::string& Path,const std::string& DictPath);\n\n    // Return hidden states of emotion state.\n    // -> Seq: Vector of words\n    // <- Returns float tensor of size VoxCommon::TorchMojiEmbSize containing hidden states, ready to feed into TTS model.\n   TFTensor<float> Infer(const std::vector<std::string>& Seq);\n};\n\n#endif // TORCHMOJI_H\n"
  },
  {
    "path": "track.cpp",
    "content": "#include \"track.h\"\n\n#include <QAudioDecoder>\n\nTrack::Track(QWidget *parent)\n    : QCustomPlot(parent)\n    , decoder(new QAudioDecoder(this))\n{\n\n    wavePlot = addGraph();\n\n    QBrush FillBrush(QColor(100,100,100));\n    this->setBackground(FillBrush);\n    QPen ThePen(QColor(127,255,0));\n    wavePlot->setPen(ThePen);\n    wavePlot->setBrush(FillBrush);\n\n    yAxis->setVisible(false);\n    xAxis->setVisible(false);\n\n    // add independent layer for playrect and labels so we don't replot the entire thing every time\n\n    addLayer(\"Playing\");\n    setCurrentLayer(\"Playing\");\n    layer(\"Playing\")->setMode(QCPLayer::LayerMode::lmBuffered);\n\n\n    PlayRect = new QCPItemRect(this);\n    PlayRect->topLeft->setType(QCPItemPosition::ptViewportRatio);\n    PlayRect->bottomRight->setType(QCPItemPosition::ptViewportRatio);\n\n\n    QPen RectPen(QColor(255,255,255,150));\n    QBrush RectBrush(QColor(200,200,200,75));\n\n    RectPen.setWidth(3);\n    PlayRect->topLeft->setCoords(0,0);\n    PlayRect->bottomRight->setCoords(1,1);\n    PlayRect->setPen(RectPen);\n    PlayRect->setBrush(RectBrush);\n\n\n\n\n    timGenericTick = new QTimer(this);\n    timGenericTick->setInterval(10);\n    timGenericTick->setSingleShot(false);\n\n    timEndTick = new QTimer(this);\n    timEndTick->setInterval(1000);\n    timEndTick->setSingleShot(false);\n\n    connect(timGenericTick,&QTimer::timeout,this,&Track::TimerTick);\n    connect(timEndTick,&QTimer::timeout,this,&Track::EndSlide);\n\n    SecsTxt = new QCPItemText(this);\n    SecsTxt->setPositionAlignment(Qt::AlignTop|Qt::AlignLeft);\n    SecsTxt->position->setType(QCPItemPosition::ptViewportRatio);\n    SecsTxt->position->setCoords(0.02, 0.05);\n    SecsTxt->setText(\"Ready\");\n    SecsTxt->setFont(QFont(font().family(), 10));\n    SecsTxt->setColor(QColor(255,255,255));\n    SecsTxt->setClipToAxisRect(false);\n    DoSlide = false;\n\n    //wavePlot->setPen(ThePen);\n\n}\n\nTrack::~Track()\n{\n    delete decoder;\n    // wavePlot delete auto ?\n}\n\nvoid Track::setSource(const QAudioBuffer &inbuffer)\n{\n    buffer = inbuffer;\n\n\n    setBuffer();\n\n\n    startPlaying(((float)buffer.duration()) / 1e+6);\n\n}\n\nvoid Track::setBuffer()\n{\n    samples.clear();\n    qreal peak = getPeakValue(buffer.format());\n    const float *data = buffer.constData<float>();\n    int count = buffer.sampleCount();\n\n    for (int i=0; i<count; i += 2){\n        double val = ((double)data[i])/peak;\n        samples.append(val);\n    }\n\n}\n\nvoid Track::plot()\n{\n    QVector<double> x(samples.size());\n    for (int i=0; i<x.size(); i++)\n        x[i] = i;\n    wavePlot->addData(x, samples);\n    yAxis->setRange(QCPRange(-1.0, 1.0));\n\n    xAxis->setRange(QCPRange(0, samples.size()));\n    replot();\n}\n\nvoid Track::startPlaying(float TimeInSecs)\n{\n    //TickAdd = 1.f/( TimeInSecs / 0.025f );\n    TotSecs = TimeInSecs;\n\n\n    timGenericTick->start();\n\n    timEndTick->start((int)(TimeInSecs * 1000));\n\n\n}\n\nvoid Track::TimerTick()\n{\n    if (!DoSlide)\n        return;\n\n    float RemSecs = ((float)timEndTick->remainingTime()) / 1000.f;\n    float CurrentPos = TotSecs - RemSecs;\n    TickSet = CurrentPos/TotSecs;\n\n    PlayRect->topLeft->setCoords(TickSet,0);\n    SetTimeLabel(CurrentPos,TotSecs);\n\n\n    layer(\"Playing\")->replot();\n\n\n}\n\nvoid Track::EndSlide()\n{\n\n    timGenericTick->stop();\n    timEndTick->stop();\n    PlayRect->topLeft->setCoords(1,0);\n    SetTimeLabel(TotSecs,TotSecs);\n\n    layer(\"Playing\")->replot();\n\n}\n\nvoid Track::SetTimeLabel(float Cur, float Remaining)\n{\n    SecsTxt->setText(QString::number(Cur,'f',1) + \" / \" + QString::number(Remaining,'f',1) + \" (sec)\");\n\n\n}\n\n/**\n * https://stackoverflow.com/questions/46947668/draw-waveform-from-raw-data-using-qaudioprobe\n * @brief Track::getPeakValue\n * @param format\n * @return The peak value\n */\nqreal Track::getPeakValue(const QAudioFormat& format)\n{\n    // Note: Only the most common sample formats are supported\n    if (!format.isValid())\n        return qreal(0);\n\n    if (format.codec() != \"audio/pcm\")\n        return qreal(0);\n\n    switch (format.sampleType()) {\n    case QAudioFormat::Unknown:\n        break;\n    case QAudioFormat::Float:\n        if (format.sampleSize() != 32) // other sample formats are not supported\n            return qreal(0);\n        return qreal(1.00003);\n    case QAudioFormat::SignedInt:\n        if (format.sampleSize() == 32)\n#ifdef Q_OS_WIN\n            return qreal(INT_MAX);\n#endif\n#ifdef Q_OS_UNIX\n        return qreal(SHRT_MAX);\n#endif\n        if (format.sampleSize() == 16)\n            return qreal(SHRT_MAX);\n        if (format.sampleSize() == 8)\n            return qreal(CHAR_MAX);\n        break;\n    case QAudioFormat::UnSignedInt:\n        if (format.sampleSize() == 32)\n            return qreal(UINT_MAX);\n        if (format.sampleSize() == 16)\n            return qreal(USHRT_MAX);\n        if (format.sampleSize() == 8)\n            return qreal(UCHAR_MAX);\n        break;\n    }\n\n    return qreal(0);\n}\n"
  },
  {
    "path": "track.h",
    "content": "#ifndef TRACK_H\n#define TRACK_H\n#include \"ext/qcustomplot.h\"\n#include <QAudioBuffer>\n\n\n// Copied from https://stackoverflow.com/questions/50277132/qt-audio-file-to-wave-like-audacity\n\nclass QAudioDecoder;\n\nclass Track : public QCustomPlot\n{\n    Q_OBJECT\n\npublic:\n    Track(QWidget *parent = Q_NULLPTR);\n    ~Track();\n    void setSource(const QAudioBuffer &inbuffer);\n\npublic:\n    bool DoSlide;\n\n\n    void setBuffer();\n    void plot();\n    void startPlaying(float TimeInSecs);\n\n\npublic slots:\n    void TimerTick();\n    void EndSlide();\nprivate:\n    void SetTimeLabel(float Cur, float Remaining);\n    QTimer* timGenericTick;\n    QTimer* timEndTick;\n\n    float TickSet;\n    float TotSecs;\n\n    QCPItemRect* PlayRect;\n    QCPItemText* SecsTxt;\n\n    qreal getPeakValue(const QAudioFormat& format);\n\n    QAudioDecoder *decoder;\n    QAudioBuffer buffer;\n    QVector<double> samples;\n    QCPGraph *wavePlot;\n};\n#endif // TRACK_H\n"
  },
  {
    "path": "vits.cpp",
    "content": "#include \"vits.h\"\n\nstd::vector<int64_t> VITS::ZeroPadVec(const std::vector<int32_t> &InIDs)\n{\n    std::vector<int64_t> NewIDs;\n    NewIDs.reserve(InIDs.size() * 2);\n\n    NewIDs.push_back(0);\n\n    for (auto CharID : InIDs)\n    {\n\n        NewIDs.push_back((int64_t)CharID);\n        NewIDs.push_back(0);\n\n\n    }\n    // Add final 0\n   // NewIDs.push_back(0);\n\n\n    return NewIDs;\n\n}\n\nVITS::VITS()\n{\n\n}\n\nbool VITS::Initialize(const std::string &SavedModelFolder, ETTSRepo::Enum InTTSRepo)\n{\n    try {\n        // Deserialize the ScriptModule from a file using torch::jit::load().\n\n        Model = torch::jit::load(SavedModelFolder);\n\n    }\n    catch (const c10::Error& e) {\n        return false;\n\n    }\n\n    CurrentRepo = InTTSRepo;\n    return true;\n}\n\nTFTensor<float> VITS::DoInference(const std::vector<int32_t> &InputIDs, const std::vector<float> &ArgsFloat, const std::vector<int32_t> ArgsInt, int32_t SpeakerID, int32_t EmotionID)\n{\n    // without this memory consumption is 4x\n    torch::NoGradGuard no_grad;\n\n    // TorchMoji hidden states are added to ArgsFloat\n    const bool UsesTorchMoji = ArgsFloat.size() > 1;\n\n    std::vector<int64_t> PaddedIDs;\n\n\n    // Our current TM-enabled models don't use zero interspersion\n    if (UsesTorchMoji)\n        PaddedIDs.assign(InputIDs.begin(),InputIDs.end());\n    else\n        PaddedIDs = ZeroPadVec(InputIDs);\n\n\n    std::vector<int64_t> inLen = { (int64_t)PaddedIDs.size() };\n\n\n    // ZDisket: Is this really necessary?\n    torch::TensorOptions Opts = torch::TensorOptions().requires_grad(false);\n\n    auto InIDS = torch::tensor(PaddedIDs, Opts).unsqueeze(0);\n    auto InLens = torch::tensor(inLen, Opts);\n    auto InLenScale = torch::tensor({ ArgsFloat[0]}, Opts);\n\n\n\n    std::vector<torch::jit::IValue> inputs{ InIDS,InLens,InLenScale };\n\n    if (SpeakerID != -1){\n        auto InSpkid = torch::tensor({SpeakerID},Opts);\n        inputs.push_back(InSpkid);\n    }\n\n    if (EmotionID != -1){\n        auto InEmid = torch::tensor({EmotionID},Opts);\n        inputs.push_back(InEmid);\n    }\n\n    // Handle TorchMoji Emb\n    if (UsesTorchMoji){\n        // Make a copy stripping first elem\n        std::vector<float> TMHidden(ArgsFloat.begin() + 1, ArgsFloat.end());\n\n        auto InMoji = torch::tensor(TMHidden,Opts).unsqueeze(0);\n        inputs.push_back(InMoji);\n\n    }\n\n    // Infer\n\n    c10::IValue Output = Model.get_method(\"infer_ts\")(inputs);\n\n    // Output = tuple (audio,att)\n\n    auto OutputT = Output.toTuple();\n\n    // Grab audio\n    // [1, frames] -> [frames]\n    auto AuTens = OutputT.get()->elements()[0].toTensor().squeeze();\n\n    // Grab Attention\n    // [1, 1, x, y] -> [x, y] -> [y,x] -> [1, y, x]\n    auto AttTens = OutputT.get()->elements()[1].toTensor().squeeze().transpose(0,1).unsqueeze(0);\n\n    Attention = VoxUtil::CopyTensor<float>(AttTens);\n\n    return VoxUtil::CopyTensor<float>(AuTens);\n\n}\n"
  },
  {
    "path": "vits.h",
    "content": "#ifndef VITS_H\n#define VITS_H\n\n\n#include \"melgen.h\"\n\n\n\n\n\n// VITS is a fully E2E model; no separate vocoder needed\nclass VITS : public MelGen\n{\nprivate:\n\n\n\npublic:\n    torch::jit::script::Module Model;\n    // Most VITS model require zero-interspersed input IDs\n    std::vector<int64_t> ZeroPadVec(const std::vector<int32_t>& InIDs);\n\n\n    TFTensor<float> Attention;\n\n    VITS();\n\n    // Since VITS runs on PyTorch, we override the loader\n    /*\n    Initialize and load the model\n\n    -> SavedModelFolder: Not a folder, but path to TorchScripted .pt file\n    <- Returns: (bool)Success\n    */\n    virtual bool Initialize(const std::string& SavedModelFolder, ETTSRepo::Enum InTTSRepo);\n\n\n    /*\n    Do inference on a VITS model.\n\n    -> InputIDs: Input IDs of tokens for inference\n    -> SpeakerID: ID of the speaker in the model to do inference on. If single speaker, always leave at 0. If multispeaker, refer to your model.\n    -> ArgsFloat[0]: Length scale.\n\n    <- Returns: TFTensor<float> with shape {frames} of audio data\n    */\n    TFTensor<float> DoInference(const std::vector<int32_t>& InputIDs,const std::vector<float>& ArgsFloat,const std::vector<int32_t> ArgsInt, int32_t SpeakerID = 0, int32_t EmotionID = -1);\n};\n\n#endif // VITS_H\n"
  },
  {
    "path": "voicemanager.cpp",
    "content": "#include \"voicemanager.h\"\n#define SAFE_DELETE(pdel)if (pdel){delete pdel;}\n#include <QCoreApplication>\n\nPhonemizer* VoiceManager::LoadPhonemizer(const QString& InPhnLang,int32_t InLangNum)\n{\n\n    for (Phonemizer*& Phn : Phonemizers)\n    {\n       if (Phn->GetPhnLanguage() == InPhnLang.toStdString())\n           return Phn;\n\n\n    }\n\n\n    Phonemizer* CreatePhn = new Phonemizer;\n\n    // Initialize regularly or minimally\n    CreatePhn->Initialize(QString(QCoreApplication::applicationDirPath() + \"/g2p/\" + InPhnLang).toStdString(),\n                          InLangNum == ETTSLanguageType::Char);\n\n    CreatePhn->SetPhnLanguage(InPhnLang.toStdString());\n\n\n    Phonemizers.push_back(CreatePhn);\n\n    return Phonemizers[Phonemizers.size() - 1];\n\n\n}\n\nESpeakPhonemizer *VoiceManager::LoadESpeakPhonemizer(const QString &InVoiceName)\n{\n    for (ESpeakPhonemizer*& Phn : ENGPhonemizers)\n    {\n       if (Phn->GetVoiceName() == InVoiceName.toStdString())\n           return Phn;\n\n\n    }\n\n    ESpeakPhonemizer* CreatePhn = new ESpeakPhonemizer;\n    CreatePhn->Initialize(QString(QCoreApplication::applicationDirPath() + \"/g2p/eSpeak-NG\").toStdString()\n                          ,InVoiceName.toStdString());\n\n    ENGPhonemizers.push_back(CreatePhn);\n\n    return CreatePhn;\n\n}\n\nsize_t VoiceManager::LoadVoice(const QString &Voname)\n{\n    Voice* NuVoice = new Voice(QString(QCoreApplication::applicationDirPath() + \"/models/\" + Voname).toStdString(),Voname.toStdString(),nullptr);\n\n    QString PLang = QString::fromStdString(NuVoice->GetInfo().s_Language_Fullname);\n\n    Phonemizer* Phon = LoadPhonemizer(PLang,NuVoice->GetInfo().LangType);\n    ESpeakPhonemizer* ENG_Phon = nullptr;\n\n    if (NuVoice->GetInfo().s_eSpeakLang.size()){\n        ENG_Phon = LoadESpeakPhonemizer(QString::fromStdString(NuVoice->GetInfo().s_eSpeakLang));\n\n\n    }\n\n\n    NuVoice->AddPhonemizer(Phon,ENG_Phon);\n\n    std::string NumTxtPath = QString(QCoreApplication::applicationDirPath() + \"/num2txt/\" +\n                                     QString::fromStdString(NuVoice->GetInfo().s_Language) + \".sor\").toStdString();\n\n    NuVoice->LoadNumberText(NumTxtPath);\n\n    Voices.push_back(NuVoice);\n    Voices[Voices.size() - 1]->SetDictEntries(ManDict);\n    return Voices.size() - 1;\n}\n\nint VoiceManager::FindVoice(const QString &inName, bool autoload)\n{\n    for (size_t i = 0; i < Voices.size();i++)\n    {\n        if (Voices[i]->Name == inName.toStdString())\n            return (int)i;\n\n\n\n\n    }\n\n    if (autoload)\n        return (int)LoadVoice(inName);\n    else\n        return -1;\n\n\n}\n\nVoice *VoiceManager::operator[](size_t in)\n{\n\n    return Voices[in];\n\n}\n\nvoid VoiceManager::SetDict(const std::vector<DictEntry> &InDict)\n{\n    ManDict = InDict;\n\n}\n\nVoiceManager::VoiceManager()\n{\n\n}\n\nVoiceManager::~VoiceManager()\n{\n\n    for (Phonemizer* Phni : Phonemizers)\n    {\n        SAFE_DELETE(Phni)\n\n\n    }\n    for (Voice* Vo : Voices)\n    {\n\n        SAFE_DELETE(Vo)\n\n    }\n\n    Voices.clear();\n    Phonemizers.clear();\n\n\n\n}\n"
  },
  {
    "path": "voicemanager.h",
    "content": "#ifndef VOICEMANAGER_H\n#define VOICEMANAGER_H\n#include \"Voice.h\"\n#include <QString>\n#include \"phoneticdict.h\"\n#include \"phonemizer.h\"\nclass VoiceManager\n{\nprivate:\n    std::vector<Voice*> Voices;\n    std::vector<DictEntry> ManDict;\n\n    std::vector<Phonemizer*> Phonemizers;\n    std::vector<ESpeakPhonemizer*> ENGPhonemizers;\n\n    Phonemizer* LoadPhonemizer(const QString& InPhnLang, int32_t InLangNum);\n    ESpeakPhonemizer* LoadESpeakPhonemizer(const QString& InVoiceName);\n\n\n\npublic:\n\n    // Load a voice and return index in vector\n    size_t LoadVoice(const QString& Voname);\n    // Find a voice in Voices\n    // Returns index in Voices vector, if not found returns -1\n    int FindVoice(const QString& inName, bool autoload = true);\n\n    Voice* operator[](size_t in);\n\n    inline std::vector<Voice*>& GetVoices(){return Voices;}\n\n    void SetDict(const std::vector<DictEntry>& InDict);\n\n\n    VoiceManager();\n    ~VoiceManager();\n};\n\n#endif // VOICEMANAGER_H\n"
  },
  {
    "path": "voxer.cpp",
    "content": "#include \"voxer.h\"\nusing namespace std::chrono;\n#include \"r8b/r8bsrc.h\"\n\nfloat remap(float OldValue, float OldMin, float OldMax, float NewMin, float NewMax ){\n\n    float NewValue = (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin;\n\n    return NewValue;\n\n}\n\nstd::vector<float> Resample(const std::vector<float>& InAudata,uint32_t SrcSampleRate,uint32_t OutSampleRate)\n{\n    if (SrcSampleRate == OutSampleRate)\n        return InAudata;\n\n    // Define the resampler\n\n    int32_t SampleCount = (int32_t)InAudata.size();\n\n\n    // 2.5 is a good middle-ground number for this parameter whose name I just forgot\n    CR8BResampler Resampler = r8b_create((double)SrcSampleRate,(double)OutSampleRate,SampleCount,2.5,ER8BResamplerRes::r8brr24);\n\n    double* OutBuff = nullptr;\n\n    std::vector<double> DBuff;\n    DBuff.resize(InAudata.size());\n\n    // Cast input buffer to double\n    for (size_t i = 0; i < InAudata.size();i++)\n        DBuff[i] = (double)InAudata[i];\n\n    int32_t NumSamples = r8b_process(Resampler,DBuff.data(),SampleCount,OutBuff);\n\n    // Create output buffer\n    std::vector<float> OutAud;\n    OutAud.resize((size_t)NumSamples);\n\n\n    // Re-cast to float\n    for (size_t i = 0; i < (size_t)NumSamples;i++)\n        OutAud[i] = (float)OutBuff[i];\n\n\n    // Cleanup\n    r8b_clear(Resampler);\n    r8b_delete(Resampler);\n\n\n    return OutAud;\n\n\n\n\n\n}\n\nstd::vector<float> DoDenoise(const std::vector<float>& InAudata,DenoiseState* DenState)\n{\n //   if (!DenState)\n   //     return InAudata;\n\n    std::vector<float> NewAudata(InAudata.size());\n    float buf[RNNoiseFrameSize];\n\n    // Find the min and max vals in the vector\n    float MinVal = -1.f;\n    float MaxVal = 1.f;\n\n    for (size_t f = 0; f < InAudata.size();f += RNNoiseFrameSize)\n    {\n        //RNNoise expects a float in range [-32768.f,32768.f]\n        for (size_t y = 0; y < RNNoiseFrameSize;y++)\n        {\n            size_t TotalIndex = f + y;\n\n            if (TotalIndex > InAudata.size())\n                break;\n\n            buf[y] = remap(InAudata[TotalIndex],MinVal,MaxVal,-32768.f,32768.f);\n\n        }\n\n\n        rnnoise_process_frame(DenState,buf,buf);\n\n        for (size_t x = 0; x < RNNoiseFrameSize;x++)\n        {\n            size_t TotalIndex = f + x;\n            if (TotalIndex > NewAudata.size())\n                break;\n\n            NewAudata[TotalIndex] = remap(buf[x],-32768.f,32768.f,-1.f,1.f);\n\n        }\n\n\n\n    }\n\n\n\n\n    // Due to post-normalization, the audio is about 2.1x louder. Apply makeup deamplification\n   // for (float& f : NewAudata)\n     //   f *= 0.4f;\n\n    return NewAudata;\n}\n\nvoid Voxer::run()\n{\n\n\n\n\n\n    pAttItem->setBackgroundColor(InProcessColor);\n\n\n    high_resolution_clock::time_point Start = high_resolution_clock::now();\n    std::vector<float> Audat;\n\n    VoxResults Res;\n\n    if (!ForcedAudio.size())\n    {\n        Res = pAttVoice->Vocalize(Prompt.toStdString(),Speed,SpeakerID,Energy,F0,EmotionID,EmotionOverride.toStdString());\n        Audat = Res.Audio;\n\n    }\n    else\n    {\n        Audat = ForcedAudio;\n\n    }\n\n\n    high_resolution_clock::time_point End = high_resolution_clock::now();\n\n\n    // Resample the audio to 48KHz\n    std::vector<float> AudRes = Resample(Audat,SampleRate,CommonSampleRate);\n\n\n\n    DenoiseState* Denoiser = nullptr;\n    if (Denoise)\n    {\n        // Every thread creates its own denoiser.\n        // This is because a generic passed denoiser created from the main window\n        // worked well for the first generation but later shat itself (heavy artifacts then just silence)\n\n        Denoiser = rnnoise_create(nullptr);\n        // Denoise. Function will return same vec if there is no denoiser\n        AudRes = DoDenoise(AudRes,Denoiser);\n\n\n\n\n    }\n\n    // Apply Amplification\n    for (float& f : AudRes)\n        f *= Amplify;\n\n\n\n     pAttItem->setBackgroundColor(DoneColor);\n\n\n    if (ForcedAudio.size())\n    {\n        Res.Mel.Shape.push_back(-1);\n        // see MakeInferDetails at batchdenoisedlg.cpp\n        AudRes = Resample(AudRes,CommonSampleRate,SpeakerID);\n\n\n    }\n\n\n\n\n\n\n    if (ExportFileName.size())\n    {\n        VoxUtil::ExportWAV(ExportFileName.toStdString(),AudRes,SpeakerID);\n        AudRes.clear();\n\n        CurrentID = UINT32_MAX;\n    }\n    emit Done(AudRes,Res.Mel,duration_cast<duration<double>>(End - Start),CurrentID);\n\n\n\n\n    if (Res.Alignment.Data.size() > 0)\n        emit AttentionReady(Res.Alignment,CurrentID);\n\n    // rnnoise_destroy throws some exception we can't do anything about\n    if (Denoise)\n    {\n        try {\n            rnnoise_destroy(Denoiser);\n\n        } catch (...) {\n\n        }\n\n    }\n\n}\n\nVoxer::Voxer()\n{\n\n}\n"
  },
  {
    "path": "voxer.h",
    "content": "#ifndef VOXER_H\n#define VOXER_H\n\n#include \"Voice.h\"\n#include <QThread>\n\n#include <QListWidgetItem>\n#include <chrono>\n#include \"rnnoise.h\"\n\nconst QColor DoneColor = QColor(0,128,0);\nconst QColor PlayingColor = QColor(168, 40, 94);\nconst QColor InProcessColor = QColor(0,0,255);\n\n// A Voxer is a thread spawned for the sole purpose of doing inference\nclass Voxer : public QThread\n{\n    Q_OBJECT\n\n    void run() override;\npublic:\n\n    Voice* pAttVoice;\n    QListWidgetItem* pAttItem;\n    QString Prompt;\n    float Speed;\n    float Energy;\n    float F0;\n    int32_t SpeakerID;\n    uint32_t SampleRate;\n    int32_t EmotionID;\n    bool Denoise;\n    QString EmotionOverride;\n\n    // DANGER: If this is set, the item will not emit anything\n    QString ExportFileName;\n\n    float Amplify;\n    Voxer();\n\n    uint32_t CurrentID;\n\n    std::vector<float> ForcedAudio;\n\n\n\nsignals:\n    void Done(std::vector<float> AudioData,TFTensor<float> Mel,std::chrono::duration<double> infer_span,uint32_t ID);\n    void AttentionReady(TFTensor<float> Att,uint32_t ID);\n\n};\n\n#endif // VOXER_H\n"
  }
]