// The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt
/*

    This example program shows how to find frontal human faces in an image.  In
    particular, this program shows how you can take a list of images from the
    command line and display each on the screen with red boxes overlaid on each
    human face.

    The examples/faces folder contains some jpg images of people.  You can run
    this program on them and see the detections by executing the following command:
        ./face_detection_ex faces/*.jpg

    This face detector is made using the now classic Histogram of Oriented
    Gradients (HOG) feature combined with a linear classifier, an image pyramid,
    and sliding window detection scheme.  This type of object detector is fairly
    general and capable of detecting many types of semi-rigid objects in
    addition to human faces.  Therefore, if you are interested in making your
    own object detectors then read the fhog_object_detector_ex.cpp example
    program.  It shows how to use the machine learning tools which were used to
    create dlib's face detector.

    Finally, note that the face detector is fastest when compiled with at least
    SSE2 instructions enabled.  So if you are using a PC with an Intel or AMD
    chip then you should enable at least SSE2 instructions.  If you are using
    cmake to compile this program you can enable them by using one of the
    following commands when you create the build project:
        cmake path_to_dlib_root/examples -DUSE_SSE2_INSTRUCTIONS=ON
        cmake path_to_dlib_root/examples -DUSE_SSE4_INSTRUCTIONS=ON
        cmake path_to_dlib_root/examples -DUSE_AVX_INSTRUCTIONS=ON
    This will set the appropriate compiler options for GCC, clang, Visual
    Studio, or the Intel compiler.  If you are using another compiler then you
    need to consult your compiler's manual to determine how to enable these
    instructions.  Note that AVX is the fastest but requires a CPU from at least
    2011.  SSE4 is the next fastest and is supported by most current machines.
*/

#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib/image_processing.h>
#include <dlib/image_io.h>
#include <iostream>
#include <string>
#include <sstream>
#include <ctime>
#include <stdio.h>
using namespace dlib;
using namespace std;

// ----------------------------------------------------------------------------------------

string makeFaceVectorRectanglesToJSON(const std::vector<rectangle>& detected_faces) {
  std::ostringstream json;
  json << "[";
  for (int i = 0; i < detected_faces.size(); i++){
    rectangle face_rect = detected_faces[i];
    json << "{";
    json << "\"x\":" << face_rect.left() / 2 << ",";
    json << "\"y\":" << face_rect.top() / 2 << ",";
    json << "\"width\":" << face_rect.width() / 2 << ",";
    json << "\"height\":" << face_rect.height() / 2;
    json << "}";
    if (i < detected_faces.size() - 1) {
      json << ",";
    }
  }
  json << "]";
  return json.str();
}

string makeFaceRectanglesToJSON(const rectangle& face_rect) {
  std::ostringstream json;
  json << "{";
  json << "\"x\":" << face_rect.left() / 2 << ",";
  json << "\"y\":" << face_rect.top() / 2 << ",";
  json << "\"width\":" << face_rect.width() / 2 << ",";
  json << "\"height\":" << face_rect.height() / 2;
  json << "}";
  return json.str();
}

string makeFaceFeaturesShapeToJSON(const full_object_detection& shape) {
  std::ostringstream json;
  json << "[";
  for (int i = 0; i < shape.num_parts(); i++) {
    point part = shape.part(i);
    json << "{";
    json << "\"x\":" << part.x() / 2 << ",";
    json << "\"y\":" << part.y() / 2;
    json << "}";
    if (i < shape.num_parts() - 1) {
      json << ",";
    }
  }
  json << "]";
  return json.str();
}

int main(int argc, char** argv)
{
    try
    {
        if (argc == 1)
        {
            cout << "Give some image files as arguments to this program." << endl;
            return 0;
        }

        frontal_face_detector detector = get_frontal_face_detector();
        // image_window win;
        // shape_predictor sp;
        // deserialize("/bin/shape_predictor_68_face_landmarks.dat") >> sp;

        // string input = "";
        // cout << "Please enter an image:\n>";
        // getline(cin, input);

        // cout << "processing image " << argv[1] << endl;
        clock_t t0 = clock();
        array2d<unsigned char> img;
        load_image(img, argv[1]);
        clock_t t1 = clock();
        // Make the image bigger by a factor of two.  This is useful since
        // the face detector looks for faces that are about 80 by 80 pixels
        // or larger.  Therefore, if you want to find faces that are smaller
        // than that then you need to upsample the image as we do here by
        // calling pyramid_up().  So this will allow it to detect faces that
        // are at least 40 by 40 pixels in size.  We could call pyramid_up()
        // again to find even smaller faces, but note that every time we
        // upsample the image we make the detector run slower since it must
        // process a larger image.
        pyramid_up(img);
        clock_t t2 = clock();

        // Now tell the face detector to give us a list of bounding boxes
        // around all the faces it can find in the image.
        std::vector<rectangle> detected_faces = detector(img);
        clock_t t3 = clock();

        // cout << "Number of faces detected: " << dets.size() << endl;

        std::ostringstream json;
        json << "[";
        for (int i = 0; i < detected_faces.size(); i++) {
          const rectangle face_rect = detected_faces[i];
          // const full_object_detection shape = sp(img, face_rect);
          const string face = makeFaceRectanglesToJSON(face_rect);
          // const string features = makeFaceFeaturesShapeToJSON(shape);
          json << "{";
          // json << "\"face\":" << face << ",";
          json << "\"face\":" << face;
          // json << "\"features\":" << features;
          json << "}";
          if (i < detected_faces.size() - 1) {
            json << ",";
          }
          // cout << "number of parts: "<< shape.num_parts() << endl;
          // cout << "pixel position of first part:  " << shape.part(0) << endl;
          // cout << "pixel position of second part: " << shape.part(1) << endl;
          // if (shape.num_parts() == 5) {
          //   cout << "{x: " << shape.part(0) << ",y:"<< shape.part(1) << "}" << endl;
          //   cout << "{x: " << shape.part(1) << ",y:"<< shape.part(4) << "}" << endl;
          //   cout << "{x: " << shape.part(4) << ",y:"<< shape.part(3) << "}" << endl;
          //   cout << "{x: " << shape.part(3) << ",y:"<< shape.part(2) << "}" << endl;
          // } else {
          //   cout << "// Around Chin. Ear to Ear" << endl;
          //   for (unsigned long i = 1; i <= 16; ++i)
          //     cout << "{x: " << shape.part(i) << ",y:"<< shape.part(i-1) << "}" << endl;
          //
          //   cout << "// Line on top of nose" << endl;
          //   for (unsigned long i = 28; i <= 30; ++i)
          //     cout << "{x: " << shape.part(i) << ",y:"<< shape.part(i-1) << "}" << endl;
          //
          //   cout << "// left eyebrow" << endl;
          //   for (unsigned long i = 18; i <= 21; ++i)
          //     cout << "{x: " << shape.part(i) << ",y:"<< shape.part(i-1) << "}" << endl;
          //   cout << "// Right eyebrow" << endl;
          //   for (unsigned long i = 23; i <= 26; ++i)
          //     cout << "{x: " << shape.part(i) << ",y:"<< shape.part(i-1) << "}" << endl;
          //   cout << "// Bottom part of the nose" << endl;
          //   for (unsigned long i = 31; i <= 35; ++i)
          //     cout << "{x: " << shape.part(i) << ",y:"<< shape.part(i-1) << "}" << endl;
          //   cout << "// Line from the nose to the bottom part above" << endl;
          //   cout << "{x: " << shape.part(30) << ",y:"<< shape.part(35) << "}" << endl;
          //
          //   cout << "// Left eye" << endl;
          //   for (unsigned long i = 37; i <= 41; ++i)
          //     cout << "{x: " << shape.part(i) << ",y:"<< shape.part(i-1) << "}" << endl;
          //   cout << "{x: " << shape.part(36) << ",y:"<< shape.part(41) << "}" << endl;
          //
          //   cout << "// Right eye" << endl;
          //   for (unsigned long i = 43; i <= 47; ++i)
          //     cout << "{x: " << shape.part(i) << ",y:"<< shape.part(i-1) << "}" << endl;
          //   cout << "{x: " << shape.part(42) << ",y:"<< shape.part(47) << "}" << endl;
          //
          //   cout << "// Lips outer part" << endl;
          //   for (unsigned long i = 49; i <= 59; ++i)
          //     cout << "{x: " << shape.part(i) << ",y:"<< shape.part(i-1) << "}" << endl;
          //   cout << "{x: " << shape.part(48) << ",y:"<< shape.part(59) << "}" << endl;
          //
          //   cout << "// Lips inside part" << endl;
          //   for (unsigned long i = 61; i <= 67; ++i)
          //     cout << "{x: " << shape.part(i) << ",y:"<< shape.part(i-1) << "}" << endl;
          //   cout << "{x: " << shape.part(60) << ",y:"<< shape.part(67) << "}" << endl;
          // }
        }
        json << "]";
        cout << json.str() << endl;

        // cout << "load " << double(t1 - t0) / CLOCKS_PER_SEC << endl;
        // cout << "pyramid " << double(t2 - t1) / CLOCKS_PER_SEC << endl;
        // cout << "detection " << double(t3 - t2) / CLOCKS_PER_SEC << endl;
        // cout << "total " << double(t3 - t0) / CLOCKS_PER_SEC << endl;

        // return 0;

        // // Loop over all the images provided on the command line.
        // for (int i = 1; i < argc; ++i)
        // {
        //     cout << "processing image " << argv[i] << endl;
        //     array2d<unsigned char> img;
        //     load_image(img, argv[i]);
        //     // Make the image bigger by a factor of two.  This is useful since
        //     // the face detector looks for faces that are about 80 by 80 pixels
        //     // or larger.  Therefore, if you want to find faces that are smaller
        //     // than that then you need to upsample the image as we do here by
        //     // calling pyramid_up().  So this will allow it to detect faces that
        //     // are at least 40 by 40 pixels in size.  We could call pyramid_up()
        //     // again to find even smaller faces, but note that every time we
        //     // upsample the image we make the detector run slower since it must
        //     // process a larger image.
        //     pyramid_up(img);
        //
        //     // Now tell the face detector to give us a list of bounding boxes
        //     // around all the faces it can find in the image.
        //     std::vector<rectangle> dets = detector(img);
        //
        //     cout << "Number of faces detected: " << dets.size() << endl;
        //
        //     JSON(dets);
        //
        //     return 0;
        // }

        // remove flag, used to remove the file (argv[1]) after usage.
        for(int i = 0; i < argc; ++i) {
          if (std::string(argv[i]) == "-rm") {
            if (remove(argv[1]) != 0) {
              perror( "Error deleting file" );
            }
            // else {
            //   puts( "File successfully deleted" );
            // }
          }
        }
        return 0;
    }
    catch (exception& e)
    {
        cout << "\nexception thrown!" << endl;
        cout << e.what() << endl;
    }
}

// ----------------------------------------------------------------------------------------


/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

#include <iomanip>
#include <sstream>
#include <vector>

#include <openssl/md5.h>
#include <openssl/sha.h>

#include <boost/filesystem.hpp>

#include <osquery/filesystem.h>
#include <osquery/logger.h>
#include <osquery/tables.h>

#include "osquery/tables/system/hash.h"

namespace osquery {

#define HASH_CHUNK_SIZE 4096

Hash::~Hash() {
  if (ctx_ != nullptr) {
    free(ctx_);
  }
}

Hash::Hash(HashType algorithm) : algorithm_(algorithm) {
  if (algorithm_ == HASH_TYPE_MD5) {
    length_ = MD5_DIGEST_LENGTH;
    ctx_ = (MD5_CTX*)malloc(sizeof(MD5_CTX));
    MD5_Init((MD5_CTX*)ctx_);
  } else if (algorithm_ == HASH_TYPE_SHA1) {
    length_ = SHA_DIGEST_LENGTH;
    ctx_ = (SHA_CTX*)malloc(sizeof(SHA_CTX));
    SHA1_Init((SHA_CTX*)ctx_);
  } else if (algorithm_ == HASH_TYPE_SHA256) {
    length_ = SHA256_DIGEST_LENGTH;
    ctx_ = (SHA256_CTX*)malloc(sizeof(SHA256_CTX));
    SHA256_Init((SHA256_CTX*)ctx_);
  } else {
    throw std::domain_error("Unknown hash function");
  }
}

void Hash::update(const void* buffer, size_t size) {
  if (algorithm_ == HASH_TYPE_MD5) {
    MD5_Update((MD5_CTX*)ctx_, buffer, size);
  } else if (algorithm_ == HASH_TYPE_SHA1) {
    SHA1_Update((SHA_CTX*)ctx_, buffer, size);
  } else if (algorithm_ == HASH_TYPE_SHA256) {
    SHA256_Update((SHA256_CTX*)ctx_, buffer, size);
  }
}

std::string Hash::digest() {
  std::vector<unsigned char> hash;
  hash.assign(length_, '\0');

  if (algorithm_ == HASH_TYPE_MD5) {
    MD5_Final(hash.data(), (MD5_CTX*)ctx_);
  } else if (algorithm_ == HASH_TYPE_SHA1) {
    SHA1_Final(hash.data(), (SHA_CTX*)ctx_);
  } else if (algorithm_ == HASH_TYPE_SHA256) {
    SHA256_Final(hash.data(), (SHA256_CTX*)ctx_);
  }

  // The hash value is only relevant as a hex digest.
  std::stringstream digest;
  for (size_t i = 0; i < length_; i++) {
    digest << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
  }

  return digest.str();
}

std::string hashFromBuffer(HashType hash_type,
                           const void* buffer,
                           size_t size) {
  Hash hash(hash_type);
  hash.update(buffer, size);
  return hash.digest();
}

MultiHashes hashMultiFromFile(int mask, const std::string& path) {
  std::map<HashType, std::shared_ptr<Hash>> hashes = {
      {HASH_TYPE_MD5, std::make_shared<Hash>(HASH_TYPE_MD5)},
      {HASH_TYPE_SHA1, std::make_shared<Hash>(HASH_TYPE_SHA1)},
      {HASH_TYPE_SHA256, std::make_shared<Hash>(HASH_TYPE_SHA256)},
  };

  auto s = readFile(path,
                    0,
                    HASH_CHUNK_SIZE,
                    false,
                    true,
                    ([&hashes, &mask](std::string& buffer, size_t size) {
                      for (auto& hash : hashes) {
                        if (mask & hash.first) {
                          hash.second->update(&buffer[0], size);
                        }
                      }
                    }));

  MultiHashes mh;
  if (!s.ok()) {
    return mh;
  }

  mh.mask = mask;
  if (mask & HASH_TYPE_MD5) {
    mh.md5 = hashes.at(HASH_TYPE_MD5)->digest();
  }
  if (mask & HASH_TYPE_SHA1) {
    mh.sha1 = hashes.at(HASH_TYPE_SHA1)->digest();
  }
  if (mask & HASH_TYPE_SHA256) {
    mh.sha256 = hashes.at(HASH_TYPE_SHA256)->digest();
  }
  return mh;
}

std::string hashFromFile(HashType hash_type, const std::string& path) {
  auto hashes = hashMultiFromFile(hash_type, path);
  if (hash_type == HASH_TYPE_MD5) {
    return hashes.md5;
  } else if (hash_type == HASH_TYPE_SHA1) {
    return hashes.sha1;
  } else {
    return hashes.sha256;
  }
}

namespace tables {

void genHashForFile(const std::string& path,
                    const std::string& dir,
                    QueryContext& context,
                    QueryData& results) {
  // Must provide the path, filename, directory separate from boost path->string
  // helpers to match any explicit (query-parsed) predicate constraints.
  Row r;
  if (context.isCached(path)) {
    r = context.getCache(path);
  } else {
    auto hashes = hashMultiFromFile(
        HASH_TYPE_MD5 | HASH_TYPE_SHA1 | HASH_TYPE_SHA256, path);

    r["path"] = path;
    r["directory"] = dir;
    r["md5"] = std::move(hashes.md5);
    r["sha1"] = std::move(hashes.sha1);
    r["sha256"] = std::move(hashes.sha256);
    context.setCache(path, r);
  }
  results.push_back(r);
}

QueryData genHash(QueryContext& context) {
  QueryData results;
  boost::system::error_code ec;

  // The query must provide a predicate with constraints including path or
  // directory. We search for the parsed predicate constraints with the equals
  // operator.
  auto paths = context.constraints["path"].getAll(EQUALS);
  context.expandConstraints(
      "path",
      LIKE,
      paths,
      ([&](const std::string& pattern, std::set<std::string>& out) {
        std::vector<std::string> patterns;
        auto status =
            resolveFilePattern(pattern, patterns, GLOB_ALL | GLOB_NO_CANON);
        if (status.ok()) {
          for (const auto& resolved : patterns) {
            out.insert(resolved);
          }
        }
        return status;
      }));

  // Iterate through the file paths, adding the hash results
  for (const auto& path_string : paths) {
    boost::filesystem::path path = path_string;
    if (!boost::filesystem::is_regular_file(path, ec)) {
      continue;
    }

    genHashForFile(path_string, path.parent_path().string(), context, results);
  }

  // Now loop through constraints using the directory column constraint.
  auto directories = context.constraints["directory"].getAll(EQUALS);
  context.expandConstraints(
      "directory",
      LIKE,
      directories,
      ([&](const std::string& pattern, std::set<std::string>& out) {
        std::vector<std::string> patterns;
        auto status =
            resolveFilePattern(pattern, patterns, GLOB_FOLDERS | GLOB_NO_CANON);
        if (status.ok()) {
          for (const auto& resolved : patterns) {
            out.insert(resolved);
          }
        }
        return status;
      }));

  // Iterate over the directory paths
  for (const auto& directory_string : directories) {
    boost::filesystem::path directory = directory_string;
    if (!boost::filesystem::is_directory(directory, ec)) {
      continue;
    }

    // Iterate over the directory files and generate a hash for each regular
    // file.
    boost::filesystem::directory_iterator begin(directory), end;
    for (; begin != end; ++begin) {
      if (boost::filesystem::is_regular_file(begin->path(), ec)) {
        genHashForFile(
            begin->path().string(), directory_string, context, results);
      }
    }
  }

  return results;
}
}
}


#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE InfinityFilterTests
#include <boost/test/unit_test.hpp>

#include <vector>
#include <set>

#include "graph/PBQPGraph.hpp"
#include "graph/Vector.hpp"
#include "graph/PBQPNode.hpp"
#include "graph/PBQPEdge.hpp"
#include "reduction/InfinityFilter.hpp"
#include "reduction/degree/DegreeZeroReducer.hpp"
#include "graph/PBQPSolution.hpp"
#include "io/PBQP_Serializer.hpp"

#include "util/TestUtils.hpp"

namespace pbqppapa {

BOOST_AUTO_TEST_CASE(reduction) {
	PBQP_Serializer<unsigned long> serial ();

	PBQPGraph<unsigned long> graph = PBQPGraph<unsigned long>();
	for (int i = 0; i < 20; i++) {
		unsigned long arr[] { 3, 1 };
		Vector<unsigned long> vek (2, arr);
		PBQPNode<unsigned long>* node = graph.addNode(vek);
	}
	DegreeZeroReducer<unsigned long> zeroReducer(&graph);
	std::vector<PBQPGraph<unsigned long>*> result = zeroReducer.reduce();
	BOOST_CHECK_EQUAL(result.size(), 1);
	PBQPGraph<unsigned long>* resultGraph = result [0];
	BOOST_CHECK_EQUAL(resultGraph->getNodeCount(), 0);
}

}


/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2020 Scientific Computing and Imaging Institute,
   University of Utah.

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.
*/

#include <Core/Algorithms/Legacy/FiniteElements/BuildMatrix/BuildFEMatrix.h>

#include <Core/Datatypes/DenseMatrix.h>
#include <Core/Datatypes/SparseRowMatrix.h>

#include <Core/Thread/Barrier.h>
#include <Core/Thread/Parallel.h>

#include <Core/Datatypes/Legacy/Field/Mesh.h>
#include <Core/Datatypes/Legacy/Field/VMesh.h>
#include <Core/Datatypes/Legacy/Field/Field.h>
#include <Core/Datatypes/Legacy/Field/VField.h>
#include <Core/GeometryPrimitives/Tensor.h>
#include <Core/Algorithms/Base/AlgorithmPreconditions.h>
#include <Core/Algorithms/Base/AlgorithmVariableNames.h>
#include <Core/Logging/Log.h>

#include <string>
#include <vector>
#include <algorithm>
#include <boost/shared_array.hpp>

using namespace SCIRun;
using namespace SCIRun::Core::Geometry;
using namespace SCIRun::Core::Datatypes;
using namespace SCIRun::Core::Thread;
using namespace SCIRun::Core::Algorithms;
using namespace SCIRun::Core::Algorithms::FiniteElements;
using namespace SCIRun::Core::Logging;

namespace SCIRun {
	namespace Core {
		namespace Algorithms {
			namespace FiniteElements {

        template <typename T>
        using matrix_type = Datatypes::SparseRowMatrixGeneric<T>;
        template <typename T>
        using matrix_pointer_type = SharedPointer<matrix_type<T>>;

template <typename T>
class BuildFEMatrixAlgoImpl
{
public:
  explicit BuildFEMatrixAlgoImpl(const AlgorithmBase* algo) : algo_(algo) {}
  bool run(FieldHandle input, Datatypes::DenseMatrixHandle ctable, matrix_pointer_type<T>& output) const;
private:
  const AlgorithmBase* algo_;
  mutable int generation_ = 0;
  mutable std::vector<std::vector<T>> basis_values_;
  mutable matrix_pointer_type<T> basis_fematrix_;
};

// Helper class
template <typename T>
class FEMBuilder
{
public:
  explicit FEMBuilder(const AlgorithmBase* algo) :
    algo_(algo), numprocessors_(Parallel::NumCores()),
    barrier_("FEMBuilder Barrier", numprocessors_),
    mesh_(nullptr), field_(nullptr),
    domain_dimension(0), local_dimension_nodes(0),
    local_dimension_add_nodes(0),
    local_dimension_derivatives(0),
    local_dimension(0),
    global_dimension_nodes(0),
    global_dimension_add_nodes(0),
    global_dimension_derivatives(0),
    global_dimension(0)
  {
  }

  bool build_matrix(FieldHandle input,
                    DenseMatrixHandle ctable,
                    matrix_pointer_type<T>& output);

private:
  const AlgorithmBase* algo_;
  int numprocessors_;
  Barrier barrier_;

  VMesh* mesh_;
  VField *field_;

  matrix_pointer_type<T> fematrix_;

  std::vector<bool> success_;

  boost::shared_array<index_type> rows_;
  boost::shared_array<index_type> allcols_;
  std::vector<index_type> colidx_;

  index_type domain_dimension;

  index_type local_dimension_nodes;
  index_type local_dimension_add_nodes;
  index_type local_dimension_derivatives;
  index_type local_dimension;

  index_type global_dimension_nodes;
  index_type global_dimension_add_nodes;
  index_type global_dimension_derivatives;
  index_type global_dimension;

  // A copy of the tensors list that was generated by SetConductivities
  std::vector<std::pair<std::string, Tensor> > tensors_;
  std::vector<std::pair<std::string, T> > scalars_;

  // Entry point for the parallel version
  void parallel(int proc);

  void add_lcl_gbl(index_type row, const std::vector<index_type> &cols, const std::vector<T> &lcl_a)
  {
    for (size_t i = 0; i < lcl_a.size(); i++)
      fematrix_->coeffRef(row, cols[i]) += lcl_a[i];
  }

  void create_numerical_integration(std::vector<VMesh::coords_type>& p,
                                    std::vector<double>& w,
                                    std::vector<std::vector<double>>& d);
  bool build_local_matrix(VMesh::Elem::index_type c_ind,
                          index_type row,
                          std::vector<T>& l_stiff,
                          std::vector<VMesh::coords_type>& p,
                          std::vector<double>& w,
                          std::vector<std::vector<double>>& d);
  bool build_local_matrix_regular(VMesh::Elem::index_type c_ind,
                                  index_type row,
                                  std::vector<T>& l_stiff,
                                  std::vector<VMesh::coords_type>& p,
                                  std::vector<double>& w,
                                  std::vector<std::vector<double>>& d,
                                  std::vector<std::vector<T>>& precompute);
  bool setup();

};
}}}}

template <typename T>
bool
FEMBuilder<T>::build_matrix(FieldHandle input,
                         DenseMatrixHandle ctable,
                         matrix_pointer_type<T>& output)
{
  // Get virtual interface to data
  field_ = input->vfield();
  mesh_  = input->vmesh();

#ifdef SCIRUN4_CODE_TO_BE_ENABLED_LATER
  // If we have the Conductivity property use it, if not we assume the values on
  // the data to be the actual tensors.
  field_->get_property("conductivity_table",tensors_);
#endif

  // We added a second system of adding a conductivity table, using a matrix
  // Convert that matrix into the conductivity table
  if (ctable)
  {
    tensors_.clear();
    auto mat = ctable;
    // Only if we can convert it into a dense matrix, otherwise skip it
    if (mat)
    {
      auto data = mat->data();
      size_type m = mat->nrows();
      size_type n = mat->ncols();
      Tensor tensor;

      // Case the table has isotropic conductivities
      if (mat->ncols() == 1)
      {
        for (size_type p=0; p<m;p++)
        {
          // Set the diagonals to the proper version.
          tensor.val(0,0) = data[p*n+0];
          tensor.val(1,0) = 0.0;
          tensor.val(2,0) = 0.0;
          tensor.val(0,1) = 0.0;
          tensor.val(1,1) = data[p*n+0];
          tensor.val(2,1) = 0.0;
          tensor.val(0,2) = 0.0;
          tensor.val(1,2) = 0.0;
          tensor.val(2,2) = data[p*n+0];
          tensors_.push_back(std::make_pair("",tensor));
        }
      }

      // Use our compressed way of storing tensors
      if (mat->ncols() == 6)
      {
        for (size_type p=0; p<m;p++)
        {
          tensor.val(0,0) = data[0+p*n];
          tensor.val(1,0) = data[1+p*n];
          tensor.val(2,0) = data[2+p*n];
          tensor.val(0,1) = data[1+p*n];
          tensor.val(1,1) = data[3+p*n];
          tensor.val(2,1) = data[4+p*n];
          tensor.val(0,2) = data[2+p*n];
          tensor.val(1,2) = data[4+p*n];
          tensor.val(2,2) = data[5+p*n];
          tensors_.push_back(std::make_pair("",tensor));
        }
      }

      // Use the full symmetric tensor. We will make the tensor symmetric here.
      if (mat->ncols() == 9)
      {
        for (size_type p=0; p<m;p++)
        {
          tensor.val(0,0) = data[0+p*n];
          tensor.val(1,0) = data[1+p*n];
          tensor.val(2,0) = data[2+p*n];
          tensor.val(0,1) = data[1+p*n];
          tensor.val(1,1) = data[4+p*n];
          tensor.val(2,1) = data[5+p*n];
          tensor.val(0,2) = data[2+p*n];
          tensor.val(1,2) = data[5+p*n];
          tensor.val(2,2) = data[8+p*n];
          tensors_.push_back(std::make_pair("",tensor));
        }
      }
    }
  }

  success_.resize(numprocessors_,true);

  // Start the multi threaded FE matrix builder.
  Parallel::RunTasks([this](int i) { parallel(i); }, numprocessors_);
  for (size_t j=0; j<success_.size(); j++)
  {
    if (!success_[j])
    {
      std::ostringstream oss;
      oss << "Algorithm failed in thread " << j;
      algo_->error(oss.str());
      return false;
    }
  }

  // Make sure it is symmetric
  if (algo_->get(BuildFEMatrixAlgo::ForceSymmetry).toBool())
  {
    // Make sure the matrix is fully symmetric, this compensates for round off
    // errors
    matrix_type<T> transpose = fematrix_->transpose();
    output.reset(new matrix_type<T>(0.5*(transpose + *fematrix_)));
  }
  else
  {
    // Assume that the builder did a good job and the matrix is numerically almost
    // symmetric
    output = fematrix_;
  }
  return true;
}

template <typename T>
void
FEMBuilder<T>::create_numerical_integration(std::vector<VMesh::coords_type> &p,
                                         std::vector<double> &w,
                                         std::vector<std::vector<double> > &d)
{
  //ScopedTimeLogger s1("FEMBuilder::create_numerical_integration");
  int int_basis = 1;
  if (mesh_->is_quad_element() ||
      mesh_->is_hex_element() ||
      mesh_->is_prism_element())
  {
    int_basis = 2;
  }

  mesh_->get_gaussian_scheme(p,w,int_basis);
  d.resize(p.size());
  for (size_t j=0; j<p.size();j++)
  {
    mesh_->get_derivate_weights(p[j],d[j],1);
    size_t pad_size = ( 3 - p[ j ].size() ) * d[ j ].size();

    if (pad_size > 0)
      d[j].resize(pad_size + d[j].size(), 0.0);
  }
}

/// build line of the local stiffness matrix
template <typename T>
bool
FEMBuilder<T>::build_local_matrix(VMesh::Elem::index_type c_ind,
                               index_type row,
                               std::vector<T> &l_stiff,
                               std::vector<VMesh::coords_type> &p,
                               std::vector<double> &w,
                               std::vector<std::vector<double>>  &d)
{
  Tensor tensor;

  if (tensors_.empty())
  {
    field_->get_value(tensor,c_ind);
  }
  else
  {
    int tensor_index;
    field_->get_value(tensor_index,c_ind);
    tensor = tensors_[tensor_index].second;
  }

  auto Ca = tensor.val(0,0);
  auto Cb = tensor.val(0,1);
  auto Cc = tensor.val(0,2);
  auto Cd = tensor.val(1,1);
  auto Ce = tensor.val(1,2);
  auto Cf = tensor.val(2,2);

  if ( (Ca==0) && (Cb==0) && (Cc==0) && (Cd==0) && (Ce==0) && (Cf==0) )
  {
    for (int j = 0; j<local_dimension; j++)
    {
      l_stiff[j] = 0.0;
    }
  }
  else
  {
    /// @todo: replace with std::fill
    for (int i=0; i<local_dimension; i++)
      l_stiff[i] = 0.0;

    auto local_dimension2=2*local_dimension;

    // These calls are direct lookups in the base of the VMesh
    // The compiler should optimize these well
    auto vol = mesh_->get_element_size();
    const int dim = mesh_->dimensionality();

    if (dim < 1 || dim > 3)
    {
      algo_->error("Mesh dimension is 0 or larger than 3, for which no FE implementation is available");
      return false;
    }
    for (size_t i = 0; i < d.size(); i++)
    {
      double Ji[9];
      auto detJ = mesh_->inverse_jacobian(p[i],c_ind,Ji);

      // If Jacobian is negative there is a problem with the mesh
      if (detJ <= 0.0)
      {
        algo_->error("Mesh has elements with negative jacobians, check the order of the nodes that define an element");
        return false;
      }

      // Volume associated with the local Gaussian Quadrature point:
      // weightfactor * Volume Unit element * Volume ratio (real element/unit element)
      detJ *= w[i] * vol;

      // Build local stiffness matrix
      // Get the local derivatives of the basis functions in the basis element
      // They are all the same and are thus precomputed in matrix d
      auto Nxi = &d[i][0];
      auto Nyi = &d[i][local_dimension];
      auto Nzi = &d[i][local_dimension2];
      // Gradients associated with the node we are calculating
      const auto& Nxip = Nxi[row];
      const auto &Nyip = Nyi[row];
      const auto &Nzip = Nzi[row];
      // Calculating gradient shape function * inverse Jacobian * volume scaling factor
      const auto uxp = detJ*(Nxip*Ji[0] + Nyip*Ji[1] + Nzip*Ji[2]);
      const auto uyp = detJ*(Nxip*Ji[3] + Nyip*Ji[4] + Nzip*Ji[5]);
      const auto uzp = detJ*(Nxip*Ji[6] + Nyip*Ji[7] + Nzip*Ji[8]);
      // Matrix multiplication with conductivity tensor :
      const auto uxyzpabc = uxp*Ca + uyp*Cb + uzp*Cc;
      const auto uxyzpbde = uxp*Cb + uyp*Cd + uzp*Ce;
      const auto uxyzpcef = uxp*Cc + uyp*Ce + uzp*Cf;

      // The above is constant for this node. Now multiply with the weight function
      // We assume the weight factors are the same as the local gradients
      // Galerkin approximation:

      for (int j = 0; j<local_dimension; j++)
      {
        const auto &Nxj = Nxi[j];
        const auto &Nyj = Nyi[j];
        const auto &Nzj = Nzi[j];

        // Matrix multiplication Gradient with inverse Jacobian:
        const auto ux = Nxj*Ji[0] + Nyj*Ji[1] + Nzj*Ji[2];
        const auto uy = Nxj*Ji[3] + Nyj*Ji[4] + Nzj*Ji[5];
        const auto uz = Nxj*Ji[6] + Nyj*Ji[7] + Nzj*Ji[8];

        // Add everything together into one coefficient of the matrix
        l_stiff[j] += ux*uxyzpabc+uy*uxyzpbde+uz*uxyzpcef;
      }
    }
  }
  return true;
}

template <typename T>
bool
FEMBuilder<T>::build_local_matrix_regular(VMesh::Elem::index_type c_ind,
                                       index_type row,
                                       std::vector<T> &l_stiff,
                                       std::vector<VMesh::coords_type> &p,
                                       std::vector<double> &w,
                                       std::vector<std::vector<double>> &d,
                                       std::vector<std::vector<T>> &precompute)
{
  Tensor tensor;

  if (tensors_.empty())
  {
    // Call to virtual interface. Get the tensor value. Actually this call relies
    // on the automatic casting feature of the virtual interface to convert scalar
    // values into a tensor.
    field_->get_value(tensor,c_ind);
  }
  else
  {
    int tensor_index;
    field_->get_value(tensor_index,c_ind);
    tensor = tensors_[tensor_index].second;
  }

  auto Ca = tensor.val(0,0);
  auto Cb = tensor.val(0,1);
  auto Cc = tensor.val(0,2);
  auto Cd = tensor.val(1,1);
  auto Ce = tensor.val(1,2);
  auto Cf = tensor.val(2,2);

  if ( (Ca==0) && (Cb==0) && (Cc==0) && (Cd==0) && (Ce==0) && (Cf==0) )
  {
    for (int j = 0; j<local_dimension; j++)
    {
      l_stiff[j] = 0.0;
    }
  }
  else
  {

    if (precompute.empty())
    {
      precompute.resize(d.size());
      for (int m=0; m < static_cast<int>(d.size()); m++)
      {
        precompute[m].resize(10);
      }

      for(int i=0; i<local_dimension; i++)
        l_stiff[i] = 0.0;

      auto local_dimension2=2*local_dimension;

      auto vol = mesh_->get_element_size();

      for (size_t i = 0; i < d.size(); i++)
      {
        auto& pc = precompute[i];

        double Ji[9];
        auto detJ = mesh_->inverse_jacobian(p[i], c_ind, Ji);

        // Volume elements can return negative determinants if the order of elements
        // is put in a different order
        /// @todo: It seems to be that a negative determinant is not necessarily bad,
        // we should be more flexible on this point
        if (detJ <= 0.0)
        {
          algo_->error("Mesh has elements with negative jacobians, check the order of the nodes that define an element");
          return false;
        }
        // Volume associated with the local Gaussian Quadrature point:
        // weightfactor * Volume Unit element * Volume ratio (real element/unit element)
        detJ*=w[i]*vol;

        pc[0] = Ji[0];
        pc[1] = Ji[1];
        pc[2] = Ji[2];
        pc[3] = Ji[3];
        pc[4] = Ji[4];
        pc[5] = Ji[5];
        pc[6] = Ji[6];
        pc[7] = Ji[7];
        pc[8] = Ji[8];
        pc[9] = detJ;

        // Build local stiffness matrix
        // Get the local derivatives of the basis functions in the basis element
        // They are all the same and are thus precomputed in matrix d
        auto Nxi = &d[i][0];
        auto Nyi = &d[i][local_dimension];
        auto Nzi = &d[i][local_dimension2];
        // Gradients associated with the node we are calculating
        const auto &Nxip = Nxi[row];
        const auto &Nyip = Nyi[row];
        const auto &Nzip = Nzi[row];
        // Calculating gradient shape function * inverse Jacobian * volume scaling factor
        const auto uxp = pc[9]*(Nxip*pc[0]+Nyip*pc[1]+Nzip*pc[2]);
        const auto uyp = pc[9]*(Nxip*pc[3]+Nyip*pc[4]+Nzip*pc[5]);
        const auto uzp = pc[9]*(Nxip*pc[6]+Nyip*pc[7]+Nzip*pc[8]);
        // Matrix multiplication with conductivity tensor :
        const auto uxyzpabc = uxp*Ca+uyp*Cb+uzp*Cc;
        const auto uxyzpbde = uxp*Cb+uyp*Cd+uzp*Ce;
        const auto uxyzpcef = uxp*Cc+uyp*Ce+uzp*Cf;

        // The above is constant for this node. Now multiply with the weight function
        // We assume the weight factors are the same as the local gradients
        // Galerkin approximation:

        for (int j = 0; j<local_dimension; j++)
        {
          const auto &Nxj = Nxi[j];
          const auto &Nyj = Nyi[j];
          const auto &Nzj = Nzi[j];

          // Matrix multiplication Gradient with inverse Jacobian:
          const auto ux = Nxj*pc[0]+Nyj*pc[1]+Nzj*pc[2];
          const auto uy = Nxj*pc[3]+Nyj*pc[4]+Nzj*pc[5];
          const auto uz = Nxj*pc[6]+Nyj*pc[7]+Nzj*pc[8];

          // Add everything together into one coefficient of the matrix
          l_stiff[j] += ux*uxyzpabc+uy*uxyzpbde+uz*uxyzpcef;
        }
      }
    }
    else
    {
      for(int i=0; i<local_dimension; i++)
        l_stiff[i] = 0.0;

      auto local_dimension2=2*local_dimension;

      for (size_t i = 0; i < d.size(); i++)
      {
        auto& pc = precompute[i];

        // Build local stiffness matrix
        // Get the local derivatives of the basis functions in the basis element
        // They are all the same and are thus precomputed in matrix d
        auto Nxi = &d[i][0];
        auto Nyi = &d[i][local_dimension];
        auto Nzi = &d[i][local_dimension2];
        // Gradients associated with the node we are calculating
        const auto &Nxip = Nxi[row];
        const auto &Nyip = Nyi[row];
        const auto &Nzip = Nzi[row];
        // Calculating gradient shape function * inverse Jacobian * volume scaling factor
        const auto uxp = pc[9]*(Nxip*pc[0]+Nyip*pc[1]+Nzip*pc[2]);
        const auto uyp = pc[9]*(Nxip*pc[3]+Nyip*pc[4]+Nzip*pc[5]);
        const auto uzp = pc[9]*(Nxip*pc[6]+Nyip*pc[7]+Nzip*pc[8]);
        // Matrix multiplication with conductivity tensor :
        const auto uxyzpabc = uxp*Ca+uyp*Cb+uzp*Cc;
        const auto uxyzpbde = uxp*Cb+uyp*Cd+uzp*Ce;
        const auto uxyzpcef = uxp*Cc+uyp*Ce+uzp*Cf;

        // The above is constant for this node. Now multiply with the weight function
        // We assume the weight factors are the same as the local gradients
        // Galerkin approximation:

        for (int j = 0; j<local_dimension; j++)
        {
          const auto &Nxj = Nxi[j];
          const auto &Nyj = Nyi[j];
          const auto &Nzj = Nzi[j];

          // Matrix multiplication Gradient with inverse Jacobian:
          const auto ux = Nxj*pc[0]+Nyj*pc[1]+Nzj*pc[2];
          const auto uy = Nxj*pc[3]+Nyj*pc[4]+Nzj*pc[5];
          const auto uz = Nxj*pc[6]+Nyj*pc[7]+Nzj*pc[8];

          // Add everything together into one coefficient of the matrix
          l_stiff[j] += ux*uxyzpabc+uy*uxyzpbde+uz*uxyzpcef;
        }
      }
    }
  }
  return true;
}

template <typename T>
bool
FEMBuilder<T>::setup()
{
  // The domain dimension
  domain_dimension = mesh_->dimensionality();
  if (domain_dimension < 1)
  {
    algo_->error("This mesh type cannot be used for FE computations");
    return false;
  }

  local_dimension_nodes = mesh_->num_nodes_per_elem();
  if (field_->basis_order() == 2)
  {
    local_dimension_add_nodes = mesh_->num_enodes_per_elem();
  }
  else
  {
    local_dimension_add_nodes = 0;
  }

  local_dimension_derivatives = 0;

  // Local degrees of freedom per element
  local_dimension = local_dimension_nodes +
  local_dimension_add_nodes +
  local_dimension_derivatives; ///< degrees of freedom (dofs) of system

  VMesh::Node::size_type mns;
  mesh_->size(mns);

  // Number of mesh points (not necessarily number of nodes)
  global_dimension_nodes = mns;
  if (field_->basis_order() == 2) // quadratic
  {
    mesh_->synchronize(Mesh::ENODES_E);
    global_dimension_add_nodes = mesh_->num_enodes();
  }
  else
  {
    global_dimension_add_nodes = 0;
  }

  global_dimension_derivatives = 0;
  global_dimension = global_dimension_nodes+
  global_dimension_add_nodes+
  global_dimension_derivatives;

  if (mns > 0)
  {
    // We only need edges for the higher order basis in case of quartic Lagrangian
    // Hence we should only synchronize it for this case
    if (global_dimension_add_nodes > 0)
      mesh_->synchronize(Mesh::EDGES_E|Mesh::NODE_NEIGHBORS_E);
    else
      mesh_->synchronize(Mesh::NODE_NEIGHBORS_E);
  }
  else
  {
    algo_->error("Mesh size < 0");
    success_[0] = false;
  }
  LOG_DEBUG("Allocating buffer for nonzero row indices of size: {}", global_dimension+1);
  rows_.reset(new index_type[global_dimension+1]);

  colidx_.resize(numprocessors_+1);
  return true;
}

// -- callback routine to execute in parallel
template <typename T>
void
FEMBuilder<T>::parallel(int proc_num)
{
  success_[proc_num] = true;

  if (proc_num == 0)
  {
    try
    {
      success_[proc_num] = setup();
    }
    catch (...)
    {
      algo_->error("BuildFEMatrix could not setup FE Stiffness computation");
      success_[proc_num] = false;
    }
  }

  barrier_.wait();

  // In case one of the threads fails, we should have them fail all
  for (int q = 0; q < numprocessors_; q++)
  {
    if (!success_[q])
    {
      std::ostringstream oss;
      oss << "FEMBuilder::setup failed in thread " << q;
      algo_->error(oss.str());
      return;
    }
  }

  /// distributing dofs among processors
  const index_type start_gd = (global_dimension * proc_num)/numprocessors_;
  const index_type end_gd  = (global_dimension * (proc_num+1))/numprocessors_;

  /// creating sparse matrix structure
  std::vector<index_type> mycols;

  VMesh::Elem::array_type ca;
  VMesh::Node::array_type na;
  VMesh::Edge::array_type ea;
  std::vector<index_type> neib_dofs;

  /// loop over system dofs for this thread
  int cnt = 0;
  size_type size_gd = end_gd-start_gd;
  auto updateFrequency = 2*size_gd / 100;
  try
  {
    mycols.reserve((end_gd - start_gd)*local_dimension*8);  //<! rough estimate

    for (VMesh::Node::index_type i = start_gd; i<end_gd; ++i)
    {
      rows_[i] = mycols.size();

      neib_dofs.clear();
      /// check for nodes
      if (i < global_dimension_nodes)
      {
        /// get neighboring cells for node
        mesh_->get_elems(ca, i);
      }
      else if (i < global_dimension_nodes+global_dimension_add_nodes)
      {
        /// check for additional nodes at edges
        /// get neighboring cells for node
        VMesh::Edge::index_type ii(i-global_dimension_nodes);
        mesh_->get_elems(ca,ii);
      }
      else
      {
        // There is some functionality implemented for higher order basis functions,
        // but it seems not to be accessible, entirely implemented nor validated.
        algo_->warning("BuildFEMatrix only supports linear basis functions.");
      }

      for(size_t j = 0; j < ca.size(); j++)
      {
        /// get neighboring nodes
        mesh_->get_nodes(na, ca[j]);

        for(size_t k = 0; k < na.size(); k++)
        {
          neib_dofs.push_back(static_cast<index_type>(na[k]));
        }

        /// check for additional nodes at edges
        if (global_dimension_add_nodes)
        {
          /// get neighboring edges
          mesh_->get_edges(ea, ca[j]);

          for(size_t k = 0; k < ea.size(); k++)
            neib_dofs.push_back(global_dimension + ea[k]);
        }
      }

      std::sort(neib_dofs.begin(), neib_dofs.end());

      for (size_t j=0; j<neib_dofs.size(); j++)
      {
        if (j == 0 || neib_dofs[j] != mycols.back())
        {
          mycols.push_back(neib_dofs[j]);
        }
      }
      if (proc_num == 0)
      {
        cnt++;
        if (cnt == updateFrequency)
        {
          cnt = 0;
          algo_->update_progress_max(i,2*size_gd);
        }
      }
    }

    colidx_[proc_num] = mycols.size();
    success_[proc_num] = true;
  }
  catch (...)
  {
    algo_->error("BuildFEMatrix crashed mapping out stiffness matrix");
    success_[proc_num] = false;
  }

  /// check point
  barrier_.wait();

  // Bail out if one of the processes failed
  for (int q=0; q<numprocessors_;q++)
  {
    if (!success_[q])
    {
      return;
    }
  }

  std::vector<std::vector<T>> precompute;
  index_type st = 0;

  if (proc_num == 0)
    allcols_.reset();

  try
  {
    if (proc_num == 0)
    {
      for(int i=0; i<numprocessors_; i++)
      {
        const index_type ns = colidx_[i];
        colidx_[i] = st;
        st += ns;
      }

      colidx_[numprocessors_] = st;
      allcols_.reset(new index_type[st]);
    }
    success_[proc_num] = true;
  }
  catch (...)
  {
    if (proc_num == 0)
      allcols_.reset();

    algo_->error("Could not allocate enough memory");
    success_[proc_num] = false;
  }

  /// check point
  barrier_.wait();

  // Bail out if one of the processes failed
  for (int q=0; q<numprocessors_;q++)
  {
    if (! success_[q])
      return;
  }

  try
  {
    /// updating global column by each of the processors
    const index_type s = colidx_[proc_num];
    const size_t n = mycols.size();

    for(size_t i=0; i<n; i++)
      allcols_[i+s] = mycols[i];

    for(index_type i = start_gd; i<end_gd; i++)
      rows_[i] += s;

    success_[proc_num] = true;
  }
  catch (...)
  {
    algo_->error("BuildFEMatrix crashed while setting up row compression");
    success_[proc_num] = false;
  }

  /// check point
  barrier_.wait();

  // Bail out if one of the processes failed
  for (auto q=0; q<numprocessors_; q++)
  {
    if (!success_[q])
      return;
  }

  try
  {
    /// the main thread makes the matrix
    if (proc_num == 0)
    {
      rows_[global_dimension] = st;
      algo_->remark("Creating fematrix on main thread.");
      fematrix_ = makeShared<matrix_type<T>>(global_dimension, global_dimension, rows_.get(), allcols_.get(), st);
      rows_.reset();
      allcols_.reset();
    }
    success_[proc_num] = true;
  }
  catch (...)
  {
    algo_->error("BuildFEMatrix crashed while creating final stiffness matrix");
    success_[proc_num] = false;
  }

  /// check point
  barrier_.wait();

  // Bail out if one of the processes failed
  for (auto q=0; q<numprocessors_;q++)
  {
    if (!success_[q])
      return;
  }

  try
  {
    /// zeroing in parallel
    const auto ns = colidx_[proc_num];
    const auto ne = colidx_[proc_num+1];
    auto a = &(fematrix_->valuePtr()[ns]), ae=&(fematrix_->valuePtr()[ne]);
    while (a<ae) *a++=0.0;

    std::vector<VMesh::coords_type> ni_points;
    std::vector<double> ni_weights;
    std::vector<std::vector<double>> ni_derivatives;

    create_numerical_integration(ni_points, ni_weights, ni_derivatives);

    std::vector<T> lsml; ///< line of local stiffnes matrix
    lsml.resize(local_dimension);

    /// loop over system dofs for this thread
    cnt = 0;
    size_gd = end_gd-start_gd;
    for (VMesh::Node::index_type i = start_gd; i<end_gd; ++i)
    {
      if (i < global_dimension_nodes)
      {
        /// check for nodes
        /// get neighboring cells for node
        mesh_->get_elems(ca,i);
      }
      else if (i < global_dimension_nodes + global_dimension_add_nodes)
      {
        /// check for additional nodes at edges
        /// get neighboring cells for additional nodes
        VMesh::Edge::index_type ii(i-global_dimension_nodes);
        mesh_->get_elems(ca,ii);
      }
      else
      {
        // There is some functionality implemented for higher order basis functions,
        // but it seems not to be accessible, entirely implemented nor validated.
        algo_->warning("BuildFEMatrix only supports linear basis functions.");
      }

      /// loop over elements attributed elements

      if (mesh_->is_regularmesh())
      {
        for (size_t j = 0; j < ca.size(); j++)
        {
          mesh_->get_nodes(na, ca[j]); ///< get neighboring nodes
          neib_dofs.resize(na.size());
          for(size_t k = 0; k < na.size(); k++)
          {
            neib_dofs[k] = na[k]; // Must cast to (int) for SGI compiler :-(
          }

          for(size_t k = 0; k < na.size(); k++)
          {
            if (na[k] == i)
            {
              auto successLocal = build_local_matrix_regular(ca[j], k , lsml, ni_points, ni_weights, ni_derivatives,precompute);
							if (!successLocal)
							{
								success_[proc_num] = false;
								return;
							}
              add_lcl_gbl(i, neib_dofs, lsml);
            }
          }
        }
      }
      else
      {
        for (size_t j = 0; j < ca.size(); j++)
        {
          neib_dofs.clear();
          mesh_->get_nodes(na, ca[j]); ///< get neighboring nodes
          for(size_t k = 0; k < na.size(); k++)
          {
            neib_dofs.push_back(na[k]); // Must cast to (int) for SGI compiler :-(
          }
          /// check for additional nodes at edges
          if (global_dimension_add_nodes)
          {
            mesh_->get_edges(ea, ca[j]); ///< get neighboring edges
            for(size_t k = 0; k < ea.size(); k++)
            {
              neib_dofs.push_back(global_dimension + ea[k]);
            }
          }

          ASSERT(static_cast<int>(neib_dofs.size()) == local_dimension);

          for(size_t k = 0; k < na.size(); k++)
          {
            if (na[k] == i)
            {
              auto successLocal = build_local_matrix(ca[j], k , lsml, ni_points, ni_weights, ni_derivatives);
							if (!successLocal)
							{
								success_[proc_num] = false;
								return;
							}
              add_lcl_gbl(i, neib_dofs, lsml);
            }
          }

          if (global_dimension_add_nodes)
          {
            for (size_t k = 0; k < ea.size(); k++)
            {
              if (global_dimension + static_cast<int>(ea[k]) == i)
              {
                auto successLocal = build_local_matrix(ca[j], k+na.size(), lsml, ni_points, ni_weights, ni_derivatives);
								if (!successLocal)
								{
									success_[proc_num] = false;
									return;
								}
                add_lcl_gbl(i, neib_dofs, lsml);
              }
            }
          }
        }
      }

      if (proc_num == 0)
      {
        cnt++;
        if (cnt == updateFrequency)
        {
          cnt = 0;
          algo_->update_progress_max(i+size_gd,2*size_gd);
        }
      }
    }
    success_[proc_num] = true;
  }
  catch (...)
  {
    algo_->error("BuildFEMatrix crashed while filling out stiffness matrix");
    success_[proc_num] = false;
  }

  barrier_.wait();

  // Bail out if one of the processes failed
  for (int q=0; q<numprocessors_; q++)
  {
    if (!success_[q])
      return;
  }
}

const AlgorithmParameterName BuildFEMatrixAlgo::ForceSymmetry("ForceSymmetry");
const AlgorithmParameterName BuildFEMatrixAlgo::GenerateBasis("GenerateBasis");

template <typename T>
bool
BuildFEMatrixAlgoImpl<T>::run(FieldHandle input, DenseMatrixHandle ctable, matrix_pointer_type<T>& output) const
{
  ScopedAlgorithmStatusReporter s(algo_, "BuildFEMatrix");

  if (!input)
  {
    algo_->error("Could not obtain input field");
    return false;
  }

  if (input->vfield()->is_vector())
  {
    algo_->error("This function has not yet been defined for elements with vector data");
    return false;
  }

  if (input->vfield()->basis_order()!=0)
  {
    algo_->error("This function has only been defined for data that is located at the elements");
    return false;
  }

  if (ctable)
  {
    if ((ctable->ncols() != 1)&&(ctable->ncols() != 6)&&(ctable->ncols() != 9))
    {
      algo_->error("Conductivity table needs to have 1, 6, or 9 columns");
      return false;
    }
    if (ctable->nrows() == 0)
    {
      algo_->error("ConductivityTable is empty");
      return false;
    }
  }

  FEMBuilder<T> builder(algo_);

  if (algo_->get(BuildFEMatrixAlgo::GenerateBasis).toBool())
  {
    if (!ctable)
    {
      std::vector<std::pair<std::string,Tensor> > tens;

      input->properties().get_property("conductivity_table",tens);

      if (!tens.empty())
      {
        ctable.reset(new DenseMatrix(tens.size(), 1));
        auto data = ctable->data();
        for (size_t i=0; i<tens.size();i++)
        {
          auto t = tens[i].second.val(0,0);
          data[i] = t;
        }
      }
    }

    if (ctable)
    {
      auto nconds = ctable->nrows();
      if ( (input->vmesh()->generation() != generation_) ||
          (!basis_fematrix_) )
      {
        auto con = makeShared<DenseMatrix>(nconds, 1, 0.0);
        auto data = con->data();

        if (!builder.build_matrix(input, con, basis_fematrix_) )
        {
          algo_->error("Build matrix method failed when building FEMatrix structure");
          return false;
        }

        if (!basis_fematrix_)
        {
          algo_->error("Failed to build FEMatrix structure");
          return false;
        }

        basis_values_.resize(nconds);
        for (size_type i=0; i < nconds; i++)
        {
          matrix_pointer_type<T> stiffness;
          /// @todo: can initialize array using std::fill
          data[i] = 1.0;

          if (!builder.build_matrix(input, con, stiffness) )
          {
            algo_->error("Build matrix method failed for one of the tissue types");
            return false;
          }

          if (!stiffness)
          {
            algo_->error("Failed to build FEMatrix component for one of the tissue types");
            return false;
          }

          basis_values_[i].resize(stiffness->nonZeros());
          for (size_type p=0; p< stiffness->nonZeros(); p++)
          {
            basis_values_[i][p] = stiffness->valuePtr()[p];
          }
          data[i] = 0.0;
        }

        generation_ = input->vmesh()->generation();
      }

      output.reset(basis_fematrix_->clone());

      auto sum = output->valuePtr();
      auto cdata = ctable->data();
      auto n = ctable->ncols();

      if (!basis_values_.empty())
      {
        for (size_t p=0; p < basis_values_[0].size(); p++)
          sum[p] = 0.0;
      }

      for (auto i=0; i<nconds; i++)
      {
        auto weight = cdata[i*n];
        for (size_t p=0; p < basis_values_[i].size(); p++)
        {
          sum[p] += weight * basis_values_[i][p];
        }
      }

    }
    else
    {
      algo_->error("No conductivity table present: The generate_basis option only works for indexed conductivities");
      return false;
    }
  }

  if (!builder.build_matrix(input,ctable,output) )
  {
    algo_->error("Build matrix method failed to build output matrix");
    return false;
  }

  if (!output)
  {
    algo_->error("Could not build output matrix");
    return false;
  }

  return true;
}

const AlgorithmInputName BuildFEMatrixAlgo::Conductivity_Table("Conductivity_Table");
const AlgorithmOutputName BuildFEMatrixAlgo::Stiffness_Matrix("Stiffness_Matrix");
const AlgorithmOutputName BuildFEMatrixAlgo::Stiffness_Matrix_Complex("Stiffness_Matrix_Complex");

AlgorithmOutput BuildFEMatrixAlgo::run(const AlgorithmInput& input) const
{
  auto field = input.get<Field>(Variables::InputField);
  auto ctable = input.get<DenseMatrix>(Conductivity_Table);

	AlgorithmOutput output;
  if (field && field->vfield() && field->vfield()->is_complex_double())
	{
		matrix_pointer_type<complex> stiffness;
	  BuildFEMatrixAlgoImpl<complex> impl(this);
	  if (!impl.run(field, ctable, stiffness))
	    THROW_ALGORITHM_PROCESSING_ERROR("False returned on legacy run call.--complex detected	");
		output[Stiffness_Matrix_Complex] = stiffness;
	}
	else
	{
		matrix_pointer_type<double> stiffness;
	  BuildFEMatrixAlgoImpl<double> impl(this);
	  if (!impl.run(field, ctable, stiffness))
	    THROW_ALGORITHM_PROCESSING_ERROR("False returned on legacy run call.");
		output[Stiffness_Matrix] = stiffness;
	}

  return output;
}


/*
patterndisp.cpp

Copyright (c) 2014, 2015, 2016 Terumasa Tadano

This file is distributed under the terms of the MIT license.
Please see the file 'LICENCE.txt' in the root directory
or http://opensource.org/licenses/mit-license.php for information.
*/

#include <iostream>
#include <iomanip>
#include "patterndisp.h"
#include "memory.h"
#include "error.h"
#include "system.h"
#include "fcs.h"
#include "cluster.h"
#include "symmetry.h"
#include "constants.h"
#include "mathfunctions.h"
#include "constraint.h"
#include <map>
#include <boost/bimap.hpp>

using namespace ALM_NS;

Displace::Displace()
{
    set_default_variables();
}

Displace::~Displace()
{
    deallocate_variables();
}

void Displace::gen_displacement_pattern(const Cluster *cluster,
                                        const Symmetry *symmetry,
                                        const Fcs *fcs,
                                        const Constraint *constraint,
                                        const System *system,
                                        const int verbosity)
{
    int order;
    const auto maxorder = cluster->get_maxorder();
    std::string preferred_basis;
    std::vector<int> group_tmp;
    ConstraintSparseForm *constsym;
    std::vector<std::vector<double>> const_tmp;

    std::vector<int> pairs;
    std::set<int> *include_set;
    std::set<DispAtomSet> *dispset;

    std::vector<size_t> *nequiv;
    std::vector<FcProperty> *fc_table, *fc_zeros;

    std::vector<ConstraintTypeFix> *const_fix_tmp;
    std::vector<ConstraintTypeRelate> *const_relate_tmp;
    boost::bimap<size_t, size_t> *index_bimap_tmp;
    const auto do_rref = true;

    if (verbosity > 0) {
        std::cout << " DISPLACEMENT PATTERN" << std::endl;
        std::cout << " ====================" << std::endl << std::endl;
    }

    // Decide preferred basis (Cartesian or Lattice)
    auto ncompat_cart = 0;
    auto ncompat_latt = 0;
    for (const auto &it : symmetry->get_SymmData()) {
        if (it.compatible_with_cartesian) ++ncompat_cart;
        if (it.compatible_with_lattice) ++ncompat_latt;
    }
    if (ncompat_cart >= ncompat_latt) {
        preferred_basis = "Cartesian";
    } else {
        preferred_basis = "Lattice";
    }

    // std::cout << "Preferred_basis = " << preferred_basis << std::endl;
    // std::cout << ncompat_cart << " " << ncompat_latt << std::endl;

    allocate(fc_table, maxorder);
    allocate(fc_zeros, maxorder);
    allocate(constsym, maxorder);
    allocate(nequiv, maxorder);
    allocate(const_fix_tmp, maxorder);
    allocate(const_relate_tmp, maxorder);
    allocate(index_bimap_tmp, maxorder);

    for (order = 0; order < maxorder; ++order) {

        fcs->generate_force_constant_table(order,
                                           system->get_supercell().number_of_atoms,
                                           cluster->get_cluster_list(order),
                                           symmetry, preferred_basis,
                                           fc_table[order], nequiv[order],
                                           fc_zeros[order], false);

        fcs->get_constraint_symmetry(system->get_supercell().number_of_atoms,
                                     symmetry,
                                     order,
                                     preferred_basis,
                                     fc_table[order],
                                     nequiv[order].size(),
                                     constraint->get_tolerance_constraint(),
                                     constsym[order], do_rref);

    }

    for (order = 0; order < maxorder; ++order) {
        const_fix_tmp[order].clear();
    }

    constraint->get_mapping_constraint(maxorder,
                                       nequiv,
                                       constsym,
                                       const_fix_tmp,
                                       const_relate_tmp,
                                       index_bimap_tmp);

    if (verbosity > 0) {
        for (order = 0; order < maxorder; ++order) {
            std::cout << "  Number of free" << std::setw(9)
                      << cluster->get_ordername(order) << " FCs : "
                      << index_bimap_tmp[order].size() << std::endl;
        }
        std::cout << std::endl;
    }

    deallocate(constsym);
    deallocate(const_fix_tmp);
    deallocate(const_relate_tmp);
    deallocate(fc_zeros);

    allocate(include_set, maxorder);

    for (order = 0; order < maxorder; ++order) {
        include_set[order].clear();

        for (const auto &it : index_bimap_tmp[order]) {
            include_set[order].insert(it.right);
        }
    }

    deallocate(index_bimap_tmp);

    if (verbosity > 0) {
        std::cout << "  Generating displacement patterns in ";
        std::cout << "Cartesian coordinate... ";
    }

    allocate(dispset, maxorder);

    for (order = 0; order < maxorder; ++order) {

        size_t m = 0;

        for (size_t i = 0; i < nequiv[order].size(); ++i) {

            if (include_set[order].find(i) != include_set[order].end()) {

                group_tmp.clear();

                // Store first order + 1 indexes as a necessary displacement pattern.
                // Here, duplicate entries will be removed.
                // For example, (iij) will be reduced to (ij).
                for (auto j = 0; j < order + 1; ++j) {
                    group_tmp.push_back(fc_table[order][m].elems[j]);
                }
                group_tmp.erase(std::unique(group_tmp.begin(), group_tmp.end()),
                                group_tmp.end());

                // Avoid equivalent entries using set.
                dispset[order].insert(DispAtomSet(group_tmp));

            }

            m += nequiv[order][i];
        }
    }
    deallocate(include_set);
    deallocate(nequiv);
    deallocate(fc_table);

    allocate(pattern_all, maxorder);
    // pattern_all is updated.
    generate_pattern_all(maxorder,
                         system->get_supercell().number_of_atoms,
                         system->get_supercell().lattice_vector,
                         symmetry,
                         dispset, preferred_basis);

    deallocate(dispset);

    if (verbosity > 0) {
        std::cout << " done!" << std::endl;
        std::cout << std::endl;

        for (order = 0; order < maxorder; ++order) {
            std::cout << "  Number of disp. patterns for " << std::setw(9)
                      << cluster->get_ordername(order) << " : "
                      << pattern_all[order].size() << std::endl;
        }
        std::cout << std::endl;
    }
}

void Displace::set_trim_dispsign_for_evenfunc(const bool trim_dispsign_for_evenfunc_in)
{
    trim_dispsign_for_evenfunc = trim_dispsign_for_evenfunc_in;
}

std::string Displace::get_disp_basis() const
{
    return disp_basis;
}

void Displace::set_disp_basis(const std::string disp_basis_in)
{
    disp_basis = disp_basis_in;
}

const std::vector<AtomWithDirection> &Displace::get_pattern_all(const int order) const
{
    return pattern_all[order];
}

void Displace::set_default_variables()
{
    trim_dispsign_for_evenfunc = true;
    disp_basis = "CART";
    pattern_all = nullptr;
}

void Displace::deallocate_variables()
{
    deallocate(pattern_all);
}

void Displace::generate_pattern_all(const int maxorder,
                                    const size_t nat,
                                    const double lavec[3][3],
                                    const Symmetry *symmetry,
                                    const std::set<DispAtomSet> *dispset_in,
                                    const std::string preferred_basis) const
{
    size_t i, j;
    int order;
    double disp_tmp[3];

    std::vector<int> atoms, vec_tmp, nums;
    std::vector<double> directions, directions_copy;
    std::vector<std::vector<int>> *sign_prod, sign_reduced;

    allocate(sign_prod, maxorder);

    for (order = 0; order < maxorder; ++order) {
        vec_tmp.clear();
        generate_signvecs(order + 1, sign_prod[order], vec_tmp);
    }

    for (order = 0; order < maxorder; ++order) {

        pattern_all[order].clear();

        for (auto it = dispset_in[order].begin(); it != dispset_in[order].end(); ++it) {

            atoms.clear();
            directions.clear();
            nums.clear();

            for (i = 0; i < (*it).atomset.size(); ++i) {

                auto atom_tmp = (*it).atomset[i] / 3;

                nums.push_back((*it).atomset[i]);
                atoms.push_back(atom_tmp);

                for (j = 0; j < 3; ++j) {
                    disp_tmp[j] = 0.0;
                }
                disp_tmp[(*it).atomset[i] % 3] = 1.0;

                for (j = 0; j < 3; ++j) directions.push_back(disp_tmp[j]);
            }

            const auto natom_disp = atoms.size();

            if (trim_dispsign_for_evenfunc) {
                find_unique_sign_pairs(natom_disp, nat, symmetry,
                                       sign_prod[natom_disp - 1],
                                       nums, sign_reduced, preferred_basis);
            } else {
                sign_reduced.clear();
                std::copy(sign_prod[natom_disp - 1].begin(),
                          sign_prod[natom_disp - 1].end(),
                          std::back_inserter(sign_reduced));
            }

            directions_copy.clear();
            std::copy(directions.begin(), directions.end(),
                      std::back_inserter(directions_copy));

            for (const auto &it2 : sign_reduced) {
                directions.clear();

                for (i = 0; i < it2.size(); ++i) {
                    const auto sign_double = static_cast<double>(it2[i]);

                    for (j = 0; j < 3; ++j) {
                        disp_tmp[j] = directions_copy[3 * i + j] * sign_double;
                    }
                    if (preferred_basis == "Lattice") {
                        rotvec(disp_tmp, disp_tmp, lavec);
                        double norm = 0.0;
                        for (j = 0; j < 3; ++j) norm += disp_tmp[j] * disp_tmp[j];
                        norm = std::sqrt(norm);
                        for (j = 0; j < 3; ++j) disp_tmp[j] /= norm;
                    }

                    for (j = 0; j < 3; ++j) {
                        directions.push_back(disp_tmp[j]);
                    }
                }
                pattern_all[order].emplace_back(atoms, directions);
            }
        }
    }

    deallocate(sign_prod);
}

void Displace::generate_signvecs(const int N,
                                 std::vector<std::vector<int>> &sign,
                                 std::vector<int> vec) const
{
    // returns the product of signs ('+','-')

    if (N == 0) {
        sign.push_back(vec);
        vec.clear();
    } else {
        std::vector<int> vec_tmp;

        vec_tmp.clear();
        std::copy(vec.begin(), vec.end(), std::back_inserter(vec_tmp));

        vec_tmp.push_back(1);
        generate_signvecs(N - 1, sign, vec_tmp);

        vec_tmp.clear();
        std::copy(vec.begin(), vec.end(), std::back_inserter(vec_tmp));
        vec_tmp.push_back(-1);
        generate_signvecs(N - 1, sign, vec_tmp);
    }
}

void Displace::find_unique_sign_pairs(const int natom_disp_in,
                                      const size_t nat,
                                      const Symmetry *symmetry,
                                      const std::vector<std::vector<int>> sign_in,
                                      const std::vector<int> &pair_in,
                                      std::vector<std::vector<int>> &sign_out,
                                      const std::string preferred_basis) const
{
    size_t isym, i;
    int j, k;
    int mapped_atom;
    int mapped_index;

    bool flag_avail;

    double disp_tmp;
    double **disp, **disp_sym;

    std::vector<int> symnum_vec;
    std::vector<int>::const_iterator loc;
    std::vector<int> atom_tmp, pair_tmp;
    std::vector<std::vector<int>> sign_found;
    std::vector<int> sign_tmp;
    std::vector<int> list_disp_atom;
    std::vector<IndexWithSign> index_for_sort;

    allocate(disp, nat, 3);
    allocate(disp_sym, nat, 3);

    sign_out.clear();
    symnum_vec.clear();
    list_disp_atom.clear();

    for (i = 0; i < pair_in.size(); ++i) {
        list_disp_atom.push_back(pair_in[i] / 3);
    }
    list_disp_atom.erase(std::unique(list_disp_atom.begin(), list_disp_atom.end()),
                         list_disp_atom.end());

    for (i = 0; i < nat; ++i) {
        for (j = 0; j < 3; ++j) {
            disp[i][j] = 0.0;
        }
    }

    for (i = 0; i < pair_in.size(); ++i) {
        disp[pair_in[i] / 3][pair_in[i] % 3] = 1.0;
    }

    const size_t natom_disp = static_cast<size_t>(natom_disp_in);

    // Find symmetry operations which can be used to
    // reduce the number of sign patterns (+, -) of displacements

    for (isym = 0; isym < symmetry->get_nsym(); ++isym) {

        flag_avail = true;
        pair_tmp.clear();

        for (i = 0; i < natom_disp; ++i) {
            if (!flag_avail) break;

            mapped_atom = symmetry->get_map_sym()[pair_in[i] / 3][isym];
            mapped_index = 3 * mapped_atom + pair_in[i] % 3;

            loc = std::find(pair_in.begin(), pair_in.end(), mapped_index);
            if (loc == pair_in.end()) {
                flag_avail = false;
            }

            pair_tmp.push_back(mapped_index);
        }

        if (!flag_avail) continue;

        // The symmetry may be useful only when the pair doesn't change
        // by the symmetry operation.
        std::sort(pair_tmp.begin(), pair_tmp.end());

        if (pair_tmp == pair_in) {

            pair_tmp.clear();

            for (i = 0; i < list_disp_atom.size(); ++i) {
                mapped_atom = symmetry->get_map_sym()[list_disp_atom[i]][isym];

                for (j = 0; j < 3; ++j) {
                    disp_sym[mapped_atom][j] = 0.0;

                    if (preferred_basis == "Cartesian") {
                        for (k = 0; k < 3; ++k) {
                            disp_sym[mapped_atom][j]
                                    += symmetry->get_SymmData()[isym].rotation_cart[j][k] * disp[list_disp_atom[i]][k];
                        }
                    } else if (preferred_basis == "Lattice") {
                        for (k = 0; k < 3; ++k) {
                            disp_sym[mapped_atom][j]
                                    += static_cast<double>(symmetry->get_SymmData()[isym].rotation[j][k])
                                       * disp[list_disp_atom[i]][k];
                        }
                    } else {
                        exit("find_unique_sign_pairs",
                             "Invalid basis. This cannot happen.");
                    }

                    disp_tmp = disp_sym[mapped_atom][j];
                    if (std::abs(disp_tmp) > eps) {
                        pair_tmp.push_back(3 * mapped_atom + j);
                    }
                }
            }

            std::sort(pair_tmp.begin(), pair_tmp.end());

            if (pair_tmp == pair_in) {
                symnum_vec.push_back(isym);
            }
        }
    }

    // Now find unique pairs of displacement directions

    sign_found.clear();

    for (const auto &it : sign_in) {

        // if the sign has already been found before, cycle the loop.
        // else, add the current sign pairs to the return variable.
        if (std::find(sign_found.begin(), sign_found.end(), it) != sign_found.end()) {
            continue;
        }
        sign_out.push_back(it);

        for (i = 0; i < nat; ++i) {
            for (j = 0; j < 3; ++j) {
                disp[i][j] = 0.0;
            }
        }

        for (i = 0; i < natom_disp; ++i) {
            disp[pair_in[i] / 3][pair_in[i] % 3] = static_cast<double>(it[i]);
        }

        for (isym = 0; isym < symnum_vec.size(); ++isym) {

            index_for_sort.clear();

            for (i = 0; i < list_disp_atom.size(); ++i) {
                mapped_atom = symmetry->get_map_sym()[list_disp_atom[i]][symnum_vec[isym]];

                for (j = 0; j < 3; ++j) {
                    disp_sym[mapped_atom][j] = 0.0;
                    if (preferred_basis == "Cartesian") {
                        for (k = 0; k < 3; ++k) {
                            disp_sym[mapped_atom][j]
                                    += symmetry->get_SymmData()[symnum_vec[isym]].rotation_cart[j][k]
                                       * disp[list_disp_atom[i]][k];
                        }
                    } else if (preferred_basis == "Lattice") {
                        for (k = 0; k < 3; ++k) {
                            disp_sym[mapped_atom][j]
                                    += static_cast<double>(symmetry->get_SymmData()[symnum_vec[isym]].rotation[j][k])
                                       * disp[list_disp_atom[i]][k];
                        }
                    } else {
                        exit("find_unique_sign_pairs",
                             "Invalid basis. This cannot happen.");
                    }

                    disp_tmp = disp_sym[mapped_atom][j];

                    if (std::abs(disp_tmp) > eps) {

                        if (disp_tmp < 0.0) {
                            index_for_sort.emplace_back(3 * mapped_atom + j, -1);
                        } else {
                            index_for_sort.emplace_back(3 * mapped_atom + j, 1);
                        }
                    }
                }

            }
            std::sort(index_for_sort.begin(), index_for_sort.end());

            sign_tmp.clear();

            for (i = 0; i < index_for_sort.size(); ++i) {
                sign_tmp.push_back(index_for_sort[i].sign);
            }

            if ((sign_tmp.size() == natom_disp) &&
                (std::find(sign_found.begin(), sign_found.end(), sign_tmp) == sign_found.end())) {
                sign_found.push_back(sign_tmp);
                std::sort(sign_found.begin(), sign_found.end());
            }
        }
    }

    deallocate(disp);
    deallocate(disp_sym);
}


/**
 * Author: Junjie Shi, Ardalan Naseri
 *
 * This file is responsible for inferring the relationship between a pair of individuals.
 *
 */

#include <algorithm>
#include <iostream>
#include <unordered_set>

#include <boost/functional/hash.hpp>

#include "parser.hpp"
#include "mapper.hpp"
#include "classifier.hpp"

// Unadjusted thresholds
static double FOURTH_START_ORIGINAL = 1 / std::pow(2, 11.0 / 2);
static double THIRD_START_ORIGINAL = 1 / std::pow(2, 9.0 / 2);
static double SECOND_START_ORIGINAL = 1 / std::pow(2, 7.0 / 2);
static double PO_FS_START_ORIGINAL = 1 / std::pow(2, 5.0 / 2);
static double MZ_START_ORIGNAL = 1 / std::pow(2, 3.0 / 2);
static double FS_START_ORIGINAL = 0.1;

// Current thresholds used for inference
double FOURTH_START = 1 / std::pow(2, 11.0 / 2);
double THIRD_START = 1 / std::pow(2, 9.0 / 2);
double SECOND_START = 1 / std::pow(2, 7.0 / 2);
double PO_FS_START = 1 / std::pow(2, 5.0 / 2);
double MZ_START = 1 / std::pow(2, 3.0 / 2);
double FS_START = 0.1;

// Expected kinship coefficent for full-siblings
#define PO_FS_START_EXPECTED 0.25

const int NUM_TYPES = 7;
const std::vector<std::string> TYPES {"MZ", "PO", "FS", "2nd", "3rd", "4th", "UN"};

// Sum of kinship coefficients of all recorded full-siblings
static double FS_TOTAL_KINSHIP_COEFFICIENTS = 0;
// Number of pairs of full-siblings currently recorded
static int NUM_FS = 0;
// Number of pairs of full-siblings recorded at last adjustment
static int PREV_ADJUSTED_NUM_FS = 0;

// Mininum number of pairs of full-siblings needed to be recorded between two
// consecutive adjustment.
#define MIN_ADJUSTING_INTERVAL 50

//#define MIN_ADJUSTING_INTERVAL 10

// Maximum number of pairs of full-siblings that will be recorded.
#define MAX_NUM_FS 1000

/**
 * Record an identified full-sibling pair. Add their kinship coefficient to
 * FS_TOTAL_KINSHIP_COEFFICIENTS. If MAX_NUM_FS pairs of
 * full siblings have already been recorded, then no-op.
 *
 * @param stats a struct pair_stats that contains total IBD1 and total IBD2
 *     of the pair.
 *
 */
void
add_full_sibling(const struct pair_stats &stats)
{

// CHANGED! Commented out
    if (NUM_FS > MAX_NUM_FS) {
        return;
    }

    ++NUM_FS;
    FS_TOTAL_KINSHIP_COEFFICIENTS += compute_kinship_coefficient(
        stats.total_ibd1, stats.total_ibd2
    );
}

/**
 *
 * @return number of pairs of full-siblings that have been recorded.
 *
*/
int
get_num_full_siblings() {
    return NUM_FS;
}

/**
 * Adjust kinship coefficient and IBD2 thresholds. No-op if there are fewer than
 * MIN_NUM_FS pairs of full-siblings, or fewer than MIN_ADJUSTING_INTERVAL pairs
 * of full-siblings have been recorded since last adjustment, or there more more
 * than MAX_NUM_FS pairs of full-siblings.
 *
 */
void
shift_boundary()
{

    if (NUM_FS - PREV_ADJUSTED_NUM_FS < MIN_ADJUSTING_INTERVAL ||
        NUM_FS < MIN_NUM_FS || NUM_FS > MAX_NUM_FS) {
        return;
    }

/**
    if (NUM_FS - PREV_ADJUSTED_NUM_FS < MIN_ADJUSTING_INTERVAL ||
        NUM_FS < MIN_NUM_FS) {
        return;
    }
**/

    double mean = FS_TOTAL_KINSHIP_COEFFICIENTS / NUM_FS;
    double shift = mean / PO_FS_START_EXPECTED;

    if ( shift > 1){
	shift = 1;
	}
    std::cout << "Shifting factor is " << shift << std::endl;

    FOURTH_START = FOURTH_START_ORIGINAL * shift;
    THIRD_START = THIRD_START_ORIGINAL * shift;
    SECOND_START = SECOND_START_ORIGINAL * shift;
    PO_FS_START = PO_FS_START_ORIGINAL * shift;
    MZ_START = MZ_START_ORIGNAL * shift;
    FS_START = FS_START_ORIGINAL * shift;

    PREV_ADJUSTED_NUM_FS = NUM_FS;
}


// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "amount.h"
#include "chainparams.h"
#include "core_io.h"
#include "db.h"
#include "init.h"
#include "kernel.h"
#include "miner.h"
#include "rpcserver.h"
#include "txdb.h"
#include "util.h"

#include <boost/assign/list_of.hpp>

#include "univalue/univalue.h"

using namespace std;
using namespace boost::assign;

UniValue getsubsidy(const UniValue& params, bool fHelp)
{
    if (fHelp || params.size() > 1)
        throw runtime_error(
            "getsubsidy [nTarget]\n"
            "Returns proof-of-work subsidy value for the specified value of target.");

    CAmount nReward = GetProofOfWorkReward(0);

    return (CAmount)nReward;
}

UniValue getstakesubsidy(const UniValue& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "getstakesubsidy <hex string>\n"
            "Returns proof-of-stake subsidy value for the specified coinstake.");

    RPCTypeCheck(params, list_of(UniValue::VSTR));

    vector<unsigned char> txData(ParseHex(params[0].get_str()));
    CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
    CTransaction tx;
    try {
        ssData >> tx;
    } catch (std::exception& e) {
        throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
    }

    uint64_t nCoinAge;
    CCoinsViewCache view(pcoinsTip);
    CValidationState state;
    if (!GetCoinAge(tx, state, view, nCoinAge, chainActive.Height()))
        throw JSONRPCError(RPC_MISC_ERROR, "GetCoinAge failed");

    CAmount nStakeReward = GetProofOfStakeReward(nCoinAge, 0, chainActive.Height());

    return (CAmount)nStakeReward;
}

UniValue getmininginfo(const UniValue& params, bool fHelp)
{
    if (fHelp || params.size() != 0)
        throw runtime_error(
            "getmininginfo\n"
            "Returns an object containing mining-related information.");

    uint64_t nWeight = 0;
    if (pwalletMain)
        nWeight = pwalletMain->GetStakeWeight();

    UniValue obj(UniValue::VOBJ);
    UniValue diff(UniValue::VOBJ);
    UniValue weight(UniValue::VOBJ);
    obj.push_back(Pair("blocks", (int)chainActive.Height()));
    obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize));
    obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx));

    diff.push_back(Pair("proof-of-work", GetDifficulty()));
    diff.push_back(Pair("proof-of-stake", GetDifficulty(GetLastBlockIndex(chainActive.Tip(), true))));
    diff.push_back(Pair("search-interval", (int)nLastCoinStakeSearchInterval));
    obj.push_back(Pair("difficulty", diff));
    obj.push_back(Pair("blockvalue", (uint64_t)GetProofOfWorkReward(0)));

    obj.push_back(Pair("netmhashps", GetPoWMHashPS()));
    obj.push_back(Pair("netstakeweight", GetPoSKernelPS()));
    obj.push_back(Pair("errors", GetWarnings("statusbar")));
    obj.push_back(Pair("pooledtx", (uint64_t)mempool.size()));
    obj.push_back(Pair("chain", Params().NetworkIDString()));

    weight.push_back(Pair("minimum", (uint64_t)nWeight));
    weight.push_back(Pair("maximum", (uint64_t)0));
    weight.push_back(Pair("combined", (uint64_t)nWeight));
    obj.push_back(Pair("stakeweight", weight));

    obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC()));
    return obj;
}

UniValue getstakinginfo(const UniValue& params, bool fHelp)
{
    if (fHelp || params.size() != 0)
        throw runtime_error(
            "getstakinginfo\n"
            "Returns an object containing staking-related information.");

    uint64_t nWeight = 0;
    if (pwalletMain)
        nWeight = pwalletMain->GetStakeWeight();

    uint64_t nNetworkWeight = GetPoSKernelPS();
    bool staking = nLastCoinStakeSearchInterval && nWeight && (GetAdjustedTime() - nLastCoinStakeSearchTime < 60);
    uint64_t nExpectedTime = staking ? (Params().TargetSpacing() * nNetworkWeight / nWeight) : 0;

    UniValue obj(UniValue::VOBJ);

    obj.push_back(Pair("enabled", GetBoolArg("-staking", true)));
    obj.push_back(Pair("staking", staking));
    obj.push_back(Pair("errors", GetWarnings("statusbar")));

    obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize));
    obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx));
    obj.push_back(Pair("pooledtx", (uint64_t)mempool.size()));

    obj.push_back(Pair("difficulty", GetDifficulty(GetLastBlockIndex(chainActive.Tip(), true))));
    obj.push_back(Pair("search-interval", (int)nLastCoinStakeSearchInterval));
    obj.push_back(Pair("last-search-time", (int)nLastCoinStakeSearchTime));

    obj.push_back(Pair("weight", (uint64_t)nWeight));
    obj.push_back(Pair("netstakeweight", (uint64_t)nNetworkWeight));

    obj.push_back(Pair("expectedtime", nExpectedTime));

    return obj;
}

UniValue checkkernel(const UniValue& params, bool fHelp)
{
    if (fHelp || params.size() < 1 || params.size() > 2)
        throw runtime_error(
            "checkkernel [{\"txid\":txid,\"vout\":n},...] [createblocktemplate=false]\n"
            "Check if one of given inputs is a kernel input at the moment.\n");

    RPCTypeCheck(params, list_of(UniValue::VARR)(UniValue::VBOOL));

    UniValue inputs = params[0].get_array();
    bool fCreateBlockTemplate = params.size() > 1 ? params[1].get_bool() : false;

    if (vNodes.empty())
        throw JSONRPCError(-9, "Metrix is not connected!");

    if (IsInitialBlockDownload())
        throw JSONRPCError(-10, "Metrix is downloading blocks...");

    COutPoint kernel;
    CBlockIndex* pindexPrev = chainActive.Tip();
    unsigned int nBits = GetNextTargetRequired(pindexPrev, true);
    int64_t nTime = GetAdjustedTime();
    nTime &= ~STAKE_TIMESTAMP_MASK;

    for (unsigned int idx = 0; idx < inputs.size(); idx++) {
        const UniValue& input = inputs[idx];
        const UniValue& o = input.get_obj();

        const UniValue& txid_v = find_value(o, "txid");
        if (!txid_v.isStr())
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing txid key");
        string txid = txid_v.get_str();
        if (!IsHex(txid))
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid");

        const UniValue& vout_v = find_value(o, "vout");
        if (!vout_v.isNum())
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
        int nOutput = vout_v.get_int();
        if (nOutput < 0)
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");

        COutPoint cInput(uint256(txid), nOutput);
        if (CheckKernel(pindexPrev, nBits, nTime, cInput)) {
            kernel = cInput;
            break;
        }
    }

    UniValue result(UniValue::VOBJ);
    result.push_back(Pair("found", !kernel.IsNull()));

    if (kernel.IsNull())
        return result;

    UniValue oKernel(UniValue::VOBJ);;
    oKernel.push_back(Pair("txid", kernel.hash.GetHex()));
    oKernel.push_back(Pair("vout", (int64_t)kernel.n));
    oKernel.push_back(Pair("time", nTime));
    result.push_back(Pair("kernel", oKernel));

    if (!fCreateBlockTemplate)
        return result;
    CReserveKey reservekey(pwalletMain);
    CAmount nFees;
    auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey, pwalletMain, true));
    CBlock* pblock = &pblocktemplate->block; //! pointer for convenience
    pblock->nTime = pblock->vtx[0].nTime = nTime;

    CDataStream ss(SER_DISK, PROTOCOL_VERSION);
    ss << *pblock;

    result.push_back(Pair("blocktemplate", HexStr(ss.begin(), ss.end())));
    result.push_back(Pair("blocktemplatefees", nFees));

    CPubKey pubkey;
    result.push_back(Pair("blocktemplatesignkey", HexStr(pubkey)));

    return result;
}

// NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts
UniValue prioritisetransaction(const UniValue& params, bool fHelp)
{
    if (fHelp || params.size() != 3)
        throw runtime_error(
            "prioritisetransaction <txid> <priority delta> <fee delta>\n"
            "Accepts the transaction into mined blocks at a higher (or lower) priority\n"
            "\nArguments:\n"
            "1. \"txid\"       (string, required) The transaction id.\n"
            "2. priority delta (numeric, required) The priority to add or subtract.\n"
            "                  The transaction selection algorithm considers the tx as it would have a higher priority.\n"
            "                  (priority of a transaction is calculated: coinage * value_in_satoshis / txsize) \n"
            "3. fee delta      (numeric, required) The fee value (in satoshis) to add (or subtract, if negative).\n"
            "                  The fee is not actually paid, only the algorithm for selecting transactions into a block\n"
            "                  considers the transaction as it would have paid a higher (or lower) fee.\n"
            "\nResult\n"
            "true              (boolean) Returns true\n"
            "\nExamples:\n"
            + HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000")
            + HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000")
        );

    uint256 hash = ParseHashStr(params[0].get_str(), "txid");
    CAmount nAmount = params[2].get_int64();

    mempool.PrioritiseTransaction(hash, params[0].get_str(), params[1].get_real(), nAmount);
    return true;
}

UniValue getblocktemplate(const UniValue& params, bool fHelp)
{
    if (fHelp || params.size() > 1)
        throw runtime_error(
            "getblocktemplate [params]\n"
            "Returns data needed to construct a block to work on:\n"
            "  \"version\" : block version\n"
            "  \"previousblockhash\" : hash of current highest block\n"
            "  \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
            "  \"coinbaseaux\" : data that should be included in coinbase\n"
            "  \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
            "  \"target\" : hash target\n"
            "  \"mintime\" : minimum timestamp appropriate for next block\n"
            "  \"curtime\" : current timestamp\n"
            "  \"mutable\" : list of ways the block template may be changed\n"
            "  \"noncerange\" : range of valid nonces\n"
            "  \"sigoplimit\" : limit of sigops in blocks\n"
            "  \"sizelimit\" : limit of block size\n"
            "  \"bits\" : compressed target of next block\n"
            "  \"height\" : height of the next block\n"
            "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.");

    std::string strMode = "template";
    UniValue lpval = NullUniValue;
    if (params.size() > 0) {
        const UniValue& oparam = params[0].get_obj();
        const UniValue& modeval = find_value(oparam, "mode");
        if (modeval.isStr())
            strMode = modeval.get_str();
        else if (modeval.isNull())
        {
            /* Do nothing */
        }
        else
            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
        lpval = find_value(oparam, "longpollid");
    }

    if (strMode != "template")
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");

    if (vNodes.empty())
        throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Metrix is not connected!");

    //! if (IsInitialBlockDownload())
    //!     throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Metrix is downloading blocks...");

    if (chainActive.Height() >= Params().LastPOWBlock())
        throw JSONRPCError(RPC_MISC_ERROR, "No more PoW blocks");

    static unsigned int nTransactionsUpdatedLast;
    if (!lpval.isNull())
    {
        //! Wait to respond until either the best block changes, OR a minute has passed and there are more transactions
        uint256 hashWatchedChain;
        boost::system_time checktxtime;
        unsigned int nTransactionsUpdatedLastLP;

        if (lpval.isStr())
        {
            //! Format: <hashBestChain><nTransactionsUpdatedLast>
            std::string lpstr = lpval.get_str();
            hashWatchedChain.SetHex(lpstr.substr(0, 64));
            nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64));
        } else {
            //! NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier
            hashWatchedChain = chainActive.Tip()->GetBlockHash();
            nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
        }

        //! Release the wallet and main lock while waiting
#ifdef ENABLE_WALLET
        if (pwalletMain)
            LEAVE_CRITICAL_SECTION(pwalletMain->cs_wallet);
#endif
        LEAVE_CRITICAL_SECTION(cs_main);
        {
            checktxtime = boost::get_system_time() + boost::posix_time::minutes(1);

            boost::unique_lock<boost::mutex> lock(csBestBlock);
            while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning()) {
                if (!cvBlockChange.timed_wait(lock, checktxtime)) {
                    //! Timeout: Check transactions for update
                    if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP)
                        break;
                    checktxtime += boost::posix_time::seconds(10);
                }
            }
        }
        ENTER_CRITICAL_SECTION(cs_main);
#ifdef ENABLE_WALLET
        if (pwalletMain)
            ENTER_CRITICAL_SECTION(pwalletMain->cs_wallet);
#endif

        if (!IsRPCRunning())
            throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
        //! TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners?
    }

    //! Update block
    static CBlockIndex* pindexPrev;
    static int64_t nStart;
    static CBlockTemplate* pblocktemplate;
    if (pindexPrev != chainActive.Tip() ||
        (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5)) {
        //! Clear pindexPrev so future calls make a new block, despite any failures from here on
        pindexPrev = NULL;

        //! Store the chainActive.Tip() used before CreateNewBlock, to avoid races
        nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
        CBlockIndex* pindexPrevNew = chainActive.Tip();
        nStart = GetTime();

        //! Create new block
        if (pblocktemplate) {
            delete pblocktemplate;
            pblocktemplate = NULL;
        }
        CScript scriptDummy = CScript() << OP_TRUE;
        pblocktemplate = CreateNewBlock(scriptDummy, pwalletMain, false);
        if (!pblocktemplate)
            throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");

        //! Need to update only after we know CreateNewBlock succeeded
        pindexPrev = pindexPrevNew;
    }
    CBlock* pblock = &pblocktemplate->block; //! pointer for convenience

    //! Update nTime
    UpdateTime(*pblock, pindexPrev);
    pblock->nNonce = 0;

    UniValue transactions(UniValue::VARR);
    map<uint256, int64_t> setTxIndex;
    int i = 0;

    BOOST_FOREACH (CTransaction& tx, pblock->vtx) {
        uint256 txHash = tx.GetHash();
        setTxIndex[txHash] = i++;

        if (tx.IsCoinBase() || tx.IsCoinStake())
            continue;

        UniValue entry(UniValue::VOBJ);

        entry.push_back(Pair("data", EncodeHexTx(tx)));

        entry.push_back(Pair("hash", txHash.GetHex()));

        UniValue deps(UniValue::VARR);
        BOOST_FOREACH (const CTxIn& in, tx.vin) {
            if (setTxIndex.count(in.prevout.hash))
                deps.push_back(setTxIndex[in.prevout.hash]);
        }
        entry.push_back(Pair("depends", deps));
        int index_in_template = i - 1;
        entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template]));
        entry.push_back(Pair("sigops", pblocktemplate->vTxSigOps[index_in_template]));
        transactions.push_back(entry);
    }

    UniValue aux(UniValue::VOBJ);
    aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())));

    uint256 hashTarget = uint256().SetCompact(pblock->nBits);

    static UniValue aMutable(UniValue::VARR);
    if (aMutable.empty()) {
        aMutable.push_back("time");
        aMutable.push_back("transactions");
        aMutable.push_back("prevblock");
    }

    UniValue result(UniValue::VOBJ);
    result.push_back(Pair("version", pblock->nVersion));
    result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
    result.push_back(Pair("transactions", transactions));
    result.push_back(Pair("coinbaseaux", aux));
    result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
    result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast)));
    result.push_back(Pair("target", hashTarget.GetHex()));
    result.push_back(Pair("mintime", (int64_t)pindexPrev->GetPastTimeLimit() + 1));
    result.push_back(Pair("mutable", aMutable));
    result.push_back(Pair("noncerange", "00000000ffffffff"));
    result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS));
    result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE));
    result.push_back(Pair("curtime", pblock->GetBlockTime()));
    result.push_back(Pair("bits", strprintf("%08x", pblock->nBits)));
    result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight + 1)));

    return result;
}

UniValue estimatefee(const UniValue& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "estimatefee nblocks\n"
            "\nEstimates the approximate fee per kilobyte\n"
            "needed for a transaction to begin confirmation\n"
            "within nblocks blocks.\n"
            "\nArguments:\n"
            "1. nblocks     (numeric)\n"
            "\nResult:\n"
            "n :    (numeric) estimated fee-per-kilobyte\n"
            "\n"
            "-1.0 is returned if not enough transactions and\n"
            "blocks have been observed to make an estimate.\n"
            "\nExample:\n" +
            HelpExampleCli("estimatefee", "6"));

    RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM));

    int nBlocks = params[0].get_int();
    if (nBlocks < 1)
        nBlocks = 1;

    CFeeRate feeRate = mempool.estimateFee(nBlocks);
    if (feeRate == CFeeRate(0))
        return -1.0;

    return ValueFromAmount(feeRate.GetFeePerK());
}

UniValue estimatepriority(const UniValue& params, bool fHelp)
{
    if (fHelp || params.size() != 1)
        throw runtime_error(
            "estimatepriority nblocks\n"
            "\nEstimates the approximate priority\n"
            "a zero-fee transaction needs to begin confirmation\n"
            "within nblocks blocks.\n"
            "\nArguments:\n"
            "1. nblocks     (numeric)\n"
            "\nResult:\n"
            "n :    (numeric) estimated priority\n"
            "\n"
            "-1.0 is returned if not enough transactions and\n"
            "blocks have been observed to make an estimate.\n"
            "\nExample:\n" +
            HelpExampleCli("estimatepriority", "6"));

    RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM));

    int nBlocks = params[0].get_int();
    if (nBlocks < 1)
        nBlocks = 1;

    return mempool.estimatePriority(nBlocks);
}