text:
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*                                                                           */
/*  This file is part of the library KASKADE 7                               */
/*    see http://www.zib.de/Numerik/numsoft/kaskade7/                        */
/*                                                                           */
/*  Copyright (C) 2002-2015 Zuse Institute Berlin                            */
/*                                                                           */
/*  KASKADE 7 is distributed under the terms of the ZIB Academic License.    */
/*    see $KASKADE/academic.txt                                              */
/*                                                                           */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#ifndef ERROR_DISTRIBUTION_HH
#define ERROR_DISTRIBUTION_HH

#include <string>

#include <boost/utility.hpp>
#include <boost/fusion/include/as_vector.hpp>

#include "fem/assemble.hh"
#include "fem/functionspace.hh"
#include "fem/lagrangespace.hh"
#include "fem/shapefunctioncache.hh"

// forward declarations
namespace Dune
{
  template <class,int> class FieldVector;
  template <class,int,int> class FieldMatrix;
  template <class, int> class QuadratureRule;
}

namespace Kaskade
{
  using namespace boost::fusion;
  using namespace AssemblyDetail;

  enum class ErrorNorm { Energy, L2, H1, H1_half };

  template <class OriginalEvaluators, class ExtensionEvaluators, class Functional,
            class ArgYH, class ArgYE, class ArgUH, class ArgUE, class ArgPH, class ArgPE>
  void evaluateData(OriginalEvaluators& originalEvaluators, ExtensionEvaluators& extendedEvaluators, Functional const& f,
                    ArgYH& yl, ArgYE& yh, ArgUH& ul, ArgUE& uh, ArgPH& pl, ArgPE& ph, double w)
  {
      if( f.considerControlVariable_ )
      {
        uh.value += w * at_c<Functional::uIdx>(f.errorEstimateH.data).value(at_c<Functional::uSHIdx>(extendedEvaluators));
        ul.value += w * at_c<Functional::uIdx>(f.errorEstimateL.data).value(at_c<Functional::uSLIdx>(originalEvaluators));
        uh.gradient += w * at_c<Functional::uIdx>(f.errorEstimateH.data).gradient(at_c<Functional::uSHIdx>(extendedEvaluators));
        ul.gradient += w * at_c<Functional::uIdx>(f.errorEstimateL.data).gradient(at_c<Functional::uSLIdx>(originalEvaluators));
      }
      if( f.considerStateVariable_ )
      {
        yh.value += w * at_c<Functional::yIdx>(f.errorEstimateH.data).value(at_c<Functional::ySHIdx>(extendedEvaluators));
        yl.value += w * at_c<Functional::yIdx>(f.errorEstimateL.data).value(at_c<Functional::ySLIdx>(originalEvaluators));
        yh.gradient += w * at_c<Functional::yIdx>(f.errorEstimateH.data).gradient(at_c<Functional::ySHIdx>(extendedEvaluators));
        yl.gradient += w * at_c<Functional::yIdx>(f.errorEstimateL.data).gradient(at_c<Functional::ySLIdx>(originalEvaluators));
      }
      if( f.considerAdjointVariable_ )
      {
        ph.value += w * at_c<Functional::pIdx>(f.errorEstimateH.data).value(at_c<Functional::pSHIdx>(extendedEvaluators));
        pl.value += w * at_c<Functional::pIdx>(f.errorEstimateL.data).value(at_c<Functional::pSLIdx>(originalEvaluators));
        ph.gradient += w * at_c<Functional::pIdx>(f.errorEstimateH.data).gradient(at_c<Functional::pSHIdx>(extendedEvaluators));
        pl.gradient += w * at_c<Functional::pIdx>(f.errorEstimateL.data).gradient(at_c<Functional::pSLIdx>(extendedEvaluators));
      }
  }

  template <class ArgYL, class ArgYH, class ArgUL, class ArgUH, class ArgPL, class ArgPH>
  void clearVarArgs(ArgYL& yl, ArgYH& yh, ArgUL& ul, ArgUH& uh, ArgPL& pl, ArgPH& ph)
  {
    ul.value = 0;  ul.gradient = 0;
    yl.value = 0;  yl.gradient = 0;
    pl.value = 0;  pl.gradient = 0;
    uh.value = 0;  uh.gradient = 0;
    yh.value = 0;  yh.gradient = 0;
    ph.value = 0;  ph.gradient = 0;
  }

  template <class ArgU, class ArgY, class ArgP>
  double computeL2Error(ArgU const& u, ArgY const& y, ArgP const& p)
  {
    return u.value*u.value + y.value*y.value + p.value*p.value;
  }

  template <class ArgU, class ArgY, class ArgP>
  double computeH1HalfError(ArgU const& u, ArgY const& y, ArgP const& p)
  {
    LinAlg::EuclideanScalarProduct sp;
    return sp(u.gradient,u.gradient) + sp(y.gradient,y.gradient) + sp(p.gradient,p.gradient);
  }

  template <class Functional, class ExtendedAnsatzVars >
  class ErrorDistribution
  {
    typedef typename Functional::AnsatzVars OriginalAnsatzVars;
    typedef typename OriginalAnsatzVars::Grid Grid;
    typedef ShapeFunctionCache<Grid, typename Functional::Scalar> SfCache;
    typedef ShapeFunctionCache<Grid, typename Functional::Scalar> SfCache2;
    typedef typename OriginalAnsatzVars::Spaces OriginalSpaces;
    typedef typename ExtendedAnsatzVars::Spaces ExtendedSpaces;
    typedef typename result_of::as_vector<typename result_of::transform<OriginalSpaces, GetEvaluators<SfCache> >::type>::type OriginalEvaluators;
    typedef typename result_of::as_vector<typename result_of::transform<ExtendedSpaces, GetEvaluators<SfCache2> >::type>::type ExtendedEvaluators;
    typedef typename Grid::ctype CoordType;
    typedef Dune::QuadratureRule<typename Functional::AnsatzVars::Grid::ctype, Functional::AnsatzVars::Grid::dimension> QuadRule;
    typedef Dune::QuadratureRule<typename Functional::AnsatzVars::Grid::ctype, Functional::AnsatzVars::Grid::dimension-1> FaceQuadRule;
  public:
    typedef typename Functional::Scalar Scalar;
    typedef FEFunctionSpace<DiscontinuousLagrangeMapper<Scalar,typename Grid::LeafGridView> > AnsatzSpace;
    typedef boost::fusion::vector<AnsatzSpace const*> AnsatzSpaces;
    typedef Variable<SpaceIndex<0>,Components<1>,VariableId<0> > AnsatzVariableInformation;
    typedef boost::fusion::vector<AnsatzVariableInformation> VariableDescriptions;
    typedef VariableSetDescription<AnsatzSpaces,VariableDescriptions> AnsatzVars;
    typedef typename AnsatzVars::template CoefficientVectorRepresentation<0,1>::type ErrorVector;
    typedef AnsatzVars TestVars;
    typedef OriginalAnsatzVars OriginVars;
    //typedef AnsatzVars OriginVars;
    static int const dim = Grid::dimension;
    static ProblemType const type = Functional::type;

    static constexpr int yIdx = getStateId<Functional>();
    static constexpr int uIdx = getControlId<Functional>();
    static constexpr int pIdx = getAdjointId<Functional>();
    static constexpr int uSLIdx = result_of::value_at_c<typename OriginalAnsatzVars::Variables, uIdx>::type::spaceIndex;
    static constexpr int ySLIdx = result_of::value_at_c<typename OriginalAnsatzVars::Variables, yIdx>::type::spaceIndex;
    static constexpr int pSLIdx = result_of::value_at_c<typename OriginalAnsatzVars::Variables, pIdx>::type::spaceIndex;
    static constexpr int uSHIdx = result_of::value_at_c<typename ExtendedAnsatzVars::Variables, uIdx>::type::spaceIndex;
    static constexpr int ySHIdx = result_of::value_at_c<typename ExtendedAnsatzVars::Variables, yIdx>::type::spaceIndex;
    static constexpr int pSHIdx = result_of::value_at_c<typename ExtendedAnsatzVars::Variables, pIdx>::type::spaceIndex;

    class DomainCache
    {
    public:
      DomainCache(ErrorDistribution const& f_, typename OriginVars::VariableSet const& /*vars*/, int flags=7)
        : f(f_), domainCache(f.functional,f.iterate,flags)
      {
      }

      template <class Entity>
      void moveTo(Entity const &entity)
      {
        e  = &entity;
        domainCache.moveTo(entity);
      }

      template <class Position, class Evaluators>
      void evaluateAt(Position const&, Evaluators const& evaluators)
      {
        clearVarArgs(yl,yh,ul,uh,pl,ph);
        OriginalEvaluators originalEvaluators(transform(f.iterate.descriptions.spaces,GetEvaluators<SfCache>(&sfCache)));
        ExtendedEvaluators extendedEvaluators(transform(f.errorEstimateH.descriptions.spaces,GetEvaluators<SfCache>(&extendedSFCache)));
        QuadRule qr = QuadratureTraits<QuadRule>().rule(e->type(),f.qOrder);
        moveEvaluatorsToCell(originalEvaluators,*e);
        moveEvaluatorsToCell(extendedEvaluators,*e);
        useQuadratureRuleInEvaluators(originalEvaluators,qr,0);
        useQuadratureRuleInEvaluators(extendedEvaluators,qr,0);

        size_t nQuadPos = qr.size();
        for (size_t g=0; g<nQuadPos; ++g)
        {
          // pos of integration point
          Dune::FieldVector<CoordType,dim> quadPos = qr[g].position();
          // for all spaces involved, update the evaluators associated
          // to this quadrature point
          moveEvaluatorsToIntegrationPoint(originalEvaluators,quadPos);
          moveEvaluatorsToIntegrationPoint(extendedEvaluators,quadPos);
          // prepare evaluation of functional
          domainCache.evaluateAt(qr[g].position(),originalEvaluators);

          evaluateData(originalEvaluators, extendedEvaluators, f, yl, yh, ul, uh, pl, ph, qr[g].weight());
        }
      }

      Scalar d0() const { return 0; }

      template<int row, int dim>
      Dune::FieldVector<Scalar, TestVars::template Components<row>::m>
      d1 (VariationalArg<Scalar,dim> const& arg) const
      {
        VariationalArg<Scalar,dim,OriginalAnsatzVars::template Components<yIdx>::m> y(yh);
        VariationalArg<Scalar,dim,OriginalAnsatzVars::template Components<uIdx>::m> u(uh);
        VariationalArg<Scalar,dim,OriginalAnsatzVars::template Components<pIdx>::m> p(ph);

        if( !f.onlyH_ )
        {
          u.value += ul.value;
          u.gradient += ul.gradient;
          y.value += yl.value;
          y.gradient += yl.gradient;
          p.value += pl.value;
          p.gradient += pl.gradient;
        }

        if( f.errorNorm == ErrorNorm::L2 ) return computeL2Error(u,y,p);
        if( f.errorNorm == ErrorNorm::H1_half ) return computeH1HalfError(u,y,p);
        if( f.errorNorm == ErrorNorm::H1 ) return computeL2Error(u,y,p) + computeH1HalfError(u,y,p);

        Scalar result = 0;
        result += Functional::template D2<yIdx,yIdx>::present ? domainCache.template d2_impl<yIdx,yIdx>(y,y) : 0.;
        result += Functional::template D2<yIdx,uIdx>::present ? domainCache.template d2_impl<yIdx,uIdx>(y,u) : 0.;
        result += Functional::template D2<uIdx,yIdx>::present ? domainCache.template d2_impl<uIdx,yIdx>(u,y) : 0.;
        result += Functional::template D2<uIdx,uIdx>::present ? domainCache.template d2_impl<uIdx,uIdx>(u,u) : 0.;
        if( !f.useStateNormForAdjoint_ )
        {
          result += Functional::template D2<yIdx,pIdx>::present ? domainCache.template d2_impl<yIdx,pIdx>(y,p) : 0.;
          result += Functional::template D2<pIdx,yIdx>::present ? domainCache.template d2_impl<pIdx,yIdx>(p,y) : 0.;
          result += Functional::template D2<pIdx,uIdx>::present ? domainCache.template d2_impl<pIdx,uIdx>(p,u) : 0.;
          result += Functional::template D2<uIdx,pIdx>::present ? domainCache.template d2_impl<uIdx,pIdx>(u,p) : 0.;
          result += Functional::template D2<pIdx,pIdx>::present ? domainCache.template d2_impl<pIdx,pIdx>(p,p) : 0.;
        }
        else
        {
          result += Functional::template D2<pIdx,pIdx>::present ? domainCache.template d2_impl<yIdx,yIdx>(p,p) : 0.;
        }
        return result*arg.value[0];
      }

      template<int row, int col, int dim>
      Dune::FieldMatrix<Scalar,1,1>	d2 (VariationalArg<Scalar,dim> const&, VariationalArg<Scalar,dim> const&) const { return Dune::FieldMatrix<Scalar,1,1>(0); }

    private:
      ErrorDistribution const& f;
      typename Functional::DomainCache domainCache;
      typename AnsatzVars::Grid::template Codim<0>::Entity const* e;
      VariationalArg<Scalar,dim,OriginalAnsatzVars::template Components<yIdx>::m> yl;
      VariationalArg<Scalar,dim,ExtendedAnsatzVars::template Components<yIdx>::m> yh;
      VariationalArg<Scalar,dim,OriginalAnsatzVars::template Components<uIdx>::m> ul;
      VariationalArg<Scalar,dim,ExtendedAnsatzVars::template Components<uIdx>::m> uh;
      VariationalArg<Scalar,dim,OriginalAnsatzVars::template Components<pIdx>::m> pl;
      VariationalArg<Scalar,dim,ExtendedAnsatzVars::template Components<pIdx>::m> ph;
      SfCache sfCache;
      SfCache2 extendedSFCache;
    };

    class BoundaryCache
    {
    public:
      BoundaryCache(ErrorDistribution const& f_, typename OriginVars::VariableSet const& vars, int flags=7)
        : f(f_), boundaryCache(f.functional,f.iterate,flags)
      {}

      template <class FaceIterator>
      void moveTo(FaceIterator const& entity)
      {
        e = &entity;
        boundaryCache.moveTo(entity);
      }

      template <class Evaluators>
      void evaluateAt(Dune::FieldVector<typename AnsatzVars::Grid::ctype, AnsatzVars::Grid::dimension-1> const& xlocal, Evaluators const& evaluators)
      {
        clearVarArgs(yl,yh,ul,uh,pl,ph);
        OriginalEvaluators originalEvaluators(transform(f.iterate.descriptions.spaces,GetEvaluators<SfCache>(&sfCache)));
        ExtendedEvaluators extendedEvaluators(transform(f.errorEstimateH.descriptions.spaces,GetEvaluators<SfCache>(&extendedSFCache)));
        FaceQuadRule qr = QuadratureTraits<FaceQuadRule>().rule((*e)->geometryInInside().type(),f.qOrder);
        moveEvaluatorsToCell(originalEvaluators,*at_c<ySLIdx>(evaluators).cell_);
        moveEvaluatorsToCell(extendedEvaluators,*at_c<ySLIdx>(evaluators).cell_);
        moveEvaluatorsToCell(originalEvaluators,*e);
        moveEvaluatorsToCell(extendedEvaluators,*e);
        useQuadratureRuleInEvaluators(originalEvaluators,qr,(*e)->indexInInside());
        useQuadratureRuleInEvaluators(extendedEvaluators,qr,(*e)->indexInInside());

        size_t nQuadPos = qr.size();
        for (size_t g=0; g<nQuadPos; ++g)
        {
          // pos of integration point
          Dune::FieldVector<CoordType,dim> quadPos = (*e)->geometryInInside().global(qr[g].position());
          // for all spaces involved, update the evaluators associated
          // to this quadrature point
          moveEvaluatorsToIntegrationPoint(originalEvaluators,quadPos);
          moveEvaluatorsToIntegrationPoint(extendedEvaluators,quadPos);
          // prepare evaluation of functional
          boundaryCache.evaluateAt(qr[g].position(),originalEvaluators);

          evaluateData(originalEvaluators, extendedEvaluators, f, yl, yh, ul, uh, pl, ph, qr[g].weight());
        }
      }

      Scalar d0() const { return 0; }

      template<int row, int dim>
      Dune::FieldVector<Scalar,1> d1 (VariationalArg<Scalar,dim> const& arg) const
      {
        return Dune::FieldVector<Scalar,1>(0);
        VariationalArg<Scalar,dim,OriginalAnsatzVars::template Components<yIdx>::m> y(yh);
        VariationalArg<Scalar,dim,OriginalAnsatzVars::template Components<uIdx>::m> u(uh);
        VariationalArg<Scalar,dim,OriginalAnsatzVars::template Components<pIdx>::m> p(ph);

        if( !f.onlyH_ )
        {
          u.value += ul.value;
          u.gradient += ul.gradient;
          y.value += yl.value;
          y.gradient += yl.gradient;
          p.value += pl.value;
          p.gradient += pl.gradient;
        }

        if( f.errorNorm != ErrorNorm::Energy ) return computeL2Error(u,y,p);

        Scalar result = Functional::template D2<yIdx,yIdx>::present ? boundaryCache.template d2_impl<yIdx,yIdx>(y,y) : 0.;
        result += Functional::template D2<yIdx,uIdx>::present ? boundaryCache.template d2_impl<yIdx,uIdx>(y,u) : 0.;
        result += Functional::template D2<uIdx,yIdx>::present ? boundaryCache.template d2_impl<uIdx,yIdx>(u,y) : 0.;
        result += Functional::template D2<uIdx,uIdx>::present ? boundaryCache.template d2_impl<uIdx,uIdx>(u,u) : 0.;
        if( !f.useStateNormForAdjoint_ )
        {
          result += Functional::template D2<yIdx,pIdx>::present ? boundaryCache.template d2_impl<yIdx,pIdx>(y,p) : 0.;
          result += Functional::template D2<pIdx,yIdx>::present ? boundaryCache.template d2_impl<pIdx,yIdx>(p,y) : 0.;
          result += Functional::template D2<pIdx,uIdx>::present ? boundaryCache.template d2_impl<pIdx,uIdx>(p,u) : 0.;
          result += Functional::template D2<uIdx,pIdx>::present ? boundaryCache.template d2_impl<uIdx,pIdx>(u,p) : 0.;
          result += Functional::template D2<pIdx,pIdx>::present ? boundaryCache.template d2_impl<pIdx,pIdx>(p,p) : 0.;
        }
        else
        {
          result += Functional::template D2<pIdx,pIdx>::present ? boundaryCache.template d2_impl<yIdx,yIdx>(p,p) : 0.;
        }
        return result*arg.value[0];
      }

      template<int row, int col, int dim>
      Dune::FieldMatrix<Scalar,1,1> d2 (VariationalArg<Scalar,dim> const&, VariationalArg<Scalar,dim> const&) const { return Dune::FieldMatrix<Scalar,1,1>(0); }

    private:
      ErrorDistribution const& f;
      typename Functional::BoundaryCache boundaryCache;
      typename AnsatzVars::Grid::LeafGridView::IntersectionIterator const* e;
      VariationalArg<Scalar,dim,OriginalAnsatzVars::template Components<yIdx>::m> yl;
      VariationalArg<Scalar,dim,ExtendedAnsatzVars::template Components<yIdx>::m> yh;
      VariationalArg<Scalar,dim,OriginalAnsatzVars::template Components<uIdx>::m> ul;
      VariationalArg<Scalar,dim,ExtendedAnsatzVars::template Components<uIdx>::m> uh;
      VariationalArg<Scalar,dim,OriginalAnsatzVars::template Components<pIdx>::m> pl;
      VariationalArg<Scalar,dim,ExtendedAnsatzVars::template Components<pIdx>::m> ph;
      SfCache sfCache;
      SfCache2 extendedSFCache;
    };

    ErrorDistribution(Functional const& functional_, typename OriginalAnsatzVars::VariableSet const& iterate_,
                      typename OriginalAnsatzVars::VariableSet const& errorEstimateL_, typename ExtendedAnsatzVars::VariableSet const& errorEstimateH_)
      : functional(functional_), iterate(iterate_), errorEstimateL(errorEstimateL_), errorEstimateH(errorEstimateH_), ansatzSpace(boost::fusion::at_c<0>(iterate.descriptions.spaces)->gridManager(), iterate.descriptions.gridView,0),
        ansatzSpaces(&ansatzSpace), varName( {"error"} ), ansatzVars(ansatzSpaces,varName)

    {}

    template <int row>
    struct D1
    {
      static bool const present   = true;
      static bool const constant  = false;
    };

    template <int row, int col>
    struct D2
    {
      static bool const present = false;
      static bool const symmetric = false;
      static bool const lumped = false;
    };

    template <class Cell>
    int integrationOrder(Cell const&, int, bool) const { return 0; }

    AnsatzSpaces const& getSpaces() const { return ansatzSpaces; }

    AnsatzVars const& getVariableSetDescription() const { return ansatzVars; }

    void ignoreLowerOrderError(bool ignore) { onlyH_ = ignore; }
    void considerStateVariable(bool consider) { considerStateVariable_ = consider; }
    void considerControlVariable(bool consider) { considerControlVariable_ = consider; }
    void considerAdjointVariable(bool consider) { considerAdjointVariable_ = consider; }
    void setErrorNorm(ErrorNorm errNorm) { errorNorm = errNorm; }
    void useStateNormForAdjoint(bool useStateNorm) { useStateNormForAdjoint_ = useStateNorm; }

    friend class DomainCache;
    friend class BoundaryCache;

    Functional const& functional;
    typename OriginalAnsatzVars::VariableSet const& iterate;
    typename OriginalAnsatzVars::VariableSet const& errorEstimateL;
    typename ExtendedAnsatzVars::VariableSet const& errorEstimateH;
    AnsatzSpace ansatzSpace;
    AnsatzSpaces ansatzSpaces;
    std::string varName[1];
    AnsatzVars ansatzVars;
    ErrorNorm errorNorm = ErrorNorm::Energy;
    bool onlyH_ = false, considerStateVariable_ = true, considerControlVariable_ = true, considerAdjointVariable_ = false, useStateNormForAdjoint_ = false;
    int qOrder = 6;
  };
}
#endif

ext: hh

max_stars_repo_name: chenzongxiong/streambox

max_issues_repo_name: chenzongxiong/streambox

max_forks_repo_name: chenzongxiong/streambox


text:
#pragma once
#include "detail/config.hpp"
#include "numeric/traits.hpp"
#include "numeric/arrayref.hpp"

#include <Eigen/Core>

#include <algorithm>
#include <numeric>
#include <vector>

namespace cpb {

// add common math functions to the global namespace
using std::abs;
using std::exp;
using std::pow;
using std::sqrt;
using std::sin;
using std::cos;
using std::tan;
using std::asin;
using std::acos;

// add common Eigen types to the global namespace
using Eigen::Ref;
using Eigen::Map;
using Eigen::DenseBase;
using Eigen::Array3i;
using Eigen::ArrayXi;
using Eigen::ArrayXf;
using Eigen::ArrayXcf;
using Eigen::ArrayXd;
using Eigen::ArrayXcd;
using Eigen::ArrayXXi;
using Eigen::ArrayXXf;
using Eigen::ArrayXXcf;
using Eigen::ArrayXXd;
using Eigen::ArrayXXcd;
using Eigen::Vector3f;
using Eigen::VectorXi;
using Eigen::VectorXf;
using Eigen::VectorXcf;
using Eigen::VectorXd;
using Eigen::VectorXcd;
using Eigen::MatrixXf;
using Eigen::MatrixXcf;
using Eigen::MatrixXd;
using Eigen::MatrixXcd;

// convenient type aliases
using Cartesian = Eigen::Vector3f;
using Index3D = Eigen::Vector3i;
using Vector3b = Eigen::Matrix<bool, 3, 1>;
template<class T> using ArrayX = Eigen::Array<T, Eigen::Dynamic, 1>;
template<class T> using ArrayXX = Eigen::Array<T, Eigen::Dynamic, Eigen::Dynamic>;
template<class T> using VectorX = Eigen::Matrix<T, Eigen::Dynamic, 1>;
template<class T> using MatrixX = Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>;

template<class T>
using ColMajorArrayXX = Eigen::Array<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>;
template<class T>
using ColMajorMatrixX = Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>;

using ArrayXXdCM = ColMajorArrayXX<double>;

// array variants
using num::arrayref;
using num::ArrayConstRef;
using num::RealArrayConstRef;
using num::ComplexArrayConstRef;
using num::ArrayRef;
using num::RealArrayRef;
using num::ComplexArrayRef;

} // namespace cpb

namespace Eigen {
    // add being() and end() to Eigen namespace
    // this will enable using Eigen objects in ranged for loops
    template<class Derived>
    inline auto begin(EigenBase<Derived>& v) -> decltype(v.derived().data()) {
        return v.derived().data();
    }

    template<class Derived>
    inline auto end(EigenBase<Derived>& v) -> decltype(v.derived().data()) {
        return v.derived().data() + v.size();
    }

    template<class Derived>
    inline auto begin(const EigenBase<Derived>& v) -> decltype(v.derived().data()) {
        return v.derived().data();
    }

    template<class Derived>
    inline auto end(const EigenBase<Derived>& v) -> decltype(v.derived().data()) {
        return v.derived().data() + v.size();
    }
} // namespace Eigen

namespace cpb {

/**
 Map std::vector-like object data to an Eigen type
 */
template<template<class> class EigenType, class Vector,
         class scalar_t = typename Vector::value_type>
inline Eigen::Map<EigenType<scalar_t> const> eigen_cast(Vector const& v) {
    return {v.data(), static_cast<idx_t>(v.size())};
}

// utility functions
template<class Derived>
inline auto sum(const DenseBase<Derived>& v) -> decltype(v.sum()) {
    return v.sum();
}

template<class DerivedIn, class DerivedOut, class Fn>
inline void transform(const DenseBase<DerivedIn>& in, DenseBase<DerivedOut>& out, Fn func) {
    std::transform(begin(in), end(in), begin(out), func);
}

template<class DerivedIn1, class DerivedIn2, class DerivedOut, class Fn>
inline void transform(const DenseBase<DerivedIn1>& in1, const DenseBase<DerivedIn2>& in2,
                      DenseBase<DerivedOut>& out, Fn func) {
    std::transform(begin(in1), end(in1), begin(in2), begin(out), func);
}

/// Apply the function to the elements of the input container
/// and return the results in a new container of the given type
template<template<class...> class Container, class In, class Fn>
auto transform(In const& in, Fn func) -> Container<decltype(func(in[0]))> {
    using Out = decltype(func(in[0]));
    auto out = Container<Out>(in.size());
    std::transform(begin(in), end(in), begin(out), func);
    return out;
}

template<class Derived> inline bool any_of(const DenseBase<Derived>& v) { return v.any(); }
template<class Derived> inline bool all_of(const DenseBase<Derived>& v) { return v.all(); }
template<class Derived> inline bool none_of(const DenseBase<Derived>& v) { return !v.any(); }

template<bool is_const>
class BasicCartesianArrayRef {
public:
    using Reference = std14::conditional_t<!is_const, Eigen::Ref<ArrayXf>,
                                           Eigen::Ref<ArrayXf const>>;

    BasicCartesianArrayRef(Reference x, Reference y,  Reference z)
        : x_ref(x), y_ref(y), z_ref(z) {}

    BasicCartesianArrayRef(BasicCartesianArrayRef<false> const& o)
        : x_ref(o.x()), y_ref(o.y()), z_ref(o.z()) {}

    Reference const& x() const { return x_ref; }
    Reference const& y() const { return y_ref; }
    Reference const& z() const { return z_ref; }
    Reference& x() { return x_ref; }
    Reference& y() { return y_ref; }
    Reference& z() { return z_ref; }

    idx_t size() const { return x_ref.size(); }

private:
    Reference x_ref, y_ref, z_ref;
};

using CartesianArrayConstRef = BasicCartesianArrayRef<true>;
using CartesianArrayRef = BasicCartesianArrayRef<false>;

class CartesianArray {
private:
    struct CartesianRef {
        float &x, &y, &z;
        CartesianRef& operator=(const Cartesian& r) { x = r[0]; y = r[1]; z = r[2]; return *this; }
        operator Cartesian() { return {x, y, z}; }
    };

public:
    CartesianArray() = default;
    CartesianArray(idx_t size) : x(size), y(size), z(size) {}
    CartesianArray(ArrayXf x, ArrayXf y, ArrayXf z)
        : x(std::move(x)), y(std::move(y)), z(std::move(z)) {}

    CartesianRef operator[](idx_t i) { return {x[i], y[i], z[i]}; }
    Cartesian operator[](idx_t i) const { return {x[i], y[i], z[i]}; }

    idx_t size() const { return x.size(); }

    CartesianArrayConstRef head(idx_t size) const {
        return {x.head(size), y.head(size), z.head(size)};
    }

    CartesianArrayRef head(idx_t size) {
        return {x.head(size), y.head(size), z.head(size)};
    }

    CartesianArrayConstRef segment(idx_t start, idx_t size) const {
        return {x.segment(start, size), y.segment(start, size), z.segment(start, size)};
    }

    CartesianArrayRef segment(idx_t start, idx_t size) {
        return {x.segment(start, size), y.segment(start, size), z.segment(start, size)};
    }

    operator CartesianArrayConstRef() const { return {x, y, z}; }
    operator CartesianArrayRef() { return {x, y, z}; }

    template<class Fn>
    void for_each(Fn lambda) {
        lambda(x); lambda(y); lambda(z);
    }

    void resize(idx_t size) {
        for_each([size](ArrayX<float>& a) { a.resize(size); });
    }

    void conservativeResize(idx_t size) {
        for_each([size](ArrayX<float>& a) { a.conservativeResize(size); });
    }

public:
    ArrayX<float> x, y, z;
};

namespace num {
    // ArrayRef's MakeContainer specializations for Eigen types
    template<template<class, int...> class EigenType, class scalar_t, int cols, int... options>
    struct MakeContainer<EigenType<scalar_t, 1, cols, options...>> {
        using ConstMap = Eigen::Map<const EigenType<scalar_t, 1, cols, options...>>;
        static ConstMap make(ArrayConstRef const& ref) {
            return ConstMap{static_cast<scalar_t const*>(ref.data), ref.size()};
        }
        using Map = Eigen::Map<EigenType<scalar_t, 1, cols, options...>>;
        static Map make(ArrayRef const& ref) {
            return Map{static_cast<scalar_t*>(ref.data), ref.size()};
        }
    };

    template<template<class, int...> class EigenType, class scalar_t, int rows, int... options>
    struct MakeContainer<EigenType<scalar_t, rows, 1, options...>> {
        using ConstMap = Eigen::Map<const EigenType<scalar_t, rows, 1, options...>>;
        static ConstMap make(ArrayConstRef const& ref) {
            return ConstMap{static_cast<scalar_t const*>(ref.data), ref.size()};
        }
        using Map = Eigen::Map<EigenType<scalar_t, rows, 1, options...>>;
        static Map make(ArrayRef const& ref) {
            return Map{static_cast<scalar_t*>(ref.data), ref.size()};
        }
    };

    template<template<class, int...> class EigenType,
             class scalar_t, int rows, int cols, int... options>
    struct MakeContainer<EigenType<scalar_t, rows, cols, options...>> {
        using ConstMap = Eigen::Map<const EigenType<scalar_t, rows, cols, options...>>;
        static ConstMap make(ArrayConstRef const& ref) {
            return ConstMap{static_cast<scalar_t const*>(ref.data), ref.shape[0], ref.shape[1]};
        }
        using Map = Eigen::Map<EigenType<scalar_t, rows, cols, options...>>;
        static Map make(ArrayRef const& ref) {
            return Map{static_cast<scalar_t*>(ref.data), ref.shape[0], ref.shape[1]};
        }
    };

    /// Force cast a matrix to any scalar type (lose precision and/or imaginary part)
    template<class scalar_t>
    MatrixX<scalar_t> force_cast(MatrixXcd const& m) { return m.cast<scalar_t>(); }
    template<>
    inline MatrixX<double> force_cast<double>(MatrixXcd const& m) { return m.real(); }
    template<>
    inline MatrixX<float> force_cast<float>(MatrixXcd const& m) { return m.real().cast<float>(); }

    template<class scalar_t>
    VectorX<scalar_t> force_cast(VectorXcd const& m) { return m.cast<scalar_t>(); }
    template<>
    inline VectorX<double> force_cast<double>(VectorXcd const& m) { return m.real(); }
    template<>
    inline VectorX<float> force_cast<float>(VectorXcd const& m) { return m.real().cast<float>(); }
} // namespace num

template<class Derived>
ArrayConstRef arrayref(DenseBase<Derived> const& v) {
    auto const& d = v.derived();
    return {v.derived().data(),
            Derived::IsVectorAtCompileTime ? 1 : 2,
            Derived::IsRowMajor,
            Derived::IsVectorAtCompileTime ? d.size() : d.rows(),
            Derived::IsVectorAtCompileTime ? 0        : d.cols()};
};

template<class Derived>
auto arrayref(DenseBase<Derived>& v) -> num::detail::MakeArrayRef<decltype(v.derived().data())> {
    auto& d = v.derived();
    return {v.derived().data(),
            Derived::IsVectorAtCompileTime ? 1 : 2,
            Derived::IsRowMajor,
            Derived::IsVectorAtCompileTime ? d.size() : d.rows(),
            Derived::IsVectorAtCompileTime ? 0        : d.cols()};
};

template<class scalar_t>
ArrayConstRef arrayref(std::vector<scalar_t> const& v) {
    return arrayref(v.data(), static_cast<idx_t>(v.size()));
}

template<class scalar_t>
ArrayRef arrayref(std::vector<scalar_t>& v) {
    return arrayref(v.data(), 1, true, static_cast<idx_t>(v.size()));
}

/// Range from 0 to `size` of scalar type `T` which does not have to be an integral type
template<class T>
ArrayX<T> make_integer_range(idx_t size) {
    auto result = ArrayX<T>(size);
    for (auto n = 0; n < size; ++n) {
        result[n] = static_cast<T>(n);
    }
    return result;
}

template<class Vector, class Bools>
Vector slice(Vector const& v, Bools const& keep) {
    using std::begin; using std::end;
    auto const original_size = v.size();
    auto const result_size = std::accumulate(begin(keep), end(keep), idx_t{0});

    auto result = Vector(result_size);
    auto count = 0;
    for (auto i = idx_t{0}; i < original_size; ++i) {
        if (keep[i])
            result[count++] = v[i];
    }
    return result;
};

/// Concatenate two 1D arrays/vectors
template<class Vector>
Vector concat(Ref<Vector const> v1, Ref<Vector const> v2) {
    using std::begin; using std::end;
    auto result = Vector(v1.size() + v2.size());
    std::copy(begin(v1), end(v1), begin(result));
    std::copy(begin(v2), end(v2), begin(result) + v1.size());
    return result;
}

template<class Vector, typename R = Ref<Vector const>>
Vector concat(Vector const& v1, Vector const& v2) {
    return concat(R(v1), R(v2));
}

/// Concatenate two Cartesian arrays
inline CartesianArray concat(CartesianArrayConstRef const& ca1,
                             CartesianArrayConstRef const& ca2) {
    return {concat(ca1.x(), ca2.x()),
            concat(ca1.y(), ca2.y()),
            concat(ca1.z(), ca2.z())};
}

} // namespace cpb

ext: hpp

max_issues_repo_name: deilynazar/pybinding

max_forks_repo_name: deilynazar/pybinding


text:
/**
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * This file is part of osm2pgsql (https://osm2pgsql.org/).
 *
 * Copyright (C) 2006-2022 by the osm2pgsql developer community.
 * For a full list of authors see the git log.
 */

#include "db-copy.hpp"
#include "expire-tiles.hpp"
#include "format.hpp"
#include "geom-functions.hpp"
#include "geom-transform.hpp"
#include "logging.hpp"
#include "lua-init.hpp"
#include "lua-utils.hpp"
#include "middle.hpp"
#include "options.hpp"
#include "osmtypes.hpp"
#include "output-flex.hpp"
#include "pgsql.hpp"
#include "reprojection.hpp"
#include "thread-pool.hpp"
#include "util.hpp"
#include "version.hpp"
#include "wkb.hpp"

#include <osmium/osm/types_from_string.hpp>

extern "C"
{
#include <lauxlib.h>
#include <lualib.h>
}

#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>

#include <boost/filesystem.hpp>

#include <cassert>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <string>

// Mutex used to coordinate access to Lua code
static std::mutex lua_mutex;

// Lua can't call functions on C++ objects directly. This macro defines simple
// C "trampoline" functions which are called from Lua which get the current
// context (the output_flex_t object) and call the respective function on the
// context object.
#define TRAMPOLINE(func_name, lua_name)                                        \
    static int lua_trampoline_##func_name(lua_State *lua_state)                \
    {                                                                          \
        try {                                                                  \
            return static_cast<output_flex_t *>(luaX_get_context(lua_state))   \
                ->func_name();                                                 \
        } catch (std::exception const &e) {                                    \
            return luaL_error(lua_state, "Error in '" #lua_name "': %s\n",     \
                              e.what());                                       \
        } catch (...) {                                                        \
            return luaL_error(lua_state,                                       \
                              "Unknown error in '" #lua_name "'.\n");          \
        }                                                                      \
    }

TRAMPOLINE(app_define_table, define_table)
TRAMPOLINE(app_get_bbox, get_bbox)
TRAMPOLINE(table_name, name)
TRAMPOLINE(table_schema, schema)
TRAMPOLINE(table_cluster, cluster)
TRAMPOLINE(table_add_row, add_row)
TRAMPOLINE(table_columns, columns)
TRAMPOLINE(table_tostring, __tostring)

static char const *const osm2pgsql_table_name = "osm2pgsql.table";
static char const *const osm2pgsql_object_metatable =
    "osm2pgsql.object_metatable";

prepared_lua_function_t::prepared_lua_function_t(lua_State *lua_state,
                                                 calling_context context,
                                                 char const *name, int nresults)
{
    int const index = lua_gettop(lua_state);

    lua_getfield(lua_state, 1, name);

    if (lua_type(lua_state, -1) == LUA_TFUNCTION) {
        m_index = index;
        m_name = name;
        m_nresults = nresults;
        m_calling_context = context;
        return;
    }

    if (lua_type(lua_state, -1) == LUA_TNIL) {
        return;
    }

    throw std::runtime_error{"osm2pgsql.{} must be a function."_format(name)};
}

static void push_osm_object_to_lua_stack(lua_State *lua_state,
                                         osmium::OSMObject const &object,
                                         bool with_attributes)
{
    assert(lua_state);

    /**
     * Table will have 7 fields (id, version, timestamp, changeset, uid, user,
     * tags) for all object types plus 2 (is_closed, nodes) for ways or 1
     * (members) for relations.
     */
    constexpr int const max_table_size = 9;

    lua_createtable(lua_state, 0, max_table_size);

    luaX_add_table_int(lua_state, "id", object.id());

    if (with_attributes) {
        if (object.version() != 0U) {
            luaX_add_table_int(lua_state, "version", object.version());
        } else {
            // This is a workaround, because the middle will give us the
            // attributes as pseudo-tags.
            char const *const val = object.tags()["osm_version"];
            if (val) {
                luaX_add_table_int(lua_state, "version",
                                   osmium::string_to_object_version(val));
            }
        }

        if (object.timestamp().valid()) {
            luaX_add_table_int(lua_state, "timestamp",
                               object.timestamp().seconds_since_epoch());
        } else {
            // This is a workaround, because the middle will give us the
            // attributes as pseudo-tags.
            char const *const val = object.tags()["osm_timestamp"];
            if (val) {
                auto const timestamp = osmium::Timestamp{val};
                luaX_add_table_int(lua_state, "timestamp",
                                   timestamp.seconds_since_epoch());
            }
        }

        if (object.changeset() != 0U) {
            luaX_add_table_int(lua_state, "changeset", object.changeset());
        } else {
            char const *const val = object.tags()["osm_changeset"];
            // This is a workaround, because the middle will give us the
            // attributes as pseudo-tags.
            if (val) {
                luaX_add_table_int(lua_state, "changeset",
                                   osmium::string_to_changeset_id(val));
            }
        }

        if (object.uid() != 0U) {
            luaX_add_table_int(lua_state, "uid", object.uid());
        } else {
            // This is a workaround, because the middle will give us the
            // attributes as pseudo-tags.
            char const *const val = object.tags()["osm_uid"];
            if (val) {
                luaX_add_table_int(lua_state, "uid",
                                   osmium::string_to_uid(val));
            }
        }

        if (object.user()[0] != '\0') {
            luaX_add_table_str(lua_state, "user", object.user());
        } else {
            // This is a workaround, because the middle will give us the
            // attributes as pseudo-tags.
            char const *const val = object.tags()["osm_user"];
            if (val) {
                luaX_add_table_str(lua_state, "user", val);
            }
        }
    }

    if (object.type() == osmium::item_type::way) {
        auto const &way = static_cast<osmium::Way const &>(object);
        luaX_add_table_bool(lua_state, "is_closed", way.is_closed());
        luaX_add_table_array(lua_state, "nodes", way.nodes(),
                             [&](osmium::NodeRef const &wn) {
                                 lua_pushinteger(lua_state, wn.ref());
                             });
    } else if (object.type() == osmium::item_type::relation) {
        auto const &relation = static_cast<osmium::Relation const &>(object);
        luaX_add_table_array(
            lua_state, "members", relation.members(),
            [&](osmium::RelationMember const &member) {
                lua_createtable(lua_state, 0, 3);
                std::array<char, 2> tmp{"x"};
                tmp[0] = osmium::item_type_to_char(member.type());
                luaX_add_table_str(lua_state, "type", &tmp[0]);
                luaX_add_table_int(lua_state, "ref", member.ref());
                luaX_add_table_str(lua_state, "role", member.role());
            });
    }

    lua_pushliteral(lua_state, "tags");
    lua_createtable(lua_state, 0, (int)object.tags().size());
    for (auto const &tag : object.tags()) {
        luaX_add_table_str(lua_state, tag.key(), tag.value());
    }
    lua_rawset(lua_state, -3);

    // Set the metatable of this object
    lua_pushlightuserdata(lua_state, (void *)osm2pgsql_object_metatable);
    lua_gettable(lua_state, LUA_REGISTRYINDEX);
    lua_setmetatable(lua_state, -2);
}

static int sgn(double val) noexcept
{
    if (val > 0) {
        return 1;
    }
    if (val < 0) {
        return -1;
    }
    return 0;
}

static void write_null(db_copy_mgr_t<db_deleter_by_type_and_id_t> *copy_mgr,
                       flex_table_column_t const &column)
{
    if (column.not_null()) {
        throw std::runtime_error{
            "Can not add NULL to column '{}' declared NOT NULL."_format(
                column.name())};
    }
    copy_mgr->add_null_column();
}

static void write_boolean(db_copy_mgr_t<db_deleter_by_type_and_id_t> *copy_mgr,
                          flex_table_column_t const &column, char const *str)
{
    if ((std::strcmp(str, "yes") == 0) || (std::strcmp(str, "true") == 0) ||
        std::strcmp(str, "1") == 0) {
        copy_mgr->add_column(true);
        return;
    }

    if ((std::strcmp(str, "no") == 0) || (std::strcmp(str, "false") == 0) ||
        std::strcmp(str, "0") == 0) {
        copy_mgr->add_column(false);
        return;
    }

    write_null(copy_mgr, column);
}

static void
write_direction(db_copy_mgr_t<db_deleter_by_type_and_id_t> *copy_mgr,
                flex_table_column_t const &column, char const *str)
{
    if ((std::strcmp(str, "yes") == 0) || (std::strcmp(str, "1") == 0)) {
        copy_mgr->add_column(1);
        return;
    }

    if ((std::strcmp(str, "no") == 0) || (std::strcmp(str, "0") == 0)) {
        copy_mgr->add_column(0);
        return;
    }

    if (std::strcmp(str, "-1") == 0) {
        copy_mgr->add_column(-1);
        return;
    }

    write_null(copy_mgr, column);
}

template <typename T>
void write_integer(db_copy_mgr_t<db_deleter_by_type_and_id_t> *copy_mgr,
                   flex_table_column_t const &column, char const *str)
{
    if (*str == '\0') {
        write_null(copy_mgr, column);
        return;
    }

    char *end = nullptr;
    errno = 0;
    auto const value = std::strtoll(str, &end, 10);

    if (errno != 0 || *end != '\0') {
        write_null(copy_mgr, column);
        return;
    }

    if (value >= std::numeric_limits<T>::min() &&
        value <= std::numeric_limits<T>::max()) {
        copy_mgr->add_column(value);
        return;
    }

    write_null(copy_mgr, column);
}

static void write_double(db_copy_mgr_t<db_deleter_by_type_and_id_t> *copy_mgr,
                         flex_table_column_t const &column, char const *str)
{
    if (*str == '\0') {
        write_null(copy_mgr, column);
        return;
    }

    char *end = nullptr;
    double const value = std::strtod(str, &end);

    if (end && *end != '\0') {
        write_null(copy_mgr, column);
        return;
    }

    copy_mgr->add_column(value);
}

using json_writer_type = rapidjson::Writer<rapidjson::StringBuffer>;
using table_register_type = std::vector<void const *>;

/**
 * Check that the value on the top of the Lua stack is a simple array.
 * This means that all keys must be consecutive integers starting from 1.
 */
static bool is_lua_array(lua_State *lua_state)
{
    uint32_t n = 1;
    lua_pushnil(lua_state);
    while (lua_next(lua_state, -2) != 0) {
        lua_pop(lua_state, 1); // remove value from stack
#if LUA_VERSION_NUM >= 503
        if (!lua_isinteger(lua_state, -1)) {
            lua_pop(lua_state, 1);
            return false;
        }
        int okay = 0;
        auto const num = lua_tointegerx(lua_state, -1, &okay);
        if (!okay || num != n++) {
            lua_pop(lua_state, 1);
            return false;
        }
#else
        if (!lua_isnumber(lua_state, -1)) {
            lua_pop(lua_state, 1);
            return false;
        }
        double const num = lua_tonumber(lua_state, -1);
        double intpart = 0.0;
        if (std::modf(num, &intpart) != 0.0 || intpart < 0 ||
            static_cast<uint32_t>(num) != n++) {
            lua_pop(lua_state, 1);
            return false;
        }
#endif
    }

    // An empty lua table could be both, we decide here that it is not stored
    // as a JSON array but as a JSON object.
    return n != 1;
}

static void write_json(json_writer_type *writer, lua_State *lua_state,
                       table_register_type *tables);

static void write_json_table(json_writer_type *writer, lua_State *lua_state,
                             table_register_type *tables)
{
    void const *table_ptr = lua_topointer(lua_state, -1);
    assert(table_ptr);
    auto const it = std::find(tables->cbegin(), tables->cend(), table_ptr);
    if (it != tables->cend()) {
        throw std::runtime_error{"Loop detected in table"};
    }
    tables->push_back(table_ptr);

    if (is_lua_array(lua_state)) {
        writer->StartArray();
        lua_pushnil(lua_state);
        while (lua_next(lua_state, -2) != 0) {
            write_json(writer, lua_state, tables);
            lua_pop(lua_state, 1);
        }
        writer->EndArray();
    } else {
        writer->StartObject();
        lua_pushnil(lua_state);
        while (lua_next(lua_state, -2) != 0) {
            int const ltype_key = lua_type(lua_state, -2);
            if (ltype_key != LUA_TSTRING) {
                throw std::runtime_error{
                    "Incorrect data type '{}' as key."_format(
                        lua_typename(lua_state, ltype_key))};
            }
            char const *const key = lua_tostring(lua_state, -2);
            writer->Key(key);
            write_json(writer, lua_state, tables);
            lua_pop(lua_state, 1);
        }
        writer->EndObject();
    }
}

static void write_json_number(json_writer_type *writer, lua_State *lua_state)
{
#if LUA_VERSION_NUM >= 503
    int okay = 0;
    auto const num = lua_tointegerx(lua_state, -1, &okay);
    if (okay) {
        writer->Int64(num);
    } else {
        writer->Double(lua_tonumber(lua_state, -1));
    }
#else
    double const num = lua_tonumber(lua_state, -1);
    double intpart = 0.0;
    if (std::modf(num, &intpart) == 0.0) {
        writer->Int64(static_cast<int64_t>(num));
    } else {
        writer->Double(num);
    }
#endif
}

static void write_json(json_writer_type *writer, lua_State *lua_state,
                       table_register_type *tables)
{
    assert(writer);
    assert(lua_state);

    int const ltype = lua_type(lua_state, -1);
    switch (ltype) {
    case LUA_TNIL:
        writer->Null();
        break;
    case LUA_TBOOLEAN:
        writer->Bool(lua_toboolean(lua_state, -1) != 0);
        break;
    case LUA_TNUMBER:
        write_json_number(writer, lua_state);
        break;
    case LUA_TSTRING:
        writer->String(lua_tostring(lua_state, -1));
        break;
    case LUA_TTABLE:
        write_json_table(writer, lua_state, tables);
        break;
    default:
        throw std::runtime_error{
            "Invalid type '{}' for json/jsonb column."_format(
                lua_typename(lua_state, ltype))};
    }
}

void output_flex_t::write_column(
    db_copy_mgr_t<db_deleter_by_type_and_id_t> *copy_mgr,
    flex_table_column_t const &column)
{
    // If there is nothing on the Lua stack, then the Lua function add_row()
    // was called without a table parameter. In that case this column will
    // be set to NULL.
    if (lua_gettop(lua_state()) == 0) {
        write_null(copy_mgr, column);
        return;
    }

    lua_getfield(lua_state(), -1, column.name().c_str());
    int const ltype = lua_type(lua_state(), -1);

    // Certain Lua types can never be added to the database
    if (ltype == LUA_TFUNCTION || ltype == LUA_TUSERDATA ||
        ltype == LUA_TTHREAD) {
        throw std::runtime_error{
            "Can not add Lua objects of type function, userdata, or thread."};
    }

    // A Lua nil value is always translated to a database NULL
    if (ltype == LUA_TNIL) {
        write_null(copy_mgr, column);
        lua_pop(lua_state(), 1);
        return;
    }

    if (column.type() == table_column_type::text) {
        auto const *const str = lua_tolstring(lua_state(), -1, nullptr);
        if (!str) {
            throw std::runtime_error{
                "Invalid type '{}' for text column."_format(
                    lua_typename(lua_state(), ltype))};
        }
        copy_mgr->add_column(str);
    } else if (column.type() == table_column_type::boolean) {
        switch (ltype) {
        case LUA_TBOOLEAN:
            copy_mgr->add_column(lua_toboolean(lua_state(), -1) != 0);
            break;
        case LUA_TNUMBER:
            copy_mgr->add_column(lua_tonumber(lua_state(), -1) != 0);
            break;
        case LUA_TSTRING:
            write_boolean(copy_mgr, column,
                          lua_tolstring(lua_state(), -1, nullptr));
            break;
        default:
            throw std::runtime_error{
                "Invalid type '{}' for boolean column."_format(
                    lua_typename(lua_state(), ltype))};
        }
    } else if (column.type() == table_column_type::int2) {
        if (ltype == LUA_TNUMBER) {
            int64_t const value = lua_tointeger(lua_state(), -1);
            if (value >= std::numeric_limits<int16_t>::min() &&
                value <= std::numeric_limits<int16_t>::max()) {
                copy_mgr->add_column(value);
            } else {
                write_null(copy_mgr, column);
            }
        } else if (ltype == LUA_TSTRING) {
            write_integer<int16_t>(copy_mgr, column,
                                   lua_tolstring(lua_state(), -1, nullptr));
        } else if (ltype == LUA_TBOOLEAN) {
            copy_mgr->add_column(lua_toboolean(lua_state(), -1));
        } else {
            throw std::runtime_error{
                "Invalid type '{}' for int2 column."_format(
                    lua_typename(lua_state(), ltype))};
        }
    } else if (column.type() == table_column_type::int4) {
        if (ltype == LUA_TNUMBER) {
            int64_t const value = lua_tointeger(lua_state(), -1);
            if (value >= std::numeric_limits<int32_t>::min() &&
                value <= std::numeric_limits<int32_t>::max()) {
                copy_mgr->add_column(value);
            } else {
                write_null(copy_mgr, column);
            }
        } else if (ltype == LUA_TSTRING) {
            write_integer<int32_t>(copy_mgr, column,
                                   lua_tolstring(lua_state(), -1, nullptr));
        } else if (ltype == LUA_TBOOLEAN) {
            copy_mgr->add_column(lua_toboolean(lua_state(), -1));
        } else {
            throw std::runtime_error{
                "Invalid type '{}' for int4 column."_format(
                    lua_typename(lua_state(), ltype))};
        }
    } else if (column.type() == table_column_type::int8) {
        if (ltype == LUA_TNUMBER) {
            copy_mgr->add_column(lua_tointeger(lua_state(), -1));
        } else if (ltype == LUA_TSTRING) {
            write_integer<int64_t>(copy_mgr, column,
                                   lua_tolstring(lua_state(), -1, nullptr));
        } else if (ltype == LUA_TBOOLEAN) {
            copy_mgr->add_column(lua_toboolean(lua_state(), -1));
        } else {
            throw std::runtime_error{
                "Invalid type '{}' for int8 column."_format(
                    lua_typename(lua_state(), ltype))};
        }
    } else if (column.type() == table_column_type::real) {
        if (ltype == LUA_TNUMBER) {
            copy_mgr->add_column(lua_tonumber(lua_state(), -1));
        } else if (ltype == LUA_TSTRING) {
            write_double(copy_mgr, column,
                         lua_tolstring(lua_state(), -1, nullptr));
        } else {
            throw std::runtime_error{
                "Invalid type '{}' for real column."_format(
                    lua_typename(lua_state(), ltype))};
        }
    } else if (column.type() == table_column_type::hstore) {
        if (ltype == LUA_TTABLE) {
            copy_mgr->new_hash();

            lua_pushnil(lua_state());
            while (lua_next(lua_state(), -2) != 0) {
                char const *const key = lua_tostring(lua_state(), -2);
                char const *const val = lua_tostring(lua_state(), -1);
                if (key == nullptr) {
                    int const ltype_key = lua_type(lua_state(), -2);
                    throw std::runtime_error{
                        "NULL key for hstore. Possibly this is due to"
                        " an incorrect data type '{}' as key."_format(
                            lua_typename(lua_state(), ltype_key))};
                }
                if (val == nullptr) {
                    int const ltype_value = lua_type(lua_state(), -1);
                    throw std::runtime_error{
                        "NULL value for hstore. Possibly this is due to"
                        " an incorrect data type '{}' for key '{}'."_format(
                            lua_typename(lua_state(), ltype_value), key)};
                }
                copy_mgr->add_hash_elem(key, val);
                lua_pop(lua_state(), 1);
            }

            copy_mgr->finish_hash();
        } else {
            throw std::runtime_error{
                "Invalid type '{}' for hstore column."_format(
                    lua_typename(lua_state(), ltype))};
        }
    } else if ((column.type() == table_column_type::json) ||
               (column.type() == table_column_type::jsonb)) {
        rapidjson::StringBuffer stream;
        json_writer_type writer{stream};
        table_register_type tables;
        write_json(&writer, lua_state(), &tables);
        copy_mgr->add_column(stream.GetString());
    } else if (column.type() == table_column_type::direction) {
        switch (ltype) {
        case LUA_TBOOLEAN:
            copy_mgr->add_column(lua_toboolean(lua_state(), -1));
            break;
        case LUA_TNUMBER:
            copy_mgr->add_column(sgn(lua_tonumber(lua_state(), -1)));
            break;
        case LUA_TSTRING:
            write_direction(copy_mgr, column,
                            lua_tolstring(lua_state(), -1, nullptr));
            break;
        default:
            throw std::runtime_error{
                "Invalid type '{}' for direction column."_format(
                    lua_typename(lua_state(), ltype))};
        }
    } else {
        throw std::runtime_error{
            "Column type {} not implemented."_format(static_cast<uint8_t>(column.type()))};
    }

    lua_pop(lua_state(), 1);
}

void output_flex_t::write_row(table_connection_t *table_connection,
                              osmium::item_type id_type, osmid_t id,
                              geom::geometry_t const &geom, int srid)
{
    assert(table_connection);
    table_connection->new_line();
    auto *copy_mgr = table_connection->copy_mgr();

    geom::geometry_t projected_geom;
    geom::geometry_t const* output_geom = &geom;
    if (srid && geom.srid() != srid) {
        auto const proj = reprojection::create_projection(srid);
        projected_geom = geom::transform(geom, *proj);
        output_geom = &projected_geom;
    }

    for (auto const &column : table_connection->table()) {
        if (column.create_only()) {
            continue;
        }
        if (column.type() == table_column_type::id_type) {
            copy_mgr->add_column(type_to_char(id_type));
        } else if (column.type() == table_column_type::id_num) {
            copy_mgr->add_column(id);
        } else if (column.is_geometry_column()) {
            assert(!geom.is_null());
            auto const type = column.type();
            bool const wrap_multi =
                (type == table_column_type::multilinestring ||
                 type == table_column_type::multipolygon);
            copy_mgr->add_hex_geom(geom_to_ewkb(*output_geom, wrap_multi));
        } else if (column.type() == table_column_type::area) {
            if (geom.is_null()) {
                write_null(copy_mgr, column);
            } else {
                // if srid of the area column is the same as for the geom column
                double area = 0;
                if (column.srid() == 4326) {
                    area = geom::area(geom);
                } else if (column.srid() == srid) {
                    area = geom::area(projected_geom);
                } else {
                    // XXX there is some overhead here always dynamically
                    // creating the same projection object. Needs refactoring.
                    auto const mproj = reprojection::create_projection(column.srid());
                    area = geom::area(geom::transform(geom, *mproj));
                }
                copy_mgr->add_column(area);
            }
        } else {
            write_column(copy_mgr, column);
        }
    }

    copy_mgr->finish_line();
}

/**
 * Helper function to push the lon/lat of the specified location onto the
 * Lua stack
 */
static void push_location(lua_State *lua_state,
                          osmium::Location location) noexcept
{
    lua_pushnumber(lua_state, location.lon());
    lua_pushnumber(lua_state, location.lat());
}

int output_flex_t::app_get_bbox()
{
    if (m_calling_context != calling_context::process_node &&
        m_calling_context != calling_context::process_way &&
        m_calling_context != calling_context::process_relation) {
        throw std::runtime_error{
            "The function get_bbox() can only be called from the "
            "process_node/way/relation() functions."};
    }

    if (lua_gettop(lua_state()) > 1) {
        throw std::runtime_error{"No parameter(s) needed for get_box()."};
    }

    if (m_calling_context == calling_context::process_node) {
        push_location(lua_state(), m_context_node->location());
        push_location(lua_state(), m_context_node->location());
        return 4;
    }

    if (m_calling_context == calling_context::process_way) {
        m_way_cache.add_nodes(middle());
        auto const bbox = m_way_cache.get().envelope();
        if (bbox.valid()) {
            push_location(lua_state(), bbox.bottom_left());
            push_location(lua_state(), bbox.top_right());
            return 4;
        }
        return 0;
    }

    if (m_calling_context == calling_context::process_relation) {
        m_relation_cache.add_members(middle());
        osmium::Box bbox;

        // Bounding boxes of all the member nodes
        for (auto const &wnl :
             m_relation_cache.members_buffer().select<osmium::WayNodeList>()) {
            bbox.extend(wnl.envelope());
        }

        // Bounding boxes of all the member ways
        for (auto const &way :
             m_relation_cache.members_buffer().select<osmium::Way>()) {
            bbox.extend(way.nodes().envelope());
        }

        if (bbox.valid()) {
            push_location(lua_state(), bbox.bottom_left());
            push_location(lua_state(), bbox.top_right());
            return 4;
        }
    }

    return 0;
}

static void check_name(std::string const &name, char const *in)
{
    auto const pos = name.find_first_of("\"',.;$%&/()<>{}=?^*#");

    if (pos == std::string::npos) {
        return;
    }

    throw std::runtime_error{
        "Special characters are not allowed in {} names: '{}'."_format(in,
                                                                      name)};
}

flex_table_t &output_flex_t::create_flex_table()
{
    std::string const table_name =
        luaX_get_table_string(lua_state(), "name", -1, "The table");

    check_name(table_name, "table");

    auto const it = std::find_if(m_tables->cbegin(), m_tables->cend(),
                                 [&table_name](flex_table_t const &table) {
                                     return table.name() == table_name;
                                 });
    if (it != m_tables->cend()) {
        throw std::runtime_error{
            "Table with name '{}' already exists."_format(table_name)};
    }

    m_tables->emplace_back(table_name);
    auto &new_table = m_tables->back();

    lua_pop(lua_state(), 1);

    // optional "schema" field
    lua_getfield(lua_state(), -1, "schema");
    if (lua_isstring(lua_state(), -1)) {
        std::string const schema = lua_tostring(lua_state(), -1);
        check_name(schema, "schema");
        new_table.set_schema(schema);
    }
    lua_pop(lua_state(), 1);

    // optional "cluster" field
    lua_getfield(lua_state(), -1, "cluster");
    int const cluster_type = lua_type(lua_state(), -1);
    if (cluster_type == LUA_TSTRING) {
        std::string const cluster = lua_tostring(lua_state(), -1);
        if (cluster == "auto") {
            new_table.set_cluster_by_geom(true);
        } else if (cluster == "no") {
            new_table.set_cluster_by_geom(false);
        } else {
            throw std::runtime_error{
                "Unknown value '{}' for 'cluster' table option"
                " (use 'auto' or 'no')."_format(cluster)};
        }
    } else if (cluster_type == LUA_TNIL) {
        // ignore
    } else {
        throw std::runtime_error{
            "Unknown value for 'cluster' table option: Must be string."};
    }
    lua_pop(lua_state(), 1);

    // optional "data_tablespace" field
    lua_getfield(lua_state(), -1, "data_tablespace");
    if (lua_isstring(lua_state(), -1)) {
        std::string const tablespace = lua_tostring(lua_state(), -1);
        check_name(tablespace, "data_tablespace");
        new_table.set_data_tablespace(tablespace);
    }
    lua_pop(lua_state(), 1);

    // optional "index_tablespace" field
    lua_getfield(lua_state(), -1, "index_tablespace");
    if (lua_isstring(lua_state(), -1)) {
        std::string const tablespace = lua_tostring(lua_state(), -1);
        check_name(tablespace, "index_tablespace");
        new_table.set_index_tablespace(tablespace);
    }
    lua_pop(lua_state(), 1);

    return new_table;
}

void output_flex_t::setup_id_columns(flex_table_t *table)
{
    assert(table);
    lua_getfield(lua_state(), -1, "ids");
    if (lua_type(lua_state(), -1) != LUA_TTABLE) {
        log_warn("Table '{}' doesn't have an id column. Two-stage"
                 " processing, updates and expire will not work!",
                 table->name());
        lua_pop(lua_state(), 1); // ids
        return;
    }

    std::string const type{
        luaX_get_table_string(lua_state(), "type", -1, "The ids field")};

    if (type == "node") {
        table->set_id_type(osmium::item_type::node);
    } else if (type == "way") {
        table->set_id_type(osmium::item_type::way);
    } else if (type == "relation") {
        table->set_id_type(osmium::item_type::relation);
    } else if (type == "area") {
        table->set_id_type(osmium::item_type::area);
    } else if (type == "any") {
        table->set_id_type(osmium::item_type::undefined);
        lua_getfield(lua_state(), -2, "type_column");
        if (lua_isstring(lua_state(), -1)) {
            std::string const column_name =
                lua_tolstring(lua_state(), -1, nullptr);
            check_name(column_name, "column");
            auto &column = table->add_column(column_name, "id_type", "");
            column.set_not_null();
        } else if (!lua_isnil(lua_state(), -1)) {
            throw std::runtime_error{"type_column must be a string or nil."};
        }
        lua_pop(lua_state(), 1); // type_column
    } else {
        throw std::runtime_error{"Unknown ids type: {}."_format(type)};
    }

    std::string const name =
        luaX_get_table_string(lua_state(), "id_column", -2, "The ids field");
    check_name(name, "column");

    auto &column = table->add_column(name, "id_num", "");
    column.set_not_null();
    lua_pop(lua_state(), 3); // id_column, type, ids
}

void output_flex_t::setup_flex_table_columns(flex_table_t *table)
{
    assert(table);
    lua_getfield(lua_state(), -1, "columns");
    if (lua_type(lua_state(), -1) != LUA_TTABLE) {
        throw std::runtime_error{
            "No columns defined for table '{}'."_format(table->name())};
    }

    std::size_t num_columns = 0;
    lua_pushnil(lua_state());
    while (lua_next(lua_state(), -2) != 0) {
        if (!lua_isnumber(lua_state(), -2)) {
            throw std::runtime_error{
                "The 'columns' field must contain an array."};
        }
        if (!lua_istable(lua_state(), -1)) {
            throw std::runtime_error{
                "The entries in the 'columns' array must be tables."};
        }

        char const *const type = luaX_get_table_string(lua_state(), "type", -1,
                                                       "Column entry", "text");
        char const *const name =
            luaX_get_table_string(lua_state(), "column", -2, "Column entry");
        check_name(name, "column");
        char const *const sql_type = luaX_get_table_string(
            lua_state(), "sql_type", -3, "Column entry", "");

        auto &column = table->add_column(name, type, sql_type);

        column.set_not_null(luaX_get_table_bool(lua_state(), "not_null", -4,
                                                "Entry 'not_null'", false));

        column.set_create_only(luaX_get_table_bool(
            lua_state(), "create_only", -5, "Entry 'create_only'", false));

        lua_getfield(lua_state(), -6, "projection");
        if (!lua_isnil(lua_state(), -1)) {
            if (column.is_geometry_column() ||
                column.type() == table_column_type::area) {
                column.set_projection(lua_tostring(lua_state(), -1));
            } else {
                throw std::runtime_error{
                    "Projection can only be set on geometry and area columns."};
            }
        }

        // stack has: projection, create_only, not_null, sql_type, column,
        //            type, table
        lua_pop(lua_state(), 7);
        ++num_columns;
    }

    if (num_columns == 0) {
        throw std::runtime_error{
            "No columns defined for table '{}'."_format(table->name())};
    }
}

int output_flex_t::app_define_table()
{
    if (m_calling_context != calling_context::main) {
        throw std::runtime_error{
            "Database tables have to be defined in the"
            " main Lua code, not in any of the callbacks."};
    }

    luaL_checktype(lua_state(), 1, LUA_TTABLE);

    auto &new_table = create_flex_table();
    setup_id_columns(&new_table);
    setup_flex_table_columns(&new_table);

    lua_pushlightuserdata(lua_state(), (void *)(m_tables->size()));
    luaL_getmetatable(lua_state(), osm2pgsql_table_name);
    lua_setmetatable(lua_state(), -2);

    return 1;
}

// Check that the first element on the Lua stack is an osm2pgsql.table
// parameter and return its internal table index.
static std::size_t table_idx_from_param(lua_State *lua_state)
{
    void const *const user_data = lua_touserdata(lua_state, 1);

    if (user_data == nullptr || !lua_getmetatable(lua_state, 1)) {
        throw std::runtime_error{
            "First parameter must be of type osm2pgsql.table."};
    }

    luaL_getmetatable(lua_state, osm2pgsql_table_name);
    if (!lua_rawequal(lua_state, -1, -2)) {
        throw std::runtime_error{
            "First parameter must be of type osm2pgsql.table."};
    }
    lua_pop(lua_state, 2);

    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
    return reinterpret_cast<uintptr_t>(user_data) - 1;
}

// Get the flex table that is as first parameter on the Lua stack.
flex_table_t const &output_flex_t::get_table_from_param()
{
    if (lua_gettop(lua_state()) != 1) {
        throw std::runtime_error{
            "Need exactly one parameter of type osm2pgsql.table."};
    }

    auto const &table = m_tables->at(table_idx_from_param(lua_state()));
    lua_remove(lua_state(), 1);
    return table;
}

int output_flex_t::table_tostring()
{
    auto const &table = get_table_from_param();

    std::string const str{"osm2pgsql.table[{}]"_format(table.name())};
    lua_pushstring(lua_state(), str.c_str());

    return 1;
}

bool output_flex_t::way_cache_t::init(middle_query_t const &middle, osmid_t id)
{
    m_buffer.clear();
    m_num_way_nodes = std::numeric_limits<std::size_t>::max();

    if (!middle.way_get(id, &m_buffer)) {
        return false;
    }
    m_way = &m_buffer.get<osmium::Way>(0);
    return true;
}

void output_flex_t::way_cache_t::init(osmium::Way *way)
{
    m_buffer.clear();
    m_num_way_nodes = std::numeric_limits<std::size_t>::max();

    m_way = way;
}

std::size_t output_flex_t::way_cache_t::add_nodes(middle_query_t const &middle)
{
    if (m_num_way_nodes == std::numeric_limits<std::size_t>::max()) {
        m_num_way_nodes = middle.nodes_get_list(&m_way->nodes());
    }

    return m_num_way_nodes;
}

bool output_flex_t::relation_cache_t::init(middle_query_t const &middle,
                                           osmid_t id)
{
    m_relation_buffer.clear();
    m_members_buffer.clear();

    if (!middle.relation_get(id, &m_relation_buffer)) {
        return false;
    }
    m_relation = &m_relation_buffer.get<osmium::Relation>(0);
    return true;
}

void output_flex_t::relation_cache_t::init(osmium::Relation const &relation)
{
    m_relation_buffer.clear();
    m_members_buffer.clear();

    m_relation = &relation;
}

bool output_flex_t::relation_cache_t::add_members(middle_query_t const &middle)
{
    if (m_members_buffer.committed() == 0) {
        auto const num_members = middle.rel_members_get(
            *m_relation, &m_members_buffer,
            osmium::osm_entity_bits::node | osmium::osm_entity_bits::way);

        if (num_members == 0) {
            return false;
        }

        for (auto &nodes : m_members_buffer.select<osmium::WayNodeList>()) {
            middle.nodes_get_list(&nodes);
        }

        for (auto &way : m_members_buffer.select<osmium::Way>()) {
            middle.nodes_get_list(&(way.nodes()));
        }
    }

    return true;
}

int output_flex_t::table_add_row()
{
    if (m_disable_add_row) {
        return 0;
    }

    if (m_calling_context != calling_context::process_node &&
        m_calling_context != calling_context::process_way &&
        m_calling_context != calling_context::process_relation) {
        throw std::runtime_error{
            "The function add_row() can only be called from the "
            "process_node/way/relation() functions."};
    }

    // Params are the table object and an optional Lua table with the contents
    // for the fields.
    auto const num_params = lua_gettop(lua_state());
    if (num_params < 1 || num_params > 2) {
        throw std::runtime_error{
            "Need two parameters: The osm2pgsql.table and the row data."};
    }

    auto &table_connection =
        m_table_connections.at(table_idx_from_param(lua_state()));
    auto const &table = table_connection.table();

    // It there is a second parameter, it must be a Lua table.
    if (num_params == 2) {
        luaL_checktype(lua_state(), 2, LUA_TTABLE);
    }
    lua_remove(lua_state(), 1);

    if (m_calling_context == calling_context::process_node) {
        if (!table.matches_type(osmium::item_type::node)) {
            throw std::runtime_error{
                "Trying to add node to table '{}'."_format(table.name())};
        }
        add_row(&table_connection, *m_context_node);
    } else if (m_calling_context == calling_context::process_way) {
        if (!table.matches_type(osmium::item_type::way)) {
            throw std::runtime_error{
                "Trying to add way to table '{}'."_format(table.name())};
        }
        add_row(&table_connection, m_way_cache.get());
    } else if (m_calling_context == calling_context::process_relation) {
        if (!table.matches_type(osmium::item_type::relation)) {
            throw std::runtime_error{
                "Trying to add relation to table '{}'."_format(table.name())};
        }
        add_row(&table_connection, m_relation_cache.get());
    }

    return 0;
}

int output_flex_t::table_columns()
{
    auto const &table = get_table_from_param();

    lua_createtable(lua_state(), (int)table.num_columns(), 0);

    int n = 0;
    for (auto const &column : table) {
        lua_pushinteger(lua_state(), ++n);
        lua_newtable(lua_state());

        luaX_add_table_str(lua_state(), "name", column.name().c_str());
        luaX_add_table_str(lua_state(), "type", column.type_name().c_str());
        luaX_add_table_str(lua_state(), "sql_type",
                           column.sql_type_name().c_str());
        luaX_add_table_str(lua_state(), "sql_modifiers",
                           column.sql_modifiers().c_str());
        luaX_add_table_bool(lua_state(), "not_null", column.not_null());
        luaX_add_table_bool(lua_state(), "create_only", column.create_only());

        lua_rawset(lua_state(), -3);
    }
    return 1;
}

int output_flex_t::table_name()
{
    auto const &table = get_table_from_param();
    lua_pushstring(lua_state(), table.name().c_str());
    return 1;
}

int output_flex_t::table_schema()
{
    auto const &table = get_table_from_param();
    lua_pushstring(lua_state(), table.schema().c_str());
    return 1;
}

int output_flex_t::table_cluster()
{
    auto const &table = get_table_from_param();
    lua_pushboolean(lua_state(), table.cluster_by_geom());
    return 1;
}

static std::unique_ptr<geom_transform_t>
get_transform(lua_State *lua_state, flex_table_column_t const &column)
{
    assert(lua_state);
    assert(lua_gettop(lua_state) == 1);

    std::unique_ptr<geom_transform_t> transform{};

    lua_getfield(lua_state, -1, column.name().c_str());
    int const ltype = lua_type(lua_state, -1);
    if (ltype != LUA_TTABLE) {
        lua_pop(lua_state, 1); // geom field
        return transform;
    }

    lua_getfield(lua_state, -1, "create");
    char const *create_type = lua_tostring(lua_state, -1);
    if (create_type == nullptr) {
        throw std::runtime_error{
            "Missing geometry transformation for column '{}'."_format(
                column.name())};
    }

    transform = create_geom_transform(create_type);
    lua_pop(lua_state, 1); // 'create' field
    init_geom_transform(transform.get(), lua_state);
    if (!transform->is_compatible_with(column.type())) {
        throw std::runtime_error{
            "Geometry transformation is not compatible "
            "with column type '{}'."_format(column.type_name())};
    }

    lua_pop(lua_state, 1); // geom field

    return transform;
}

static geom_transform_t const *
get_default_transform(flex_table_column_t const &column,
                      osmium::item_type object_type)
{
    static geom_transform_point_t const default_transform_node_to_point{};
    static geom_transform_line_t const default_transform_way_to_line{};
    static geom_transform_area_t const default_transform_way_to_area{};

    switch (object_type) {
    case osmium::item_type::node:
        if (column.type() == table_column_type::point) {
            return &default_transform_node_to_point;
        }
        break;
    case osmium::item_type::way:
        if (column.type() == table_column_type::linestring) {
            return &default_transform_way_to_line;
        }
        if (column.type() == table_column_type::polygon) {
            return &default_transform_way_to_area;
        }
        break;
    default:
        break;
    }

    throw std::runtime_error{
        "Missing geometry transformation for column '{}'."_format(
            column.name())};
}

geom::geometry_t output_flex_t::run_transform(reprojection const &proj,
                                              geom_transform_t const *transform,
                                              osmium::Node const &node)
{
    return transform->convert(proj, node);
}

geom::geometry_t output_flex_t::run_transform(reprojection const &proj,
                                              geom_transform_t const *transform,
                                              osmium::Way const & /*way*/)
{
    if (m_way_cache.add_nodes(middle()) <= 1U) {
        return {};
    }

    return transform->convert(proj, m_way_cache.get());
}

geom::geometry_t output_flex_t::run_transform(reprojection const &proj,
                                              geom_transform_t const *transform,
                                              osmium::Relation const &relation)
{
    if (!m_relation_cache.add_members(middle())) {
        return {};
    }

    return transform->convert(proj, relation,
                              m_relation_cache.members_buffer());
}

template <typename OBJECT>
void output_flex_t::add_row(table_connection_t *table_connection,
                            OBJECT const &object)
{
    assert(table_connection);
    auto const &table = table_connection->table();

    osmid_t const id = table.map_id(object.type(), object.id());

    if (!table.has_geom_column()) {
        write_row(table_connection, object.type(), id, {}, 0);
        return;
    }

    // From here we are handling the case where the table has a geometry
    // column. In this case the second parameter to the Lua function add_row()
    // must be present.
    if (lua_gettop(lua_state()) == 0) {
        throw std::runtime_error{
            "Need two parameters: The osm2pgsql.table and the row data."};
    }

    auto const geom_transform = get_transform(lua_state(), table.geom_column());
    assert(lua_gettop(lua_state()) == 1);

    geom_transform_t const *transform = geom_transform.get();

    if (!transform) {
        transform = get_default_transform(table.geom_column(), object.type());
    }

    auto const &proj = table_connection->proj();
    auto const type = table.geom_column().type();

    // The geometry returned by run_transform() is in 4326 if it is a
    // (multi)polygon. If it is a point or linestring, it is already in the
    // target geometry.
    auto const geom = run_transform(proj, transform, object);

    // We need to split a multi geometry into its parts if the geometry
    // column can only take non-multi geometries or if the transform
    // explicitly asked us to split, which is the case when an area
    // transform explicitly set `split_at = 'multi'`.
    bool const split_multi = type == table_column_type::linestring ||
                             type == table_column_type::polygon ||
                             transform->split();

    auto const geoms = geom::split_multi(geom, split_multi);
    for (auto const &sgeom : geoms) {
        m_expire.from_geometry(sgeom, id);
        write_row(table_connection, object.type(), id, sgeom,
                  table.geom_column().srid());
    }
}

void output_flex_t::call_lua_function(prepared_lua_function_t func,
                                      osmium::OSMObject const &object)
{
    m_calling_context = func.context();

    lua_pushvalue(lua_state(), func.index()); // the function to call
    push_osm_object_to_lua_stack(
        lua_state(), object,
        get_options()->extra_attributes); // the single argument

    luaX_set_context(lua_state(), this);
    if (luaX_pcall(lua_state(), 1, func.nresults())) {
        throw std::runtime_error{
            "Failed to execute Lua function 'osm2pgsql.{}':"
            " {}."_format(func.name(), lua_tostring(lua_state(), -1))};
    }

    m_calling_context = calling_context::main;
}

void output_flex_t::get_mutex_and_call_lua_function(
    prepared_lua_function_t func, osmium::OSMObject const &object)
{
    std::lock_guard<std::mutex> guard{lua_mutex};
    call_lua_function(func, object);
}

void output_flex_t::pending_way(osmid_t id)
{
    if (!m_process_way) {
        return;
    }

    if (!m_way_cache.init(middle(), id)) {
        return;
    }

    way_delete(id);

    get_mutex_and_call_lua_function(m_process_way, m_way_cache.get());
}

void output_flex_t::select_relation_members()
{
    if (!m_select_relation_members) {
        return;
    }

    std::lock_guard<std::mutex> guard{lua_mutex};
    call_lua_function(m_select_relation_members, m_relation_cache.get());

    // If the function returned nil there is nothing to be marked.
    if (lua_type(lua_state(), -1) == LUA_TNIL) {
        lua_pop(lua_state(), 1); // return value (nil)
        return;
    }

    if (lua_type(lua_state(), -1) != LUA_TTABLE) {
        throw std::runtime_error{"select_relation_members() returned something "
                                 "other than nil or a table."};
    }

    // We have established that we have a table. Get the 'ways' field...
    lua_getfield(lua_state(), -1, "ways");
    int const ltype = lua_type(lua_state(), -1);

    // No 'ways' field, that is okay, nothing to be marked.
    if (ltype == LUA_TNIL) {
        lua_pop(lua_state(), 2); // return value (a table), ways field (nil)
        return;
    }

    if (ltype != LUA_TTABLE) {
        throw std::runtime_error{
            "Table returned from select_relation_members() contains 'ways' "
            "field, but it isn't an array table."};
    }

    // Iterate over the 'ways' table to get all ids...
    lua_pushnil(lua_state());
    while (lua_next(lua_state(), -2) != 0) {
        if (!lua_isnumber(lua_state(), -2)) {
            throw std::runtime_error{
                "Table returned from select_relation_members() contains 'ways' "
                "field, but it isn't an array table."};
        }

        osmid_t const id = lua_tointeger(lua_state(), -1);
        if (id == 0) {
            throw std::runtime_error{
                "Table returned from select_relation_members() contains 'ways' "
                "field, which must contain an array of non-zero integer way "
                "ids."};
        }

        m_stage2_way_ids->set(id);
        lua_pop(lua_state(), 1); // value pushed by lua_next()
    }

    lua_pop(lua_state(), 2); // return value (a table), ways field (a table)
}

void output_flex_t::select_relation_members(osmid_t id)
{
    if (!m_select_relation_members) {
        return;
    }

    if (!m_relation_cache.init(middle(), id)) {
        return;
    }

    select_relation_members();
}

void output_flex_t::pending_relation(osmid_t id)
{
    if (!m_process_relation && !m_select_relation_members) {
        return;
    }

    if (!m_relation_cache.init(middle(), id)) {
        return;
    }

    select_relation_members();
    delete_from_tables(osmium::item_type::relation, id);

    if (m_process_relation) {
        get_mutex_and_call_lua_function(m_process_relation,
                                        m_relation_cache.get());
    }
}

void output_flex_t::pending_relation_stage1c(osmid_t id)
{
    if (!m_process_relation) {
        return;
    }

    if (!m_relation_cache.init(middle(), id)) {
        return;
    }

    m_disable_add_row = true;
    get_mutex_and_call_lua_function(m_process_relation, m_relation_cache.get());
    m_disable_add_row = false;
}

void output_flex_t::sync()
{
    for (auto &table : m_table_connections) {
        table.sync();
    }
}

void output_flex_t::stop()
{
    for (auto &table : m_table_connections) {
        table.task_set(thread_pool().submit([&]() {
            table.stop(get_options()->slim && !get_options()->droptemp,
                       get_options()->append);
        }));
    }

    if (get_options()->expire_tiles_zoom_min > 0) {
        m_expire.output_and_destroy(
            get_options()->expire_tiles_filename.c_str(),
            get_options()->expire_tiles_zoom_min);
    }
}

void output_flex_t::wait()
{
    for (auto &table : m_table_connections) {
        table.task_wait();
    }
}

void output_flex_t::node_add(osmium::Node const &node)
{
    if (!m_process_node) {
        return;
    }

    m_context_node = &node;
    get_mutex_and_call_lua_function(m_process_node, node);
    m_context_node = nullptr;
}

void output_flex_t::way_add(osmium::Way *way)
{
    assert(way);

    if (!m_process_way) {
        return;
    }

    m_way_cache.init(way);
    get_mutex_and_call_lua_function(m_process_way, m_way_cache.get());
}

void output_flex_t::relation_add(osmium::Relation const &relation)
{
    if (!m_process_relation) {
        return;
    }

    m_relation_cache.init(relation);
    select_relation_members();
    get_mutex_and_call_lua_function(m_process_relation, relation);
}

void output_flex_t::delete_from_table(table_connection_t *table_connection,
                                      osmium::item_type type, osmid_t osm_id)
{
    assert(table_connection);
    auto const id = table_connection->table().map_id(type, osm_id);

    if (m_expire.enabled() && table_connection->table().has_geom_column()) {
        auto const result = table_connection->get_geom_by_id(type, id);

        if (result.num_tuples() == 0) {
            return;
        }

        m_expire.from_result(result, id);
    }

    table_connection->delete_rows_with(type, id);
}

void output_flex_t::delete_from_tables(osmium::item_type type, osmid_t osm_id)
{
    for (auto &table : m_table_connections) {
        if (table.table().matches_type(type) && table.table().has_id_column()) {
            delete_from_table(&table, type, osm_id);
        }
    }
}

/* Delete is easy, just remove all traces of this object. We don't need to
 * worry about finding objects that depend on it, since the same diff must
 * contain the change for that also. */
void output_flex_t::node_delete(osmid_t osm_id)
{
    delete_from_tables(osmium::item_type::node, osm_id);
}

void output_flex_t::way_delete(osmid_t osm_id)
{
    delete_from_tables(osmium::item_type::way, osm_id);
}

void output_flex_t::relation_delete(osmid_t osm_id)
{
    select_relation_members(osm_id);
    delete_from_tables(osmium::item_type::relation, osm_id);
}

void output_flex_t::node_modify(osmium::Node const &node)
{
    node_delete(node.id());
    node_add(node);
}

void output_flex_t::way_modify(osmium::Way *way)
{
    way_delete(way->id());
    way_add(way);
}

void output_flex_t::relation_modify(osmium::Relation const &rel)
{
    relation_delete(rel.id());
    relation_add(rel);
}

void output_flex_t::init_clone()
{
    for (auto &table : m_table_connections) {
        table.connect(get_options()->database_options.conninfo());
        table.prepare();
    }
}

void output_flex_t::start()
{
    for (auto &table : m_table_connections) {
        table.connect(get_options()->database_options.conninfo());
        table.start(get_options()->append);
    }
}

std::shared_ptr<output_t>
output_flex_t::clone(std::shared_ptr<middle_query_t> const &mid,
                     std::shared_ptr<db_copy_thread_t> const &copy_thread) const
{
    return std::make_shared<output_flex_t>(
        mid, m_thread_pool, *get_options(), copy_thread, true, m_lua_state,
        m_process_node, m_process_way, m_process_relation,
        m_select_relation_members, m_tables, m_stage2_way_ids);
}

output_flex_t::output_flex_t(
    std::shared_ptr<middle_query_t> const &mid,
    std::shared_ptr<thread_pool_t> thread_pool, options_t const &o,
    std::shared_ptr<db_copy_thread_t> const &copy_thread, bool is_clone,
    std::shared_ptr<lua_State> lua_state, prepared_lua_function_t process_node,
    prepared_lua_function_t process_way,
    prepared_lua_function_t process_relation,
    prepared_lua_function_t select_relation_members,
    std::shared_ptr<std::vector<flex_table_t>> tables,
    std::shared_ptr<idset_t> stage2_way_ids)
: output_t(mid, std::move(thread_pool), o), m_tables(std::move(tables)),
  m_stage2_way_ids(std::move(stage2_way_ids)), m_copy_thread(copy_thread),
  m_lua_state(std::move(lua_state)),
  m_expire(o.expire_tiles_zoom, o.expire_tiles_max_bbox, o.projection),
  m_process_node(process_node), m_process_way(process_way),
  m_process_relation(process_relation),
  m_select_relation_members(select_relation_members)
{
    assert(copy_thread);

    if (!is_clone) {
        init_lua(get_options()->style);

        // If the osm2pgsql.select_relation_members() Lua function is defined
        // it means we need two-stage processing which in turn means we need
        // the full ways stored in the middle.
        if (m_select_relation_members) {
            m_output_requirements.full_ways = true;
        }
    }

    if (m_tables->empty()) {
        throw std::runtime_error{
            "No tables defined in Lua config. Nothing to do!"};
    }

    assert(m_table_connections.empty());
    for (auto &table : *m_tables) {
        m_table_connections.emplace_back(&table, m_copy_thread);
    }

    if (is_clone) {
        init_clone();
    }
}

void output_flex_t::init_lua(std::string const &filename)
{
    m_lua_state.reset(luaL_newstate(),
                      [](lua_State *state) { lua_close(state); });

    // Set up global lua libs
    luaL_openlibs(lua_state());

    // Set up global "osm2pgsql" object
    lua_newtable(lua_state());

    luaX_add_table_str(lua_state(), "version", get_osm2pgsql_short_version());
    luaX_add_table_str(lua_state(), "mode",
                       get_options()->append ? "append" : "create");
    luaX_add_table_int(lua_state(), "stage", 1);

    std::string dir_path =
        boost::filesystem::path{filename}.parent_path().string();
    if (!dir_path.empty()) {
        dir_path += boost::filesystem::path::preferred_separator;
    }
    luaX_add_table_str(lua_state(), "config_dir", dir_path.c_str());

    luaX_add_table_func(lua_state(), "define_table",
                        lua_trampoline_app_define_table);

    lua_setglobal(lua_state(), "osm2pgsql");

    // Define "osmpgsql.table" metatable
    if (luaL_newmetatable(lua_state(), osm2pgsql_table_name) != 1) {
        throw std::runtime_error{"Internal error: Lua newmetatable failed."};
    }
    lua_pushvalue(lua_state(), -1);
    lua_setfield(lua_state(), -2, "__index");
    luaX_add_table_func(lua_state(), "__tostring",
                        lua_trampoline_table_tostring);
    luaX_add_table_func(lua_state(), "add_row", lua_trampoline_table_add_row);
    luaX_add_table_func(lua_state(), "name", lua_trampoline_table_name);
    luaX_add_table_func(lua_state(), "schema", lua_trampoline_table_schema);
    luaX_add_table_func(lua_state(), "cluster", lua_trampoline_table_cluster);
    luaX_add_table_func(lua_state(), "columns", lua_trampoline_table_columns);

    // Clean up stack
    lua_settop(lua_state(), 0);

    // Load compiled in init.lua
    if (luaL_dostring(lua_state(), lua_init())) {
        throw std::runtime_error{"Internal error in Lua setup: {}."_format(
            lua_tostring(lua_state(), -1))};
    }

    // Store the "get_bbox" in the "object_metatable".
    lua_getglobal(lua_state(), "object_metatable");
    lua_getfield(lua_state(), -1, "__index");
    luaX_add_table_func(lua_state(), "get_bbox", lua_trampoline_app_get_bbox);
    lua_settop(lua_state(), 0);

    // Store the global object "object_metatable" defined in the init.lua
    // script in the registry and then remove the global object. It will
    // later be used as metatable for OSM objects.
    lua_pushlightuserdata(lua_state(), (void *)osm2pgsql_object_metatable);
    lua_getglobal(lua_state(), "object_metatable");
    lua_settable(lua_state(), LUA_REGISTRYINDEX);
    lua_pushnil(lua_state());
    lua_setglobal(lua_state(), "object_metatable");

    // Load user config file
    luaX_set_context(lua_state(), this);
    if (luaL_dofile(lua_state(), filename.c_str())) {
        throw std::runtime_error{"Error loading lua config: {}."_format(
            lua_tostring(lua_state(), -1))};
    }

    // Check whether the process_* functions are available and store them on
    // the Lua stack for fast access later
    lua_getglobal(lua_state(), "osm2pgsql");

    m_process_node = prepared_lua_function_t{
        lua_state(), calling_context::process_node, "process_node"};
    m_process_way = prepared_lua_function_t{
        lua_state(), calling_context::process_way, "process_way"};
    m_process_relation = prepared_lua_function_t{
        lua_state(), calling_context::process_relation, "process_relation"};
    m_select_relation_members = prepared_lua_function_t{
        lua_state(), calling_context::select_relation_members,
        "select_relation_members", 1};

    lua_remove(lua_state(), 1); // global "osm2pgsql"
}

idset_t const &output_flex_t::get_marked_way_ids()
{
    if (m_stage2_way_ids->empty()) {
        log_info("Skipping stage 1c (no marked ways).");
    } else {
        log_info("Entering stage 1c processing of {} ways..."_format(
            m_stage2_way_ids->size()));
        m_stage2_way_ids->sort_unique();
    }

    return *m_stage2_way_ids;
}

void output_flex_t::reprocess_marked()
{
    if (m_stage2_way_ids->empty()) {
        log_info("No marked ways (Skipping stage 2).");
        return;
    }

    log_info("Reprocess marked ways (stage 2)...");

    if (!get_options()->append) {
        util::timer_t timer;

        for (auto &table : m_table_connections) {
            if (table.table().matches_type(osmium::item_type::way) &&
                table.table().has_id_column()) {
                table.create_id_index();
            }
        }

        log_info("Creating id indexes took {}"_format(
            util::human_readable_duration(timer.stop())));
    }

    lua_gc(lua_state(), LUA_GCCOLLECT, 0);
    log_debug("Lua program uses {} MBytes",
              lua_gc(lua_state(), LUA_GCCOUNT, 0) / 1024);

    lua_getglobal(lua_state(), "osm2pgsql");
    lua_pushinteger(lua_state(), 2);
    lua_setfield(lua_state(), -2, "stage");
    lua_pop(lua_state(), 1); // osm2pgsql

    m_stage2_way_ids->sort_unique();

    log_info(
        "There are {} ways to reprocess..."_format(m_stage2_way_ids->size()));

    for (osmid_t const id : *m_stage2_way_ids) {
        if (!m_way_cache.init(middle(), id)) {
            continue;
        }
        way_delete(id);
        if (m_process_way) {
            get_mutex_and_call_lua_function(m_process_way, m_way_cache.get());
        }
    }

    // We don't need these any more so can free the memory.
    m_stage2_way_ids->clear();
}

void output_flex_t::merge_expire_trees(output_t *other)
{
    auto *opgsql = dynamic_cast<output_flex_t *>(other);
    if (opgsql) {
        m_expire.merge_and_destroy(&opgsql->m_expire);
    }
}

ext: cpp


text:
#include "communicator.h"
#include <pthread.h>
#include <stdexcept>
#include <boost/asio.hpp>
#include <iostream>
#include <unistd.h>

// This global variable is necessary in order to pass objects between threads and calling function
static void* shared_memory[4];

// The main loop of the receiver thread.
void* reciever_main_loop(void* args)
{
	// Get values of shared memory.
	bool* is_active = ((bool**)shared_memory)[3];
	std::queue<Message>* incoming_msg = ((std::queue<Message>**)shared_memory)[2];

	// Set up socket.
	boost::asio::io_service comm_service;
	boost::asio::ip::udp::socket comm_socket(comm_service, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), UDP_RX_PORT));

	boost::asio::ip::udp::endpoint recieved_endpoint;
	unsigned char recv_buffer[65537];

	while(*is_active)
	{
		// This function blocks until something is received.
		comm_socket.receive_from(boost::asio::buffer(recv_buffer, 65537), recieved_endpoint);

		// Place recieved packet into the buffer.
		Packet p(recv_buffer);
		incoming_msg->push(package_message(p, recieved_endpoint.address().to_string()));
	}

	pthread_exit(nullptr);
}

// The main loop of the transceiver thread.
void* transciever_main_loop(void* args)
{
	// Get values from shared memory.
	bool* is_active = ((bool**)shared_memory)[0];
	std::queue<Message>* outgoing_msg = ((std::queue<Message>**)shared_memory)[1];

	// Set up socket.
	boost::asio::io_service comm_service;
	boost::asio::ip::udp::socket comm_socket(comm_service, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), UDP_TX_PORT));

	while(*is_active)
	{
		// Loop through the queue of messages to send.
		while(!(outgoing_msg->empty()))
		{
			// Send each message out.
			Message m = outgoing_msg->front();
			boost::asio::ip::udp::endpoint destination(boost::asio::ip::address::from_string(get_endpoint(m)), UDP_RX_PORT);
			comm_socket.send_to(boost::asio::buffer(get_packet(m).bytestream(), get_packet(m).size()), destination);
			outgoing_msg->pop();
		}

		// Sleep is necessary because there are no blocking calls here - this is every 100ms
		usleep(100000);
	}

	pthread_exit(nullptr);
}

Message package_message(const Packet& p, std::string endpoint)
{
	Message m(p, endpoint);

	return m;
}

Packet get_packet(const Message& m)
{
	return m.first;
}

std::string get_endpoint(const Message& m)
{
	return m.second;
}

Communicator::Communicator()
{
	this->rx_active = false;
	this->tx_active = false;

	this->start();
}

Communicator::~Communicator()
{
	this->kill();
}

void Communicator::start()
{
	// If the transmitter thread is not active, activate it
	if(!this->tx_active)
	{
		this->tx_active = true;

		shared_memory[0] = (void*)&this->tx_active;
		shared_memory[1] = (void*)&this->outgoing_msg;

		if(pthread_create(&this->thread_trans, nullptr, &transciever_main_loop, nullptr))
		{
			this->tx_active = false;
			throw std::runtime_error("Cannot create transciever thread");
		}

	}

	// If the receiver thread is not active, activate it
	if(!this->rx_active)
	{
		this->rx_active = true;

		shared_memory[2] = (void*)&this->incoming_msg;
		shared_memory[3] = (void*)&this->rx_active;

		if(pthread_create(&this->thread_recv, nullptr, &reciever_main_loop, nullptr))
		{
			this->rx_active = false;
			throw std::runtime_error("Cannot create reciever thread");
		}
	}
}

void Communicator::kill()
{
	if(this->rx_active)
	{
		// This process is necessary because of the blocking receive call.
		this->rx_active = false;
		usleep(100000);
		pthread_cancel(this->thread_recv);
		pthread_join(this->thread_recv, nullptr);
	}

	if(this->tx_active)
	{
		this->tx_active = false;
		pthread_join(this->thread_trans, nullptr);
	}
}

void Communicator::send_message(const Message m)
{
	this->outgoing_msg.push(m);
}

bool Communicator::message_available()
{
	return !(this->incoming_msg.empty());
}

Message Communicator::read_message()
{
	if(this->incoming_msg.empty())
		throw std::runtime_error("Communicator::read_message() cannot read message from empty message list");

	Message m = this->incoming_msg.front();
	this->incoming_msg.pop();
	return m;
}

max_stars_repo_name: spinach-juice/SSRFTP

max_issues_repo_name: spinach-juice/SSRFTP

max_forks_repo_name: spinach-juice/SSRFTP