/*
 * Software License Agreement (BSD License)
 *
 * Copyright (c) 2013, The University of Texas at Dallas
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of The University of Texas at Dallas nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

/*
 * Filename: falcon_driver.cpp
 *
 * Description: This file contains the ROS node that interfaces with the
 * Novint Falcon 3D Controller
 *
 * Log
 * ----
 * 2015-02-09 File created by Hazen Eckert
 *
 */

// ROS includes
#include "ros/ros.h"
#include "geometry_msgs/Vector3.h"

#include "novint_falcon_driver/NovintFalcon.h"

#include <string>
#include <Eigen/Core>
#include <Eigen/Dense>
#include <Eigen/Geometry>

/*
Process:
- get configuration parameters
- setup flacon class
-
*/
using namespace Eigen;
Vector3d force;

void force_callback(const geometry_msgs::Vector3::ConstPtr& msg)
{
	force(0) = msg->x;
	force(1) = msg->y;
	force(2) = msg->z;
}

 int main(int argc, char **argv)
{
	// ROS Initalization
	ros::init(argc, argv, "falcon_driver");
	ros::NodeHandle n;
	ros::NodeHandle private_n("~");

	// ROS Parameters
	std::string firmware;
	bool force_firmware, skip_checksum;
	int device_index;

	private_n.param<std::string>("firmware", firmware, "nvent_firmware");

	private_n.param<bool>("force_firmware", force_firmware, false);

	private_n.param<bool>("skip_checksum", skip_checksum, true);

	private_n.param<int>("device_index", device_index, 0);

	// ROS Subscribers

	ros::Subscriber force_sub = n.subscribe("force", 10, force_callback);

	// ROS Publishers

	ros::Publisher position_pub = n.advertise<geometry_msgs::Vector3>("position", 10);

	// Falcon Initialization
	NovintFalcon falcon;
	if(!falcon.initialize( firmware, force_firmware, skip_checksum, device_index ))
	{
		ROS_FATAL("Unable to initialize the falcon device. Exiting...");
		return 1;
	}

	while(!falcon.calibrate() && ros::ok());

	// ROS loop

	while(ros::ok())
	{
		if (!falcon.update())
			continue;

		ros::spinOnce();

		Vector3d position = falcon.getPosition();

		geometry_msgs::Vector3 msg;
		msg.x = position(0);
		msg.y = position(1);
		msg.z = position(2);

		position_pub.publish(msg);

		if(force_sub.getNumPublishers() != 1)
			falcon.setForce(Vector3d(0,0,0));
		else
			falcon.setForce(force);

	}

}


// chapter 10

#include <iostream>

#include <boost/type_traits/is_reference.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/is_pointer.hpp>
#include <boost/type_traits/is_float.hpp>
#include <boost/type_traits/alignment_of.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/type_traits/add_pointer.hpp>
#include <boost/type_traits/is_function.hpp>
#include <boost/type_traits/is_const.hpp>
#include <boost/static_assert.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/apply.hpp>

#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/not_equal_to.hpp>
#include <boost/mpl/greater.hpp>
#include <boost/mpl/plus.hpp>
#include <boost/mpl/minus.hpp>
#include <boost/mpl/or.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/multiplies.hpp>

#include <boost/mpl/at.hpp>
#include <boost/mpl/next.hpp>
#include <boost/mpl/prior.hpp>
#include <boost/mpl/begin.hpp>
#include <boost/mpl/end.hpp>
#include <boost/mpl/advance.hpp>
#include <boost/mpl/push_front.hpp>
#include <boost/mpl/pop_front.hpp>
#include <boost/mpl/insert.hpp>
#include <boost/mpl/size.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/mpl/max_element.hpp>
#include <boost/mpl/min_element.hpp>
#include <boost/mpl/sizeof.hpp>
#include <boost/mpl/less.hpp>
#include <boost/mpl/copy.hpp>
#include <boost/mpl/print.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/bind.hpp>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>

#pragma warning(disable: 4819)
#include <boost/spirit/core.hpp>
#include <boost/spirit/attribute.hpp>

// using namespace boost::mpl::placeholders;
//namespace mpl = boost::mpl;
// using namespace boost::lambda;

#include <iterator>
#include <utility>
#include <list>
#include <map>
#include <vector>
#include <string>
#include <assert.h>
#include <algorithm>

//using namespace boost;
//using namespace boost::mpl;

using namespace boost::spirit;
using namespace phoenix;

struct vars : boost::spirit::closure<vars, int>
{
	member1 value;
};

struct calculator
	: public grammar<calculator, vars::context_t>
{
	template<class Tokenizer>
	struct definition
	{
		rule<Tokenizer, vars::context_t>
			expression, term, factor, group, integer;

		rule<Tokenizer> top;

		definition(calculator const &self)
		{
			top = expression[self.value = arg1];

			group = '(' >> expression[group.value = arg1] >> ')';

			factor = integer[factor.value = arg1]
				| group[factor.value = arg1]
				;

			term = factor[term.value = arg1]
			>> *( ('*' >> factor[term.value *= arg1])
				| ('/' >> factor[term.value /= arg1])
				)
				;

			expression = term[expression.value = arg1]
			>> *( ('+' >> term[expression.value += arg1])
				| ('-' >> term[expression.value -= arg1])
				)
				;

			integer = int_p[integer.value = arg1];
		}

		rule<Tokenizer> const &start() { return top; }

	};
};

void main()
{
	calculator calc;

	std::string str;
	while (std::getline(std::cin, str))
	{
		int n= 0;
// 		parse(str.c_str(), calc[var(n) = arg1], space_p);
// 		std::cout << "result = " << n << std::endl;

	}

}


// Copyright 2020-2022 OpenDR European Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef GAUSSIAN_MIXTURE_MODEL_H
#define GAUSSIAN_MIXTURE_MODEL_H

#include <eigen_conversions/eigen_msg.h>
#include <ros/assert.h>
#include <ros/ros.h>
#include <tf/tf.h>
#include <tf/transform_listener.h>
#include <tf_conversions/tf_eigen.h>
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/system/error_code.hpp>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include "tf/transform_datatypes.h"

#include <mobile_manipulation_rl/utils.hpp>

class GaussianMixtureModel {
public:
  // GaussianMixtureModel();
  ~GaussianMixtureModel();
  GaussianMixtureModel(double max_speed_gripper_rot, double max_speed_base_rot);

  void adaptModel(tf::Transform obj_origin_goal, tf::Vector3 gmm_base_offset);
  bool loadFromFile(std::string &filename);
  void integrateModel(double current_time, double dt, Eigen::VectorXf *current_pose, Eigen::VectorXf *current_speed,
                      const double &min_velocity, const double &max_velocity, bool do_update);

  int getNrModes() const { return _nr_modes; };
  std::string getType() const { return _type; };
  void setType(std::string type) { _type = type; };
  double getkP() const { return _kP; };
  double getkV() const { return _kV; };
  std::vector<double> getPriors() const { return _Priors; };
  std::vector<Eigen::VectorXf> getMu() const { return _MuEigen; };
  std::vector<Eigen::MatrixXf> getSigma() const { return _Sigma; };
  tf::StampedTransform getGoalState() const { return _goalState; };
  tf::Transform getStartState() const { return _startState; };
  tf::Transform getGraspPose() const { return _related_object_grasp_pose; };
  tf::Transform getRelatedObjPose() const { return _related_object_pose; };
  tf::Transform getLastMuEigenBckGripper() const;
  std::string getObjectName() const { return _related_object_name; };

  double gmm_time_offset_;

protected:
  int _nr_modes;
  std::string _type;
  double _kP;
  double _kV;
  double _motion_duration;
  double _max_speed_gripper_rot;
  double _max_speed_base_rot;
  std::vector<double> _Priors;
  std::vector<std::vector<double>> _Mu;
  std::vector<Eigen::VectorXf> _MuEigen;
  std::vector<Eigen::VectorXf> _MuEigenBck;
  std::vector<Eigen::MatrixXf> _Sigma;
  std::vector<Eigen::MatrixXf> _SigmaBck;
  tf::StampedTransform _goalState;
  tf::Transform _startState;
  tf::Transform _related_object_pose;
  tf::Transform _related_object_grasp_pose;
  std::string _related_object_name;

  template<typename T> bool parseVector(std::ifstream &is, std::vector<T> &pts, const std::string &name);
  double gaussPDF(double &current_time, int mode_nr);
  void plotEllipses(Eigen::VectorXf &curr_pose, Eigen::VectorXf &curr_speed, double dt);
  void clearMarkers(int nrPoints);
  template<typename T1, typename T2> T1 extract(const T2 &full, const T1 &ind);
};

#endif  // GAUSSIAN_MIXTURE_MODEL_H


/*
 * Analysis.cpp
 *
 *  Created on: 12 Jul 2010
 *      Author: kreczko
 */

#include "Analysis.h"
#include "TROOT.h"
#include <iostream>
#include <boost/scoped_ptr.hpp>
#include <boost/array.hpp>
#include "../interface/EventCounter.h"
#include <cmath>
#include <math.h>
#include "../interface/Printers/EventTablePrinter.h"
#include "../interface/ReconstructionModules/ChiSquaredBasedTopPairReconstruction.h"
#include "../interface/LumiReWeighting.h"
#include "../interface/GlobalVariables.h"
#include "../interface/BTagWeight.h"

using namespace reweight;
using namespace BAT;
using namespace std;

void Analysis::analyse() {
	createHistograms();
	cout << "detected samples:" << endl;
	for (unsigned int sample = 0; sample < DataType::NUMBER_OF_DATA_TYPES; ++sample) {
		if (eventReader->getSeenDatatypes()[sample])
			cout << DataType::names[sample] << endl;
	}
	while (eventReader->hasNextEvent()) {
		initiateEvent();
		printNumberOfProccessedEventsEvery(Globals::printEveryXEvents);
		inspectEvents();
		const JetCollection jets(currentEvent->Jets());
		unsigned int numberOfJets(jets.size());
		unsigned int numberOfBJets(0);
		for (unsigned int index = 0; index < numberOfJets; ++index) {
			const JetPointer jet(currentEvent->Jets().at(index));
			if (jet->isBJet(BtagAlgorithm::CombinedSecondaryVertex, BtagAlgorithm::MEDIUM))
				++numberOfBJets;
		}
		histMan->setCurrentBJetBin(numberOfBJets);
		histMan->setCurrentJetBin(numberOfJets);

		vector<double> bjetWeights;
		if (currentEvent->isRealData()) {
			for (unsigned int index = 0; index <= numberOfBJets; ++index) {
				if (index == numberOfBJets)
					bjetWeights.push_back(1.);
				else
					bjetWeights.push_back(0);
			}
		} else
			bjetWeights = BjetWeights(jets, numberOfBJets);

		eventcountAnalyser->analyse(currentEvent);
//		mttbarAnalyser->analyse(currentEvent);
		ttbar_plus_X_analyser_->analyse(currentEvent);
		diffVariablesAnalyser->analyse(currentEvent);
//		binningAnalyser->analyse(currentEvent);
	}
}

void Analysis::printNumberOfProccessedEventsEvery(unsigned long printEvery) {
	unsigned long eventIndex = eventReader->getNumberOfProccessedEvents();
	if (eventIndex % printEvery == 0 || eventIndex == 1) {
		cout << "Analysing event no " << eventIndex << ", sample: " << DataType::names[currentEvent->getDataType()]
				<< endl;
		cout << "File: " << eventReader->getCurrentFile() << endl;
	}

}

void Analysis::initiateEvent() {
	currentEvent = eventReader->getNextEvent();
	weight = 1.;
	histMan->setCurrentDataType(currentEvent->getDataType());
	histMan->setCurrentJetBin(currentEvent->Jets().size());
	histMan->setCurrentBJetBin(0);

	if (!currentEvent->isRealData()) {
		weight = weights->getWeight(currentEvent->getDataType());
		//TODO: fix this dirty little thing
		pileUpWeight = weights->reweightPileUp(currentEvent->getTrueNumberOfVertices().at(1));
		weight *= pileUpWeight;
		if (Globals::pdfWeightNumber != 0) {
			try {
				double pdf_weight(currentEvent->PDFWeights().at(Globals::pdfWeightNumber) / currentEvent->PDFWeights().at(0));
				weight *= pdf_weight;

				histMan->setCurrentHistogramFolder("");
				histMan->H1D("PDFweights")->Fill(pdf_weight);
			} catch (exception& e) {
				cout << "PDF weight assigning exception: " << e.what() << endl;
			}
		}
	}

	//top pt weight
	if(Globals::applyTopPtReweighting == true && currentEvent->getDataType() == DataType::TTJets){

	double topPtweight = 1.;
	topPtweight = weights->reweightTopPt(currentEvent);

	weight *= topPtweight;
	}

	currentEvent->setEventWeight(weight);
	currentEvent->setPileUpWeight(pileUpWeight);

}

void Analysis::inspectEvents() {
	std::vector<InterestingEvent> eventsToInspect;

	for (unsigned int index = 0; index < eventsToInspect.size(); ++index) {
		if ((currentEvent->runnumber() == eventsToInspect.at(index).candidate->runnumber()
				&& currentEvent->eventnumber() == eventsToInspect.at(index).candidate->eventnumber())) {
			cout << "file: " << eventReader->getCurrentFile() << endl;
			currentEvent->inspect();
		}
	}

}

void Analysis::printInterestingEvents() {
	cout << "Interesting events:" << endl;
	for (unsigned int index = 0; index < interestingEvents.size(); ++index) {
		interestingEvents.at(index).print();
	}
}

void Analysis::printSummary() {
	cout << "total number of processed events: " << eventReader->getNumberOfProccessedEvents() << endl;
	cout << "number of events without electrons: " << brokenEvents.size() << endl;
	cout << "number of events with too high pileup: " << weights->getNumberOfEventsWithTooHighPileUp() << endl;
}

void Analysis::createHistograms() {
	histMan->prepareForSeenDataTypes(eventReader->getSeenDatatypes());
	unsigned int numberOfHistograms(0), lastNumberOfHistograms(0);

	eventcountAnalyser->createHistograms();
	numberOfHistograms = histMan->size();
	cout << "Number of histograms added by eventcountAnalyser: " << numberOfHistograms - lastNumberOfHistograms << endl;
	lastNumberOfHistograms = numberOfHistograms;

	ttbar_plus_X_analyser_->createHistograms();
	numberOfHistograms = histMan->size();
	cout << "Number of histograms added by ttbar_plus_X_analyser: " << numberOfHistograms - lastNumberOfHistograms
			<< endl;
	lastNumberOfHistograms = numberOfHistograms;

	diffVariablesAnalyser->createHistograms();
	numberOfHistograms = histMan->size();
	cout << "Number of histograms added by diffVariablesAnalyser: " << numberOfHistograms - lastNumberOfHistograms << endl;
	lastNumberOfHistograms = numberOfHistograms;

	histMan->setCurrentHistogramFolder("");
	histMan->addH1D("PDFweights", "PDF weights", 1000, 0.8, 1.2);

	cout << "Total number of histograms: " << histMan->size() << endl;
}

Analysis::Analysis(std::string datasetInfoFile) : //
		eventReader(new NTupleEventReader()), //
		currentEvent(), //
		histMan(new BAT::HistogramManager()), //
		interestingEvents(), //
		brokenEvents(), //
		eventCheck(), //
		weights(new EventWeightProvider(datasetInfoFile)), //
		weight(0), //
		pileUpWeight(1), //
		abcdMethodAnalyser_(new ABCDMethodAnalyser(histMan)), //
		bjetAnalyser(new BJetAnalyser(histMan)), //
		diElectronAnalyser(new DiElectronAnalyser(histMan)), //
		electronAnalyser(new ElectronAnalyser(histMan)), //
		eventcountAnalyser(new EventCountAnalyser(histMan)), //
		hltriggerAnalyser(new HLTriggerTurnOnAnalyser(histMan)), //
		hltriggerQCDAnalyserInclusive_(new HLTriggerQCDAnalyser(histMan, "HLTQCDAnalyser_inclusive", false)), //
		hltriggerQCDAnalyserExclusive_(new HLTriggerQCDAnalyser(histMan, "HLTQCDAnalyser_exclusive", true)), //
		jetAnalyser(new JetAnalyser(histMan)), //
		mcAnalyser(new MCAnalyser(histMan)), //
		metAnalyser(new METAnalyser(histMan)), //
		mttbarAnalyser(new MTtbarAnalyser(histMan)), //
		muonAnalyser(new MuonAnalyser(histMan)), //
		mvAnalyser(new MVAnalyser(histMan)), //
		neutrinoRecoAnalyser(new NeutrinoReconstructionAnalyser(histMan)), //
		ttbar_plus_X_analyser_(new TTbar_plus_X_analyser(histMan)), //
		vertexAnalyser(new VertexAnalyser(histMan)),
		diffVariablesAnalyser(new DiffVariablesAnalyser(histMan)),
		binningAnalyser(new BinningAnalyser(histMan)) {
	histMan->enableDebugMode(true);
}

Analysis::~Analysis() {

}

void Analysis::finishAnalysis() {
	printSummary();
	histMan->writeToDisk();
}

void Analysis::addInputFile(const char* fileName) {
	eventReader->addInputFile(fileName);
}

void Analysis::setMaximalNumberOfEvents(long maxEvents) {
	if (maxEvents > 0) {
		eventReader->setMaximumNumberOfEvents(maxEvents);
	}
}

void Analysis::checkForDuplicatedEvents() {
	map<unsigned long, std::vector<unsigned long> >::const_iterator iter;
	std::vector<pair<unsigned long, unsigned long> > duplicateEvents;

	for (iter = eventCheck.begin(); iter != eventCheck.end(); ++iter) {
		std::vector<unsigned long> events = (*iter).second;
		std::sort(events.begin(), events.end());
		for (unsigned long ev = 0; ev < events.size() - 1; ++ev) {
			if (events.at(ev) == events.at(ev + 1)) {
				duplicateEvents.push_back(make_pair((*iter).first, events.at(ev)));
			}
		}
	}

	if (duplicateEvents.size() > 0) {
		cout << "found duplicate events" << endl;
		for (unsigned long ev = 0; ev < duplicateEvents.size() - 1; ++ev) {
			cout << "run: " << duplicateEvents.at(ev).first << " event: " << duplicateEvents.at(ev).second << endl;
		}
	}
}

unsigned long Analysis::getNumberOfProccessedEvents() const {
	return eventReader->getNumberOfProccessedEvents();
}


#include "translation_table.h"

#include <string>
#include <vector>

#include <boost/functional/hash.hpp>

#include "alignment.h"
#include "data_array.h"

using namespace std;

namespace extractor {

TranslationTable::TranslationTable(shared_ptr<DataArray> source_data_array,
                                   shared_ptr<DataArray> target_data_array,
                                   shared_ptr<Alignment> alignment) :
    source_data_array(source_data_array), target_data_array(target_data_array) {
  const vector<int>& source_data = source_data_array->GetData();
  const vector<int>& target_data = target_data_array->GetData();

  unordered_map<int, int> source_links_count;
  unordered_map<int, int> target_links_count;
  unordered_map<pair<int, int>, int, PairHash> links_count;

  // For each pair of aligned source target words increment their link count by
  // 1. Unaligned words are paired with the NULL token.
  for (size_t i = 0; i < source_data_array->GetNumSentences(); ++i) {
    vector<pair<int, int>> links = alignment->GetLinks(i);
    int source_start = source_data_array->GetSentenceStart(i);
    int target_start = target_data_array->GetSentenceStart(i);
    // Ignore END_OF_LINE markers.
    int next_source_start = source_data_array->GetSentenceStart(i + 1) - 1;
    int next_target_start = target_data_array->GetSentenceStart(i + 1) - 1;
    vector<int> source_sentence(source_data.begin() + source_start,
        source_data.begin() + next_source_start);
    vector<int> target_sentence(target_data.begin() + target_start,
        target_data.begin() + next_target_start);
    vector<int> source_linked_words(source_sentence.size());
    vector<int> target_linked_words(target_sentence.size());

    for (pair<int, int> link: links) {
      source_linked_words[link.first] = 1;
      target_linked_words[link.second] = 1;
      IncrementLinksCount(source_links_count, target_links_count, links_count,
          source_sentence[link.first], target_sentence[link.second]);
    }

    for (size_t i = 0; i < source_sentence.size(); ++i) {
      if (!source_linked_words[i]) {
        IncrementLinksCount(source_links_count, target_links_count, links_count,
                            source_sentence[i], DataArray::NULL_WORD);
      }
    }

    for (size_t i = 0; i < target_sentence.size(); ++i) {
      if (!target_linked_words[i]) {
        IncrementLinksCount(source_links_count, target_links_count, links_count,
                            DataArray::NULL_WORD, target_sentence[i]);
      }
    }
  }

  // Calculating:
  //   p(e | f) = count(e, f) / count(f)
  //   p(f | e) = count(e, f) / count(e)
  for (pair<pair<int, int>, int> link_count: links_count) {
    int source_word = link_count.first.first;
    int target_word = link_count.first.second;
    double score1 = 1.0 * link_count.second / source_links_count[source_word];
    double score2 = 1.0 * link_count.second / target_links_count[target_word];
    translation_probabilities[link_count.first] = make_pair(score1, score2);
  }
}

TranslationTable::TranslationTable() {}

TranslationTable::~TranslationTable() {}

void TranslationTable::IncrementLinksCount(
    unordered_map<int, int>& source_links_count,
    unordered_map<int, int>& target_links_count,
    unordered_map<pair<int, int>, int, PairHash>& links_count,
    int source_word_id,
    int target_word_id) const {
  ++source_links_count[source_word_id];
  ++target_links_count[target_word_id];
  ++links_count[make_pair(source_word_id, target_word_id)];
}

double TranslationTable::GetTargetGivenSourceScore(
    const string& source_word, const string& target_word) {
  int source_id = source_data_array->GetWordId(source_word);
  int target_id = target_data_array->GetWordId(target_word);
  if (source_id == -1 || target_id == -1) {
    return -1;
  }

  auto entry = make_pair(source_id, target_id);
  auto it = translation_probabilities.find(entry);
  if (it == translation_probabilities.end()) {
    return 0;
  }
  return it->second.first;
}

double TranslationTable::GetSourceGivenTargetScore(
    const string& source_word, const string& target_word) {
  int source_id = source_data_array->GetWordId(source_word);
  int target_id = target_data_array->GetWordId(target_word);
  if (source_id == -1 || target_id == -1) {
    return -1;
  }

  auto entry = make_pair(source_id, target_id);
  auto it = translation_probabilities.find(entry);
  if (it == translation_probabilities.end()) {
    return 0;
  }
  return it->second.second;
}

bool TranslationTable::operator==(const TranslationTable& other) const {
  return *source_data_array == *other.source_data_array &&
         *target_data_array == *other.target_data_array &&
         translation_probabilities == other.translation_probabilities;
}

} // namespace extractor


#include "llvm/Pass.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/Transforms/Utils/UnifyFunctionExitNodes.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/CFG.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Debug.h"

#include "crab_llvm/config.h"
#include "crab_llvm/crab_domains.hh"
#include "crab_llvm/wrapper_domain.hh"
#include "crab_llvm/CrabLlvm.hh"
#include "crab_llvm/CfgBuilder.hh"
#include "crab_llvm/HeapAbstraction.hh"
#include "crab_llvm/Support/NameValues.hh"

#include "crab/common/debug.hpp"
#include "crab/common/stats.hpp"
#include "crab/analysis/fwd_analyzer.hpp"
#include "crab/analysis/bwd_analyzer.hpp"
#include "crab/analysis/inter_fwd_analyzer.hpp"
#include "crab/analysis/dataflow/liveness.hpp"
#include "crab/analysis/dataflow/assumptions.hpp"
#include "crab/checkers/assertion.hpp"
#include "crab/checkers/null.hpp"
#include "crab/checkers/checker.hpp"
#include "crab/cg/cg.hpp"
#include "crab/cg/cg_bgl.hpp"

#include "./crab/path_analyzer.hpp"

#include <boost/shared_ptr.hpp>
#include <boost/unordered_map.hpp>
#include <boost/unordered_set.hpp>
#include <boost/range/iterator_range.hpp>
#include <memory>
#include <functional>
#include <map>

#ifdef HAVE_DSA
#include "dsa/DSNode.h"
#include "dsa/Steensgaard.hh"
#endif

using namespace llvm;
using namespace crab_llvm;
using namespace crab::cfg;

cl::opt<bool>
CrabPrintAns ("crab-print-invariants",
              cl::desc ("Print Crab invariants"),
              cl::init (false));

cl::opt<bool>
CrabPrintSumm ("crab-print-summaries",
               cl::desc ("Print Crab function summaries"),
               cl::init (false));

cl::opt<bool>
CrabPrintPreCond ("crab-print-preconditions",
               cl::desc ("Print Crab necessary preconditions"),
               cl::init (false));

cl::opt<bool>
CrabStoreInvariants ("crab-store-invariants",
               cl::desc ("Store invariants"),
               cl::init (true));

cl::opt<bool>
CrabStats ("crab-stats",
           cl::desc ("Show Crab statistics and analysis results"),
           cl::init (false));

cl::opt<bool>
CrabPrintAssumptions ("crab-print-unjustified-assumptions",
       cl::desc ("Print unjustified assumptions done by Crab (for now only integer overflow)"),
       cl::init (false));

cl::opt<unsigned int>
CrabWideningDelay("crab-widening-delay",
   cl::desc("Max number of fixpoint iterations until widening is applied"),
   cl::init (1));

cl::opt<unsigned int>
CrabNarrowingIters("crab-narrowing-iterations",
                   cl::desc("Max number of narrowing iterations"),
                   cl::init (10));

cl::opt<unsigned int>
CrabWideningJumpSet("crab-widening-jump-set",
                    cl::desc("Size of the jump set used for widening"),
                    cl::init (0));

cl::opt<CrabDomain>
CrabLlvmDomain("crab-dom",
      cl::desc ("Crab numerical abstract domain used to infer invariants"),
      cl::values
      (clEnumValN (INTERVALS, "int",
		   "Classical interval domain (default)"),
       clEnumValN (TERMS_INTERVALS, "term-int",
		   "Intervals with uninterpreted functions."),
       clEnumValN (INTERVALS_CONGRUENCES, "ric",
		   "Reduced product of intervals with congruences"),
       clEnumValN (DIS_INTERVALS, "dis-int",
		   "Disjunctive intervals based on Clousot's DisInt domain"),
       clEnumValN (TERMS_DIS_INTERVALS, "term-dis-int",
		   "Disjunctive Intervals with uninterpreted functions."),
       clEnumValN (BOXES, "boxes",
		   "Disjunctive intervals based on ldds"),
       clEnumValN (ZONES_SPLIT_DBM, "zones",
		   "Zones domain with Sparse DBMs in Split Normal Form"),
       clEnumValN (OPT_OCT_APRON, "oct",
		   "Optimized octagons domain using Elina"),
       clEnumValN (PK_APRON, "pk",
		   "Polyhedra domain using Apron library"),
       clEnumValN (TERMS_ZONES, "rtz",
		   "Reduced product of term-dis-int and zones."),
       clEnumValN (WRAPPED_INTERVALS, "w-int",
		   "Wrapped interval domain"),
       clEnumValEnd),
       cl::init (INTERVALS));

cl::opt<bool>
CrabBackward ("crab-backward",
           cl::desc ("Perform an iterative forward/backward analysis\n"
		     "(Only intra-procedural version implemented)"),
           cl::init (false));

// If domain is num
cl::opt<unsigned>
CrabRelationalThreshold("crab-relational-threshold",
   cl::desc("Max number of live vars per block before switching "
	    "to a non-relational domain"),
   cl::init (10000),
   cl::Hidden);

cl::opt<bool>
CrabLive("crab-live",
	 cl::desc("Run Crab with live ranges. "
		  "It can lose precision if relational domains"),
	 cl::init (false));

cl::opt<bool>
CrabInter ("crab-inter",
           cl::desc ("Crab Inter-procedural analysis"),
           cl::init (false));

// It does not make much sense to have non-relational domains here.
cl::opt<CrabDomain>
CrabSummDomain("crab-inter-sum-dom",
    cl::desc ("Crab relational domain to generate function summaries"),
    cl::values
    (clEnumValN (ZONES_SPLIT_DBM, "zones",
		 "Zones domain with sparse DBMs in Split Normal Form"),
     clEnumValN (OPT_OCT_APRON, "oct",
		 "Optimized octagons using Elina"),
     clEnumValN (TERMS_ZONES, "rtz",
		 "Reduced product of term-dis-int and zones."),
     clEnumValEnd),
    cl::init (ZONES_SPLIT_DBM));

cl::opt<enum tracked_precision>
CrabTrackLev("crab-track",
   cl::desc ("Track abstraction level of the Crab Cfg"),
   cl::values
    (clEnumValN (NUM, "num", "Integer and Boolean registers only"),
     clEnumValN (PTR, "ptr", "num + pointer offsets"),
     clEnumValN (ARR, "arr", "ptr + memory contents via array abstraction"),
     clEnumValEnd),
   cl::init (tracked_precision::NUM));

// llvm-dsa options
cl::opt<bool>
CrabDsaDisambiguateUnknown ("crab-dsa-disambiguate-unknown",
    cl::desc ("Disambiguate unknown pointers (unsound)"),
    cl::init (false),
    cl::Hidden);

cl::opt<bool>
CrabDsaDisambiguatePtrCast ("crab-dsa-disambiguate-ptr-cast",
    cl::desc ("Disambiguate pointers that have been casted from/to integers (unsound)"),
    cl::init (false),
    cl::Hidden);

cl::opt<bool>
CrabDsaDisambiguateExternal ("crab-dsa-disambiguate-external",
    cl::desc ("Disambiguate pointers that have been passed to external functions (unsound)"),
    cl::init (false),
    cl::Hidden);

// Prove assertions
cl::opt<assert_check_kind_t>
CrabCheck ("crab-check",
	   cl::desc ("Check user assertions"),
	   cl::values(
	       clEnumValN (NOCHECKS  , "none"  , "None"),
	       clEnumValN (ASSERTION , "assert", "User assertions"),
	       clEnumValN (NULLITY   , "null"  , "Null dereference"),
	       clEnumValEnd),
	   cl::init (assert_check_kind_t::NOCHECKS));

cl::opt<unsigned int>
CrabCheckVerbose ("crab-check-verbose",
                 cl::desc ("Print verbose information about checks"),
                 cl::init (0));

// Important to crab-llvm clients (e.g., SeaHorn):
// Shadow variables are variables that cannot be mapped back to a
// const Value*. These are created for instance for memory heaps.
cl::opt<bool>
CrabKeepShadows ("crab-keep-shadows",
    cl::desc ("Preserve shadow variables in invariants, summaries, and preconditions"),
    cl::init (false),
    cl::Hidden);

// In C++11 we need to pass to std::bind "this" (if class member
// functions are called) as well as all placeholders for each
// argument. This is wrapper to avoid that and improve readability.
template <class C, typename Ret, typename ... Ts>
static std::function<Ret(Ts...)> bind_this(C* c, Ret (C::*m)(Ts...)) {
  return [=](Ts&&... args) { return (c->*m)(std::forward<decltype(args)>(args)...); };
}

namespace crab_llvm {

  static crab::crab_os& get_crab_os(bool show_time = true) {
    crab::crab_os* result = &crab::outs();
    if (show_time) {
      time_t now = time(0);
      struct tm tstruct;
      char buf[80];
      tstruct = *localtime(&now);
      strftime(buf, sizeof(buf), "[%Y-%m-%d.%X] ", &tstruct);
      *result << buf;
    }
    return *result;
  }

  using namespace crab::analyzer;
  using namespace crab::checker;
  using namespace crab::cg;

  /** Begin typedefs **/
  typedef crab::analyzer::liveness<cfg_ref_t> liveness_t;
  typedef crab::cg::call_graph<cfg_ref_t> call_graph_t;
  typedef crab::cg::call_graph_ref<call_graph_t> call_graph_ref_t;
  typedef boost::unordered_map<cfg_ref_t, const liveness_t*> liveness_map_t;
  typedef DenseMap<const BasicBlock*, lin_cst_sys_t> assumption_map_t;
  typedef typename IntraCrabLlvm::wrapper_dom_ptr wrapper_dom_ptr;
  typedef typename IntraCrabLlvm::checks_db_t checks_db_t;
  typedef typename IntraCrabLlvm::invariant_map_t invariant_map_t;
  typedef typename IntraCrabLlvm::heap_abs_ptr heap_abs_ptr;
  /** End typedefs **/

  /** Begin global counters **/
  static unsigned num_invars; // some measure for the size of invariants
  static unsigned num_nontrivial_blocks;
  /** End global counters **/

  static bool isRelationalDomain(CrabDomain dom) {
    return (dom == ZONES_SPLIT_DBM || dom == OPT_OCT_APRON ||
	    dom == PK_APRON        || dom == TERMS_ZONES);
  }

  static bool isTrackable(const Function &fun) {
    return !fun.isDeclaration () && !fun.empty () && !fun.isVarArg ();
  }

  /** convenient wrapper for the invariance analysis datastructures **/
  struct InvarianceAnalysisResults {
    // invariants that hold at the entry of a block
    invariant_map_t &premap;
    // invariants that hold at the exit of a block
    invariant_map_t &postmap;
    // database with all the checks
    checks_db_t &checksdb;

    InvarianceAnalysisResults(invariant_map_t &pre, invariant_map_t &post,
			      checks_db_t &db)
      : premap(pre), postmap(post), checksdb(db) {}
  };

  /** return invariant for block in table but filtering out shadow_varnames **/
  static wrapper_dom_ptr lookup(const invariant_map_t &table,
				const llvm::BasicBlock &block,
				const std::vector<varname_t> &shadow_varnames) {
    auto it = table.find (&block);
    if (it == table.end()) {
      return nullptr;
    }

    if (shadow_varnames.empty()) {
      return it->second;
    } else {
      std::vector<var_t> shadow_vars;
      shadow_vars.reserve(shadow_varnames.size());
      for(unsigned i=0; i<shadow_vars.size(); ++i) {
	// we need to create a typed variable
	shadow_vars.push_back(var_t(shadow_varnames[i], crab::UNK_TYPE, 0));
      }
      auto invs = it->second->clone();
      invs->forget(shadow_vars);
      return invs;
    }
  }

  /** update table with pre or post invariants **/
  static bool update(invariant_map_t &table,
		     const llvm::BasicBlock &block, wrapper_dom_ptr absval) {
    bool already = false;
    auto it = table.find(&block);
    if (it == table.end()) {
      table.insert(std::make_pair(&block, absval));
    } else {
      it->second = absval;
      already = true;
    }
    return already;
  }

  /** Pretty-printer utilities **/
  namespace pretty_printer_impl {

    /** Generic class for a block annotation **/
    class block_annotation {
    public:
      typedef typename cfg_ref_t::statement_t statement_t;

      block_annotation() {}
      virtual ~block_annotation() {}

      virtual std::string name() const = 0;
      virtual void print_begin(basic_block_label_t bbl, crab::crab_os &o) const {}
      virtual void print_end(basic_block_label_t bbl, crab::crab_os &o) const {}
      virtual void print_begin(const statement_t &s, crab::crab_os &o) const {}
      virtual void print_end(const statement_t &s, crab::crab_os &o) const {}

    };

    /** Annotation for invariants **/
    class invariant_annotation: public block_annotation {
    private:
      const invariant_map_t &m_premap;
      const invariant_map_t &m_postmap;
      std::vector<varname_t> m_shadow_vars;

    public:
      invariant_annotation (const llvm_variable_factory &vfac,
			    const invariant_map_t &premap,
			    const invariant_map_t &postmap,
			    const bool keep_shadows)
	: block_annotation(), m_premap(premap), m_postmap(postmap) {
	if (keep_shadows) {
	  m_shadow_vars.reserve(std::distance(vfac.get_shadow_vars().begin(),
					      vfac.get_shadow_vars().end()));
	  m_shadow_vars.insert(m_shadow_vars.begin(),
			       vfac.get_shadow_vars().begin(),
			       vfac.get_shadow_vars().end());
	}
      }

      std::string name() const { return "INVARIANTS";}

      void print_begin(basic_block_label_t bbl, crab::crab_os &o) const {
	if (const llvm::BasicBlock *bb = bbl.get_basic_block()) {
	  wrapper_dom_ptr pre = lookup(m_premap, *bb, m_shadow_vars);
	  o << "  " << name() << ": ";
	  if (pre){
	    o << pre << "\n";
	  } else {
	    o << "null\n";
	  }
	}
      }

      void print_end(basic_block_label_t bbl, crab::crab_os &o) const {
	if (const llvm::BasicBlock *bb = bbl.get_basic_block()) {
	  wrapper_dom_ptr post = lookup(m_postmap, *bb, m_shadow_vars);
	  o << "  " << name() << ": ";
	  if (post) {
	    o << post << "\n";
	  } else {
	    o << "null\n";
	  }
	}
      }
    };

    /** Annotation for neccesary_preconditions **/
    template<typename Analyzer>
    class nec_precondition_annotation: public block_annotation {
    private:
      Analyzer &m_analyzer;

    public:
      nec_precondition_annotation (Analyzer &analyzer)
	: block_annotation(), m_analyzer(analyzer) {}

      std::string name() const { return "NECESSARY PRECONDITIONS";}

      void print_begin(basic_block_label_t bbl, crab::crab_os &o) const {
	auto pre = m_analyzer.get_preconditions(bbl);
	o << "  " << name() << ": " << pre << "\n";
      }
    };

    /** Annotation for unjustified assumptions done by the analysis **/
    class assumption_annotation: public block_annotation {
    private:
      typedef typename assumption_analysis<cfg_ref_t>::assumption_ptr assumption_ptr;
      typedef assumption_analysis<cfg_ref_t> assumption_analysis_t;
      typedef typename cfg_ref_t::statement_t statement_t;

      cfg_ref_t m_cfg;
      assumption_analysis_t *m_analyzer;

    public:
      assumption_annotation (cfg_ref_t cfg, assumption_analysis_t *analyzer)
	: block_annotation(), m_cfg(cfg), m_analyzer(analyzer) { }

      std::string name() const { return "UNJUSTIFIED ASSUMPTIONS";}

      void print_begin(const statement_t &s, crab::crab_os &o) const {
	std::vector<assumption_ptr> assumes;
	if (s.is_assert()) {
	  typedef typename cfg_ref_t::basic_block_t::assert_t assert_t;
	  m_analyzer->get_assumptions(static_cast<const assert_t *>(&s), assumes);
	  if (!assumes.empty()) {
	    o << "  /** assert verified as ";
	    for (std::vector<assumption_ptr>::iterator it = assumes.begin(),
		   et = assumes.end(); it!=et;) {
	      o << (*it)->get_id_str();
	      ++it;
	      if (it != et)
		o << ",";
	      else
		o << ";";
	    }
	    o << "**/\n";
	  }
	} else {
	  m_analyzer->get_originated_assumptions(&s, assumes);
	  for (auto assume_ptr: assumes) {
	    o << "  /** "; assume_ptr->write(o); o << "**/\n";
	  }
	}
      }

    };

    /** Print a block together with its annotations **/
    class print_block {
      cfg_ref_t m_cfg;
      crab::crab_os &m_o;
      const std::vector<std::unique_ptr<block_annotation>> &m_annotations;

    public:

      print_block (cfg_ref_t cfg, crab::crab_os &o,
		   const std::vector<std::unique_ptr<block_annotation>> &annotations)
	: m_cfg(cfg), m_o(o), m_annotations(annotations) {}

      void operator()(basic_block_label_t bbl) const {
	// do not print block if no annotations
	if (m_annotations.empty()) return;

	m_o << bbl.get_name() << ":\n";

	crab::crab_string_os o;
	for (auto& p: m_annotations) {
	  p->print_begin(bbl,o);
	}
	if (o.str() != "") {
	  m_o << "/**\n" << o.str() << "**/\n";
	}

	const basic_block_t &bb = m_cfg.get_node(bbl);
	bool empty_block = (std::distance(bb.begin(), bb.end()) == 0);
	for (auto const &s: bb) {
	  for (auto& p: m_annotations) {
	    p->print_begin(s, m_o);
	  }
	  m_o << "  " << s << ";\n";
	  for (auto& p: m_annotations) {
	    p->print_end(s, m_o);
	  }
	}
	if (!empty_block) {
	  crab::crab_string_os o;
	  for (auto& p: m_annotations) {
	    p->print_end(bbl, o);
	  }
	  if (o.str() != "") {
	    m_o << "/**\n" << o.str() << "**/\n";
	  }
	}

	std::pair<cfg_ref_t::const_succ_iterator,
		  cfg_ref_t::const_succ_iterator> p = bb.next_blocks();
	cfg_ref_t::const_succ_iterator it = p.first;
	cfg_ref_t::const_succ_iterator et = p.second;
	if (it != et) {
	  m_o << "  " << "goto ";
	  for (; it != et; ) {
	    m_o << crab::cfg_impl::get_label_str (*it);
	    ++it;
	    if (it == et) {
	      m_o << ";";
	    } else {
	      m_o << ",";
	    }
	  }
	}
	m_o << "\n";
      }
    };

    typedef boost::unordered_set<basic_block_label_t> visited_t;
    template<typename T>
    void dfs_rec (cfg_ref_t cfg, basic_block_label_t curId, visited_t &visited, T f) {
      if (visited.find (curId) != visited.end ()) return;
      visited.insert (curId);
      const basic_block_t &cur = cfg.get_node(curId);
      f (curId);
      for (auto const n : boost::make_iterator_range (cur.next_blocks ())) {
    	dfs_rec (cfg, n, visited, f);
      }
    }

    template<typename T>
    void dfs (cfg_ref_t cfg, T f) {
      visited_t visited;
      dfs_rec (cfg, cfg.entry(), visited, f);
    }

    void print_annotations(cfg_ref_t cfg, const std::vector<std::unique_ptr<block_annotation>> &annotations) {
      print_block f(cfg, crab::outs(), annotations);
      dfs(cfg, f);
    }
  } //end namespace

  static std::string dom_to_str(CrabDomain dom) {
    switch (dom) {
    case INTERVALS:             return interval_domain_t::getDomainName();
    case INTERVALS_CONGRUENCES: return ric_domain_t::getDomainName();
    case BOXES:                 return boxes_domain_t::getDomainName();
    case DIS_INTERVALS:         return dis_interval_domain_t::getDomainName();
    case ZONES_SPLIT_DBM:       return split_dbm_domain_t::getDomainName();
    case TERMS_DIS_INTERVALS:   return term_dis_int_domain_t::getDomainName();
    case TERMS_ZONES:           return num_domain_t::getDomainName();
    case OPT_OCT_APRON:         return opt_oct_apron_domain_t::getDomainName();
    case PK_APRON:              return pk_apron_domain_t::getDomainName();
    case WRAPPED_INTERVALS:     return wrapped_interval_domain_t::getDomainName();
    default:                    return "none";
    }
  }

  std::string AnalysisParams::abs_dom_to_str() const {
    return dom_to_str(dom);
  }

  std::string AnalysisParams::sum_abs_dom_to_str() const {
    return dom_to_str(sum_dom);
  }

  /* CFG Manager class */
  CfgManager::CfgManager(){}
  CfgManager::~CfgManager(){
    for (auto &kv: m_cfg_map) {
      delete kv.second;
    }
  }

  bool CfgManager::has_cfg(const Function &f) const {
    return m_cfg_map.find(&f) != m_cfg_map.end();
  }

  cfg_ref_t CfgManager::operator[](const Function &f) const {
    assert(has_cfg(f));

    auto it = m_cfg_map.find(&f);
    cfg_t *cfg = it->second;
    return cfg_ref_t(*cfg);
  }

  void CfgManager::add(const Function &f, cfg_t *cfg) {
    if (!has_cfg(f)) {
      m_cfg_map.insert(std::make_pair(&f, cfg));
    }
  }

  /**
   * Internal implementation of the intra-procedural analysis
   **/
  class IntraCrabLlvm_Impl {

    cfg_t *m_cfg;
    Function &m_fun;
    llvm_variable_factory &m_vfac;
    typename CfgBuilder::edge_to_bb_map_t m_edge_bb_map;

    template<typename Dom>
    void analyzeCfg(const AnalysisParams &params,
		    const BasicBlock *entry,
		    const assumption_map_t &assumptions, liveness_t *live,
		    InvarianceAnalysisResults &results) {

      // -- we use the combined forward/backward analyzer
      typedef intra_forward_backward_analyzer<cfg_ref_t,Dom> intra_analyzer_t;
      // -- checkers for assertions and nullity
      typedef intra_checker<intra_analyzer_t> intra_checker_t;
      typedef assert_property_checker<intra_analyzer_t> assert_prop_t;
      typedef null_property_checker<intra_analyzer_t> null_prop_t;

      CRAB_VERBOSE_IF(1,
		      auto fdecl = m_cfg->get_func_decl ();
		      assert (fdecl);
		      get_crab_os() << "Running intra-procedural analysis with "
		                    << "\"" << Dom::getDomainName ()  << "\""
		                    << " for "  << (*fdecl).get_func_name ()
		                    << "  ... \n";);

      // -- run intra-procedural analysis
      intra_analyzer_t analyzer (*m_cfg);
      typename intra_analyzer_t::assumption_map_t crab_assumptions;
      typedef typename intra_analyzer_t::assumption_map_t::value_type binding_t;
      // reconstruct a crab assumption map from our assumption DenseMap
      for (auto &kv: assumptions) {
	Dom absval = Dom::top();
	absval += kv.second;
	crab_assumptions.insert(binding_t(kv.first, absval));
      }

      Dom post_cond = Dom::top();
      if (params.check && params.run_backward) {
	// XXX: we compute preconditions that ensure that the program
	// fail. If those preconditions are false then we can conclude
	// the program is safe.
	post_cond = Dom::bottom();
      }

      analyzer.run(basic_block_label_t(entry), Dom::top(), post_cond,
		   !params.run_backward, crab_assumptions, live,
		   params.widening_delay, params.narrowing_iters, params.widening_jumpset);
      CRAB_VERBOSE_IF(1, get_crab_os() << "Finished intra-procedural analysis.\n");

      // -- store invariants
      if (params.store_invariants || params.print_invars) {
	CRAB_VERBOSE_IF(1, get_crab_os() << "Storing invariants.\n");
	for (basic_block_label_t bl: boost::make_iterator_range(m_cfg->label_begin(),
								m_cfg->label_end())) {
	  const BasicBlock *B = bl.get_basic_block();
	  if (!B) continue; // we only store those which correspond to llvm basic blocks

	  // --- invariants that hold at the entry of the blocks
	  auto pre = analyzer.get_pre (bl);
	  update(results.premap, *B,  mkGenericAbsDomWrapper(pre));
	  // --- invariants that hold at the exit of the blocks
	  auto post = analyzer.get_post (bl);
	  update(results.postmap, *B,  mkGenericAbsDomWrapper(post));
	  if (params.stats) {
	    unsigned num_block_invars = 0;
	    // TODO CRAB: for boxes we would like to use
	    // to_disjunctive_linear_constraint_system() but it needs to
	    // be exposed to all domains
	    num_block_invars += pre.to_linear_constraint_system().size();
	    num_invars += num_block_invars;
	    if (num_block_invars > 0) num_nontrivial_blocks++;
	  }
	}
	CRAB_VERBOSE_IF(1, get_crab_os() << "All invariants stored.\n");
      }

      // -- print all cfg annotations (if any)
      if (params.print_invars ||
	  (params.print_preconds && params.run_backward) ||
	  params.print_assumptions) {

	typedef pretty_printer_impl::block_annotation block_annotation_t;
	typedef pretty_printer_impl::invariant_annotation inv_annotation_t;
	typedef pretty_printer_impl::nec_precondition_annotation<intra_analyzer_t> pre_annotation_t;
	typedef pretty_printer_impl::assumption_annotation assume_annotation_t;
	std::vector<std::unique_ptr<block_annotation_t>> pool_annotations;

	llvm::outs() << "\n" << "function " << m_fun.getName() << "\n";
	if (params.print_invars) {
	  pool_annotations.emplace_back(
	       make_unique<inv_annotation_t>(m_vfac, results.premap, results.postmap,
					     params.keep_shadow_vars));
	}

	if (params.print_preconds && params.run_backward) {
	  pool_annotations.emplace_back(make_unique<pre_annotation_t>(analyzer));
	}

	// XXX: it must be alive when print_annotations is called.
	#if 0
	assumption_naive_analysis<cfg_ref_t> assumption_analyzer(*m_cfg);
	#else
	assumption_dataflow_analysis<cfg_ref_t> assumption_analyzer(*m_cfg);
	#endif

	if (params.print_assumptions) {
	  // -- run first the analysis
	  assumption_analyzer.exec();
	  pool_annotations.emplace_back(
	       make_unique<assume_annotation_t>(*m_cfg, &assumption_analyzer));
	}

	pretty_printer_impl::print_annotations(*m_cfg, pool_annotations);
      }

      if (params.check) {
	// --- checking assertions and collecting data
	CRAB_VERBOSE_IF(1, get_crab_os() << "Checking assertions ... \n");
	typename intra_checker_t::prop_checker_ptr
	  prop (new assert_prop_t (params.check_verbose));
	if (params.check == NULLITY)
	  prop.reset (new null_prop_t(params.check_verbose));
	intra_checker_t checker (analyzer, {prop});
	checker.run ();
	CRAB_VERBOSE_IF(1,
			llvm::outs() << "Function " << m_fun.getName() << "\n";
			checker.show (crab::outs()));
	results.checksdb += checker.get_all_checks();
	CRAB_VERBOSE_IF(1, get_crab_os() << "Finished assert checking.\n");
      }

      return;
    }

    template<typename AbsDom>
    void wrapperPathAnalyze(const std::vector<llvm_basic_block_wrapper>& path,
			    std::vector<crab::cfg::statement_wrapper>& core,
			    bool populate_maps,
			    invariant_map_t& post, invariant_map_t& pre, bool &res) {

      typedef path_analyzer<cfg_ref_t, AbsDom> path_analyzer_t;
      AbsDom init;
      path_analyzer_t path_analyzer(*m_cfg, init);
      res = path_analyzer.solve(path, /*compute_preconditions=*/ populate_maps);
      if (populate_maps) {
	for(auto n: path) {
	  if (const llvm::BasicBlock* bb = n.get_basic_block()) {
	    AbsDom abs_val = path_analyzer.get_fwd_constraints(n);
	    post.insert(std::make_pair(bb, mkGenericAbsDomWrapper(abs_val)));
	    if (abs_val.is_bottom()) {
	      // the rest of blocks must be also bottom so we don't
	      // bother storing them.
	      break;
	    }
	  }
	}
      }

      if (!res) {
	path_analyzer.get_unsat_core(core);

	if (populate_maps) {
	  for(auto n: path) {
	    if (const llvm::BasicBlock* bb = n.get_basic_block()) {
	      AbsDom abs_val = path_analyzer.get_bwd_constraints(n);
	      pre.insert(std::make_pair(bb, mkGenericAbsDomWrapper(abs_val)));
	      if (abs_val.is_bottom()) {
		// the rest of blocks must be also bottom so we don't
		// bother storing them.
		break;
	      }
	    }
	  }
	}
      }
    }

    struct intra_analysis {
      std::function<void(const AnalysisParams&,
			 const BasicBlock*,
			 const assumption_map_t&,
			 liveness_t*,
			 InvarianceAnalysisResults&)> analyze;
      std::string name;
    };

    struct path_analysis {
      std::function<void(const std::vector<llvm_basic_block_wrapper>&,
			 std::vector<crab::cfg::statement_wrapper>&,
			 bool, invariant_map_t&, invariant_map_t&,bool&)> analyze;
      std::string name;
    };

    // Domains used for intra-procedural analysis
    const std::map<CrabDomain, intra_analysis> intra_analyses {
      { INTERVALS               , { bind_this(this, &IntraCrabLlvm_Impl::analyzeCfg<interval_domain_t>), "classical intervals" } }
      #ifdef HAVE_ALL_DOMAINS
      , { INTERVALS_CONGRUENCES , { bind_this(this, &IntraCrabLlvm_Impl::analyzeCfg<ric_domain_t>), "reduced product of intervals and congruences" }}
      , { DIS_INTERVALS         , { bind_this(this, &IntraCrabLlvm_Impl::analyzeCfg<dis_interval_domain_t>), "disjunctive intervals" }}
      , { TERMS_INTERVALS       , { bind_this(this, &IntraCrabLlvm_Impl::analyzeCfg<term_int_domain_t>), "terms with intervals" }}
      #endif
      , { WRAPPED_INTERVALS     , { bind_this(this, &IntraCrabLlvm_Impl::analyzeCfg<wrapped_interval_domain_t>), "wrapped intervals" }}
      , { ZONES_SPLIT_DBM       , { bind_this(this, &IntraCrabLlvm_Impl::analyzeCfg<split_dbm_domain_t>), "zones" }}
      , { BOXES                 , { bind_this(this, &IntraCrabLlvm_Impl::analyzeCfg<boxes_domain_t>), "boxes" }}
      , { OPT_OCT_APRON         , { bind_this(this, &IntraCrabLlvm_Impl::analyzeCfg<opt_oct_apron_domain_t>), "elina octagons" }}
      , { PK_APRON              , { bind_this(this, &IntraCrabLlvm_Impl::analyzeCfg<pk_apron_domain_t>), "apron pk" }}
      , { TERMS_ZONES           , { bind_this(this, &IntraCrabLlvm_Impl::analyzeCfg<num_domain_t>), "terms with zones" }}
      , { TERMS_DIS_INTERVALS   , { bind_this(this, &IntraCrabLlvm_Impl::analyzeCfg<term_dis_int_domain_t>), "terms with disjunctive intervals" }}
    };

    // Domains used for path-based analysis
    const std::map<CrabDomain, path_analysis> path_analyses {
      { INTERVALS               , { bind_this(this, &IntraCrabLlvm_Impl::wrapperPathAnalyze<interval_domain_t>), "classical intervals" } }
      #ifdef HAVE_ALL_DOMAINS
      , { TERMS_INTERVALS       , { bind_this(this, &IntraCrabLlvm_Impl::wrapperPathAnalyze<term_int_domain_t>), "terms with intervals" }}
      #endif
      , { WRAPPED_INTERVALS     , { bind_this(this, &IntraCrabLlvm_Impl::wrapperPathAnalyze<wrapped_interval_domain_t>), "wrapped intervals" }}
      , { ZONES_SPLIT_DBM       , { bind_this(this, &IntraCrabLlvm_Impl::wrapperPathAnalyze<split_dbm_domain_t>), "zones" }}
      , { TERMS_ZONES           , { bind_this(this, &IntraCrabLlvm_Impl::wrapperPathAnalyze<num_domain_t>), "terms with zones" }}
      /*
	 To add new domains here make sure you add an explicit
	 instantiation in crab/path_analyzer.cc
      */
      //, { TERMS_DIS_INTERVALS , { bind_this(this, &IntraCrabLlvm_Impl::wrapperPathAnalyze<term_dis_int_domain_t>), "terms with disjunctive intervals" }}
    };

  public:

    IntraCrabLlvm_Impl(Function &fun,
		       crab::cfg::tracked_precision cfg_precision,
		       heap_abs_ptr mem, llvm_variable_factory &vfac,
		       CfgManager &cfg_man, const TargetLibraryInfo &tli)

      : m_cfg(nullptr), m_fun(fun), m_vfac(vfac) {
      if (isTrackable(m_fun)) {
	// -- build a crab cfg for func
	CfgBuilder builder(m_fun, m_vfac, *mem, cfg_precision, true, &tli);
	m_cfg = builder.get_cfg();
	m_edge_bb_map = builder.getEdgeToBBMap();
	CRAB_VERBOSE_IF(1, llvm::outs() << "Built Crab CFG for "
			                << fun.getName() << "\n");
	cfg_man.add(fun, m_cfg);

      } else {
	CRAB_VERBOSE_IF(1, llvm::outs() << "Cannot build CFG for "
			                << fun.getName() << "\n");
      }
    }

    void Analyze(AnalysisParams &params,
		 const llvm::BasicBlock *entry,
		 const assumption_map_t &assumptions,
		 InvarianceAnalysisResults &results) {

      if (!m_cfg) {
	CRAB_VERBOSE_IF(1, llvm::outs() << "Skipped analysis for "
			                << m_fun.getName() << "\n");
	return;
      }

      // -- run liveness
      liveness_t live(*m_cfg);
      if (params.run_liveness || isRelationalDomain(params.dom)) {
	CRAB_VERBOSE_IF(1,
			auto fdecl = m_cfg->get_func_decl ();
			assert (fdecl);
			get_crab_os() << "Running liveness analysis for "
			              << (*fdecl).get_func_name ()
		                      << "  ...\n";);
	live.exec ();
	CRAB_VERBOSE_IF(1, get_crab_os() << "Finished liveness analysis.\n");
	// some stats
	unsigned total_live, avg_live_per_blk, max_live_per_blk;
	live.get_stats (total_live, max_live_per_blk, avg_live_per_blk);
	CRAB_VERBOSE_IF(1,
		  crab::outs() << "-- Max number of out live vars per block="
                               << max_live_per_blk << "\n"
                               << "-- Avg number of out live vars per block="
                               << avg_live_per_blk << "\n";);
	crab::CrabStats::count_max ("Liveness.count.maxOutVars",
				    max_live_per_blk);

	if (isRelationalDomain(params.dom)) {
	  CRAB_VERBOSE_IF(1,
		    crab::outs() << "Max live per block: "
		                 << max_live_per_blk << "\n"
		                 << "Threshold: "
		                 << params.relational_threshold << "\n");
	  if (max_live_per_blk > params.relational_threshold) {
	    // default domain
	    params.dom = INTERVALS;
	  }
	}
      }

      if (intra_analyses.count(params.dom)) {
      	intra_analyses.at(params.dom).analyze(params, entry, assumptions,
					     (params.run_liveness)? &live : nullptr,
					     results);
      } else {
      	crab::outs() << "Warning: abstract domain not found or enabled.\n"
      		     << "Running " << intra_analyses.at(INTERVALS).name << " ...\n";
      	intra_analyses.at(INTERVALS).analyze(params, entry, assumptions,
					    (params.run_liveness)? &live : nullptr,
					    results);
      }
    }

    bool pathAnalyze(const AnalysisParams& params,
		     const std::vector<const llvm::BasicBlock*>& blocks,
		     std::vector<crab::cfg::statement_wrapper>& core,
		     bool populate_maps,
		     invariant_map_t& post, invariant_map_t& pre) const {
      assert(m_cfg);

      // build the full path (included internal basic blocks added
      // during the translation to Crab)
      std::vector<llvm_basic_block_wrapper> path;
      path.reserve(blocks.size());
      for(unsigned i=0; i < blocks.size(); ++i) {
	path.push_back(blocks[i]);
	if (i < blocks.size() - 1) {
	  auto it = m_edge_bb_map.find(std::make_pair(blocks[i], blocks[i+1]));
	  if (it != m_edge_bb_map.end()) {
	    path.push_back(it->second);
	  }
	}
      }

      bool res;
      if (path_analyses.count(params.dom)) {
      	path_analyses.at(params.dom).analyze(path, core, populate_maps, post, pre, res);
      } else {
      	crab::outs() << "Warning: abstract domain not found or enabled.\n"
      		     << "Running " << path_analyses.at(INTERVALS).name << " ...\n";
      	path_analyses.at(INTERVALS).analyze(path, core, populate_maps, post, pre, res);

      }
      return res;
    }

  }; // end class

  /**
   *   Begin IntraCrabLlvm methods
   **/
  IntraCrabLlvm::IntraCrabLlvm(Function &fun, const TargetLibraryInfo &tli,
			       CfgManager &cfg_man,
			       crab::cfg::tracked_precision cfg_precision,
			       heap_abs_ptr heap_abs)
    : m_impl(nullptr), m_fun(&fun) {
    if (!heap_abs)
      heap_abs = boost::make_shared<DummyHeapAbstraction>();

    m_impl = make_unique<IntraCrabLlvm_Impl>(fun, cfg_precision,
					     heap_abs, m_vfac, cfg_man, tli);
  }

  IntraCrabLlvm::~IntraCrabLlvm() {}

  void IntraCrabLlvm::clear() {
    m_pre_map.clear();
    m_post_map.clear();
    m_checks_db.clear();
  }

  void IntraCrabLlvm::analyze(AnalysisParams &params,
			      const assumption_map_t &assumptions) {
    InvarianceAnalysisResults results = { m_pre_map, m_post_map, m_checks_db};

    m_impl->Analyze(params, &(m_fun->getEntryBlock()), assumptions, results);
  }

  void IntraCrabLlvm::analyze(AnalysisParams &params,
			      const llvm::BasicBlock *entry,
			      const assumption_map_t &assumptions) {
    InvarianceAnalysisResults results = { m_pre_map, m_post_map, m_checks_db};
    m_impl->Analyze(params, entry, assumptions, results);
  }

  template<>
  bool IntraCrabLlvm::path_analyze(const AnalysisParams& params,
				   const std::vector<const llvm::BasicBlock*>& path,
				   std::vector<crab::cfg::statement_wrapper>& core) const {
    invariant_map_t pre_conditions, post_conditions;
    return m_impl->pathAnalyze(params, path, core, false, post_conditions, pre_conditions);
  }

  template<>
  bool IntraCrabLlvm::path_analyze(const AnalysisParams& params,
				   const std::vector<const llvm::BasicBlock*>& path,
				   std::vector<crab::cfg::statement_wrapper>& core,
				   invariant_map_t& post_conditions,
				   invariant_map_t& pre_conditions) const {
    return m_impl->pathAnalyze(params, path, core, true, post_conditions, pre_conditions);
  }

  wrapper_dom_ptr IntraCrabLlvm::get_pre(const llvm::BasicBlock *block,
					 bool keep_shadows) const {
    std::vector<varname_t> shadows;
    if (!keep_shadows)
      shadows = std::vector<varname_t>(m_vfac.get_shadow_vars().begin(),
				       m_vfac.get_shadow_vars().end());
    return lookup(m_pre_map, *block, shadows);
  }

  wrapper_dom_ptr IntraCrabLlvm::get_post(const llvm::BasicBlock *block,
					  bool keep_shadows) const {
    std::vector<varname_t> shadows;
    if (!keep_shadows)
      shadows = std::vector<varname_t>(m_vfac.get_shadow_vars().begin(),
				       m_vfac.get_shadow_vars().end());
    return lookup(m_post_map, *block, shadows);
  }

  const checks_db_t& IntraCrabLlvm::get_checks_db() const { return m_checks_db;}

  /**
   *   End IntraCrabLlvm methods
   **/

  /**
   *   Internal implementation of the inter-procedural analysis
   **/
  class InterCrabLlvm_Impl {
    std::unique_ptr<call_graph_t> m_cg;
    Module& m_M;
    llvm_variable_factory &m_vfac;
    liveness_map_t m_live_map;

    /** Run inter-procedural analysis on the whole call graph **/
    template<typename BUDom, typename TDDom>
    void analyzeCg(const AnalysisParams &params,
		   InvarianceAnalysisResults &results) {

      typedef inter_fwd_analyzer<call_graph_ref_t, BUDom, TDDom> inter_analyzer_t;
      typedef inter_checker<inter_analyzer_t> inter_checker_t;
      typedef assert_property_checker<inter_analyzer_t> assert_prop_t;
      typedef null_property_checker<inter_analyzer_t> null_prop_t;

      CRAB_VERBOSE_IF(1,
 		      get_crab_os() << "Running inter-procedural analysis with "
		                    << "forward domain:"
		                    << "\"" << TDDom::getDomainName () << "\""
		                    << " and bottom-up domain:"
		                    << "\"" << BUDom::getDomainName () << "\""
		                    << "  ...\n";);

      inter_analyzer_t analyzer(*m_cg, (params.run_liveness ? &m_live_map : nullptr),
				params.widening_delay,
				params.narrowing_iters,
				params.widening_jumpset);
      analyzer.Run (TDDom::top ());

      CRAB_VERBOSE_IF(1, get_crab_os() << "Finished inter-procedural analysis.\n");

      // -- store invariants
      if (params.store_invariants || params.print_invars) {
	CRAB_VERBOSE_IF(1, get_crab_os() << "Storing invariants.\n");
      }

      for (auto &n: boost::make_iterator_range(vertices(*m_cg))) {
	cfg_ref_t cfg = n.get_cfg ();
	if (const Function *F = m_M.getFunction(n.name())) {

	  if (params.store_invariants || params.print_invars) {
	    for (auto &B : *F) {
	      // --- invariants that hold at the entry of the blocks
	      auto pre = analyzer.get_pre (cfg, &B);
	      update(results.premap, B, mkGenericAbsDomWrapper(pre));
	      // --- invariants that hold at the exit of the blocks
	      auto post = analyzer.get_post (cfg, &B);
	      update(results.postmap, B, mkGenericAbsDomWrapper(post));

	      if (params.stats) {
		unsigned num_block_invars = 0;
		// TODO CRAB: for boxes we would like to use
		// to_disjunctive_linear_constraint_system() but it needs to
		// be exposed to all domains
		num_block_invars += pre.to_linear_constraint_system().size();
		num_invars += num_block_invars;
	      if (num_block_invars > 0) num_nontrivial_blocks++;
	      }
	    }

	    // --- print invariants and summaries
	    if (params.print_invars && isTrackable(*F)) {
	      llvm::outs() << "\n" << "function " << F->getName () << "\n";
	      std::vector<std::unique_ptr<pretty_printer_impl::block_annotation>> annotations;
	      annotations.emplace_back(make_unique<pretty_printer_impl::invariant_annotation>
				       (m_vfac, results.premap, results.postmap,
					params.keep_shadow_vars));
	      pretty_printer_impl::print_annotations(cfg, annotations);
	    }
	  }

	  // Summaries are not currently stored but it would be easy to do so.
	  if (params.print_summaries && analyzer.has_summary (cfg)) {
	    auto summ = analyzer.get_summary (cfg);
	    crab::outs() << "SUMMARY " << *summ << "\n";
	  }
	}
      }

      if (params.store_invariants || params.print_invars) {
	CRAB_VERBOSE_IF(1, get_crab_os() << "All invariants stored.\n");
      }

      // --- checking assertions and collecting data
      if (params.check) {
	CRAB_VERBOSE_IF(1, get_crab_os() << "Checking assertions ... \n");
	typename inter_checker_t::prop_checker_ptr
	  prop(new assert_prop_t(params.check_verbose));
	if (params.check == NULLITY)
	  prop.reset (new null_prop_t(params.check_verbose));
	inter_checker_t checker (analyzer, {prop});
	checker.run ();
	//CRAB_VERBOSE_IF(1, checker.show (crab::outs()));
	results.checksdb += checker.get_all_checks();
	CRAB_VERBOSE_IF(1, get_crab_os() << "Finished assert checking.\n");
      }
      return;
    }
    // Domains used for inter-procedural analysis
    struct inter_analysis {
      std::function<void(const AnalysisParams&, InvarianceAnalysisResults&)> analyze;
      std::string name;
    };

    // Domains used for intra-procedural analysis
    const std::map<std::pair<CrabDomain,CrabDomain>, inter_analysis> inter_analyses {
        {{ZONES_SPLIT_DBM, INTERVALS},
	 { bind_this(this, &InterCrabLlvm_Impl::analyzeCg<split_dbm_domain_t, interval_domain_t>), "bottom-up:zones, top-down:intervals" }}
      , {{ZONES_SPLIT_DBM, WRAPPED_INTERVALS},
	  { bind_this(this, &InterCrabLlvm_Impl::analyzeCg<split_dbm_domain_t, wrapped_interval_domain_t>), "bottom-up:zones, top-down:wrapped intervals" }}
      , {{ZONES_SPLIT_DBM, ZONES_SPLIT_DBM},
	  { bind_this(this, &InterCrabLlvm_Impl::analyzeCg<split_dbm_domain_t, split_dbm_domain_t>), "bottom-up:zones, top-down:zones" }}
      , {{ZONES_SPLIT_DBM, BOXES},
	  { bind_this(this, &InterCrabLlvm_Impl::analyzeCg<split_dbm_domain_t, boxes_domain_t>), "bottom-up:zones, top-down:boxes" }}
      , {{ZONES_SPLIT_DBM, OPT_OCT_APRON},
	  { bind_this(this, &InterCrabLlvm_Impl::analyzeCg<split_dbm_domain_t, opt_oct_apron_domain_t>), "bottom-up:zones, top-down:elina oct" }}
      , {{ZONES_SPLIT_DBM, PK_APRON},
	  { bind_this(this, &InterCrabLlvm_Impl::analyzeCg<split_dbm_domain_t, pk_apron_domain_t>), "bottom-up:zones, top-down:apron pk" }}
      , {{ZONES_SPLIT_DBM, TERMS_ZONES},
	  { bind_this(this, &InterCrabLlvm_Impl::analyzeCg<split_dbm_domain_t, num_domain_t>), "bottom-up:zones, top-down:terms+zones" }}
      , {{ZONES_SPLIT_DBM, TERMS_DIS_INTERVALS},
	  { bind_this(this, &InterCrabLlvm_Impl::analyzeCg<split_dbm_domain_t, term_dis_int_domain_t>), "bottom-up:zones, top-down:terms+dis_intervals" }}
      //////////////////////////////////////////////////////
      , {{OPT_OCT_APRON, INTERVALS},
	 { bind_this(this, &InterCrabLlvm_Impl::analyzeCg<opt_oct_apron_domain_t, interval_domain_t>), "bottom-up:elina oct, top-down:intervals" }}
      , {{OPT_OCT_APRON, WRAPPED_INTERVALS},
	  { bind_this(this, &InterCrabLlvm_Impl::analyzeCg<opt_oct_apron_domain_t, wrapped_interval_domain_t>), "bottom-up:elina oct, top-down:wrapped intervals" }}
      , {{OPT_OCT_APRON, ZONES_SPLIT_DBM},
	  { bind_this(this, &InterCrabLlvm_Impl::analyzeCg<opt_oct_apron_domain_t, split_dbm_domain_t>), "bottom-up:elina oct, top-down:zones" }}
      , {{OPT_OCT_APRON, BOXES},
	  { bind_this(this, &InterCrabLlvm_Impl::analyzeCg<opt_oct_apron_domain_t, boxes_domain_t>), "bottom-up:elina oct, top-down:boxes" }}
      , {{OPT_OCT_APRON, OPT_OCT_APRON},
	  { bind_this(this, &InterCrabLlvm_Impl::analyzeCg<opt_oct_apron_domain_t, opt_oct_apron_domain_t>), "bottom-up:elina oct, top-down:elina oct" }}
      , {{OPT_OCT_APRON, PK_APRON},
	  { bind_this(this, &InterCrabLlvm_Impl::analyzeCg<opt_oct_apron_domain_t, pk_apron_domain_t>), "bottom-up:elina oct, top-down:apron pk" }}
      , {{OPT_OCT_APRON, TERMS_ZONES},
	  { bind_this(this, &InterCrabLlvm_Impl::analyzeCg<opt_oct_apron_domain_t, num_domain_t>), "bottom-up:elina oct, top-down:terms+zones" }}
      , {{OPT_OCT_APRON, TERMS_DIS_INTERVALS},
	  { bind_this(this, &InterCrabLlvm_Impl::analyzeCg<opt_oct_apron_domain_t, term_dis_int_domain_t>), "bottom-up:elina oct, top-down:terms+dis_intervals" }}
    };

  public:

    InterCrabLlvm_Impl(Module& M,
		       crab::cfg::tracked_precision cfg_precision,
		       heap_abs_ptr mem, llvm_variable_factory &vfac,
		       CfgManager &cfg_man, const TargetLibraryInfo &tli)
      : m_cg(nullptr), m_M(M), m_vfac(vfac) {

      std::vector<cfg_ref_t> cfg_ref_vector;
      for (auto &F : m_M) {
        if (!isTrackable(F)) continue;
        // -- build cfg's
        CfgBuilder B(F, m_vfac, *mem, cfg_precision,
		     /*include function decls and callsites*/
		     true,  &tli);
        cfg_t *cfg = B.get_cfg();
	cfg_man.add(F, cfg);
        cfg_ref_vector.push_back (*cfg);
      }
      // build call graph
      m_cg = make_unique<call_graph_t>(cfg_ref_vector.begin(), cfg_ref_vector.end());
    }

    void Analyze(AnalysisParams &params,
		 const assumption_map_t &/*assumptions*/ /*unused*/,
		 InvarianceAnalysisResults &results) {

      // If the number of live variables per block is too high we
      // switch to a cheap domain regardless what the user wants.
      CrabDomain absdom =  params.dom;

      /* Compute liveness information and choose statically the
	 abstract domain */
      if (params.run_liveness || isRelationalDomain(absdom)) {
	unsigned max_live_per_blk = 0;
	for (auto cg_node: boost::make_iterator_range(vertices(*m_cg))) {
	  auto cfg_ref = cg_node.get_cfg();
          CRAB_VERBOSE_IF(1,
			  auto fdecl = cfg_ref.get_func_decl ();
			  assert (fdecl);
			  get_crab_os() << "Running liveness analysis for "
			                << (*fdecl).get_func_name () << "  ...\n";);
	  liveness_t* live = new liveness_t (cfg_ref);
          live->exec ();
          CRAB_VERBOSE_IF(1, get_crab_os() << "Finished liveness analysis.\n";);
          // some stats
          unsigned total_live, max_live_per_blk_, avg_live_per_blk;
          live->get_stats (total_live, max_live_per_blk_, avg_live_per_blk);
          max_live_per_blk = std::max (max_live_per_blk, max_live_per_blk_);
          CRAB_VERBOSE_IF(1,
		    crab::outs() << "-- Max number of out live vars per block="
                                 << max_live_per_blk_ << "\n";
		    crab::outs() << "-- Avg number of out live vars per block="
                                 << avg_live_per_blk << "\n";);
          crab::CrabStats::count_max ("Liveness.count.maxOutVars",
				      max_live_per_blk);

	  if (isRelationalDomain(absdom)) {
	    // FIXME: the selection of the final domain is fixed for the
	    //        whole program. That is, if there is one function that
	    //        exceeds the threshold then the cheaper domain will be
	    //        used for all functions. We should be able to change
	    //        from one function to another.
	    CRAB_VERBOSE_IF(1,
		      crab::outs() << "Max live per block: "
		                   << max_live_per_blk << "\n"
		                   << "Threshold: "
		                   << params.relational_threshold << "\n");
	    if (max_live_per_blk > params.relational_threshold) {
	      // default domain
	      absdom = INTERVALS;
	    }
	  }

	  if (params.run_liveness) {
	    m_live_map.insert(std::make_pair(cfg_ref, live));
	  } else {
	    delete live;
	  }
	} // end for
      }

      // -- run the interprocedural analysis
      if (inter_analyses.count({params.sum_dom, params.dom})) {
      	inter_analyses.at({params.sum_dom, params.dom}).analyze(params, results);
      } else {
      	crab::outs() << "Warning: abstract domains not found or enabled.\n"
      		     << "Running " << inter_analyses.at({ZONES_SPLIT_DBM, INTERVALS}).name << "\n";
      	inter_analyses.at({ZONES_SPLIT_DBM, INTERVALS}).analyze(params, results);
      }

      // free liveness map
      if (params.run_liveness) {
        for (auto &p : m_live_map) {
          delete p.second;
	}
      }
    }
  };

  /**
   *   Begin InterCrabLlvm methods
   **/
  InterCrabLlvm::InterCrabLlvm(Module &module, const TargetLibraryInfo &tli,
			       CfgManager &cfg_man,
			       crab::cfg::tracked_precision cfg_precision,
			       heap_abs_ptr heap_abs)
    : m_impl(nullptr) {
    if (!heap_abs)
      heap_abs = boost::make_shared<DummyHeapAbstraction>();

    m_impl = make_unique<InterCrabLlvm_Impl>(module, cfg_precision,
					     heap_abs, m_vfac, cfg_man, tli);
  }

  InterCrabLlvm::~InterCrabLlvm() {}

  void InterCrabLlvm::clear() {
    m_pre_map.clear();
    m_post_map.clear();
    m_checks_db.clear();
  }

  void InterCrabLlvm::analyze(AnalysisParams &params,
			      const assumption_map_t &assumptions) {
    InvarianceAnalysisResults results = { m_pre_map, m_post_map, m_checks_db};
    m_impl->Analyze(params, assumptions, results);
  }

  wrapper_dom_ptr InterCrabLlvm::get_pre(const llvm::BasicBlock *block,
					 bool keep_shadows) const {
    std::vector<varname_t> shadows;
    if (!keep_shadows)
      shadows = std::vector<varname_t>(m_vfac.get_shadow_vars().begin(),
				       m_vfac.get_shadow_vars().end());
    return lookup(m_pre_map, *block, shadows);
  }

  wrapper_dom_ptr InterCrabLlvm::get_post(const llvm::BasicBlock *block,
					  bool keep_shadows) const {
    std::vector<varname_t> shadows;
    if (!keep_shadows)
      shadows = std::vector<varname_t>(m_vfac.get_shadow_vars().begin(),
				       m_vfac.get_shadow_vars().end());
    return lookup(m_post_map, *block, shadows);
  }

  const checks_db_t& InterCrabLlvm::get_checks_db() const { return m_checks_db;}

  /**
   * End InterCrabLlvm methods
   **/

  /**
   * Begin CrabLlvmPass methods
   **/
  CrabLlvmPass::CrabLlvmPass ()
    : llvm::ModulePass (ID),
      m_mem(boost::make_shared<DummyHeapAbstraction>()),
      m_tli(nullptr) { }

  void CrabLlvmPass::releaseMemory () {
    m_pre_map.clear();
    m_post_map.clear();
    m_checks_db.clear();
  }

  bool CrabLlvmPass::runOnFunction (Function &F) {
    if (!CrabInter && isTrackable(F)) {
      IntraCrabLlvm_Impl crab(F, CrabTrackLev, m_mem, m_vfac, m_cfg_man, *m_tli);
      InvarianceAnalysisResults results = { m_pre_map, m_post_map, m_checks_db};
      crab.Analyze(m_params, &F.getEntryBlock(), assumption_map_t(), results);
    }
    return false;
  }

  bool CrabLlvmPass::runOnModule (Module &M) {
    #ifdef HAVE_DSA
    m_mem.reset
      (new LlvmDsaHeapAbstraction(M,&getAnalysis<SteensgaardDataStructures>(),
				  CrabDsaDisambiguateUnknown,
				  CrabDsaDisambiguatePtrCast,
				  CrabDsaDisambiguateExternal));
    #endif

    m_tli = &getAnalysis<TargetLibraryInfoWrapperPass>().getTLI();

    m_params.dom = CrabLlvmDomain;
    m_params.sum_dom = CrabSummDomain;
    m_params.run_backward = CrabBackward;
    m_params.run_liveness = CrabLive;
    m_params.relational_threshold = CrabRelationalThreshold;
    m_params.widening_delay = CrabWideningDelay;
    m_params.narrowing_iters = CrabNarrowingIters;
    m_params.widening_jumpset = CrabWideningJumpSet;
    m_params.stats = CrabStats;
    m_params.print_invars = CrabPrintAns;
    m_params.print_preconds = CrabPrintPreCond;
    m_params.print_assumptions = CrabPrintAssumptions;
    m_params.print_summaries = CrabPrintSumm;
    m_params.store_invariants = CrabStoreInvariants;
    m_params.keep_shadow_vars = CrabKeepShadows;
    m_params.check = CrabCheck;
    m_params.check_verbose = CrabCheckVerbose;

    CRAB_VERBOSE_IF(1,
	     get_crab_os() << "Started crab-llvm\n";
             unsigned num_analyzed_funcs = 0;
             for (auto &F : M) {
	       if (!isTrackable(F)) continue;
               num_analyzed_funcs++;
             }
             get_crab_os() << "Total number of analyzed functions:"
                           << num_analyzed_funcs << "\n";);

    if (CrabInter){
      InterCrabLlvm_Impl inter_crab(M, CrabTrackLev, m_mem, m_vfac, m_cfg_man, *m_tli);
      InvarianceAnalysisResults results = { m_pre_map, m_post_map, m_checks_db};
      inter_crab.Analyze(m_params, assumption_map_t(), results);
    } else {
      for (auto &f : M) {
        runOnFunction (f);
      }
    }

    if (CrabStats) {
      crab::CrabStats::PrintBrunch (crab::outs());
    }

    if (CrabCheck) {
      llvm::outs() << "\n************** ANALYSIS RESULTS ****************\n";
      print_checks(llvm::outs());
      llvm::outs() << "************** ANALYSIS RESULTS END*************\n";

      if (CrabStats) {
        llvm::outs() << "\n************** BRUNCH STATS ********************\n";
        if (get_total_checks() == 0) {
	  llvm::outs() << "BRUNCH_STAT Result NOCHECKS\n";
        } else if (get_total_error_checks() > 0) {
 	  llvm::outs() << "BRUNCH_STAT Result FALSE\n";
        } else if (get_total_warning_checks() == 0) {
	  llvm::outs() << "BRUNCH_STAT Result TRUE\n";
        } else {
  	  llvm::outs() << "BRUNCH_STAT Result INCONCLUSIVE\n";
        }
        llvm::outs() << "BRUNCH_STAT NumOfBlocksWithInvariants "
 	 	     << num_nontrivial_blocks << "\n";
        llvm::outs() << "BRUNCH_STAT SizeOfInvariants "
		     << num_invars << "\n";
        llvm::outs() << "************** BRUNCH STATS END *****************\n\n";
      }
    }
   return false;
  }

  void CrabLlvmPass::getAnalysisUsage (AnalysisUsage &AU) const {
    AU.setPreservesAll ();
    #ifdef HAVE_DSA
    AU.addRequiredTransitive<SteensgaardDataStructures> ();
    #endif
    AU.addRequired<TargetLibraryInfoWrapperPass>();
    AU.addRequired<UnifyFunctionExitNodes>();
    AU.addRequired<crab_llvm::NameValues>();
  }

  /**
   * For crab-llvm clients
   **/

  bool CrabLlvmPass::has_cfg(llvm::Function &F) {
    return m_cfg_man.has_cfg(F);
  }

  cfg_ref_t CrabLlvmPass::get_cfg(llvm::Function &F) {
    assert(m_cfg_man.has_cfg(F));
    return m_cfg_man[F];
  }

  // return invariants that hold at the entry of block
  wrapper_dom_ptr
  CrabLlvmPass::get_pre(const llvm::BasicBlock *block, bool keep_shadows) const {
    std::vector<varname_t> shadows;
    if (!keep_shadows)
      shadows = std::vector<varname_t>(m_vfac.get_shadow_vars().begin(),
				       m_vfac.get_shadow_vars().end());
    return lookup(m_pre_map, *block, shadows);
  }

  // return invariants that hold at the exit of block
  wrapper_dom_ptr
  CrabLlvmPass::get_post(const llvm::BasicBlock *block, bool keep_shadows) const {
    std::vector<varname_t> shadows;
    if (!keep_shadows)
      shadows = std::vector<varname_t>(m_vfac.get_shadow_vars().begin(),
				       m_vfac.get_shadow_vars().end());
    return lookup(m_post_map, *block, shadows);
  }

  /**
   * For assertion checking
   **/

  unsigned CrabLlvmPass::get_total_checks() const {
    return get_total_safe_checks() +
           get_total_error_checks() +
           get_total_warning_checks();
  }

  unsigned CrabLlvmPass::get_total_safe_checks () const {
    return m_checks_db.get_total_safe();
  }

  unsigned CrabLlvmPass::get_total_error_checks () const {
    return m_checks_db.get_total_error();
  }

  unsigned CrabLlvmPass::get_total_warning_checks () const {
    return m_checks_db.get_total_warning();
  }

  void CrabLlvmPass::print_checks (raw_ostream &o) const {
    unsigned safe = get_total_safe_checks();
    unsigned unsafe = get_total_error_checks ();
    unsigned warning = get_total_warning_checks ();
    std::vector<unsigned> cnts = { safe, unsafe, warning};
    unsigned MaxValLen = 0;
    for (auto c: cnts)
      MaxValLen = std::max(MaxValLen,
			   (unsigned)std::to_string(c).size());
    o << std::string((int) MaxValLen - std::to_string(safe).size(), ' ')
      << safe << std::string (2, ' ') << "Number of total safe checks\n"
      << std::string((int) MaxValLen - std::to_string(unsafe).size(), ' ')
      << unsafe << std::string (2, ' ') << "Number of total error checks\n"
      << std::string((int) MaxValLen - std::to_string(warning).size(), ' ')
      << warning << std::string(2, ' ') << "Number of total warning checks\n";
  }

  char crab_llvm::CrabLlvmPass::ID = 0;

} // end namespace

static RegisterPass<crab_llvm::CrabLlvmPass>
X ("crab-llvm", "Infer invariants using Crab", false, false);


/**********************************************************************
Copyright (c) 2017, team frAIburg
Licensed under BSD-3.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the <organization> nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

**********************************************************************/
//
//  map_element.hpp
//  map elemnts used in the GlobalMap
//      - id,types, timestamps
//      - a polygons (boost geometry) in a local and global frame
//        to describe the potions
//      - updates distances, angle measurments based on the car position
//      - simple similar check to new elements
//      -
//  Created by Markus on 05.07.17.

#ifndef AADCUSER_FRAIBURG_MAP_ELEMENT_LIB_H_
#define AADCUSER_FRAIBURG_MAP_ELEMENT_LIB_H_

// set the maximum of points a map element can have when its fused
#define MAP_ELEMENT_RESOURCES_MAX_NUMER_OF_POINTS 500U
#define MAP_ELEMENT_FUSE_CNT_INFO_MSG 1000

#include <math.h>
#include <stdio.h>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>

#include "adtf_log_macros.h"
#include "map_types.h"

#include <boost/version.hpp>
#if BOOST_VERSION >= 106000
#include <boost/qvm/mat_operations.hpp>
#endif
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/geometry.hpp>
#include <boost/geometry/algorithms/centroid.hpp>
#include <boost/geometry/strategies/transform.hpp>
#include <boost/geometry/strategies/transform/matrix_transformers.hpp>
#include <boost/thread/mutex.hpp>

namespace frAIburg {
namespace map {

class __attribute__((visibility("default"))) MapElement {
 public:
    /*! created am map element defined by a polygon and type
     *   \param type enum type of the element
     *   \param local_poly define the outline of an map element, in [m]
     *           local car frame: to the front x pos, to the left y pos,
     *           points in clockwise order
     *   \param timestamp creation time
     *   \note -transform to a gloabal frame by calling local_to_global.
     *         the map calls this method if not called before
     *         -if timestamp creation time is 0, this element will be ignored
     *          by the map in remove_elements_over_distance
     *          and update_reposition_car()
     *    \note the id will bet set by the map if the element is added
     */
    MapElement(MapElementType type, const std::vector<tMapPoint> &local_poly,
               tTimeMapStamp timestamp = 0);

    /*! see above MapElement(...)
     *   \param local_orientation_angle set and enable a angle angle, in [rad],
     *            angle is define zero at front and positive vals to the left
     */
    MapElement(MapElementType type, const std::vector<tMapPoint> &local_poly,
               tTimeMapStamp timestamp, tMapData local_orientation_angle);

    virtual ~MapElement(){};  // polymorphic class to enable dynamic_cast child
                              // types

 public:
    /* user_tag_ which can be set by the user to add addtion information      */
    std::string user_tag_;

    /* user_tag_ which will be shown instelead of the type name in the map ui */
    std::string user_tag_ui_;  // use only in debug mode
    /* user color for the ui, poly fill color and outer line
     * valid names: https://www.w3.org/TR/SVG/types.html#ColorKeywords */
    std::string user_color_ui_;  // use only in debug mode

    /*! check if the elemnt is one from the given types
    *   \param types types to check
    *   \return true if the elemnt is one of the types in the vector
    */
    bool IsType(const std::vector<MapElementType> &types) const;

    /*! calculate distance to MapElement
    *   \param MapElement el to comapre with
    *   \param to_center true distance form the center, else closest distance
    *   \note update_mutex_ is used
    *   \note if the elements are Disjoint and to_center = false, the distatnce
    *         is zero
    *   \return distance
    */
    tMapData GlobalDistance(const MapElement &el, bool to_center = true);

    /*! calculate distance to Point
    *   \param MapElement el to comapre with
    *   \param to_center true distance form the center, else closest distance
    *   \note update_mutex_ is used
    *   \return distance
    */
    tMapData GlobalDistance(const tMapPoint &p, bool to_center = true);

    /* check if elements are disjoint in the global frame
     * \note check with if(!el->GlobalPolyDisjoint(el2)) for collisions
     */
    bool GlobalPolyDisjoint(const MapElement &p);
    /* check if a map point is disjoint in the global polyon frame
     */
    bool GlobalPolyDisjoint(const tMapPoint &p);

    /* check if other element is (complitly) within the global polygon*/
    bool GlobalPolyWithin(const MapElement &p);
    /* check if other element is touches the outer line of the global polygon*/
    bool GlobalPolyTouches(const MapElement &p);
    bool GlobalPolyOverlaps(const MapElement &p);

    /* \return true if local_orientation_angle was used in the constructor*/
    bool IsOrientationUsed() const;
    /* return the angle of the element in the global fram in [rad]*/
    tMapData GetGlobalOrientation() const;

    tMapData GetArea() const;
    /* \return an copy of the polygon in the global frame */
    tMapPolygon GetGlobalPoly() const;

    /* \return polygon center in the global frame if transfromed from local to
     *    global before
     *  EXAMPLE: tMapPoint center = el->GetGlobalPolyCenter();
     *           tMapData x = center.get<0>();
    //  *           tMapData y = center.get<1>();
     */
    tMapPoint GetGlobalPolyCenter() const;

    /*! orientation angle based on the poly center point from -pi to pi
     * returns zero if the element is in the front, negative value to the right
     */
    tMapData GetOrientationAngleToCar() const;

    /* ret the distance to the center of the poly form the last update call
       with car pos
      \note distance and angle updated by the map with the call of Update
    */
    tMapData GetDistanceToCar() const;
    tMapData GetPreviousDistanceCar() const;

    /*! return prejedted distance based on the OrientationAngleToCar of the last
     *  Update() call
     */
    tMapData ProjectedDistanceToCar() const;

    tTimeMapStamp GetUpdateTime() const;
    tTimeMapStamp GetCreateTime() const;

    MapElementType GetType() const;

    /*! return how often the element was fused */
    unsigned int GetFuseCount() const;

    /*! element string for the element with different information*/
    virtual std::string ToString() const;

    /*! set a live time for the map element, after this time the map deletes
    *    the element
    *   \param life_time_microseconds set a live time of the element
    *   \param start_time_microseconds optinal start time of the life time,
    *          use if AFTER the element is GlobalMap::AddFuseElement to
    * overwrite
    *          the life time if fused
    *   \note call BEFORE the element is GlobalMap::AddFuseElement map if
    *         life time reset wanted with fuse start_time_microseconds
    *   \note call AFTER the element is GlobalMap::AddFuseElement map if
    *         life time reset form creation time of the first element added to
    *          the map
    *   \note creation timestamp must no be zero if used or negative
    *   \note map delets then the element with RemoveAllElementsOverLifeTime
    *   \note if elements if fused the life time setting of the new elment will
    *         be taken
    *   \note once enableld the life time can not disabled
    */
    void EnableTimeOfLife(tTimeMapStamp life_time_microseconds,
                          tTimeMapStamp *start_time_microseconds = NULL);
    bool IsOverTimeOfLive(tTimeMapStamp time_microseconds);

    /*! get the ID of the alement
    *   \note call after element was added to the map
    *   \note the id will bet set by the map
    */
    tMapID GetID() const;

    /*! set if the elment is updated in a map repostion jump,
       call after the element is added to the map, in case the of a fusion,
       the default is ture
    */
    void SetUpdateWithRepostionJumps(bool set);

    // FUNCTION CALLED BY THE MAP:

    /*! set the ID of the alement
    *   \note the id will bet set by the map if the element is added
    */
    void SetID(tMapID id);

    /*! transfrom the local polygon and angle if set a glabale frame
    *   \param tMapCarPosition position of the car to transformation
    *   \note only use this there is a long time needed for data porcessing:
    *         save the car postion with the sensor information and
    *         perform the tranfromation after the computation
    *   \note this method is called by the map, unless it was called before
    *   \note element update time stamp is set to car pos time_pos_update
    *   \note update_mutex_ is used
    */
    void LocalToGlobal(const tMapCarPosition &global_car_pos);

    /*! transfrom the globale polygon, trans and roation based on car pos
     *  \note distance and angle to car is not updated
     *  \note element update time stamp is set to car pos time_pos_update
     *  \note update_mutex_ is used
     */
    void TransfromGlobal(const tMapCarPosition &global_car_pos);

    /*! rotate the globale polygon, angle in rad
     *  \note distance and angle to car is not updated
     *  \note update_mutex_ is used
     */
    void RotateAroundCenterGlobal(const tMapData angle_clockwise,
                                  tTimeMapStamp t = 0);

    /*! rotate the globale polygon, angle in rad
     *  \note distance and angle to car is not updated
     *  \note update_mutex_ is used
     */
    void RotateAroundCenterThenTranslateGlobal(
        const tMapData angle_rad_clockwise, const tMapData trans_x,
        const tMapData trans_y, tTimeMapStamp t = 0);

    /*! update global position distance and angle to the car
     *   \param tMapCarPosition position update of the car
     *   \note timestamp is set to the global_car_pos time_pos_update
     *   \note this method is called by the map
     *   \note update_mutex_ is used
     */
    void Update(const tMapCarPosition &global_car_pos);

    /*! combines the GLOBAL polygon with a diffrent element,
     *  user_tag_ and user_tag_ui_ will be used from the element to fuse with
     *   \param type enum type of the element
     *   \param element_fuse to fuse with
     *   \param local_poly define the, ob
     *           local car frame: to the front x pos, to the left y pos,
     *           points in clockwise order
     *   \param timestamp creation time
     *   \param fuse_type MAP_FUSE_APPEND, polygon points will be appended.
     *                    MAP_FUSE_REPLACE, replace the polygon
     *                    MAP_FUSE_FIXED_POINT_MEAN, number of points stays
     *                     same by computing the mean,
     *                     point number and order must be the same
     *   \return MapFuseReturn to inform the map if which element to keep in
     *           the map
     *   \note  this method is called by the map
     *   \note user_tag_, user_tag_ui_, user_color_ui_ will be change to
     *        the one of element_fuse if not empty
     *   \note update_mutex_ is used
     */
    virtual MapFuseReturn UpdateFuse(const tMapCarPosition &global_car_pos,
                                     MapElement &element_fuse,
                                     tTimeMapStamp timestamp = 0,
                                     MapFuseType fuse_type = MAP_FUSE_APPEND);

    /*! check if element are similar based of area, type and center distance
    *   \note  the map calls IsSimilar to check if elements should be fused
    *          child classes can change this func to fuse with differnt types
    *   \param MapElement element to compare with
    *   \param tolerance_distance max distance between the two elements,
    *            -1 to ignore
    *   \param tolerance_area,-1 to ignore
    *            note area might change if element s were fuesed before
    *   \param similar_fuse true to check if the elements should
    *          be fused: the type must be same,
    *          if fuse the type of the elment is ignored
    *   \return true if the element are similar based on the args
    */
    virtual bool IsSimilar(const MapElement &el,
                           tMapData tolerance_distance = 0.1,
                           tMapData tolerance_area = -1,
                           bool similar_fuse = true);//TODO as const

    /*! simialr factor based on distance, type and area
    *   \param MapElement element to compare with
    *   \return simialr factor based on distance, type and area
    *   \note update_mutex_ is used
    */
    float SimilarFactor(const MapElement &el);

    /*! return ture if the element should be updated in a repostion jump*/
    bool IsUpdateWithRepostionJumpEnabled() const;

 private:
    /* mutx car pos update and vector updated */
    boost::mutex update_mutex_;
    /* type of element */
    const MapElementType type_;
    /* id of the element wich can be set by the map*/
    tMapID id_;
    /* keep track if the angle for the map element is set in a
     construtctor*/
    const bool element_angle_used_;

    /*counter with the indicates how often the element was fused*/
    unsigned int fused_cnt_;
    tMapData fuse_center_mean_decay_weight_factor_;
    bool update_with_repostion_jump_;
    tMapData fuse_sin_global_mean_;
    tMapData fuse_cos_global_mean_;

    /* local positions and oriantation of the element*/
    tMapPolygon local_element_polygon_;
    tMapData local_element_orientation_angle_;

    /* global position and oriantation of the element*/
    tMapPolygon global_element_polygon_;
    tMapPoint global_element_polygon_center_;
    tMapData global_element_orientation_angle_;  // in rad
    // zero if the element is in the front, to the right negative, updated
    tMapData orientation_angle_to_car_;  // in rad

    /* distance to the last update car position of the center of the poly */
    tMapData car_distance_;
    tMapData car_distance_previous_;
    // tMapData hight_;//TODO(markus) needed?

    /* save the last calculated area*/
    tMapData polygon_area_;

    /// TIMESTAMPS
    /* creation timestamp set in the construtctor */
    const tTimeMapStamp timestamp_create_microseconds_;
    /* creation timestamp for the last update for the element */
    tTimeMapStamp timestamp_update_microseconds_;
    tTimeMapStamp timestamp_of_life_microseconds_;
    tTimeMapStamp timestamp_start_life_microseconds_;

    bool time_of_life_is_used_;
    bool time_of_life_reset_with_fuse_;

    /*! init func, for multiple contributors, c11 is not used */
    void Init(const std::vector<tMapPoint> &local_poly);

    /*! calculate the areo of the polygon*/
    void UpdateArea();

    /*! set the decrete orientation to the last update position
     */
    void UpdateOrientationToCar(const tMapCarPosition &global_car_pos);

    /*calculate the inear distance to the car pos and saved center of the global
     * polygon */
    void UpdateDistanceToCar(const tMapCarPosition &global_car_pos);
    /*calculate the center of the gobal polygon*/
    void UpdateGlobalPolyCenter();

    /*! change the global angle of the map el in clockwise in rad*/
    void DoAngleRotation(const tMapData angle_diff_rad_clockwise);

    /*! transforamtion of the local to the gloabal car coord based on the
     *  current car pos */
    void Transformation(const tMapPolygon &poly_from, tMapPolygon &poly_to,
                        const tMapCarPosition &global_car_pos);

    /// FUSE FUNCTIONS all are using update_mutex_
    void DoFuseReplace(const MapElement &p);
    void DoFuseAppend(const MapElement &p);

    /*fuse with fix number of points and same area
      decay_factor to -1 to disable, if not and between 1 and zero
      the new center poly is computed:
       new_center=(old_mean * decay_factor + new)
                    / (old_mean * decay_factor + 1 )
    */
    void DoFuseCenterMean(const MapElement &p,
                          const tMapData decay_factor = -1.);

    void DoFuseExpDecay(MapElement const &p, const tMapData decay_factor);
    /*! creats a convex hull of two poly, -1 to disable simplify.
    simplify_distance (in units of input coordinates)
    of a vertex to other segments to be removed
    */
    void DoFuseConvecHull(const MapElement &p,
                          const tMapData simplify_distance = -1.);
    /*enable time of life if set for one element and
      use the time of life and creation time form p (the new element)*/
    void DoTimeOfLifeFuse(const MapElement &p);
};
}
} /*NAME SPACE frAIburg,map*/

#endif /* AADCUSER_FRAIBURG_MAP_ELEMENT_LIB_H_ */