/* SPDX-License-Identifier: BSD-3-Clause */
/* Copyright © 2019 Fragcolor Pte. Ltd. */

#include "blockwrapper.hpp"
#include "chainblocks.h"
#include "number_types.hpp"
#include "shared.hpp"
#include <boost/beast/core/detail/base64.hpp>
#include <type_traits>

namespace chainblocks {
struct FromImage {
  // TODO SIMD this
  template <CBType OF>
  void toSeq(std::vector<Var> &output, const CBVar &input) {
    if constexpr (OF == CBType::Float) {
      // assume we want 0-1 normalized values
      if (input.valueType != CBType::Image)
        throw ActivationError("Expected Image type.");

      auto pixsize = 1;
      if ((input.payload.imageValue.flags & CBIMAGE_FLAGS_16BITS_INT) ==
          CBIMAGE_FLAGS_16BITS_INT)
        pixsize = 2;
      else if ((input.payload.imageValue.flags & CBIMAGE_FLAGS_32BITS_FLOAT) ==
               CBIMAGE_FLAGS_32BITS_FLOAT)
        pixsize = 4;

      const int w = int(input.payload.imageValue.width);
      const int h = int(input.payload.imageValue.height);
      const int c = int(input.payload.imageValue.channels);
      const int flatsize = w * h * c * pixsize;

      output.resize(flatsize, Var(0.0));

      if (pixsize == 1) {
        for (int i = 0; i < flatsize; i++) {
          const auto fval = double(input.payload.imageValue.data[i]) / 255.0;
          output[i].payload.floatValue = fval;
        }
      } else if (pixsize == 2) {
        const auto u16 =
            reinterpret_cast<uint16_t *>(input.payload.imageValue.data);
        for (int i = 0; i < flatsize; i++) {
          const auto fval = double(u16[i]) / 65535.0;
          output[i].payload.floatValue = fval;
        }
      } else if (pixsize == 4) {
        const auto f32 =
            reinterpret_cast<float *>(input.payload.imageValue.data);
        for (int i = 0; i < flatsize; i++) {
          output[i].payload.floatValue = f32[i];
        }
      }
    } else {
      throw ActivationError("Conversion pair not implemented yet.");
    }
  }
};

struct FromSeq {
  template <CBType OF>
  void toImage(std::vector<uint8_t> &buffer, int w, int h, int c,
               const CBVar &input) {
    // TODO SIMD this
    if (input.payload.seqValue.len == 0)
      throw ActivationError("Input sequence was empty.");

    const int flatsize = std::min(w * h * c, int(input.payload.seqValue.len));
    buffer.resize(flatsize);

    if constexpr (OF == CBType::Float) {
      // assuming it's scaled 0-1
      for (int i = 0; i < flatsize; i++) {
        buffer[i] = uint8_t(
            input.payload.seqValue.elements[i].payload.floatValue * 255.0);
      }
    } else {
      throw ActivationError("Conversion pair not implemented yet.");
    }
  }

  template <CBType OF>
  void toBytes(std::vector<uint8_t> &buffer, const CBVar &input) {
    // TODO SIMD this
    if (input.payload.seqValue.len == 0)
      throw ActivationError("Input sequence was empty.");

    buffer.resize(size_t(input.payload.seqValue.len));

    if constexpr (OF == CBType::Int) {
      for (uint32_t i = 0; i < input.payload.seqValue.len; i++) {
        const auto val = input.payload.seqValue.elements[i].payload.intValue;
        if (val > UINT8_MAX || val < 0)
          throw ActivationError("Value out of byte range (0~255)");

        buffer[i] = uint8_t(val);
      }
    } else {
      throw ActivationError("Conversion pair not implemented yet.");
    }
  }
};

struct FromBytes {
  template <CBType OF>
  void toSeq(std::vector<Var> &output, const CBVar &input) {
    if constexpr (OF == CBType::Int) {
      // assume we want 0-1 normalized values
      if (input.valueType != CBType::Bytes)
        throw ActivationError("Expected Bytes type.");

      output.resize(input.payload.bytesSize);

      for (uint32_t i = 0; i < input.payload.bytesSize; i++) {
        const auto b = int64_t(input.payload.bytesValue[i]);
        output[i] = Var(b);
      }
    } else {
      throw ActivationError("Conversion pair not implemented yet.");
    }
  }
};

template <CBType CBTYPE, CBType CBOTHER> struct ToSeq {
  static inline Type _inputType{{CBTYPE}};
  static inline Type _outputElemType{{CBOTHER}};
  static inline Type _outputType{{CBType::Seq, {.seqTypes = _outputElemType}}};

  static CBTypesInfo inputTypes() { return _inputType; }
  static CBTypesInfo outputTypes() { return _outputType; }

  std::vector<Var> _output;

  CBVar activate(CBContext *context, const CBVar &input) {
    // do not .clear here, for speed, let From manage that
    if constexpr (CBTYPE == CBType::Image) {
      FromImage c;
      c.toSeq<CBOTHER>(_output, input);
    } else if constexpr (CBTYPE == CBType::Bytes) {
      FromBytes c;
      c.toSeq<CBOTHER>(_output, input);
    } else {
      throw ActivationError("Conversion pair not implemented yet.");
    }
    return Var(_output);
  }
};

template <CBType CBTYPE> struct ToString1 {
  static inline Type _inputType{{CBTYPE}};

  static CBTypesInfo inputTypes() { return _inputType; }
  static CBTypesInfo outputTypes() { return CoreInfo::StringType; }

  std::string _output;

  CBVar activate(CBContext *context, const CBVar &input) {
    // do not .clear here, for speed, let From manage that
    if constexpr (CBTYPE == CBType::Bytes) {
      _output.clear();
      _output.assign((const char *)input.payload.bytesValue,
                     size_t(input.payload.bytesSize));
    } else {
      throw ActivationError("Conversion pair not implemented yet.");
    }
    return Var(_output);
  }
};

template <CBType FROMTYPE> struct ToImage {
  static inline Type _inputElemType{{FROMTYPE}};
  static inline Type _inputType{{CBType::Seq, {.seqTypes = _inputElemType}}};

  static CBTypesInfo inputTypes() { return _inputType; }
  static CBTypesInfo outputTypes() { return CoreInfo::ImageType; }

  static inline Parameters _params{
      {"Width", CBCCSTR("The width of the output image."), {CoreInfo::IntType}},
      {"Height",
       CBCCSTR("The height of the output image."),
       {CoreInfo::IntType}},
      {"Channels",
       CBCCSTR("The channels of the output image."),
       {CoreInfo::IntType}}};

  static CBParametersInfo parameters() { return _params; }

  CBVar getParam(int index) {
    switch (index) {
    case 0:
      return Var(int(_width));
    case 1:
      return Var(int(_height));
    case 2:
      return Var(int(_channels));
    default:
      return {};
    }
  }

  void setParam(int index, const CBVar &value) {
    switch (index) {
    case 0:
      _width = uint16_t(value.payload.intValue);
      break;
    case 1:
      _height = uint16_t(value.payload.intValue);
      break;
    case 2:
      _channels = uint8_t(
          std::min(CBInt(4), std::max(CBInt(1), value.payload.intValue)));
      break;
    }
  }

  CBVar activate(CBContext *context, const CBVar &input) {
    FromSeq c;
    c.toImage<FROMTYPE>(_buffer, int(_width), int(_height), int(_channels),
                        input);
    return Var(&_buffer.front(), _width, _height, _channels, 0);
  }

private:
  uint8_t _channels = 1;
  uint16_t _width = 16;
  uint16_t _height = 16;
  std::vector<uint8_t> _buffer;
};

template <CBType FROMTYPE> struct ToBytes {
  static inline Type _inputElemType{{FROMTYPE}};
  static inline Type _inputType{{CBType::Seq, {.seqTypes = _inputElemType}}};

  static CBTypesInfo inputTypes() { return _inputType; }
  static CBTypesInfo outputTypes() { return CoreInfo::BytesType; }

  CBVar activate(CBContext *context, const CBVar &input) {
    FromSeq c;
    c.toBytes<FROMTYPE>(_buffer, input);
    return Var(_buffer);
  }

private:
  std::vector<uint8_t> _buffer;
};

template <CBType ToType> struct ToNumber {
  const VectorTypeTraits *_outputVectorType{nullptr};
  const NumberTypeTraits *_outputNumberType{nullptr};

  // If known at compose time these are set
  // otherwise each element should be evaluated during activate
  const VectorTypeTraits *_inputVectorType{nullptr};
  const NumberConversion *_numberConversion{nullptr};

  CBTypesInfo inputTypes() { return CoreInfo::AnyType; }
  CBTypesInfo outputTypes() {
    if (!_outputVectorType)
      return CoreInfo::AnyType;
    return _outputVectorType->type;
  }

  static const NumberTypeTraits *getEnumNumberType() {
    static const NumberTypeTraits *result = nullptr;
    if (!result) {
      result = NumberTypeLookup::getInstance().get(NumberType::Int32);
      cbassert(result);
    }
    return result;
  }

  const NumberTypeTraits *
  determineFixedSeqNumberType(const CBTypeInfo &typeInfo) {
    if (!typeInfo.fixedSize) {
      return nullptr;
    }

    const NumberTypeTraits *fixedElemNumberType = nullptr;
    for (size_t i = 0; i < typeInfo.seqTypes.len; i++) {
      const CBTypeInfo &elementType = typeInfo.seqTypes.elements[i];
      const NumberTypeTraits *currentElemNumberType =
          NumberTypeLookup::getInstance().get(elementType.basicType);
      if (!currentElemNumberType)
        return nullptr;
      if (i == 0) {
        fixedElemNumberType = currentElemNumberType;
      } else {
        if (currentElemNumberType != fixedElemNumberType) {
          return nullptr;
        }
      }
    }

    return fixedElemNumberType;
  }

  CBTypeInfo compose(const CBInstanceData &data) {
    _outputVectorType = VectorTypeLookup::getInstance().get(ToType);
    if (!_outputVectorType) {
      throw ComposeError("Conversion not implemented for this type");
    }

    _outputNumberType =
        NumberTypeLookup::getInstance().get(_outputVectorType->numberType);
    _inputVectorType =
        VectorTypeLookup::getInstance().get(data.inputType.basicType);

    if (_inputVectorType) {
      _numberConversion = NumberTypeLookup::getInstance().getConversion(
          _inputVectorType->numberType, _outputVectorType->numberType);
      cbassert(_numberConversion);
    } else if (data.inputType.basicType == Seq) {
      const NumberTypeTraits *fixedNumberType =
          determineFixedSeqNumberType(data.inputType);
      if (fixedNumberType) {
        _numberConversion =
            fixedNumberType->conversionTable.get(_outputNumberType->type);
        cbassert(_numberConversion);

        OVERRIDE_ACTIVATE(data, activateSeqElementsFixed);
      }
    }

    return _outputVectorType->type;
  }

  void parseVector(CBVar &output, const CBVar &input) {
    const VectorTypeTraits *inputVectorType =
        _inputVectorType ? _inputVectorType
                         : VectorTypeLookup::getInstance().get(input.valueType);
    if (!inputVectorType) {
      throw ActivationError("Cannot cast given input type.");
    }

    const NumberConversion *conversion =
        _numberConversion
            ? _numberConversion
            : NumberTypeLookup::getInstance().getConversion(
                  inputVectorType->numberType, _outputVectorType->numberType);
    cbassert(conversion);

    // Can reduce call overhead by adding a single ConvertMultiple function with
    // in/out lengths
    uint8_t *srcPtr = (uint8_t *)&input.payload;
    uint8_t *dstPtr = (uint8_t *)&output.payload;
    size_t numToConvert =
        std::min(_outputVectorType->dimension, inputVectorType->dimension);
    for (size_t i = 0; i < numToConvert; i++) {
      conversion->convertOne(srcPtr, dstPtr);
      srcPtr += conversion->inStride;
      dstPtr += conversion->outStride;
    }
  }

  void parseSeqElements(CBVar &output, const CBSeq &sequence) {
    uint8_t *dstPtr = (uint8_t *)&output.payload;
    for (size_t i = 0; i < sequence.len; i++) {
      const CBVar &elem = sequence.elements[i];

      const NumberTypeTraits *elemNumberType =
          NumberTypeLookup::getInstance().get(elem.valueType);
      if (!elemNumberType) {
        throw ActivationError("Cannot cast given sequence element type.");
      }

      const NumberConversion *conversion =
          elemNumberType->conversionTable.get(_outputVectorType->numberType);
      cbassert(conversion);

      conversion->convertOne(&elem.payload, dstPtr);
      dstPtr += conversion->outStride;
    }
  }

  void skipStringSeparators(const char *&strPtr) {
    while (*strPtr &&
           (std::isspace(strPtr[0]) || strPtr[0] == ',' || strPtr[0] == ';')) {
      ++strPtr;
    }
  }

  void parseStringElements(CBVar &output, const char *str, size_t length) {
    uint8_t *dstPtr = (uint8_t *)&output.payload;
    const char *strPtr = str;

    // Skip seq header
    while (*strPtr && (std::isspace(strPtr[0]) || strPtr[0] == '(' ||
                       strPtr[0] == '{' || strPtr[0] == '[')) {
      ++strPtr;
    }

    for (size_t i = 0; i < _outputVectorType->dimension; i++) {
      _outputNumberType->convertParse(dstPtr, strPtr, (char **)&strPtr);
      skipStringSeparators(strPtr);
      dstPtr += _outputNumberType->size;
    }
  }

  void parseEnumValue(CBVar &output, CBEnum value) {
    _numberConversion =
        getEnumNumberType()->conversionTable.get(_outputVectorType->numberType);
    cbassert(_numberConversion);

    _numberConversion->convertOne(&value, &output.payload);
  }

  CBVar activateSeqElementsFixed(CBContext *context, const CBVar &input) {
    cbassert(_outputVectorType);
    cbassert(_numberConversion);

    const CBSeq &sequence = input.payload.seqValue;
    CBVar output{};
    output.valueType = _outputVectorType->cbType;

    uint8_t *dstPtr = (uint8_t *)&output.payload;
    for (size_t i = 0; i < sequence.len; i++) {
      const CBVar &elem = sequence.elements[i];

      _numberConversion->convertOne(&elem.payload, dstPtr);
      dstPtr += _numberConversion->outStride;
    }

    return output;
  }

  CBVar activate(CBContext *context, const CBVar &input) {
    cbassert(_outputVectorType);

    CBVar output{};
    output.valueType = _outputVectorType->cbType;

    switch (input.valueType) {
    case Seq:
      parseSeqElements(output, input.payload.seqValue);
      break;
    case Enum:
      parseEnumValue(output, input.payload.enumValue);
      break;
    case String:
      parseStringElements(output, input.payload.stringValue,
                          input.payload.stringLen);
      break;
    default:
      parseVector(output, input);
      break;
    }

    return output;
  }
};

struct ToString {
  VarStringStream stream;
  static CBTypesInfo inputTypes() { return CoreInfo::AnyType; }
  static CBTypesInfo outputTypes() { return CoreInfo::StringType; }
  CBVar activate(CBContext *context, const CBVar &input) {
    stream.write(input);
    return Var(stream.str());
  }
};

RUNTIME_CORE_BLOCK(ToString);
RUNTIME_BLOCK_inputTypes(ToString);
RUNTIME_BLOCK_outputTypes(ToString);
RUNTIME_BLOCK_activate(ToString);
RUNTIME_BLOCK_END(ToString);

struct ToHex {
  VarStringStream stream;
  static inline Types toHexTypes{CoreInfo::IntType, CoreInfo::BytesType,
                                 CoreInfo::StringType};
  static CBTypesInfo inputTypes() { return toHexTypes; }
  static CBTypesInfo outputTypes() { return CoreInfo::StringType; }
  CBVar activate(CBContext *context, const CBVar &input) {
    stream.tryWriteHex(input);
    return Var(stream.str());
  }
};

RUNTIME_CORE_BLOCK(ToHex);
RUNTIME_BLOCK_inputTypes(ToHex);
RUNTIME_BLOCK_outputTypes(ToHex);
RUNTIME_BLOCK_activate(ToHex);
RUNTIME_BLOCK_END(ToHex);

struct VarAddr {
  VarStringStream stream;
  static CBTypesInfo inputTypes() { return CoreInfo::StringType; }
  static CBTypesInfo outputTypes() { return CoreInfo::IntType; }
  CBVar activate(CBContext *context, const CBVar &input) {
    auto v = referenceVariable(context, input.payload.stringValue);
    auto res = Var(reinterpret_cast<int64_t>(v));
    releaseVariable(v);
    return res;
  }
};

RUNTIME_CORE_BLOCK(VarAddr);
RUNTIME_BLOCK_inputTypes(VarAddr);
RUNTIME_BLOCK_outputTypes(VarAddr);
RUNTIME_BLOCK_activate(VarAddr);
RUNTIME_BLOCK_END(VarAddr);

struct BitSwap32 {
  static CBTypesInfo inputTypes() { return CoreInfo::IntType; }
  static CBTypesInfo outputTypes() { return CoreInfo::IntType; }
  CBVar activate(CBContext *context, const CBVar &input) {
    auto i32 = static_cast<uint32_t>(input.payload.intValue);
    i32 = __builtin_bswap32(i32);
    return Var(static_cast<int64_t>(i32));
  }
};

RUNTIME_CORE_BLOCK(BitSwap32);
RUNTIME_BLOCK_inputTypes(BitSwap32);
RUNTIME_BLOCK_outputTypes(BitSwap32);
RUNTIME_BLOCK_activate(BitSwap32);
RUNTIME_BLOCK_END(BitSwap32);

struct BitSwap64 {
  static CBTypesInfo inputTypes() { return CoreInfo::IntType; }
  static CBTypesInfo outputTypes() { return CoreInfo::IntType; }
  CBVar activate(CBContext *context, const CBVar &input) {
    auto i64 = static_cast<uint64_t>(input.payload.intValue);
    i64 = __builtin_bswap64(i64);
    return Var(static_cast<int64_t>(i64));
  }
};

RUNTIME_CORE_BLOCK(BitSwap64);
RUNTIME_BLOCK_inputTypes(BitSwap64);
RUNTIME_BLOCK_outputTypes(BitSwap64);
RUNTIME_BLOCK_activate(BitSwap64);
RUNTIME_BLOCK_END(BitSwap64);

template <CBType ET> struct ExpectX {
  static inline Type outputType{{ET}};
  CBTypesInfo inputTypes() { return CoreInfo::AnyType; }
  CBTypesInfo outputTypes() { return outputType; }
  CBVar activate(CBContext *context, const CBVar &input) {
    if (unlikely(input.valueType != ET)) {
      CBLOG_ERROR("Unexpected value: {}", input);
      throw ActivationError("Expected type " + type2Name(ET) + " got instead " +
                            type2Name(input.valueType));
    }
    return input;
  }
};

#define EXPECT_BLOCK(_name_, _cbtype_)                                         \
  struct Expect##_name_ : public ExpectX<_cbtype_> {};                         \
  RUNTIME_CORE_BLOCK(Expect##_name_);                                          \
  RUNTIME_BLOCK_inputTypes(Expect##_name_);                                    \
  RUNTIME_BLOCK_outputTypes(Expect##_name_);                                   \
  RUNTIME_BLOCK_activate(Expect##_name_);                                      \
  RUNTIME_BLOCK_END(Expect##_name_);

EXPECT_BLOCK(Float, Float);
EXPECT_BLOCK(Float2, Float2);
EXPECT_BLOCK(Float3, Float3);
EXPECT_BLOCK(Float4, Float4);
EXPECT_BLOCK(Bool, Bool);
EXPECT_BLOCK(Int, Int);
EXPECT_BLOCK(Int2, Int2);
EXPECT_BLOCK(Int3, Int3);
EXPECT_BLOCK(Int4, Int4);
EXPECT_BLOCK(Bytes, Bytes);
EXPECT_BLOCK(String, String);
EXPECT_BLOCK(Image, Image);
EXPECT_BLOCK(Seq, Seq);
EXPECT_BLOCK(Table, Table);
EXPECT_BLOCK(Chain, CBType::Chain);

template <Type &ET> struct ExpectXComplex {
  static inline Parameters params{
      {"Unsafe",
       CBCCSTR("If we should skip performing deep type hashing and comparison. "
               "(generally fast but this might improve performance)"),
       {CoreInfo::BoolType}}};
  static inline uint64_t ExpectedHash = deriveTypeHash(ET);

  bool _unsafe{false};

  static CBTypesInfo inputTypes() { return CoreInfo::AnyType; }
  static CBTypesInfo outputTypes() { return ET; }

  void setParam(int index, const CBVar &value) {
    _unsafe = value.payload.boolValue;
  }

  CBVar getParam(int index) { return Var(_unsafe); }

  CBVar activate(CBContext *context, const CBVar &input) {
    const static CBTypeInfo info = ET;
    if (unlikely(input.valueType != info.basicType)) {
      CBLOG_ERROR("Unexpected value: {} expected type: {}", input, info);
      throw ActivationError("Expected type " + type2Name(info.basicType) +
                            " got instead " + type2Name(input.valueType));
    } else if (!_unsafe) {
      auto inputTypeHash = deriveTypeHash(input);
      if (unlikely(inputTypeHash != ExpectedHash)) {
        CBLOG_ERROR("Unexpected value: {} expected type: {}", input, info);
        throw ActivationError("Expected type " + type2Name(info.basicType) +
                              " got instead " + type2Name(input.valueType));
      }
    }
    return input;
  }
};

struct ExpectLike {
  ParamVar _example{};
  CBTypeInfo _expectedType{};
  bool _dispose{false};
  uint64_t _outputTypeHash{0};
  bool _unsafe{false};
  bool _derived{false};

  static CBTypesInfo inputTypes() { return CoreInfo::AnyType; }
  static CBTypesInfo outputTypes() { return CoreInfo::AnyType; }

  static inline Parameters params{
      {"Example",
       CBCCSTR("The example value to expect, in the case of a used chain, the "
               "output type of that chain will be used."),
       {CoreInfo::AnyType}},
      {"Unsafe",
       CBCCSTR("If we should skip performing deep type hashing and comparison. "
               "(generally fast but this might improve performance)"),
       {CoreInfo::BoolType}}};
  static CBParametersInfo parameters() { return params; }

  void setParam(int index, const CBVar &value) {
    if (index == 0)
      _example = value;
    else if (index == 1)
      _unsafe = value.payload.boolValue;
  }

  CBVar getParam(int index) {
    if (index == 0)
      return _example;
    else
      return Var(_unsafe);
  }

  void destroy() {
    if (_expectedType.basicType != None && _dispose) {
      freeDerivedInfo(_expectedType);
      _expectedType = {};
      _dispose = false;
    }
  }

  CBTypeInfo compose(const CBInstanceData &data) {
    if (_example.isVariable()) {
      throw ComposeError(
          "The example value of ExpectLike cannot be a variable");
    } else {
      if (_expectedType.basicType != None && _dispose) {
        freeDerivedInfo(_expectedType);
        _expectedType = {};
        _dispose = false;
      }
      if (_example->valueType == CBType::Chain) {
        auto chain = CBChain::sharedFromRef(_example->payload.chainValue);
        _expectedType = chain->outputType;
        _outputTypeHash = deriveTypeHash(_expectedType);
        return _expectedType;
      } else {
        CBVar example = _example;
        _expectedType = deriveTypeInfo(example, data);
        _dispose = true;
        _outputTypeHash = deriveTypeHash(_expectedType);
        return _expectedType;
      }
    }
  }

  CBVar activate(CBContext *context, const CBVar &input) {
    if (unlikely(input.valueType != _expectedType.basicType)) {
      CBLOG_ERROR("Unexpected value: {} expected type: {}", input,
                  _expectedType);
      throw ActivationError("Unexpected input type");
    } else if (!_unsafe) {
      auto inputTypeHash = deriveTypeHash(input);
      if (unlikely(inputTypeHash != _outputTypeHash)) {
        CBLOG_ERROR("Unexpected value: {} expected type: {}", input,
                    _expectedType);
        throw ActivationError("Unexpected input type");
      }
    }
    return input;
  }
};

struct ToBase64 {
  std::string output;
  static inline Types _inputTypes{{CoreInfo::BytesType, CoreInfo::StringType}};
  static CBTypesInfo inputTypes() { return _inputTypes; }
  static CBTypesInfo outputTypes() { return CoreInfo::StringType; }
  CBVar activate(CBContext *context, const CBVar &input) {
    output.clear();
    if (input.valueType == Bytes) {
      auto req =
          boost::beast::detail::base64::encoded_size(input.payload.bytesSize);
      output.resize(req);
      auto written = boost::beast::detail::base64::encode(
          output.data(), input.payload.bytesValue, input.payload.bytesSize);
      output.resize(written);
    } else {
      const auto len =
          input.payload.stringLen > 0 || input.payload.stringValue == nullptr
              ? input.payload.stringLen
              : strlen(input.payload.stringValue);
      auto req = boost::beast::detail::base64::encoded_size(len);
      output.resize(req);
      auto written = boost::beast::detail::base64::encode(
          output.data(), input.payload.stringValue, len);
      output.resize(written);
    }
    return Var(output);
  }
};

struct FromBase64 {
  std::vector<uint8_t> output;
  static CBTypesInfo inputTypes() { return CoreInfo::StringType; }
  static CBTypesInfo outputTypes() { return CoreInfo::BytesType; }
  CBVar activate(CBContext *context, const CBVar &input) {
    output.clear();
    auto len =
        input.payload.stringLen > 0 || input.payload.stringValue == nullptr
            ? input.payload.stringLen
            : strlen(input.payload.stringValue);
    auto req = boost::beast::detail::base64::decoded_size(len);
    output.resize(req);
    auto [written, _] = boost::beast::detail::base64::decode(
        output.data(), input.payload.stringValue, len);
    output.resize(written);
    return Var(output.data(), output.size());
  }
};

struct HexToBytes {
  std::vector<uint8_t> output;
  static CBTypesInfo inputTypes() { return CoreInfo::StringType; }
  static CBTypesInfo outputTypes() { return CoreInfo::BytesType; }

  static int char2int(char input) {
    if (input >= '0' && input <= '9')
      return input - '0';
    if (input >= 'A' && input <= 'F')
      return input - 'A' + 10;
    if (input >= 'a' && input <= 'f')
      return input - 'a' + 10;
    throw std::invalid_argument("Invalid input string");
  }

  CBVar activate(CBContext *context, const CBVar &input) {
    output.clear();
    auto src = input.payload.stringValue;
    // allow 0x prefix
    if (src[0] == '0' && (src[1] == 'x' || src[1] == 'X'))
      src += 2;
    while (*src && src[1]) {
      output.emplace_back(char2int(*src) * 16 + char2int(src[1]));
      src += 2;
    }
    return Var(output.data(), output.size());
  }
};

struct StringToBytes {
  static CBTypesInfo inputTypes() { return CoreInfo::StringType; }
  static CBTypesInfo outputTypes() { return CoreInfo::BytesType; }

  CBVar activate(CBContext *context, const CBVar &input) {
    const auto len = CBSTRLEN(input);
    return Var((uint8_t *)input.payload.stringValue, uint32_t(len));
  }
};

void registerCastingBlocks() {
  REGISTER_CBLOCK("ToInt", ToNumber<Int>);
  REGISTER_CBLOCK("ToInt2", ToNumber<Int2>);
  REGISTER_CBLOCK("ToInt3", ToNumber<Int3>);
  REGISTER_CBLOCK("ToInt4", ToNumber<Int4>);
  REGISTER_CBLOCK("ToInt8", ToNumber<Int8>);
  REGISTER_CBLOCK("ToInt16", ToNumber<Int16>);
  REGISTER_CBLOCK("ToColor", ToNumber<Color>);
  REGISTER_CBLOCK("ToFloat", ToNumber<Float>);
  REGISTER_CBLOCK("ToFloat2", ToNumber<Float2>);
  REGISTER_CBLOCK("ToFloat3", ToNumber<Float3>);
  REGISTER_CBLOCK("ToFloat4", ToNumber<Float4>);

  REGISTER_CORE_BLOCK(ToString);
  REGISTER_CORE_BLOCK(ToHex);

  REGISTER_CORE_BLOCK(VarAddr);

  REGISTER_CORE_BLOCK(BitSwap32);
  REGISTER_CORE_BLOCK(BitSwap64);

  REGISTER_CORE_BLOCK(ExpectInt);
  REGISTER_CORE_BLOCK(ExpectInt2);
  REGISTER_CORE_BLOCK(ExpectInt3);
  REGISTER_CORE_BLOCK(ExpectInt4);
  REGISTER_CORE_BLOCK(ExpectFloat);
  REGISTER_CORE_BLOCK(ExpectFloat2);
  REGISTER_CORE_BLOCK(ExpectFloat3);
  REGISTER_CORE_BLOCK(ExpectFloat4);
  REGISTER_CORE_BLOCK(ExpectBytes);
  REGISTER_CORE_BLOCK(ExpectString);
  REGISTER_CORE_BLOCK(ExpectImage);
  REGISTER_CORE_BLOCK(ExpectBool);
  REGISTER_CORE_BLOCK(ExpectSeq);
  REGISTER_CORE_BLOCK(ExpectChain);
  REGISTER_CORE_BLOCK(ExpectTable);

  using ImageToFloats = ToSeq<CBType::Image, CBType::Float>;
  using FloatsToImage = ToImage<CBType::Float>;
  REGISTER_CBLOCK("ImageToFloats", ImageToFloats);
  REGISTER_CBLOCK("FloatsToImage", FloatsToImage);

  using BytesToInts = ToSeq<CBType::Bytes, CBType::Int>;
  using BytesToString = ToString1<CBType::Bytes>;
  using IntsToBytes = ToBytes<CBType::Int>;
  REGISTER_CBLOCK("BytesToInts", BytesToInts);
  REGISTER_CBLOCK("BytesToString", BytesToString);
  REGISTER_CBLOCK("IntsToBytes", IntsToBytes);
  REGISTER_CBLOCK("StringToBytes", StringToBytes);

  using ExpectFloatSeq = ExpectXComplex<CoreInfo::FloatSeqType>;
  REGISTER_CBLOCK("ExpectFloatSeq", ExpectFloatSeq);
  using ExpectIntSeq = ExpectXComplex<CoreInfo::IntSeqType>;
  REGISTER_CBLOCK("ExpectIntSeq", ExpectIntSeq);
  using ExpectBytesSeq = ExpectXComplex<CoreInfo::BytesSeqType>;
  REGISTER_CBLOCK("ExpectBytesSeq", ExpectBytesSeq);

  REGISTER_CBLOCK("ExpectLike", ExpectLike);

  REGISTER_CBLOCK("ToBase64", ToBase64);
  REGISTER_CBLOCK("FromBase64", FromBase64);
  REGISTER_CBLOCK("HexToBytes", HexToBytes);
}
}; // namespace chainblocks


/*
* The MIT License (MIT)
*
* Copyright (c) 2018 Sylko Olzscher
*
*/

#include <cyng/crypto/x509.h>
#include <openssl/crypto.h>	//	OPENSSL_cleanse
#include <boost/predef.h>
#include <iostream>
#include <stdio.h>

namespace cyng
{
	namespace crypto
	{
		EVP_PKEY * generate_key()
		{
			/* Allocate memory for the EVP_PKEY structure. */
			EVP_PKEY * pkey = EVP_PKEY_new();
			if (!pkey)
			{
				std::cerr << "Unable to create EVP_PKEY structure." << std::endl;
				return NULL;
			}
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL

            RSA  *rsa = RSA_new();
            if (rsa == NULL) {
                return NULL;
            }

            BIGNUM * e = BN_new();
            if (e == NULL) {
                RSA_free(rsa);
                return NULL;
            }

            if (BN_set_word(e, RSA_F4) == 0) {
                BN_free(e);
                RSA_free(rsa);
                return NULL;
            }

            if (RSA_generate_key_ex(rsa, 2048, e, NULL) == 0) {
                BN_free(e);
                RSA_free(rsa);
                return NULL;
            }

            BN_free(e);
#else
			/* Generate the RSA key and assign it to pkey. */
			RSA * rsa = RSA_generate_key(2048, RSA_F4, NULL, NULL);
#endif
			if (!EVP_PKEY_assign_RSA(pkey, rsa))
			{
				std::cerr << "Unable to generate 2048-bit RSA key." << std::endl;
				EVP_PKEY_free(pkey);
				return NULL;
			}

			/* The key has been generated, return it. */
			return pkey;
		}

		X509 * generate_x509(EVP_PKEY * pkey
			, long seconds
			, const char* pC
			, const char* pO
			, const char* pCN)
		{
			/* Allocate memory for the X509 structure. */
			X509 * x509 = X509_new();
			if (!x509)
			{
				std::cerr << "Unable to create X509 structure." << std::endl;
				return NULL;
			}

			/* Set the serial number. */
			ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);

			/* This certificate is valid from now until exactly one year from now. */
			X509_gmtime_adj(X509_get_notBefore(x509), 0);
			//X509_gmtime_adj(X509_get_notAfter(x509), 31536000L);	//	seconds
			X509_gmtime_adj(X509_get_notAfter(x509), seconds);	//	seconds

			/* Set the public key for our certificate. */
			X509_set_pubkey(x509, pkey);

			/* We want to copy the subject name to the issuer name. */
			X509_NAME * name = X509_get_subject_name(x509);

			/* Set the country code and common name. */
			//X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *)"CA", -1, -1, 0);
			//X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char *)"MyCompany", -1, -1, 0);
			//X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)"localhost", -1, -1, 0);
			::X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *)pC, -1, -1, 0);
			::X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char *)pO, -1, -1, 0);
			::X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)pCN, -1, -1, 0);

			/* Now set the issuer name. */
			X509_set_issuer_name(x509, name);

			/* Actually sign the certificate with our key. */
			if (!X509_sign(x509, pkey, EVP_sha1()))
			{
				std::cerr << "Error signing certificate." << std::endl;
				X509_free(x509);
				return NULL;
			}

			return x509;
		}

		bool write_to_disk(EVP_PKEY * pkey, X509 * x509)
		{
            //  open the PEM file for writing the key to disk
#if BOOST_OS_WINDOWS
			FILE * pkey_file = nullptr;
            auto err = fopen_s(&pkey_file, "key.pem", "wb");
			if (err != 0)
			{
				std::cerr << "Unable to open \"key.pem\" for writing." << std::endl;
				return false;
			}
#else
            FILE * pkey_file = fopen("key.pem", "wb");
            if (pkey_file == nullptr)
            {
                std::cerr << "Unable to open \"key.pem\" for writing." << std::endl;
                return false;
            }

#endif
            //  Write the key to disk
			bool ret = ::PEM_write_PrivateKey(pkey_file, pkey, NULL, NULL, 0, NULL, NULL);
			fclose(pkey_file);

			if (!ret)
			{
				std::cerr << "Unable to write private key to disk." << std::endl;
				return false;
			}

#if BOOST_OS_WINDOWS
            /* Open the PEM file for writing the certificate to disk. */
			FILE * x509_file;
			err = ::fopen_s(&x509_file, "cert.pem", "wb");
			if (err != 0)
			{
				std::cerr << "Unable to open \"cert.pem\" for writing." << std::endl;
				return false;
			}
#else
            //  open the PEM file for writing the certificate to disk
            FILE * x509_file = fopen("cert.pem", "wb");
            if (x509_file == nullptr)
            {
                std::cerr << "Unable to open \"cert.pem\" for writing." << std::endl;
                return false;
            }
#endif

            //  Write the certificate to disk
			ret = ::PEM_write_X509(x509_file, x509);
			fclose(x509_file);

			if (!ret)
			{
				std::cerr << "Unable to write certificate to disk." << std::endl;
				return false;
			}

			return true;
		}

	}
}


/**
 * @file
 * @copyright This code is licensed under the 3-clause BSD license.\n
 *            Copyright ETH Zurich, Laboratory of Physical Chemistry, Reiher Group.\n
 *            See LICENSE.txt for details.
 */

#include <Utils/Bonds/BondOrderCollection.h>
#include <Utils/DataStructures/MolecularOrbitals.h>
#include <pybind11/eigen.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <boost/optional.hpp>

using namespace Scine::Utils;

void init_molecular_orbitals(pybind11::module& m) {
  pybind11::class_<MolecularOrbitals> molecular_orbitals(m, "MolecularOrbitals");

  molecular_orbitals.def_property_readonly(
      "alpha_matrix", pybind11::overload_cast<>(&MolecularOrbitals::alphaMatrix, pybind11::const_),
      "Returns the coefficient matrix for electrons with alpha spin from an unrestricted calculation.");
  molecular_orbitals.def_property_readonly(
      "beta_matrix", pybind11::overload_cast<>(&MolecularOrbitals::betaMatrix, pybind11::const_),
      "Returns the coefficient matrix for electrons with beta spin from an unrestricted calculation.");
  molecular_orbitals.def_property_readonly(
      "restricted_matrix", pybind11::overload_cast<>(&MolecularOrbitals::restrictedMatrix, pybind11::const_),
      "Returns the coefficient matrix for all electrons from a restricted calculation.");
}


#pragma once

#include <eosio/chain/authority.hpp>
#include <eosio/chain/chain_config.hpp>
#include <eosio/chain/config.hpp>
#include <eosio/chain/types.hpp>

#include <boost/multiprecision/cpp_int.hpp>

namespace eosio { namespace chain { namespace contracts {

using namespace boost::multiprecision;

template<size_t Size>
using uint_t = number<cpp_int_backend<Size, Size, unsigned_magnitude, unchecked, void> >;
template<size_t Size>
using int_t = number<cpp_int_backend<Size, Size, signed_magnitude, unchecked, void> >;

using uint8     = uint_t<8>;
using uint16    = uint_t<16>;
using uint32    = uint_t<32>;
using uint64    = uint_t<64>;

using fixed_string32 = fc::fixed_string<fc::array<uint64,4>>;
using fixed_string16 = fc::fixed_string<>;
using type_name      = fixed_string32;
using field_name     = fixed_string16;
using table_name     = name;
using action_name    = eosio::chain::action_name;

struct type_def {
   type_def() = default;
   type_def(const type_name& new_type_name, const type_name& type)
   :new_type_name(new_type_name), type(type)
   {}

   type_name   new_type_name;
   type_name   type;
};

struct field_def {
   field_def() = default;
   field_def(const field_name& name, const type_name& type)
   :name(name), type(type)
   {}

   field_name name;
   type_name  type;

   bool operator==(const field_def& other) const {
      return std::tie(name, type) == std::tie(other.name, other.type);
   }
};

struct struct_def {
   struct_def() = default;
   struct_def(const type_name& name, const type_name& base, const vector<field_def>& fields)
   :name(name), base(base), fields(fields)
   {}

   type_name            name;
   type_name            base;
   vector<field_def>    fields;

   bool operator==(const struct_def& other) const {
      return std::tie(name, base, fields) == std::tie(other.name, other.base, other.fields);
   }
};

struct action_def {
   action_def() = default;
   action_def(const action_name& name, const type_name& type)
   :name(name), type(type)
   {}

   action_name name;
   type_name type;
};

struct table_def {
   table_def() = default;
   table_def(const table_name& name, const type_name& index_type, const vector<field_name>& key_names, const vector<type_name>& key_types, const type_name& type)
   :name(name), index_type(index_type), key_names(key_names), key_types(key_types), type(type)
   {}

   table_name         name;        // the name of the table
   type_name          index_type;  // the kind of index, i64, i128i128, etc
   vector<field_name> key_names;   // names for the keys defined by key_types
   vector<type_name>  key_types;   // the type of key parameters
   type_name          type;        // type of binary data stored in this table
};

struct abi_def {
   abi_def() = default;
   abi_def(const vector<type_def>& types, const vector<struct_def>& structs, const vector<action_def>& actions, const vector<table_def>& tables)
   :types(types), structs(structs), actions(actions), tables(tables)
   {}

   vector<type_def>     types;
   vector<struct_def>   structs;
   vector<action_def>   actions;
   vector<table_def>    tables;
};

struct newaccount {
   account_name                     creator;
   account_name                     name;
   authority                        owner;
   authority                        active;
   authority                        recovery;

   static account_name get_account() {
      return config::system_account_name;
   }

   static action_name get_name() {
      return N(newaccount);
   }
};

struct setcode {
   account_name                     account;
   uint8                            vmtype;
   uint8                            vmversion;
   bytes                            code;

   static account_name get_account() {
      return config::system_account_name;
   }

   static action_name get_name() {
      return N(setcode);
   }
};

struct setabi {
   account_name                     account;
   abi_def                          abi;

   static account_name get_account() {
      return config::system_account_name;
   }

   static action_name get_name() {
      return N(setabi);
   }
};

struct updateauth {
   account_name                      account;
   permission_name                   permission;
   permission_name                   parent;
   authority                         data;

   static account_name get_account() {
      return config::system_account_name;
   }

   static action_name get_name() {
      return N(updateauth);
   }
};

struct deleteauth {
   deleteauth() = default;
   deleteauth(const account_name& account, const permission_name& permission)
   :account(account), permission(permission)
   {}

   account_name                      account;
   permission_name                   permission;

   static account_name get_account() {
      return config::system_account_name;
   }

   static action_name get_name() {
      return N(deleteauth);
   }
};

struct linkauth {
   linkauth() = default;
   linkauth(const account_name& account, const account_name& code, const action_name& type, const permission_name& requirement)
   :account(account), code(code), type(type), requirement(requirement)
   {}

   account_name                      account;
   account_name                      code;
   action_name                       type;
   permission_name                   requirement;

   static account_name get_account() {
      return config::system_account_name;
   }

   static action_name get_name() {
      return N(linkauth);
   }
};

struct unlinkauth {
   unlinkauth() = default;
   unlinkauth(const account_name& account, const account_name& code, const action_name& type)
   :account(account), code(code), type(type)
   {}

   account_name                      account;
   account_name                      code;
   action_name                       type;

   static account_name get_account() {
      return config::system_account_name;
   }

   static action_name get_name() {
      return N(unlinkauth);
   }
};

struct onerror : bytes {
   using bytes::bytes;

   static account_name get_account() {
      return config::system_account_name;
   }

   static action_name get_name() {
      return N(onerror);
   }
};

struct postrecovery {
   account_name       account;
   authority          data;
   string             memo;

   static account_name get_account() {
      return config::system_account_name;
   }

   static action_name get_name() {
      return N(postrecovery);
   }
};

struct passrecovery {
   account_name   account;

   static account_name get_account() {
      return config::system_account_name;
   }

   static action_name get_name() {
      return N(passrecovery);
   }
};

struct vetorecovery {
   account_name   account;

   static account_name get_account() {
      return config::system_account_name;
   }

   static action_name get_name() {
      return N(vetorecovery);
   }
};

} } } /// namespace eosio::chain::contracts

FC_REFLECT( eosio::chain::contracts::type_def                         , (new_type_name)(type) )
FC_REFLECT( eosio::chain::contracts::field_def                        , (name)(type) )
FC_REFLECT( eosio::chain::contracts::struct_def                       , (name)(base)(fields) )
FC_REFLECT( eosio::chain::contracts::action_def                       , (name)(type) )
FC_REFLECT( eosio::chain::contracts::table_def                        , (name)(index_type)(key_names)(key_types)(type) )
FC_REFLECT( eosio::chain::contracts::abi_def                          , (types)(structs)(actions)(tables) )

FC_REFLECT( eosio::chain::contracts::newaccount                       , (creator)(name)(owner)(active)(recovery) )
FC_REFLECT( eosio::chain::contracts::setcode                          , (account)(vmtype)(vmversion)(code) ) //abi
FC_REFLECT( eosio::chain::contracts::setabi                           , (account)(abi) )
FC_REFLECT( eosio::chain::contracts::updateauth                       , (account)(permission)(parent)(data) )
FC_REFLECT( eosio::chain::contracts::deleteauth                       , (account)(permission) )
FC_REFLECT( eosio::chain::contracts::linkauth                         , (account)(code)(type)(requirement) )
FC_REFLECT( eosio::chain::contracts::unlinkauth                       , (account)(code)(type) )
FC_REFLECT( eosio::chain::contracts::postrecovery                     , (account)(data)(memo) )
FC_REFLECT( eosio::chain::contracts::passrecovery                     , (account) )
FC_REFLECT( eosio::chain::contracts::vetorecovery                     , (account) )


//==================================================================================================
/**
  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
**/
//==================================================================================================

#ifndef BOOST_SIMD_FUNCTION_SIMD_COSH_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_SIMD_COSH_HPP_INCLUDED

#include <boost/simd/function/scalar/cosh.hpp>
#include <boost/simd/arch/common/generic/function/autodispatcher.hpp>
#include <boost/simd/arch/common/simd/function/cosh.hpp>

#endif


#include <cctbx/boost_python/flex_fwd.h>

#include <boost/python/tuple.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/class.hpp>
#include <boost/python/args.hpp>
#include <boost/python/return_value_policy.hpp>
#include <boost/python/return_internal_reference.hpp>
#include <cctbx/crystal_orientation.h>

namespace cctbx { namespace boost_python { namespace {

  struct crystal_orientation_wrappers
  {
    typedef crystal_orientation w_t;

    static void
    wrap()
    {
      using namespace boost::python;
      class_<w_t>("crystal_orientation", init<w_t>())
        .enable_pickling()
        .def(init<cctbx::oc_mat3 const&, bool const&>())
        .def("unit_cell", &w_t::unit_cell)
        .def("unit_cell_inverse", &w_t::unit_cell_inverse)
        .def("direct_matrix", &w_t::direct_matrix)
        .def("reciprocal_matrix", &w_t::reciprocal_matrix)
        .def("change_basis",
          (w_t (w_t::*)(
            sgtbx::change_of_basis_op const&) const)
              &w_t::change_basis)
        .def("change_basis",
          (w_t (w_t::*)(oc_mat3 const&) const)
            &w_t::change_basis)
        .def("rotate_thru",
          (w_t (w_t::*)(oc_vec3 const&, double const&) const)
            &w_t::rotate_thru, (
              arg("unit_axis"), arg("angle")))
        .def("direct_mean_square_difference",
          &w_t::direct_mean_square_difference, (
            arg("other")))
        .def("difference_Z_score",
          &w_t::difference_Z_score, (
            arg("other")))
        .def("best_similarity_transformation",
          &w_t::best_similarity_transformation, (
            arg("other"),
            arg("fractional_length_tolerance"),
            arg("unimodular_generator_range")))
      ;
    }
  };

  void init_module()
  {
    crystal_orientation_wrappers::wrap();
  }

}}} // namespace cctbx::boost_python::orientation_ext

BOOST_PYTHON_MODULE(cctbx_orientation_ext)
{
  cctbx::boost_python::init_module();
}


#ifndef MBGL_TEXT_COLLISION
#define MBGL_TEXT_COLLISION

#include <mbgl/text/types.hpp>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#ifdef __clang__
#pragma GCC diagnostic ignored "-Wdeprecated-register"
#else
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/index/rtree.hpp>
#pragma GCC diagnostic pop

namespace mbgl {

namespace bg = boost::geometry;
namespace bgm = bg::model;
namespace bgi = bg::index;
typedef bgm::point<float, 2, bg::cs::cartesian> Point;
typedef bgm::box<Point> Box;
typedef std::pair<Box, PlacementBox> PlacementValue;
typedef bgi::rtree<PlacementValue, bgi::rstar<16>> Tree;

class Collision {

public:
    Collision(float zoom, float tileExtent, float tileSize, float placementDepth = 1);

    float getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale, bool avoidEdges);
    PlacementRange getPlacementRange(const GlyphBoxes &glyphs, float placementScale,
                                     bool horizontal);
    void insert(const GlyphBoxes &glyphs, const CollisionAnchor &anchor, float placementScale,
                const PlacementRange &placementRange, bool horizontal);

private:
    Tree hTree;
    Tree cTree;
    PlacementValue leftEdge;
    PlacementValue topEdge;
    PlacementValue rightEdge;
    PlacementValue bottomEdge;

public:
    const float tilePixelRatio;
    const float zoom;
    const float maxPlacementScale;
};
}

#endif


//Copyright 2019 CrazyOverdose

#ifndef  INCLUDE_HEADER_HPP_
#define  INCLUDE_HEADER_HPP_
#include "picosha2.hpp"
#include <string>
#include <iostream>
#include <thread>
#include <stdlib.h>
#include <random>
#include <ctime>
#include <boost/log/trivial.hpp>

class SHA256{
public:
    static void getting_a_hash() {
        for (;;) {
           const std::string end_hash = "0000";
            //случайные взодные данные
            std::mt19937 gen;
            gen.seed(time(0));
            std::string random = std::to_string(gen());

            //значение хеш-функции
            if (picosha2::hash256_hex_string(random).substr(60) == end_hash) {
                //сообщения журнала на консоли
                BOOST_LOG_TRIVIAL(info) << "Suitable SHA256(\"" << random
 << "\") = \"" << picosha2::hash256_hex_string(random) << "\";"
<< std::endl;} else {
                BOOST_LOG_TRIVIAL(info) << "Thread ID "
  << std::this_thread::get_id() << "  UNSuitable SHA256(\"" << random
 << ") = \"" << picosha2::hash256_hex_string(random) << ";" << std::endl;}
        }
    }
};
#endif // INCLUDE_HEADER_HPP_


#ifndef STAN_MATH_TORSTEN_PKMODELONECPT_HPP
#define STAN_MATH_TORSTEN_PKMODELONECPT_HPP

#include <Eigen/Dense>
#include <boost/math/tools/promotion.hpp>
#include <stan/math/torsten/PKModel/PKModel.hpp>
#include <stan/math/torsten/PKModel/Pred/Pred1_oneCpt.hpp>
#include <stan/math/torsten/PKModel/Pred/PredSS_oneCpt.hpp>
#include <string>
#include <vector>

namespace torsten {

/**
 * Computes the predicted amounts in each compartment at each event
 * for a one compartment model with first oder absorption.
 *
 * @tparam T0 type of scalar for time of events.
 * @tparam T1 type of scalar for amount at each event.
 * @tparam T2 type of scalar for rate at each event.
 * @tparam T3 type of scalar for inter-dose inteveral at each event.
 * @tparam T4 type of scalars for the model parameters.
 * @tparam T5 type of scalar for bio-variability F.
 * @tparam T6 type of scalar for lag times.
 * @param[in] pMatrix parameters at each event
 * @param[in] time times of events
 * @param[in] amt amount at each event
 * @param[in] rate rate at each event
 * @param[in] ii inter-dose interval at each event
 * @param[in] evid event identity:
 *                    (0) observation
 *                    (1) dosing
 *                    (2) other
 *                    (3) reset
 *                    (4) reset AND dosing
 * @param[in] cmt compartment number at each event
 * @param[in] addl additional dosing at each event
 * @param[in] ss steady state approximation at each event (0: no, 1: yes)
 * @return a matrix with predicted amount in each compartment
 *         at each event.
 */
template <typename T0, typename T1, typename T2, typename T3, typename T4,
          typename T5, typename T6>
Eigen::Matrix <typename boost::math::tools::promote_args<T0, T1, T2, T3,
  typename boost::math::tools::promote_args<T4, T5, T6>::type>::type,
  Eigen::Dynamic, Eigen::Dynamic>
PKModelOneCpt(const std::vector<T0>& time,
              const std::vector<T1>& amt,
              const std::vector<T2>& rate,
              const std::vector<T3>& ii,
              const std::vector<int>& evid,
              const std::vector<int>& cmt,
              const std::vector<int>& addl,
              const std::vector<int>& ss,
              const std::vector<std::vector<T4> >& pMatrix,
              const std::vector<std::vector<T5> >& biovar,
              const std::vector<std::vector<T6> >& tlag) {
  using std::vector;
  using Eigen::Dynamic;
  using Eigen::Matrix;
  using boost::math::tools::promote_args;
  using stan::math::check_positive_finite;

  int nCmt = 2;
  int nParm = 3;

  // Check arguments -- FIX ME: handle the new parameter arguments
  static const char* function("PKModelOneCpt");
  torsten::pmetricsCheck(time, amt, rate, ii, evid, cmt, addl, ss,
                pMatrix, biovar, tlag, function);
  for (size_t i = 0; i < pMatrix.size(); i++) {
    check_positive_finite(function, "PK parameter CL", pMatrix[i][0]);
    check_positive_finite(function, "PK parameter V2", pMatrix[i][1]);
  }

  // FIX ME - we want to check every array of pMatrix, not
  // just the first one (at index 0)
  std::string message4 = ", but must equal the number of parameters in the model: " // NOLINT
    + boost::lexical_cast<std::string>(nParm) + "!";
  const char* length_error4 = message4.c_str();
  if (!(pMatrix[0].size() == (size_t) nParm))
    stan::math::invalid_argument(function,
    "The number of parameters per event (length of a vector in the ninth argument) is", // NOLINT
    pMatrix[0].size(), "", length_error4);

  std::string message5 = ", but must equal the number of compartments in the model: " // NOLINT
    + boost::lexical_cast<std::string>(nCmt) + "!";
  const char* length_error5 = message5.c_str();
  if (!(biovar[0].size() == (size_t) nCmt))
    stan::math::invalid_argument(function,
    "The number of biovariability parameters per event (length of a vector in the tenth argument) is", // NOLINT
    biovar[0].size(), "", length_error5);

  if (!(tlag[0].size() == (size_t) nCmt))
    stan::math::invalid_argument(function,
                                 "The number of lag times parameters per event (length of a vector in the eleventh argument) is", // NOLINT
                                 tlag[0].size(), "", length_error5);

  // Construct dummy matrix for last argument of pred
  Eigen::Matrix<T4, Eigen::Dynamic, Eigen::Dynamic> dummy_system;
  std::vector<Eigen::Matrix<T4, Eigen::Dynamic, Eigen::Dynamic> >
    dummy_systems(1, dummy_system);

  return Pred(time, amt, rate, ii, evid, cmt, addl, ss,
              pMatrix, biovar, tlag,
              nCmt, dummy_systems,
              Pred1_oneCpt(), PredSS_oneCpt());
}

/**
 * Overload function to allow user to pass an std::vector for pMatrix.
 */
template <typename T0, typename T1, typename T2, typename T3, typename T4,
          typename T5, typename T6>
Eigen::Matrix <typename boost::math::tools::promote_args<T0, T1, T2, T3,
  typename boost::math::tools::promote_args<T4, T5, T6>::type>::type,
  Eigen::Dynamic, Eigen::Dynamic>
PKModelOneCpt(const std::vector<T0>& time,
              const std::vector<T1>& amt,
              const std::vector<T2>& rate,
              const std::vector<T3>& ii,
              const std::vector<int>& evid,
              const std::vector<int>& cmt,
              const std::vector<int>& addl,
              const std::vector<int>& ss,
              const std::vector<T4>& pMatrix,
              const std::vector<std::vector<T5> >& biovar,
              const std::vector<std::vector<T6> >& tlag) {
  std::vector<std::vector<T4> > vec_pMatrix(1, pMatrix);

  return PKModelOneCpt(time, amt, rate, ii, evid, cmt, addl, ss,
                       vec_pMatrix, biovar, tlag);
}

/**
 * Overload function to allow user to pass an std::vector for pMatrix,
 * and biovar.
 */
template <typename T0, typename T1, typename T2, typename T3, typename T4,
          typename T5, typename T6>
Eigen::Matrix <typename boost::math::tools::promote_args<T0, T1, T2, T3,
  typename boost::math::tools::promote_args<T4, T5, T6>::type>::type,
  Eigen::Dynamic, Eigen::Dynamic>
PKModelOneCpt(const std::vector<T0>& time,
              const std::vector<T1>& amt,
              const std::vector<T2>& rate,
              const std::vector<T3>& ii,
              const std::vector<int>& evid,
              const std::vector<int>& cmt,
              const std::vector<int>& addl,
              const std::vector<int>& ss,
              const std::vector<T4>& pMatrix,
              const std::vector<T5>& biovar,
              const std::vector<std::vector<T6> >& tlag) {
  std::vector<std::vector<T4> > vec_pMatrix(1, pMatrix);
  std::vector<std::vector<T5> > vec_biovar(1, biovar);

  return PKModelOneCpt(time, amt, rate, ii, evid, cmt, addl, ss,
                       vec_pMatrix, vec_biovar, tlag);
}

/**
 * Overload function to allow user to pass an std::vector for pMatrix,
 * and tlag.
 */
template <typename T0, typename T1, typename T2, typename T3, typename T4,
          typename T5, typename T6>
Eigen::Matrix <typename boost::math::tools::promote_args<T0, T1, T2, T3,
  typename boost::math::tools::promote_args<T4, T5, T6>::type>::type,
  Eigen::Dynamic, Eigen::Dynamic>
PKModelOneCpt(const std::vector<T0>& time,
              const std::vector<T1>& amt,
              const std::vector<T2>& rate,
              const std::vector<T3>& ii,
              const std::vector<int>& evid,
              const std::vector<int>& cmt,
              const std::vector<int>& addl,
              const std::vector<int>& ss,
              const std::vector<T4>& pMatrix,
              const std::vector<std::vector<T5> >& biovar,
              const std::vector<T6>& tlag) {
  std::vector<std::vector<T4> > vec_pMatrix(1, pMatrix);
  std::vector<std::vector<T6> > vec_tlag(1, tlag);

  return PKModelOneCpt(time, amt, rate, ii, evid, cmt, addl, ss,
                       vec_pMatrix, biovar, vec_tlag);
}

/**
 * Overload function to allow user to pass an std::vector for pMatrix,
 * biovar, and tlag.
 */
template <typename T0, typename T1, typename T2, typename T3, typename T4,
          typename T5, typename T6>
Eigen::Matrix <typename boost::math::tools::promote_args<T0, T1, T2, T3,
  typename boost::math::tools::promote_args<T4, T5, T6>::type>::type,
  Eigen::Dynamic, Eigen::Dynamic>
PKModelOneCpt(const std::vector<T0>& time,
              const std::vector<T1>& amt,
              const std::vector<T2>& rate,
              const std::vector<T3>& ii,
              const std::vector<int>& evid,
              const std::vector<int>& cmt,
              const std::vector<int>& addl,
              const std::vector<int>& ss,
              const std::vector<T4>& pMatrix,
              const std::vector<T5>& biovar,
              const std::vector<T6>& tlag) {
  std::vector<std::vector<T4> > vec_pMatrix(1, pMatrix);
  std::vector<std::vector<T5> > vec_biovar(1, biovar);
  std::vector<std::vector<T6> > vec_tlag(1, tlag);

  return PKModelOneCpt(time, amt, rate, ii, evid, cmt, addl, ss,
                       vec_pMatrix, vec_biovar, vec_tlag);
}

/**
 * Overload function to allow user to pass an std::vector for biovar.
 */
template <typename T0, typename T1, typename T2, typename T3, typename T4,
          typename T5, typename T6>
Eigen::Matrix <typename boost::math::tools::promote_args<T0, T1, T2, T3,
  typename boost::math::tools::promote_args<T4, T5, T6>::type>::type,
  Eigen::Dynamic, Eigen::Dynamic>
PKModelOneCpt(const std::vector<T0>& time,
              const std::vector<T1>& amt,
              const std::vector<T2>& rate,
              const std::vector<T3>& ii,
              const std::vector<int>& evid,
              const std::vector<int>& cmt,
              const std::vector<int>& addl,
              const std::vector<int>& ss,
              const std::vector<std::vector<T4> >& pMatrix,
              const std::vector<T5>& biovar,
              const std::vector<std::vector<T6> >& tlag) {
  std::vector<std::vector<T5> > vec_biovar(1, biovar);

  return PKModelOneCpt(time, amt, rate, ii, evid, cmt, addl, ss,
                       pMatrix, vec_biovar, tlag);
}

/**
 * Overload function to allow user to pass an std::vector for biovar,
 * and tlag.
 */
template <typename T0, typename T1, typename T2, typename T3, typename T4,
          typename T5, typename T6>
Eigen::Matrix <typename boost::math::tools::promote_args<T0, T1, T2, T3,
  typename boost::math::tools::promote_args<T4, T5, T6>::type>::type,
  Eigen::Dynamic, Eigen::Dynamic>
PKModelOneCpt(const std::vector<T0>& time,
              const std::vector<T1>& amt,
              const std::vector<T2>& rate,
              const std::vector<T3>& ii,
              const std::vector<int>& evid,
              const std::vector<int>& cmt,
              const std::vector<int>& addl,
              const std::vector<int>& ss,
              const std::vector<std::vector<T4> >& pMatrix,
              const std::vector<T5>& biovar,
              const std::vector<T6>& tlag) {
  std::vector<std::vector<T5> > vec_biovar(1, biovar);
  std::vector<std::vector<T6> > vec_tlag(1, tlag);

  return PKModelOneCpt(time, amt, rate, ii, evid, cmt, addl, ss,
                       pMatrix, vec_biovar, vec_tlag);
}

/**
 * Overload function to allow user to pass an std::vector for tlag.
 */
template <typename T0, typename T1, typename T2, typename T3, typename T4,
          typename T5, typename T6>
Eigen::Matrix <typename boost::math::tools::promote_args<T0, T1, T2, T3,
  typename boost::math::tools::promote_args<T4, T5, T6>::type>::type,
  Eigen::Dynamic, Eigen::Dynamic>
PKModelOneCpt(const std::vector<T0>& time,
              const std::vector<T1>& amt,
              const std::vector<T2>& rate,
              const std::vector<T3>& ii,
              const std::vector<int>& evid,
              const std::vector<int>& cmt,
              const std::vector<int>& addl,
              const std::vector<int>& ss,
              const std::vector<std::vector<T4> >& pMatrix,
              const std::vector<std::vector<T5> >& biovar,
              const std::vector<T6>& tlag) {
  std::vector<std::vector<T6> > vec_tlag(1, tlag);

  return PKModelOneCpt(time, amt, rate, ii, evid, cmt, addl, ss,
                       pMatrix, biovar, vec_tlag);
}

}
#endif


#include "builders/terrain/TerraExtras.hpp"
#include "entities/Area.hpp"

#include <boost/test/unit_test.hpp>
#include "test_utils/DependencyProvider.hpp"
#include "test_utils/ElementUtils.hpp"

using namespace utymap;
using namespace utymap::builders;
using namespace utymap::entities;
using namespace utymap::mapcss;
using namespace utymap::math;
using namespace utymap::lsys;
using namespace utymap::tests;

namespace {
const ColorGradient gradient = ColorGradient();
const TextureRegion textureRegion = TextureRegion();
const std::string stylesheetStr = "area|z16[amenity=forest] {"
    "lsys: tree;"
    "lsys-size: 1m;"
    "lsys-colors: gray,yellow;"
    "lsys-texture-indices: 0,0;"
    "lsys-texture-types: background,grass;"
    "lsys-texture-scales: 50,200;"
    "}";

StyleSheet createStyleSheet() {
  auto stylesheet = utymap::mapcss::MapCssParser().parse(stylesheetStr);
  LSystem lsystem;
  lsystem.axiom.push_back(std::make_shared<MoveForwardRule>());
  stylesheet.lsystems.emplace("tree", lsystem);
  return stylesheet;
}

struct Builders_Terrain_TerraExtrasFixture {
  Builders_Terrain_TerraExtrasFixture() :
      quadKey(16, 35203, 21493),
      dependencyProvider(),
      builderContext(
          quadKey,
          *dependencyProvider.getStyleProvider(createStyleSheet()),
          *dependencyProvider.getStringTable(),
          *dependencyProvider.getElevationProvider(),
          std::bind(&Builders_Terrain_TerraExtrasFixture::verifyMesh, this, std::placeholders::_1),
          nullptr,
          dependencyProvider.getCancellationToken()),
      isVerified(false) {
  }

  std::shared_ptr<Mesh> generateMesh() {
    auto mesh = std::make_shared<Mesh>("area");
    Polygon polygon(4, 0);
    polygon.addContour(std::vector<Vector2> {{0, 0}, {10, 0}, {10, 10}, {0, 10}});
    MeshBuilder builder(quadKey, *dependencyProvider.getElevationProvider());
    builder.addPolygon(*mesh, polygon,
                       MeshBuilder::GeometryOptions(5, 0, 0, 0),
                       MeshBuilder::AppearanceOptions(gradient, 0, 0, textureRegion, 0));
    return mesh;
  }

  Style generateStyle() {
    Area area = ElementUtils::createElement<Area>(*dependencyProvider.getStringTable(), 0,
                                                  {{"amenity", "forest"}},
                                                  {{0, 0}, {10, 0}, {10, 10}, {0, 10}});
    return dependencyProvider.getStyleProvider(createStyleSheet())->forElement(area, 16);
  }

  void verifyMesh(const Mesh &mesh) {
    BOOST_CHECK_GT(mesh.vertices.size(), 0);
    BOOST_CHECK_GT(mesh.triangles.size(), 0);
    BOOST_CHECK_GT(mesh.colors.size(), 0);

    BOOST_CHECK_EQUAL(*std::max_element(mesh.triangles.begin(), mesh.triangles.end()),
                      static_cast<int>(mesh.vertices.size() - 1)/3);
    BOOST_CHECK_EQUAL(mesh.vertices.size()/3, mesh.colors.size());

    isVerified = true;
  }

  QuadKey quadKey;
  DependencyProvider dependencyProvider;
  BuilderContext builderContext;
  bool isVerified;
};
}

BOOST_FIXTURE_TEST_SUITE(Builders_Terrain_TerraExtras, Builders_Terrain_TerraExtrasFixture)

BOOST_AUTO_TEST_CASE(GivenMesh_WhenAddForest_ThenTreesAreAdded) {
  auto mesh = generateMesh();
  auto style = generateStyle();
  TerraExtras::Context extrasContext(*mesh, style);
  extrasContext.startVertex = 0, extrasContext.startTriangle = 0, extrasContext.startColor = 0;

  TerraExtras::addForest(builderContext, extrasContext);

  BOOST_CHECK(isVerified);
}

BOOST_AUTO_TEST_SUITE_END()