From 9a4b6ce6917799b72ed31b1ab7a739945f8f99d2 Mon Sep 17 00:00:00 2001
From: Abd Errahmane Kiouche <abd-errahmane.kiouche@etu.univ-lyon1.fr>
Date: Thu, 25 Jun 2020 17:32:48 +0200
Subject: [PATCH] Update docopt.h, docopt_private.h, docopt_value.h, graph.h,
 io.h, hash.h, io.cpp, docopt_util.h, graph.cpp, main.cpp, param.h, util.h
 files

---
 docopt.h         |  65 +++++++++
 docopt_private.h | 309 +++++++++++++++++++++++++++++++++++++++++++
 docopt_util.h    |  97 ++++++++++++++
 docopt_value.h   | 321 ++++++++++++++++++++++++++++++++++++++++++++
 graph.cpp        | 198 +++++++++++++++++++++++++++
 graph.h          |  52 ++++++++
 hash.h           |  38 ++++++
 io.cpp           | 163 +++++++++++++++++++++++
 io.h             |  22 +++
 main.cpp         | 338 +++++++++++++++++++++++++++++++++++++++++++++++
 param.h          |  24 ++++
 util.h           |  23 ++++
 12 files changed, 1650 insertions(+)
 create mode 100644 docopt.h
 create mode 100644 docopt_private.h
 create mode 100644 docopt_util.h
 create mode 100644 docopt_value.h
 create mode 100644 graph.cpp
 create mode 100644 graph.h
 create mode 100644 hash.h
 create mode 100644 io.cpp
 create mode 100644 io.h
 create mode 100644 main.cpp
 create mode 100644 param.h
 create mode 100644 util.h

diff --git a/docopt.h b/docopt.h
new file mode 100644
index 0000000..dc72026
--- /dev/null
+++ b/docopt.h
@@ -0,0 +1,65 @@
+//
+//  docopt.h
+//  docopt
+//
+//  Created by Jared Grubb on 2013-11-03.
+//  Copyright (c) 2013 Jared Grubb. All rights reserved.
+//
+
+#ifndef docopt__docopt_h_
+#define docopt__docopt_h_
+
+#include "docopt_value.h"
+
+#include <map>
+#include <vector>
+#include <string>
+
+namespace docopt {
+	
+	// Usage string could not be parsed (ie, the developer did something wrong)
+	struct DocoptLanguageError : std::runtime_error { using runtime_error::runtime_error; };
+	
+	// Arguments passed by user were incorrect (ie, developer was good, user is wrong)
+	struct DocoptArgumentError : std::runtime_error { using runtime_error::runtime_error; };
+	
+	// Arguments contained '--help' and parsing was aborted early
+	struct DocoptExitHelp : std::runtime_error { DocoptExitHelp(); };
+	
+	// Arguments contained '--version' and parsing was aborted early
+	struct DocoptExitVersion : std::runtime_error { DocoptExitVersion(); };
+	
+	/// Parse user options from the given option string.
+	///
+	/// @param doc   The usage string
+	/// @param argv  The user-supplied arguments
+	/// @param help  Whether to end early if '-h' or '--help' is in the argv
+	/// @param version Whether to end early if '--version' is in the argv
+	/// @param options_first  Whether options must precede all args (true), or if args and options
+	///                can be arbitrarily mixed.
+	///
+	/// @throws DocoptLanguageError if the doc usage string had errors itself
+	/// @throws DocoptExitHelp if 'help' is true and the user has passed the '--help' argument
+	/// @throws DocoptExitVersion if 'version' is true and the user has passed the '--version' argument
+	/// @throws DocoptArgumentError if the user's argv did not match the usage patterns
+	std::map<std::string, value> docopt_parse(std::string const& doc,
+					    std::vector<std::string> const& argv,
+					    bool help = true,
+					    bool version = true,
+					    bool options_first = false);
+	
+	/// Parse user options from the given string, and exit appropriately
+	///
+	/// Calls 'docopt_parse' and will terminate the program if any of the exceptions above occur:
+	///  * DocoptLanguageError - print error and terminate (with exit code -1)
+	///  * DocoptExitHelp - print usage string and terminate (with exit code 0)
+	///  * DocoptExitVersion - print version and terminate (with exit code 0)
+	///  * DocoptArgumentError - print error and usage string and terminate (with exit code -1)
+	std::map<std::string, value> docopt(std::string const& doc,
+					    std::vector<std::string> const& argv,
+					    bool help = true,
+					    std::string const& version = {},
+					    bool options_first = false) noexcept;
+}
+
+#endif /* defined(docopt__docopt_h_) */
diff --git a/docopt_private.h b/docopt_private.h
new file mode 100644
index 0000000..b323a2e
--- /dev/null
+++ b/docopt_private.h
@@ -0,0 +1,309 @@
+//
+//  docopt_private.h
+//  docopt
+//
+//  Created by Jared Grubb on 2013-11-04.
+//  Copyright (c) 2013 Jared Grubb. All rights reserved.
+//
+
+#ifndef docopt_docopt_private_h
+#define docopt_docopt_private_h
+
+#include <vector>
+#include <memory>
+#include <unordered_set>
+
+#include "docopt_value.h"
+
+namespace docopt {
+	
+	class Pattern;
+	class LeafPattern;
+	
+	using PatternList = std::vector<std::shared_ptr<Pattern>>;
+	
+	// Utility to use Pattern types in std hash-containers
+	struct PatternHasher {
+		template <typename P>
+		size_t operator()(std::shared_ptr<P> const& pattern) const {
+			return pattern->hash();
+		}
+		template <typename P>
+		size_t operator()(P const* pattern) const {
+			return pattern->hash();
+		}
+		template <typename P>
+		size_t operator()(P const& pattern) const {
+			return pattern.hash();
+		}
+	};
+	
+	// Utility to use 'hash' as the equality operator as well in std containers
+	struct PatternPointerEquality {
+		template <typename P1, typename P2>
+		bool operator()(std::shared_ptr<P1> const& p1, std::shared_ptr<P2> const& p2) const {
+			return p1->hash()==p2->hash();
+		}
+		template <typename P1, typename P2>
+		bool operator()(P1 const* p1, P2 const* p2) const {
+			return p1->hash()==p2->hash();
+		}
+	};
+	
+	// A hash-set that uniques by hash value
+	using UniquePatternSet = std::unordered_set<std::shared_ptr<Pattern>, PatternHasher, PatternPointerEquality>;
+
+	
+	class Pattern {
+	public:
+		// flatten out children, stopping descent when the given filter returns 'true'
+		virtual std::vector<Pattern*> flat(bool (*filter)(Pattern const*)) = 0;
+		
+		// flatten out all children into a list of LeafPattern objects
+		virtual void collect_leaves(std::vector<LeafPattern*>&) = 0;
+		
+		// flatten out all children into a list of LeafPattern objects
+		std::vector<LeafPattern*> leaves();
+		
+		// Attempt to find something in 'left' that matches this pattern's spec, and if so, move it to 'collected'
+		virtual bool match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const = 0;
+		
+		virtual std::string const& name() const = 0;
+		
+		virtual bool hasValue() const { return false; }
+		
+		virtual size_t hash() const = 0;
+
+		virtual ~Pattern() = default;
+	};
+	
+	class LeafPattern
+	: public Pattern {
+	public:
+		LeafPattern(std::string name, value v = {})
+		: fName(std::move(name)),
+		  fValue(std::move(v))
+		{}
+		
+		virtual std::vector<Pattern*> flat(bool (*filter)(Pattern const*)) override {
+			if (filter(this)) {
+				return { this };
+			}
+			return {};
+		}
+		
+		virtual void collect_leaves(std::vector<LeafPattern*>& lst) override final {
+			lst.push_back(this);
+		}
+		
+		virtual bool match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const override;
+		
+		virtual bool hasValue() const override { return static_cast<bool>(fValue); }
+		
+		value const& getValue() const { return fValue; }
+		void setValue(value&& v) { fValue = std::move(v); }
+				
+		virtual std::string const& name() const override { return fName; }
+		
+		virtual size_t hash() const override {
+			size_t seed = typeid(*this).hash_code();
+			hash_combine(seed, fName);
+			hash_combine(seed, fValue);
+			return seed;
+		}
+
+	protected:
+		virtual std::pair<size_t, std::shared_ptr<LeafPattern>> single_match(PatternList const&) const = 0;
+		
+	private:
+		std::string fName;
+		value fValue;
+	};
+	
+	class BranchPattern
+	: public Pattern {
+	public:
+		BranchPattern(PatternList children = {})
+		: fChildren(std::move(children))
+		{}
+		
+		Pattern& fix() {
+			UniquePatternSet patterns;
+			fix_identities(patterns);
+			fix_repeating_arguments();
+			return *this;
+		}
+		
+		virtual std::string const& name() const override {
+			throw std::runtime_error("Logic error: name() shouldnt be called on a BranchPattern");
+		}
+		
+		virtual value const& getValue() const {
+			throw std::runtime_error("Logic error: name() shouldnt be called on a BranchPattern");
+		}
+		
+		virtual std::vector<Pattern*> flat(bool (*filter)(Pattern const*)) override {
+			if (filter(this)) {
+				return {this};
+			}
+			
+			std::vector<Pattern*> ret;
+			for(auto& child : fChildren) {
+				auto sublist = child->flat(filter);
+				ret.insert(ret.end(), sublist.begin(), sublist.end());
+			}
+			return ret;
+		}
+		
+		virtual void collect_leaves(std::vector<LeafPattern*>& lst) override final {
+			for(auto& child : fChildren) {
+				child->collect_leaves(lst);
+			}
+		}
+		
+		void setChildren(PatternList children) {
+			fChildren = std::move(children);
+		}
+		
+		PatternList const& children() const { return fChildren; }
+		
+		virtual void fix_identities(UniquePatternSet& patterns) {
+			for(auto& child : fChildren) {
+				// this will fix up all its children, if needed
+				if (auto bp = dynamic_cast<BranchPattern*>(child.get())) {
+					bp->fix_identities(patterns);
+				}
+				
+				// then we try to add it to the list
+				auto inserted = patterns.insert(child);
+				if (!inserted.second) {
+					// already there? then reuse the existing shared_ptr for that thing
+					child = *inserted.first;
+				}
+			}
+		}
+		
+		virtual size_t hash() const override {
+			size_t seed = typeid(*this).hash_code();
+			hash_combine(seed, fChildren.size());
+			for(auto const& child : fChildren) {
+				hash_combine(seed, child->hash());
+			}
+			return seed;
+		}
+	private:
+		void fix_repeating_arguments();
+		
+	protected:
+		PatternList fChildren;
+	};
+	
+	class Argument
+	: public LeafPattern {
+	public:
+		using LeafPattern::LeafPattern;
+		
+	protected:
+		virtual std::pair<size_t, std::shared_ptr<LeafPattern>> single_match(PatternList const& left) const override;
+	};
+	
+	class Command : public Argument {
+	public:
+		Command(std::string name, value v = value{false})
+		: Argument(std::move(name), std::move(v))
+		{}
+		
+	protected:
+		virtual std::pair<size_t, std::shared_ptr<LeafPattern>> single_match(PatternList const& left) const override;
+	};
+	
+	class Option final
+	: public LeafPattern
+	{
+	public:
+		static Option parse(std::string const& option_description);
+		
+		Option(std::string shortOption,
+		       std::string longOption,
+		       int argcount = 0,
+		       value v = value{false})
+		: LeafPattern(longOption.empty() ? shortOption : longOption,
+			      std::move(v)),
+		  fShortOption(std::move(shortOption)),
+		  fLongOption(std::move(longOption)),
+		  fArgcount(argcount)
+		{
+			// From Python:
+			//   self.value = None if value is False and argcount else value
+			if (argcount && v.isBool() && !v.asBool()) {
+				setValue(value{});
+			}
+		}
+		
+		Option(Option const&) = default;
+		Option(Option&&) = default;
+		Option& operator=(Option const&) = default;
+		Option& operator=(Option&&) = default;
+		
+		using LeafPattern::setValue;
+		
+		std::string const& longOption() const { return fLongOption; }
+		std::string const& shortOption() const { return fShortOption; }
+		int argCount() const { return fArgcount; }
+		
+		virtual size_t hash() const override {
+			size_t seed = LeafPattern::hash();
+			hash_combine(seed, fShortOption);
+			hash_combine(seed, fLongOption);
+			hash_combine(seed, fArgcount);
+			return seed;
+		}
+		
+	protected:
+		virtual std::pair<size_t, std::shared_ptr<LeafPattern>> single_match(PatternList const& left) const override;
+
+	private:
+		std::string fShortOption;
+		std::string fLongOption;
+		int fArgcount;
+	};
+
+	class Required : public BranchPattern {
+	public:
+		using BranchPattern::BranchPattern;
+		
+		bool match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const override;
+	};
+
+	class Optional : public BranchPattern {
+	public:
+		using BranchPattern::BranchPattern;
+		
+		bool match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const override {
+			for(auto const& pattern : fChildren) {
+				pattern->match(left, collected);
+			}
+			return true;
+		}
+	};
+
+	class OptionsShortcut : public Optional {
+		using Optional::Optional;
+	};
+
+	class OneOrMore : public BranchPattern {
+	public:
+		using BranchPattern::BranchPattern;
+		
+		bool match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const override;
+	};
+
+	class Either : public BranchPattern {
+	public:
+		using BranchPattern::BranchPattern;
+		
+		bool match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const override;
+	};
+}
+
+#endif
diff --git a/docopt_util.h b/docopt_util.h
new file mode 100644
index 0000000..75288c8
--- /dev/null
+++ b/docopt_util.h
@@ -0,0 +1,97 @@
+//
+//  docopt_util.h
+//  docopt
+//
+//  Created by Jared Grubb on 2013-11-04.
+//  Copyright (c) 2013 Jared Grubb. All rights reserved.
+//
+
+#ifndef docopt_docopt_util_h
+#define docopt_docopt_util_h
+
+
+namespace {
+	bool starts_with(std::string const& str, std::string const& prefix)
+	{
+		if (str.length() < prefix.length())
+			return false;
+		return std::equal(prefix.begin(), prefix.end(),
+				  str.begin());
+	}
+	
+	std::string trim(std::string&& str,
+			 const std::string& whitespace = " \t\n")
+	{
+		const auto strEnd = str.find_last_not_of(whitespace);
+		if (strEnd==std::string::npos)
+			return {}; // no content
+		str.erase(strEnd+1);
+		
+		const auto strBegin = str.find_first_not_of(whitespace);
+		str.erase(0, strBegin);
+		
+		return std::move(str);
+	}
+	
+	std::vector<std::string> split(std::string const& str, size_t pos = 0)
+	{
+		const char* const anySpace = " \t\r\n\v\f";
+		
+		std::vector<std::string> ret;
+		while (pos != std::string::npos) {
+			auto start = str.find_first_not_of(anySpace, pos);
+			if (start == std::string::npos) break;
+			
+			auto end = str.find_first_of(anySpace, start);
+			auto size = end==std::string::npos ? end : end-start;
+			ret.emplace_back(str.substr(start, size));
+			
+			pos = end;
+		}
+		
+		return ret;
+	}
+	
+	std::tuple<std::string, std::string, std::string> partition(std::string str, std::string const& point)
+	{
+		std::tuple<std::string, std::string, std::string> ret;
+		
+		auto i = str.find(point);
+		
+		if (i == std::string::npos) {
+			// no match: string goes in 0th spot only
+		} else {
+			std::get<2>(ret) = str.substr(i + point.size());
+			std::get<1>(ret) = point;
+			str.resize(i);
+		}
+		std::get<0>(ret) = std::move(str);
+		
+		return ret;
+	}
+	
+	template <typename I>
+	std::string join(I iter, I end, std::string const& delim) {
+		if (iter==end)
+			return {};
+		
+		std::string ret = *iter;
+		for(++iter; iter!=end; ++iter) {
+			ret.append(delim);
+			ret.append(*iter);
+		}
+		return ret;
+	}
+}
+
+namespace docopt {
+	template <class T>
+	inline void hash_combine(std::size_t& seed, T const& v)
+	{
+		// stolen from boost::hash_combine
+		std::hash<T> hasher;
+		seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
+	}
+}
+
+#endif
diff --git a/docopt_value.h b/docopt_value.h
new file mode 100644
index 0000000..8f32778
--- /dev/null
+++ b/docopt_value.h
@@ -0,0 +1,321 @@
+//
+//  value.h
+//  docopt
+//
+//  Created by Jared Grubb on 2013-10-14.
+//  Copyright (c) 2013 Jared Grubb. All rights reserved.
+//
+
+#ifndef docopt__value_h_
+#define docopt__value_h_
+
+#include <string>
+#include <vector>
+#include <functional> // std::hash
+#include <iosfwd>
+
+namespace docopt {
+
+	/// A generic type to hold the various types that can be produced by docopt.
+	///
+	/// This type can be one of: {bool, long, string, vector<string>}, or empty.
+	struct value {
+		/// An empty value
+		value() {}
+
+		value(std::string);
+		value(std::vector<std::string>);
+		
+		explicit value(bool);
+		explicit value(long);
+		explicit value(int v) : value(static_cast<long>(v)) {}
+
+		~value();
+		value(value const&);
+		value(value&&) noexcept;
+		value& operator=(value const&);
+		value& operator=(value&&) noexcept;
+		
+		// Test if this object has any contents at all
+		explicit operator bool() const { return kind != Kind::Empty; }
+		
+		// Test the type contained by this value object
+		bool isBool()       const { return kind==Kind::Bool; }
+		bool isString()     const { return kind==Kind::String; }
+		bool isLong()       const { return kind==Kind::Long; }
+		bool isStringList() const { return kind==Kind::StringList; }
+
+		// Throws std::invalid_argument if the type does not match
+		bool asBool() const;
+		long asLong() const;
+		std::string const& asString() const;
+		std::vector<std::string> const& asStringList() const;
+
+		size_t hash() const noexcept;
+		
+		// equality is based on hash-equality
+		friend bool operator==(value const&, value const&);
+		friend bool operator!=(value const&, value const&);
+
+	private:
+		enum class Kind {
+			Empty,
+			Bool,
+			Long,
+			String,
+			StringList
+		};
+		
+		union Variant {
+			Variant() {}
+			~Variant() {  /* do nothing; will be destroyed by ~value */ }
+			
+			bool boolValue;
+			long longValue;
+			std::string strValue;
+			std::vector<std::string> strList;
+		};
+		
+		static const char* kindAsString(Kind);
+		void throwIfNotKind(Kind expected) const;
+
+	private:
+		Kind kind = Kind::Empty;
+		Variant variant {};
+	};
+
+	/// Write out the contents to the ostream
+	std::ostream& operator<<(std::ostream&, value const&);
+}
+
+namespace std {
+	template <>
+	struct hash<docopt::value> {
+		size_t operator()(docopt::value const& val) const noexcept {
+			return val.hash();
+		}
+	};
+}
+
+namespace docopt {
+	inline
+	value::value(bool v)
+	: kind(Kind::Bool)
+	{
+		variant.boolValue = v;
+	}
+
+	inline
+	value::value(long v)
+	: kind(Kind::Long)
+	{
+		variant.longValue = v;
+	}
+
+	inline
+	value::value(std::string v)
+	: kind(Kind::String)
+	{
+		new (&variant.strValue) std::string(std::move(v));
+	}
+
+	inline
+	value::value(std::vector<std::string> v)
+	: kind(Kind::StringList)
+	{
+		new (&variant.strList) std::vector<std::string>(std::move(v));
+	}
+
+	inline
+	value::value(value const& other)
+	: kind(other.kind)
+	{
+		switch (kind) {
+			case Kind::String:
+				new (&variant.strValue) std::string(other.variant.strValue);
+				break;
+
+			case Kind::StringList:
+				new (&variant.strList) std::vector<std::string>(other.variant.strList);
+				break;
+
+			case Kind::Bool:
+				variant.boolValue = other.variant.boolValue;
+				break;
+
+			case Kind::Long:
+				variant.longValue = other.variant.longValue;
+				break;
+
+			case Kind::Empty:
+			default:
+				break;
+		}
+	}
+
+	inline
+	value::value(value&& other) noexcept
+	: kind(other.kind)
+	{
+		switch (kind) {
+			case Kind::String:
+				new (&variant.strValue) std::string(std::move(other.variant.strValue));
+				break;
+
+			case Kind::StringList:
+				new (&variant.strList) std::vector<std::string>(std::move(other.variant.strList));
+				break;
+
+			case Kind::Bool:
+				variant.boolValue = other.variant.boolValue;
+				break;
+
+			case Kind::Long:
+				variant.longValue = other.variant.longValue;
+				break;
+
+			case Kind::Empty:
+			default:
+				break;
+		}
+	}
+
+	inline
+	value::~value()
+	{
+		switch (kind) {
+			case Kind::String:
+				variant.strValue.~basic_string();
+				break;
+
+			case Kind::StringList:
+				variant.strList.~vector();
+				break;
+
+			case Kind::Empty:
+			case Kind::Bool:
+			case Kind::Long:
+			default:
+				// trivial dtor
+				break;
+		}
+	}
+
+	inline
+	value& value::operator=(value const& other) {
+		// make a copy and move from it; way easier.
+		return *this = value{other};
+	}
+
+	inline
+	value& value::operator=(value&& other) noexcept {
+		// move of all the types involved is noexcept, so we dont have to worry about 
+		// these two statements throwing, which gives us a consistency guarantee.
+		this->~value();
+		new (this) value(std::move(other));
+
+		return *this;
+	}
+
+	template <class T>
+	void hash_combine(std::size_t& seed, const T& v);
+
+	inline
+	size_t value::hash() const noexcept
+	{
+		switch (kind) {
+			case Kind::String:
+				return std::hash<std::string>()(variant.strValue);
+
+			case Kind::StringList: {
+				size_t seed = std::hash<size_t>()(variant.strList.size());
+				for(auto const& str : variant.strList) {
+					hash_combine(seed, str);
+				}
+				return seed;
+			}
+
+			case Kind::Bool:
+				return std::hash<bool>()(variant.boolValue);
+
+			case Kind::Long:
+				return std::hash<long>()(variant.longValue);
+
+			case Kind::Empty:
+			default:
+				return std::hash<void*>()(nullptr);
+		}
+	}
+
+	inline
+	bool value::asBool() const
+	{
+		throwIfNotKind(Kind::Bool);
+		return variant.boolValue;
+	}
+
+	inline
+	long value::asLong() const
+	{
+		// Attempt to convert a string to a long
+		if (kind == Kind::String) {
+			const std::string& str = variant.strValue;
+			std::size_t pos;
+			const long ret = stol(str, &pos); // Throws if it can't convert
+			if (pos != str.length()) {
+				// The string ended in non-digits.
+				throw std::runtime_error( str + " contains non-numeric characters.");
+			}
+			return ret;
+		}
+		throwIfNotKind(Kind::Long);
+		return variant.longValue;
+	}
+
+	inline
+	std::string const& value::asString() const
+	{
+		throwIfNotKind(Kind::String);
+		return variant.strValue;
+	}
+
+	inline
+	std::vector<std::string> const& value::asStringList() const
+	{
+		throwIfNotKind(Kind::StringList);
+		return variant.strList;
+	}
+
+	inline
+	bool operator==(value const& v1, value const& v2)
+	{
+		if (v1.kind != v2.kind)
+			return false;
+		
+		switch (v1.kind) {
+			case value::Kind::String:
+				return v1.variant.strValue==v2.variant.strValue;
+
+			case value::Kind::StringList:
+				return v1.variant.strList==v2.variant.strList;
+
+			case value::Kind::Bool:
+				return v1.variant.boolValue==v2.variant.boolValue;
+
+			case value::Kind::Long:
+				return v1.variant.longValue==v2.variant.longValue;
+
+			case value::Kind::Empty:
+			default:
+				return true;
+		}
+	}
+
+	inline
+	bool operator!=(value const& v1, value const& v2)
+	{
+		return !(v1 == v2);
+	}
+}
+
+#endif /* defined(docopt__value_h_) */
diff --git a/graph.cpp b/graph.cpp
new file mode 100644
index 0000000..da7e97f
--- /dev/null
+++ b/graph.cpp
@@ -0,0 +1,198 @@
+//
+// Created by Abderrahmane on 6/16/2018.
+//
+#include <algorithm>
+#include <iostream>
+#include <set>
+#include <cmath>
+#include "graph.h"
+#include "hash.h"
+#include "param.h"
+
+
+namespace std {
+
+
+
+    double compute_branch_edit_distance(Branch &Br1, Branch &Br2) {
+        double bed = 0;
+        double max_bed = 1+max(Br1.d_out, Br2.d_out)+max(Br1.d_in, Br2.d_in);
+        if (Br1.r != Br2.r) bed += 1;
+        bed += max(Br1.d_out, Br2.d_out);
+        bed += max(Br1.d_in, Br2.d_in);
+        if (max(Br1.d_out, Br2.d_out) == Br2.d_out) {
+            for (auto &a : Br1.es_out)bed -= min(a.second, Br2.es_out[a.first]);
+        } else {
+            for (auto &a : Br2.es_out)bed -= min(a.second, Br1.es_out[a.first]);
+        }
+        if (max(Br1.d_in, Br2.d_in) == Br2.d_in) {
+            for (auto &a : Br1.es_in)bed -= min(a.second, Br2.es_in[a.first]);
+        } else {
+            for (auto &a : Br2.es_in)bed -= min(a.second, Br1.es_in[a.first]);
+        }
+        return bed/max_bed;
+    }
+
+    double euclidean_distance(const vector<double> &a,const vector<double> &b){
+        double dist = 0;
+        for (int i=0;i<M;i++){
+            dist += pow(abs(a[i]-b[i]),2);
+        }
+        return  sqrt(dist);
+    }
+
+    void update_BED(edge &e,uint32_t &branch_id,Branch &Br,unordered_map<uint32_t,vector<double>>& branches_distances,
+                     vector<Branch> &prototype_branches,double &graph_size){
+
+        // read the edge
+        uint32_t  src_id = get<F_S>(e);
+        uint32_t  dst_id = get<F_D>(e);
+        string src_type = get<F_STYPE>(e);
+        string dst_type = get<F_DTYPE>(e);
+        string e_type = get<F_ETYPE>(e);
+        if (src_id==branch_id) // is a source branch
+        {
+            for (int i=0;i<M;i++){
+                double max_bed= 1+max(Br.d_out, prototype_branches.at(i).d_out)+
+                                max(Br.d_in, prototype_branches.at(i).d_in);
+                double bed = branches_distances[branch_id].at(i)*max_bed;
+                bed = round(bed);
+                if (((Br.d_out+1) > prototype_branches.at(i).d_out) &&
+                ( ( Br.es_out[e_type] +1) > prototype_branches.at(i).es_out[e_type] )){
+                    bed++;
+                }
+                else if (((Br.d_out+1) <= prototype_branches.at(i).d_out) &&
+                         ( ( Br.es_out[e_type] +1)<= prototype_branches.at(i).es_out[e_type] )){
+                    bed--;
+                }
+
+                double new_max = 1+max(Br.d_out+1, prototype_branches.at(i).d_out)+
+                                 max(Br.d_in, prototype_branches.at(i).d_in);
+                branches_distances[branch_id].at(i) =bed/new_max;
+                if ((bed / new_max)<0 || (bed / new_max)>1) exit(35);
+            }
+            // update the branch
+            Br.d_out++;
+            Br.es_out[e_type]++;
+        }
+        if (dst_id ==branch_id) // is a dst branch
+        {
+            for (int i=0;i<M;i++){
+                double max_bed= 1+max(Br.d_out, prototype_branches.at(i).d_out)+
+                                max(Br.d_in, prototype_branches.at(i).d_in);
+                double bed = branches_distances[branch_id].at(i)*max_bed;
+                bed = round(bed);
+                if (((Br.d_in+1) > prototype_branches.at(i).d_in) &&
+                    ( ( Br.es_in[e_type] +1)> prototype_branches.at(i).es_in[e_type] )){
+                    bed++;
+                }
+                else if (((Br.d_in+1) <= prototype_branches.at(i).d_in) &&
+                         ( ( Br.es_in[e_type] +1)<= prototype_branches.at(i).es_in[e_type] )){
+                    bed--;
+                }
+                double new_max = 1+max(Br.d_out, prototype_branches.at(i).d_out)+
+                                 max(Br.d_in+1, prototype_branches.at(i).d_in);
+                branches_distances[branch_id].at(i) =bed/new_max;
+                if ((bed / new_max)<0 || (bed / new_max)>1) exit(35);
+            }
+            // update the branch
+            Br.d_in++;
+            Br.es_in[e_type]++;
+        }
+    }
+
+
+    void update_graph_vector2 (uint32_t  &gid,edge   &e,
+                               vector<Branch> &prototype_branches,
+                               unordered_map<uint32_t, unordered_map<uint32_t, vector<double>>> &graphs_to_branches_distances,
+                               unordered_map<uint32_t, unordered_map<uint32_t, Branch>> &graphs_to_branches,
+                               unordered_map<uint32_t, graph_vector> &graphs_vectors,
+                               unordered_map<uint32_t ,double > &graphs_sizes){
+
+        // read edge
+        uint32_t  src_id = get<F_S>(e);
+        uint32_t  dst_id = get<F_D>(e);
+        string src_type = get<F_STYPE>(e);
+        string dst_type = get<F_DTYPE>(e);
+        string e_type = get<F_ETYPE>(e);
+
+
+        // update src_id branch
+        graphs_to_branches[gid][src_id].r = src_type;
+        uint32_t  d_out = graphs_to_branches[gid][src_id].d_out;
+        uint32_t  d_in = graphs_to_branches[gid][src_id].d_in;
+        if ( (d_out+d_in)==0 ) {   // a new branch
+            graphs_sizes[gid]+=1;
+            graphs_to_branches[gid][src_id].d_out=1;
+            graphs_to_branches[gid][src_id].es_out[e_type]=1;
+            int k = 0;
+
+            for (auto &brp:prototype_branches) {
+                double bed = compute_branch_edit_distance(graphs_to_branches[gid][src_id], brp);
+                graphs_vectors[gid].at(k) *= 2*(graphs_sizes[gid]-1);
+                graphs_vectors[gid].at(k) += (1-bed);
+                graphs_vectors[gid].at(k)/=2*graphs_sizes[gid];
+                graphs_to_branches_distances[gid][src_id].push_back(bed);
+                k++;
+            }
+        }
+        else{   // a old branch
+
+            // substract the old impact of the branch src_id
+            for (int k=0;k<M;k++) {
+                graphs_vectors[gid].at(k) *= (2*graphs_sizes[gid]);
+                graphs_vectors[gid].at(k) -=  (1-graphs_to_branches_distances[gid][src_id].at(k)) * (graphs_to_branches[gid][src_id].d_in
+                                                                                                     + graphs_to_branches[gid][src_id].d_out);
+            }
+            // update the BED's
+            graphs_sizes[gid]+=1;
+            update_BED(e,src_id,graphs_to_branches[gid][src_id],graphs_to_branches_distances[gid],prototype_branches,graphs_sizes[gid]);
+
+            // add the new impact of the branch src_id
+
+            for (int k=0;k<M;k++) {
+                graphs_vectors[gid].at(k) += (1- graphs_to_branches_distances[gid][src_id].at(k))*(graphs_to_branches[gid][src_id].d_in
+                                                                                                   + graphs_to_branches[gid][src_id].d_out);
+                graphs_vectors[gid].at(k) /= (2*graphs_sizes[gid]);
+            }
+        }
+        // update dst_id branch
+        graphs_to_branches[gid][dst_id].r = dst_type;
+        d_out = graphs_to_branches[gid][dst_id].d_out;
+        d_in = graphs_to_branches[gid][dst_id].d_in;
+        if ( (d_out+d_in)==0 ) {   // a new branch
+            graphs_to_branches[gid][dst_id].d_in=1;
+            graphs_to_branches[gid][dst_id].es_in[e_type]=1;
+            int k = 0;
+            for (auto &brp:prototype_branches) {
+                double bed = compute_branch_edit_distance(graphs_to_branches[gid][dst_id], brp);
+                graphs_vectors[gid].at(k) *= 2*graphs_sizes[gid];
+                graphs_vectors[gid].at(k) += (1-bed);
+                graphs_vectors[gid].at(k)/=2*graphs_sizes[gid];
+                graphs_to_branches_distances[gid][dst_id].push_back(bed);
+                k++;
+            }
+        }
+        else{   // a old branch
+            // substract the old impact of the branch src_id
+            for (int k=0;k<M;k++) {
+                graphs_vectors[gid].at(k) *= (2*graphs_sizes[gid]);
+                graphs_vectors[gid].at(k) -=  (1-graphs_to_branches_distances[gid][dst_id].at(k))*(graphs_to_branches[gid][dst_id].d_in
+                                                                                                  + graphs_to_branches[gid][dst_id].d_out);
+            }
+            // update the BED's
+            update_BED(e,dst_id,graphs_to_branches[gid][dst_id],graphs_to_branches_distances[gid],prototype_branches,graphs_sizes[gid]);
+
+            // add the new impact of the branch src_id
+            for (int k=0;k<M;k++) {
+                graphs_vectors[gid].at(k) += (1- graphs_to_branches_distances[gid][dst_id].at(k))*(graphs_to_branches[gid][dst_id].d_in
+                                                                                                   + graphs_to_branches[gid][dst_id].d_out);
+                graphs_vectors[gid].at(k) /= (2*graphs_sizes[gid]);
+            }
+        }
+    }
+
+
+
+
+}
diff --git a/graph.h b/graph.h
new file mode 100644
index 0000000..bddb6fc
--- /dev/null
+++ b/graph.h
@@ -0,0 +1,52 @@
+//
+// Created by Abderrahmane on 6/16/2018.
+//
+
+#ifndef TESTMAP_GRAPH_H
+#define TESTMAP_GRAPH_H
+
+#include <vector>
+#include <tuple>
+#include <unordered_map>
+
+namespace std {
+
+// edge field indices
+    #define F_S               0           // source node id
+    #define F_STYPE           1           // source node type
+    #define F_D               2           // destination node id
+    #define F_DTYPE           3           // destination node type
+    #define F_ETYPE           4           // edge type
+    #define F_GID             5           // graph id (tag)
+
+// data structures
+    typedef struct Branch{
+        string r; // the root of the branch
+        unordered_map<string, int> es_out; // the edge structure of the outgoing edges from r
+        unordered_map<string, int> es_in;  // the edge structure of the incoming edges to r
+        uint32_t d_out; // the number of outgoing edges
+        uint32_t d_in;  // the number of incoming edges
+    } Branch;
+
+    typedef tuple<uint32_t, string, uint32_t, string, string, uint32_t> edge;
+    typedef unordered_map<pair<uint32_t,string>, vector<tuple<uint32_t,string,string>>> graph; //
+    typedef vector<double> graph_vector; // vector representation of a graph
+
+
+    double compute_branch_edit_distance(Branch &Br1,Branch &Br2);
+
+    double euclidean_distance(const vector<double> &a,const vector<double> &b);
+
+
+    void update_BED(edge &e,uint32_t &branch_id,Branch &Br,unordered_map<uint32_t,vector<double>>& branches_distances,
+                    vector<Branch> &prototype_branches,double &graph_size);
+    void update_graph_vector2 (uint32_t  &gid,edge   &e,
+                               vector<Branch> &prototype_branches,
+                               unordered_map<uint32_t, unordered_map<uint32_t, vector<double>>> &graphs_to_branches_distances,
+                               unordered_map<uint32_t, unordered_map<uint32_t, Branch>> &graphs_to_branches,
+                               unordered_map<uint32_t, graph_vector> &graphs_vectors,
+                               unordered_map<uint32_t ,double > &graphs_sizes);
+
+}
+
+#endif //TESTMAP_GRAPH_H
diff --git a/hash.h b/hash.h
new file mode 100644
index 0000000..446f579
--- /dev/null
+++ b/hash.h
@@ -0,0 +1,38 @@
+//
+// Created by Abderrahmane on 6/18/2018.
+//
+
+#ifndef TESTMAP_HASH_H
+#define TESTMAP_HASH_H
+
+#include <string>
+#include <vector>
+
+namespace std {
+
+
+/* Combination hash from Boost */
+    template <class T>
+    inline void hash_combine(size_t& seed, const T& v)
+    {
+        hash<T> hasher;
+        seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+    }
+
+    template<typename S, typename T> struct hash<pair<S, T>>
+    {
+        inline size_t operator()(const pair<S, T>& v) const
+        {
+            size_t seed = 0;
+            hash_combine(seed, v.first);
+            hash_combine(seed, v.second);
+            return seed;
+        }
+    };
+/* End combination hash from Boost */
+
+
+
+}
+
+#endif //TESTMAP_HASH_H
diff --git a/io.cpp b/io.cpp
new file mode 100644
index 0000000..809b34b
--- /dev/null
+++ b/io.cpp
@@ -0,0 +1,163 @@
+
+
+#include <fcntl.h>
+#include <fstream>
+#include "graph.h"
+#include "io.h"
+#include <iostream>
+#include "param.h"
+#include <string>
+#include <sstream>
+#include <tuple>
+#include <unistd.h>
+#include "util.h"
+#include <vector>
+#include <algorithm>
+#include <unordered_set>
+
+namespace std {
+
+
+    tuple<unordered_map<uint32_t, graph_vector>, vector<uint32_t>> read_train_graph_vectors(string filename) {
+
+      unordered_map<uint32_t, graph_vector> graph_vectors;
+      vector<uint32_t> train_gids;
+      ifstream f(filename);
+      string line;
+
+      while (getline(f, line)) {
+        uint32_t gid;
+        double e;
+        stringstream ss;
+        ss.str(line);
+        ss >> gid;
+        train_gids.push_back(gid);
+        while (ss >> e) {
+          graph_vectors[gid].push_back(e);
+        }
+      }
+      return make_tuple(graph_vectors, train_gids);
+    }
+
+    vector<Branch> read_prototype_branches(string filename) {
+
+      vector<Branch> prototype_branches;
+      ifstream f(filename);
+      string line;
+      while (getline(f, line)) {
+        Branch br;
+        stringstream ss;
+        ss.str(line);
+        ss >> br.r;
+        ss >> br.d_out;
+        ss >> br.d_in;
+        getline(f, line);
+        if (br.d_out > 0) {
+          stringstream ss;
+          ss.str(line);
+          string c;
+          while (ss >> c) {
+              int f;
+            ss >> f;
+            if (f > 0) br.es_out[c] = f;
+          }
+        }
+        getline(f, line);
+        if (br.d_in > 0) {
+          stringstream ss;
+          ss.str(line);
+          string c;
+          int f;
+          while (ss >> c) {
+            ss >> f;
+            if (f > 0) br.es_in[c] = f;
+          }
+        }
+        prototype_branches.push_back(br);
+      }
+
+      return prototype_branches;
+    }
+
+    tuple<uint32_t, unordered_map<uint32_t, vector<edge>>> read_edges
+            (string filename, vector<uint32_t> &train_gids, unordered_set<uint32_t> scenarios) {
+
+
+      cerr << "Reading edges from: " << filename << endl;
+      vector<edge> train_edges;
+      unordered_map<uint32_t, vector<edge>> test_edges;
+      uint32_t num_train_edges = 0;
+      ifstream f(filename);
+      string line;
+
+
+      // read edges from the file
+      uint32_t i = 0;
+      uint32_t max_gid = 0;
+
+
+      while (getline(f, line)) {
+        string src_type, dst_type, e_type;
+        uint32_t src_id, dst_id, graph_id;
+        stringstream ss;
+        ss.str(line);
+        ss >> src_id;
+        ss >> src_type;
+        ss >> dst_id;
+        ss >> dst_type;
+        ss >> e_type;
+        ss >> graph_id;
+        if (graph_id > max_gid) {
+          max_gid = graph_id;
+        }
+
+        i++; // skip newline
+
+
+        uint32_t scenario = graph_id / 100;
+        if (scenarios.find(scenario) != scenarios.end()) {
+          // add an edge to memory
+          if (find(train_gids.begin(), train_gids.end(), graph_id) == train_gids.end()) { // is a test edge
+            test_edges[graph_id].push_back(make_tuple(src_id, src_type,
+                                                      dst_id, dst_type,
+                                                      e_type, graph_id));
+
+          }
+        }
+      }
+      return make_tuple(max_gid + 1, test_edges);
+    }
+
+    tuple<vector<vector<uint32_t>>, vector<double>, double> read_bootstrap_clusters(string bootstrap_file) {
+      int nclusters;
+      double global_threshold;
+      ifstream f(bootstrap_file);
+      string line;
+      stringstream ss;
+
+      getline(f, line);
+      int num_graphs;
+      ss.str(line);
+      ss >> nclusters >> num_graphs >> global_threshold;
+      cout << nclusters << " , " << global_threshold << endl;
+      vector<double> cluster_thresholds(nclusters);
+      vector<vector<uint32_t>> clusters(nclusters);
+
+      for (int i = 0; i < nclusters; i++) {
+        getline(f, line);
+        ss.clear();
+        ss.str(line);
+
+        double cluster_threshold;
+        ss >> cluster_threshold;
+        cluster_thresholds[i] = cluster_threshold;
+
+        double gid;
+        while (ss >> gid) {
+          clusters[i].push_back((int) gid);
+        }
+      }
+
+      return make_tuple(clusters, cluster_thresholds, global_threshold);
+    }
+}
\ No newline at end of file
diff --git a/io.h b/io.h
new file mode 100644
index 0000000..34f71b4
--- /dev/null
+++ b/io.h
@@ -0,0 +1,22 @@
+
+#ifndef STREAMSPOT_IO_H_
+#define STREAMSPOT_IO_H_
+
+#include "graph.h"
+#include <string>
+#include <tuple>
+#include <vector>
+#include <unordered_set>
+
+namespace std {
+
+    vector<Branch> read_prototype_branches(string filename);
+    tuple<unordered_map<uint32_t, graph_vector>,vector<uint32_t>> read_train_graph_vectors(string filename);
+
+    tuple<uint32_t,unordered_map<uint32_t,vector<edge>>> read_edges (string filename,vector<uint32_t> &train_gids,unordered_set<uint32_t> scenarios);
+
+
+    tuple<vector<vector<uint32_t>>, vector<double>, double>  read_bootstrap_clusters(string bootstrap_file);
+}
+
+#endif
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..03c4fb0
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,338 @@
+
+
+#include <algorithm>
+#include <bitset>
+#include <cassert>
+#include <deque>
+#include <iostream>
+
+#include <string>
+#include <unordered_map>
+#include <set>
+#include <unordered_set>
+#include <vector>
+#include <algorithm>
+#include <sstream>
+#include <random>
+#include <chrono>
+#include <fstream>
+
+
+#include "docopt.h"
+#include "graph.h"
+#include "hash.h"
+#include "io.h"
+#include "param.h"
+#include "cluster.h"
+
+
+using namespace std;
+
+static const char USAGE[] =
+        R"( LEADS (Anomaly detection phase).
+
+    Usage:
+    LEADS     --edges=<edge file>
+         --prototypes=<prototype branches file>
+		 --train=<train graphs file>
+         --clusters=<bootstrap cluster file>
+         --dataset=<dataset>
+         --B=<par>
+         --out=<output>
+
+   LEADS (-h | --help)
+
+    Options:
+      -h, --help                              Show this screen.
+      --edges=<edge file>                     Incoming stream of edges.
+      --prototypes=<prototype branches file>  The prototype branhces
+      --train=<train graphs file>	          train graph vectors
+      --clusters=<bootstrap cluster file>     bootstrap cluster file
+      --dataset=<dataset>                     'ALL', 'YDC', 'GFC', 'YDG','AUTH,.
+      --B=<par>                               number of parallel graphs that arrive simulatenously
+)";
+int M;
+int main(int argc, char *argv[]) {
+
+    // for measuring time
+    chrono::time_point<chrono::steady_clock> start;
+    chrono::time_point<chrono::steady_clock> end;
+    chrono::nanoseconds diff;
+    // for random
+    mt19937_64 prng(SEED);
+
+
+    // arguments
+    map<string, docopt::value> args = docopt::docopt(USAGE, {argv + 1, argv + argc});
+    string edge_file(args["--edges"].asString());
+    string prototypes_file(args["--prototypes"].asString());
+    string train_vector_file(args["--train"].asString());
+    string cluters_file (args["--clusters"].asString());
+    string dataset(args["--dataset"].asString());
+    string output_file(args["--out"].asString());
+    uint32_t num_graphs;
+
+    vector<uint32_t> train_gids;
+    unordered_map<uint32_t,vector<edge>> test_edges;
+    long par(args["--B"].asLong());
+
+
+
+    if (!(dataset.compare("ALL") == 0 ||
+          dataset.compare("AUTH")== 0 ||
+          dataset.compare("YDC") == 0 ||
+            dataset.compare("YDG") == 0 ||
+          dataset.compare("GFC") == 0)) {
+        cout << "Invalid dataset: " << dataset << ". ";
+        exit(-1);
+    }
+    unordered_set<uint32_t> scenarios;
+    if (dataset.compare("GFC") == 0) {
+        scenarios.insert(1);
+        scenarios.insert(2);
+        scenarios.insert(5);
+        scenarios.insert(3); // attack
+    } else if (dataset.compare("YDC") == 0) {
+        scenarios.insert(0);
+        scenarios.insert(4);
+        scenarios.insert(5);
+        scenarios.insert(3); // attack
+    }else if (dataset.compare("YDG")==0) {
+        scenarios.insert(0);
+        scenarios.insert(4);
+        scenarios.insert(1);
+        scenarios.insert(3); // attack
+        scenarios.insert(6);
+    }else if (dataset.compare("AUTH")==0){
+        scenarios.insert(0);
+        scenarios.insert(1);
+    } else { // ALL
+        scenarios.insert(0);
+        scenarios.insert(1);
+        scenarios.insert(2);
+        scenarios.insert(3); // attack
+        scenarios.insert(4);
+        scenarios.insert(5);
+        scenarios.insert(6);
+        scenarios.insert(7);
+    }
+
+    // the prototype branches
+    vector<Branch> prototype_branches;
+    prototype_branches = read_prototype_branches(prototypes_file);
+    M = prototype_branches.size();
+    cout << "M = " << M << endl;
+    // read train graph vectors branches
+    unordered_map<uint32_t, graph_vector> graphs_vectors; // key = gid , value = the graph vector
+    tie (graphs_vectors,train_gids) = read_train_graph_vectors(train_vector_file);
+    // read bootstrap clusters and thresholds
+    vector<vector<uint32_t>> clusters;
+    vector<double> cluster_thresholds;
+    double global_threshold;
+    tie(clusters, cluster_thresholds, global_threshold) = read_bootstrap_clusters(cluters_file);
+
+    // read the test edges
+
+    tie(num_graphs,test_edges) = read_edges(edge_file, train_gids,scenarios);
+        ///num_graphs = 600;
+
+
+
+
+    // initialization of graphs vectors
+    for (int i=0;i<num_graphs;i++){
+        if (find(train_gids.begin(),train_gids.end(),i)==train_gids.end()) { // is a test graph
+            for (int j=0;j<M;j++) graphs_vectors[i].push_back(0);
+        }
+    }
+    num_graphs = 700;
+    if (dataset.compare("AUTH")==0){
+        num_graphs = 143;
+    }
+    vector<int> cluster_map(num_graphs, UNSEEN); // gid -> cluster id
+
+    unordered_map<uint32_t, unordered_map<uint32_t, Branch>> graphs_to_branches ;
+
+    unordered_map<uint32_t ,double>  graphs_sizes; // key = gid ; value = the size of the graph
+    // initialization of graphs size
+    int gid = 0;
+    for (int i=0;i<num_graphs;i++){
+          graphs_sizes[gid]=0;
+          gid++;
+    }
+
+    // make groups of size par (parallel flowing graphs)
+    vector<uint32_t> test_gids;
+    for (uint32_t i = 0; i < num_graphs; i++) {
+        uint32_t scenario = i / 100;
+        if (scenarios.find(scenario) != scenarios.end()) {
+            if (find(train_gids.begin(), train_gids.end(), i) == train_gids.end()) {
+                test_gids.push_back(i);
+            }
+        }
+    }
+    shuffle(test_gids.begin(), test_gids.end(), prng);
+    vector<vector<uint32_t>> groups;
+    for (uint32_t i = 0; i < test_gids.size(); i += par) {
+        vector<uint32_t> group;
+
+        uint32_t end_limit;
+        if (test_gids.size() - i < par) {
+            end_limit = test_gids.size() - i;
+        } else {
+            end_limit = par;
+        }
+
+        for (uint32_t j = 0; j < end_limit; j++) {
+            group.push_back(test_gids[i + j]);
+        }
+        groups.push_back(group);
+    }
+
+    /// create train graph centers
+
+    vector<vector<double>> centroid_vectors;
+    // construct cluster centroid sketches/projections
+    cout << "Constructing bootstrap cluster centroids:" << endl;
+    centroid_vectors = construct_centroid_vectors(graphs_vectors,clusters);
+    cout << "end" << endl;
+    /// cluster map initialization
+    cout << clusters.size() << endl;
+    vector<uint32_t> cluster_sizes(clusters.size());
+    for (uint32_t i = 0; i < clusters.size(); i++) {
+        cluster_sizes[i] = clusters[i].size();
+        for (auto& gid : clusters[i]) {
+            cluster_map[gid] = i;
+        }
+    }
+    /// compute distances of training graphs to their cluster centroids
+    vector<double> anomaly_scores(num_graphs, UNSEEN);
+    for (auto& gid : train_gids) {
+        // anomaly score is a "distance"
+        anomaly_scores[gid] = euclidean_distance(graphs_vectors[gid],centroid_vectors[cluster_map[gid]]);
+    }
+    cout << "end anomaly initialization" << endl;
+    // add test edges to graphs
+    uint32_t  num_test_edges = 0;
+    for (auto &me : test_edges) num_test_edges+= me.second.size();
+
+    cout << "Streaming in " << num_test_edges << " test edges:" << endl;
+    uint32_t num_intervals = ceil(static_cast<double>(num_test_edges) /
+                                  CLUSTER_UPDATE_INTERVAL);
+    if (num_intervals == 0)
+        num_intervals = 1; // if interval length is too long
+    vector<vector<double>> anomaly_score_iterations(num_intervals,
+                                                    vector<double>(num_graphs));
+    vector<vector<int>> cluster_map_iterations(num_intervals,
+                                               vector<int>(num_graphs));
+    cout << "begin group loop" << endl;
+    uint32_t edge_num = 0;
+    vector<uint32_t> anoumalous_gids;
+    if (dataset.compare("AUTH")==0){
+        anoumalous_gids.push_back(139);
+        anoumalous_gids.push_back(140);
+        anoumalous_gids.push_back(141);
+        anoumalous_gids.push_back(142);
+    }
+    else{
+        for (uint32_t gid =300;gid<400;gid++) anoumalous_gids.push_back(gid);
+        for (uint32_t gid =600;gid<700;gid++) anoumalous_gids.push_back(gid);
+    }
+
+    start = chrono::steady_clock::now();
+    for (auto& group : groups) {
+        /// added
+
+        unordered_map<uint32_t, unordered_map<uint32_t, vector<double>>> graphs_to_branches_distances;
+
+        unordered_map<uint32_t,uint32_t> edge_offset;
+        for (auto &g : group)
+            edge_offset[g] = 0;
+
+        vector<uint32_t> group_copy(group);
+
+        while (group_copy.size() > 0) {
+
+            // used to pick a random graph
+            uniform_int_distribution<uint32_t> rand_group_idx(0, group_copy.size() - 1);
+
+            uint32_t gidx = rand_group_idx(prng);
+            uint32_t gid = group_copy[gidx];
+            uint32_t off = edge_offset[gid];
+            auto& e = test_edges[gid][off];
+            if (get<F_GID>(e)!=gid ) exit(70);
+            // process edge
+
+            auto old_gv = graphs_vectors[gid];
+
+            update_graph_vector2 (gid,e,
+                                        prototype_branches,
+                                       graphs_to_branches_distances,
+                                       graphs_to_branches,
+                                       graphs_vectors,
+                                       graphs_sizes);
+
+
+            // store current anomaly scores and cluster assignments
+            detect_anomalies(gid,graphs_vectors[gid],cluster_map,anomaly_scores,centroid_vectors,cluster_sizes,global_threshold,cluster_thresholds);
+            if ( edge_num % CLUSTER_UPDATE_INTERVAL ==0 || edge_num == num_test_edges-1 ){
+              //  cout << edge_num << endl;
+                anomaly_score_iterations[edge_num/CLUSTER_UPDATE_INTERVAL] = anomaly_scores;
+                cluster_map_iterations[edge_num/CLUSTER_UPDATE_INTERVAL] = cluster_map;
+            }
+            edge_num++;
+            edge_offset[gid]++; // increment next edge offset
+            if (edge_offset[gid] == test_edges[gid].size()) {
+                // out of edges for this gid
+                group_copy.erase(group_copy.begin() + gidx);
+                graphs_to_branches[gid].clear();
+                test_edges[gid].clear();
+            }
+        }
+        int  TP = 0;
+        int FP=0;
+        int  FN=0;
+        int  TN = 0;
+        for (uint32_t j = 0; j < num_graphs ; j++) {
+            int k = cluster_map_iterations[edge_num / CLUSTER_UPDATE_INTERVAL][j];
+            if (  find(anoumalous_gids.begin(),anoumalous_gids.end(),j)!=anoumalous_gids.end()) {
+              //  cout <<  cluster_map_iterations[edge_num / CLUSTER_UPDATE_INTERVAL][113] << endl;
+                if ( k== ANOMALY && k!=UNSEEN) TP++;
+                if ( k!=UNSEEN && k!=ANOMALY) FN++;
+            }
+            else {
+                if ( k!=UNSEEN && k!=ANOMALY) TN++;
+                if (k==ANOMALY) FP++;
+            }
+        }
+        cout << "scores : " << TP <<" "<< FP << " " << TN << " "<< FN <<" " << endl;
+        for (auto& item:graphs_to_branches_distances) {
+            for (auto& item1:item.second) vector<double>().swap(item1.second);
+        }
+        graphs_to_branches_distances.clear();
+    }
+    end = chrono::steady_clock::now();
+    diff = chrono::duration_cast<chrono::nanoseconds>(end - start);
+    cout << static_cast<double>(diff.count()) << "us" << endl;
+
+    ofstream out_file;
+    out_file.open(output_file);
+
+   // cout << "Iterations " << num_intervals << endl;
+    for (uint32_t i = 0; i < num_intervals; i++) {
+        auto& a = anomaly_score_iterations[i];
+        auto& c = cluster_map_iterations[i];
+        for (uint32_t j = 0; j < a.size(); j++) {
+            out_file << a[j] << ",";
+        }
+        out_file << endl;
+        for (uint32_t j = 0; j < a.size(); j++) {
+            out_file << c[j] << ",";
+        }
+        out_file << endl;
+    }
+    out_file << "execution time : " << static_cast<double>(diff.count()) << "ns" << endl;
+        cout << "execution time : " << static_cast<double>(diff.count()) << "ns" << endl;
+
+    return 0;
+}
diff --git a/param.h b/param.h
new file mode 100644
index 0000000..5298574
--- /dev/null
+++ b/param.h
@@ -0,0 +1,24 @@
+
+#ifndef STREAMSPOT_PARAM_H_
+#define STREAMSPOT_PARAM_H_
+
+#ifdef DEBUG
+#define NDEBUG            0
+#endif
+
+extern  int M;
+#define B                 100
+#define R                 20
+#define BUF_SIZE          50
+#define DELIMITER         '\t'
+#define L                 1000       // must be = B * R
+#define SEED              23
+#define CLUSTER_UPDATE_INTERVAL   100
+#define INF		   5000000
+#define UNSEEN          -2
+#define ANOMALY -1
+
+
+#define PI                3.1415926535897
+
+#endif
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..a779a8d
--- /dev/null
+++ b/util.h
@@ -0,0 +1,23 @@
+/* 
+ * Copyright 2016 Emaad Ahmed Manzoor
+ * License: Apache License, Version 2.0
+ * http://www3.cs.stonybrook.edu/~emanzoor/streamspot/
+ */
+
+#ifndef STREAMSPOT_UTIL_H_
+#define STREAMSPOT_UTIL_H_
+
+#include <string>
+#include <iostream>
+
+namespace std {
+
+inline void panic(string message) {
+  cout << message << endl;
+  exit(-1);
+}
+
+
+}
+
+#endif
-- 
GitLab