std::unique_ptr<typename ElementaryPotentialOperator<
    BasisFunctionType, KernelType, ResultType>::LocalAssembler>
ElementaryPotentialOperator<BasisFunctionType, KernelType, ResultType>::
    makeAssembler(const Space<BasisFunctionType> &space,
                  const arma::Mat<CoordinateType> &evaluationPoints,
                  const QuadratureStrategy &quadStrategy,
                  const EvaluationOptions &options) const {
  // Collect the standard set of data necessary for construction of
  // assemblers
  typedef Fiber::RawGridGeometry<CoordinateType> RawGridGeometry;
  typedef std::vector<const Fiber::Shapeset<BasisFunctionType> *>
  ShapesetPtrVector;
  typedef std::vector<std::vector<ResultType>> CoefficientsVector;
  typedef LocalAssemblerConstructionHelper Helper;

  shared_ptr<RawGridGeometry> rawGeometry;
  shared_ptr<GeometryFactory> geometryFactory;
  shared_ptr<Fiber::OpenClHandler> openClHandler;
  shared_ptr<ShapesetPtrVector> shapesets;

  shared_ptr<const Grid> grid = space.grid();
  Helper::collectGridData(space, rawGeometry, geometryFactory);
  Helper::makeOpenClHandler(options.parallelizationOptions().openClOptions(),
                            rawGeometry, openClHandler);
  Helper::collectShapesets(space, shapesets);

  // Now create the assembler
  return quadStrategy.makeAssemblerForPotentialOperators(
      evaluationPoints, geometryFactory, rawGeometry, shapesets,
      make_shared_from_ref(kernels()),
      make_shared_from_ref(trialTransformations()),
      make_shared_from_ref(integral()), openClHandler,
      options.parallelizationOptions(), options.verbosityLevel());
}
std::unique_ptr<typename ElementaryLocalOperator<BasisFunctionType,
                                                 ResultType>::LocalAssembler>
ElementaryLocalOperator<BasisFunctionType, ResultType>::makeAssembler(
    const QuadratureStrategy &quadStrategy,
    const AssemblyOptions &options) const {
  typedef Fiber::RawGridGeometry<CoordinateType> RawGridGeometry;
  typedef std::vector<const Fiber::Shapeset<BasisFunctionType> *>
      ShapesetPtrVector;

  shared_ptr<RawGridGeometry> testRawGeometry, trialRawGeometry;
  shared_ptr<GeometryFactory> testGeometryFactory, trialGeometryFactory;
  shared_ptr<Fiber::OpenClHandler> openClHandler;
  shared_ptr<ShapesetPtrVector> testShapesets, trialShapesets;
  bool cacheSingularIntegrals;

  this->collectDataForAssemblerConstruction(
      options, testRawGeometry, trialRawGeometry, testGeometryFactory,
      trialGeometryFactory, testShapesets, trialShapesets, openClHandler,
      cacheSingularIntegrals);
  assert(testRawGeometry == trialRawGeometry);
  assert(testGeometryFactory == trialGeometryFactory);

  return quadStrategy.makeAssemblerForLocalOperators(
      testGeometryFactory, testRawGeometry, testShapesets, trialShapesets,
      make_shared_from_ref(testTransformations()),
      make_shared_from_ref(trialTransformations()),
      make_shared_from_ref(integral()), openClHandler);
}
shared_ptr<const ComponentLists> ComponentListsCache::get(int start,
                                                          int indexCount) {
  if (indexCount == 1) {
    shared_ptr<ComponentLists> result(new ComponentLists);
    findComponents(start, result->pointIndices, result->componentIndices,
                   result->arrayIndices);
    return result;
  }

  std::pair<int, int> key(start, indexCount);
  ComponentListsMap::const_iterator it = m_map.find(key);
  if (it != m_map.end()) {
    return make_shared_from_ref(*it->second);
  }

  // The relevant local DOF list doesn't exist yet and must be created.
  ComponentLists *newLists = new ComponentLists;
  findComponents(start, indexCount, newLists->pointIndices,
                 newLists->componentIndices, newLists->arrayIndices);

  // Attempt to insert the newly created DOF list into the map
  std::pair<ComponentListsMap::iterator, bool> result =
      m_map.insert(std::make_pair(key, newLists));
  if (result.second)
    // Insertion succeeded. The newly created DOF list will be deleted in
    // our own destructor
    ;
  else
    // Insertion failed -- another thread was faster. Delete the newly
    // created DOF list.
    delete newLists;

  // Return pointer to the DOF list that ended up in the map.
  return make_shared_from_ref(*result.first->second);
}
std::auto_ptr<typename ElementaryPotentialOperator<
BasisFunctionType, KernelType, ResultType>::Evaluator>
ElementaryPotentialOperator<BasisFunctionType, KernelType, ResultType>::
makeEvaluator(
        const GridFunction<BasisFunctionType, ResultType>& argument,
        const QuadratureStrategy& quadStrategy,
        const EvaluationOptions& options) const
{
    // Collect the standard set of data necessary for construction of
    // evaluators and assemblers
    typedef Fiber::RawGridGeometry<CoordinateType> RawGridGeometry;
    typedef std::vector<const Fiber::Shapeset<BasisFunctionType>*> ShapesetPtrVector;
    typedef std::vector<std::vector<ResultType> > CoefficientsVector;
    typedef LocalAssemblerConstructionHelper Helper;

    shared_ptr<RawGridGeometry> rawGeometry;
    shared_ptr<GeometryFactory> geometryFactory;
    shared_ptr<Fiber::OpenClHandler> openClHandler;
    shared_ptr<ShapesetPtrVector> shapesets;

    const Space<BasisFunctionType>& space = *argument.space();
    shared_ptr<const Grid> grid = space.grid();
    Helper::collectGridData(space,
                            rawGeometry, geometryFactory);
    Helper::makeOpenClHandler(options.parallelizationOptions().openClOptions(),
                              rawGeometry, openClHandler);
    Helper::collectShapesets(space, shapesets);

    // In addition, get coefficients of argument's expansion in each element
    const GridView& view = space.gridView();
    const int elementCount = view.entityCount(0);

    shared_ptr<CoefficientsVector> localCoefficients =
            boost::make_shared<CoefficientsVector>(elementCount);

    std::auto_ptr<EntityIterator<0> > it = view.entityIterator<0>();
    for (int i = 0; i < elementCount; ++i) {
        const Entity<0>& element = it->entity();
        argument.getLocalCoefficients(element, (*localCoefficients)[i]);
        it->next();
    }

    // Now create the evaluator
    return quadStrategy.makeEvaluatorForIntegralOperators(
                geometryFactory, rawGeometry,
                shapesets,
                make_shared_from_ref(kernels()),
                make_shared_from_ref(trialTransformations()),
                make_shared_from_ref(integral()),
                localCoefficients,
                openClHandler,
                options.parallelizationOptions());
}
arma::Mat<ResultType>
ElementaryPotentialOperator<BasisFunctionType, KernelType, ResultType>::
    evaluateAtPoints(
        const GridFunction<BasisFunctionType, ResultType> &argument,
        const arma::Mat<CoordinateType> &evaluationPoints,
        const QuadratureStrategy &quadStrategy,
        const EvaluationOptions &options) const {
  if (evaluationPoints.n_rows != argument.grid()->dimWorld())
    throw std::invalid_argument(
        "ElementaryPotentialOperator::evaluateAtPoints(): "
        "the number of coordinates of each evaluation point must be "
        "equal to the dimension of the space containing the surface "
        "on which the grid function 'argument' is defined");

  if (options.evaluationMode() == EvaluationOptions::DENSE) {
    std::unique_ptr<Evaluator> evaluator =
        makeEvaluator(argument, quadStrategy, options);

    // right now we don't bother about far and near field
    // (this might depend on evaluation options)
    arma::Mat<ResultType> result;
    evaluator->evaluate(Evaluator::FAR_FIELD, evaluationPoints, result);
    return result;
  } else if (options.evaluationMode() == EvaluationOptions::ACA) {
    AssembledPotentialOperator<BasisFunctionType, ResultType> assembledOp =
        assemble(argument.space(), make_shared_from_ref(evaluationPoints),
                 quadStrategy, options);
    return assembledOp.apply(argument);
  } else
    throw std::invalid_argument(
        "ElementaryPotentialOperator::evaluateAtPoints(): "
        "Invalid evaluation mode");
}
PyObject*
calculateProjections(const ParameterList& parameterList,
                     PyObject* callable,
                     const Space<BasisFunctionType> &dualSpace) {
  
  const Context<BasisFunctionType, ResultType> context(parameterList);


  auto globalFunction =  shared_ptr<Fiber::Function<ResultType>>(
     new Fiber::SurfaceNormalAndDomainIndexDependentFunction<PythonFunctor<ResultType>>(
         PythonFunctor<ResultType>(callable,3,dualSpace.codomainDimension())));

  const AssemblyOptions &options = context.assemblyOptions();

  // Prepare local assembler
  typedef typename Fiber::ScalarTraits<ResultType>::RealType CoordinateType;
  typedef Fiber::RawGridGeometry<CoordinateType> RawGridGeometry;
  typedef std::vector<const Fiber::Shapeset<BasisFunctionType> *>
      ShapesetPtrVector;
  typedef LocalAssemblerConstructionHelper Helper;

  shared_ptr<RawGridGeometry> rawGeometry;
  shared_ptr<GeometryFactory> geometryFactory;
  shared_ptr<Fiber::OpenClHandler> openClHandler;
  shared_ptr<ShapesetPtrVector> testShapesets;

  Helper::collectGridData(dualSpace, rawGeometry, geometryFactory);
  Helper::makeOpenClHandler(options.parallelizationOptions().openClOptions(),
                            rawGeometry, openClHandler);
  Helper::collectShapesets(dualSpace, testShapesets);

  // Get reference to the test shapeset transformation
  const Fiber::CollectionOfShapesetTransformations<CoordinateType>
      &testTransformations = dualSpace.basisFunctionValue();

  typedef Fiber::LocalAssemblerForGridFunctions<ResultType> LocalAssembler;
  std::unique_ptr<LocalAssembler> assembler =
      context.quadStrategy()->makeAssemblerForGridFunctions(
          geometryFactory, rawGeometry, testShapesets,
          make_shared_from_ref(testTransformations),
          globalFunction, openClHandler);

  Vector<ResultType> result;
  result =  reallyCalculateProjections(dualSpace, *assembler, options);

  npy_intp pyResultDimension = dualSpace.globalDofCount();
  PyObject* pyResult = PyArray_ZEROS(1,&pyResultDimension,NumpyType<ResultType>::value,1);
  ResultType* resPtr = (ResultType*)PyArray_DATA(pyResult);

  for (int i = 0; i< pyResultDimension;++i) resPtr[i] = result(i);

  return pyResult;
}
std::pair<
shared_ptr<typename HypersingularIntegralOperator<
BasisFunctionType, KernelType, ResultType>::LocalAssembler>,
shared_ptr<typename HypersingularIntegralOperator<
BasisFunctionType, KernelType, ResultType>::LocalAssembler>
>
HypersingularIntegralOperator<BasisFunctionType, KernelType, ResultType>::
reallyMakeAssemblers(
        const QuadratureStrategy& quadStrategy,
        const shared_ptr<const GeometryFactory>& testGeometryFactory,
        const shared_ptr<const GeometryFactory>& trialGeometryFactory,
        const shared_ptr<const Fiber::RawGridGeometry<CoordinateType> >& testRawGeometry,
        const shared_ptr<const Fiber::RawGridGeometry<CoordinateType> >& trialRawGeometry,
        const shared_ptr<const std::vector<const Fiber::Shapeset<BasisFunctionType>*> >& testShapesets,
        const shared_ptr<const std::vector<const Fiber::Shapeset<BasisFunctionType>*> >& trialShapesets,
        const shared_ptr<const Fiber::OpenClHandler>& openClHandler,
        const ParallelizationOptions& parallelizationOptions,
        VerbosityLevel::Level verbosityLevel,
        bool cacheSingularIntegrals,
        bool makeSeparateOffDiagonalAssembler) const
{
    std::pair<shared_ptr<LocalAssembler>, shared_ptr<LocalAssembler> > result;
    // first element: "standard" assembler
    // second element: assembler used for admissible (off-diagonal)
    // H-matrix blocks in "disassembled mode"
    result.first.reset(quadStrategy.makeAssemblerForIntegralOperators(
                           testGeometryFactory, trialGeometryFactory,
                           testRawGeometry, trialRawGeometry,
                           testShapesets, trialShapesets,
                           make_shared_from_ref(testTransformations()),
                           make_shared_from_ref(kernels()),
                           make_shared_from_ref(trialTransformations()),
                           make_shared_from_ref(integral()),
                           openClHandler, parallelizationOptions, verbosityLevel,
                           cacheSingularIntegrals).release());
    if (makeSeparateOffDiagonalAssembler)
        result.second.reset(quadStrategy.makeAssemblerForIntegralOperators(
                                testGeometryFactory, trialGeometryFactory,
                                testRawGeometry, trialRawGeometry,
                                testShapesets, trialShapesets,
                                make_shared_from_ref(offDiagonalTestTransformations()),
                                make_shared_from_ref(offDiagonalKernels()),
                                make_shared_from_ref(offDiagonalTrialTransformations()),
                                make_shared_from_ref(offDiagonalIntegral()),
                                openClHandler, parallelizationOptions, verbosityLevel,
                                false /*cacheSingularIntegrals*/).release());
    else
        result.second = result.first;
    return result;
}