#pragma once
#include <map>
#include <cstdint>

template<typename typ>
class TransitiveClosure{
	/*
	 * This class is based on the exposition [Schmidt,Schwartzbach] (DAIMI FN - 56, Januar 1996).
	 *
	 * To make assignment of class names work correctly, it is important that all
	 * elements are "added to the base set" - either implicitly via makeEquivalent
	 * or explicitly via addToBaseSet.
	 */
	std::map<typ,std::pair<typ,int64_t>> parentList; //Each vertex has a parent and a weight, the number of descendants including itself
	std::map<typ,int64_t> classNamesOfRoots;//only valid to look up with root indices
public:
	void addToBaseSet(typ const &v)
	{
		if(parentList.count(v)==0)
			parentList[v]=make_pair(v,1);
	}
private:
	bool isRoot(typ i)
	{
		return i==parentList[i].first;
	}
	typ const &getRootRef(typ const &i) //Danger: The returned reference is either to an internal value of the map structure. But it is important to pass references here because of the recursion.
	{
		if(isRoot(i))return parentList[i].first;
		return parentList[i].first=getRootRef(parentList[i].first);
	}
public:
	bool areEquivalent(typ const &a, typ const &b)
	{
		return getRootRef(a)==getRootRef(b);
	}
	void makeEquivalent(typ i, typ j)//join
	{
		addToBaseSet(i);
		addToBaseSet(j);
		if(!areEquivalent(i,j))
		{
			typ iRoot=getRootRef(i);
			typ jRoot=getRootRef(j);
			if(parentList[jRoot].second<parentList[iRoot].second)
			{
				swap(iRoot,jRoot); //iRoot now has smallest weight
			}
			parentList[iRoot].first=jRoot;
			parentList[jRoot].second+=parentList[iRoot].second;
		}
	}
	typ getRepresentative(typ i)
	{
		return getRootRef(i);
	}
	int64_t getClassName(typ i)
	{
		assert(isRoot(i));
		return classNamesOfRoots[i];
	}
	int64_t buildClassNames()
	{
		int64_t numberOfClasses=0;

		for(auto &i:parentList)
			if(isRoot(i.first))
				classNamesOfRoots[i.first]=numberOfClasses++;

		return numberOfClasses;
	}
};
