From 517d4512f81a37821c1117b582ef83b946949a8c Mon Sep 17 00:00:00 2001
From: Jan Travnicek <Jan.Travnicek@fit.cvut.cz>
Date: Thu, 20 Feb 2014 17:57:42 +0100
Subject: [PATCH] feature regexpTotalOrdering

---
 alib/src/alphabet/Symbol.cpp      |  4 ++
 alib/src/alphabet/Symbol.h        |  1 +
 alib/src/regexp/Alternation.cpp   | 75 +++++++++++++++++++++++++++++
 alib/src/regexp/Alternation.h     | 13 +++++
 alib/src/regexp/Concatenation.cpp | 75 +++++++++++++++++++++++++++++
 alib/src/regexp/Concatenation.h   | 13 +++++
 alib/src/regexp/Iteration.cpp     | 45 ++++++++++++++++++
 alib/src/regexp/Iteration.h       | 14 ++++++
 alib/src/regexp/RegExp.h          |  2 +-
 alib/src/regexp/RegExpElement.cpp | 79 +++++++++++++++++++++++++++++++
 alib/src/regexp/RegExpElement.h   | 36 ++++++++++++++
 alib/src/regexp/RegExpEmpty.cpp   | 37 +++++++++++++++
 alib/src/regexp/RegExpEmpty.h     | 11 +++++
 alib/src/regexp/RegExpEpsilon.cpp | 37 +++++++++++++++
 alib/src/regexp/RegExpEpsilon.h   | 11 +++++
 alib/src/regexp/RegExpSymbol.cpp  | 45 ++++++++++++++++++
 alib/src/regexp/RegExpSymbol.h    | 14 ++++++
 17 files changed, 511 insertions(+), 1 deletion(-)

diff --git a/alib/src/alphabet/Symbol.cpp b/alib/src/alphabet/Symbol.cpp
index c03d42d206..b16010e828 100644
--- a/alib/src/alphabet/Symbol.cpp
+++ b/alib/src/alphabet/Symbol.cpp
@@ -25,6 +25,10 @@ bool Symbol::operator ==(const Symbol& other) const {
 	return symbol == other.symbol;
 }
 
+bool Symbol::operator >(const Symbol& other) const {
+	return symbol > other.symbol;
+}
+
 bool Symbol::operator !=(const Symbol& other) const {
 	return symbol != other.symbol;
 }
diff --git a/alib/src/alphabet/Symbol.h b/alib/src/alphabet/Symbol.h
index a49c107b60..9b82e08a35 100644
--- a/alib/src/alphabet/Symbol.h
+++ b/alib/src/alphabet/Symbol.h
@@ -33,6 +33,7 @@ public:
 
 	bool operator <(const Symbol& other) const;
 	bool operator ==(const Symbol& other) const;
+	bool operator >(const Symbol& other) const;
 	bool operator !=(const Symbol& other) const;
 
 	friend std::ostream& operator<<(std::ostream&, const Symbol&);
diff --git a/alib/src/regexp/Alternation.cpp b/alib/src/regexp/Alternation.cpp
index e5fe8a1ee7..2b72bf0cd9 100644
--- a/alib/src/regexp/Alternation.cpp
+++ b/alib/src/regexp/Alternation.cpp
@@ -50,5 +50,80 @@ RegExpElement* Alternation::clone() const {
 	return new Alternation(*this);
 }
 
+bool Alternation::operator<(const RegExpElement& other) const {
+	return !(other > *this || other == *this);
+}
+
+bool Alternation::operator==(const RegExpElement& other) const {
+	return other == *this;
+}
+
+bool Alternation::operator>(const RegExpElement& other) const {
+	return !(other < *this || other == *this);
+}
+
+
+bool Alternation::operator<(const Concatenation&) const {
+	return true;
+}
+
+bool Alternation::operator<(const Alternation& other) const {
+	int thisSize = this->elements.size();
+	int otherSize = other.elements.size();
+	if(thisSize < otherSize) return true;
+	if(thisSize > otherSize) return false;
+	
+	auto thisIter = this->elements.begin();
+	auto otherIter = other.elements.begin();
+	for(; thisIter != this->elements.end(); thisIter++) {
+	  if(**thisIter < **otherIter) return true;
+	}
+	
+	return false;
+}
+
+bool Alternation::operator==(const Alternation& other) const {
+	if(this->elements.size() != other.elements.size()) return false;
+	
+	auto thisIter = this->elements.begin();
+	auto otherIter = other.elements.begin();
+	for(; thisIter != this->elements.end(); thisIter++) {
+	  if(**thisIter != **otherIter) return false;
+	}
+	
+	return true;
+}
+
+bool Alternation::operator>(const Alternation& other) const {
+	int thisSize = this->elements.size();
+	int otherSize = other.elements.size();
+	if(thisSize < otherSize) return false;
+	if(thisSize > otherSize) return true;
+	
+	auto thisIter = this->elements.begin();
+	auto otherIter = other.elements.begin();
+	for(; thisIter != this->elements.end(); thisIter++) {
+	  if(**thisIter > **otherIter) return true;
+	}
+	
+	return false;
+}
+
+bool Alternation::operator>(const Iteration&) const {
+	return true;
+}
+
+bool Alternation::operator>(const RegExpSymbol&) const {
+	return true;
+}
+
+bool Alternation::operator>(const RegExpEpsilon&) const {
+	return true;
+}
+
+bool Alternation::operator>(const RegExpEmpty&) const {
+	return true;
+}
+
 } /* namespace regexp */
 
diff --git a/alib/src/regexp/Alternation.h b/alib/src/regexp/Alternation.h
index 1435dcf848..ab414c41dd 100644
--- a/alib/src/regexp/Alternation.h
+++ b/alib/src/regexp/Alternation.h
@@ -37,6 +37,19 @@ public:
 	 * @copydoc RegExpElement::clone() const
 	 */
 	RegExpElement* clone() const;
+	
+	virtual bool operator<(const RegExpElement&) const;
+	virtual bool operator==(const RegExpElement&) const;
+	virtual bool operator>(const RegExpElement&) const;
+	
+	virtual bool operator<(const Concatenation&) const;
+	virtual bool operator<(const Alternation&) const;
+	virtual bool operator==(const Alternation&) const;
+	virtual bool operator>(const Alternation&) const;
+	virtual bool operator>(const Iteration&) const;
+	virtual bool operator>(const RegExpSymbol&) const;
+	virtual bool operator>(const RegExpEpsilon&) const;
+	virtual bool operator>(const RegExpEmpty&) const;
 };
 
 } /* namespace regexp */
diff --git a/alib/src/regexp/Concatenation.cpp b/alib/src/regexp/Concatenation.cpp
index bf4637c178..c09e901827 100644
--- a/alib/src/regexp/Concatenation.cpp
+++ b/alib/src/regexp/Concatenation.cpp
@@ -50,4 +50,79 @@ RegExpElement* Concatenation::clone() const {
 	return new Concatenation(*this);
 }
 
+bool Concatenation::operator<(const RegExpElement& other) const {
+	return !(other > *this || other == *this);
+}
+
+bool Concatenation::operator==(const RegExpElement& other) const {
+	return other == *this;
+}
+
+bool Concatenation::operator>(const RegExpElement& other) const {
+	return !(other < *this || other == *this);
+}
+
+
+bool Concatenation::operator<(const Concatenation& other) const {
+	int thisSize = this->elements.size();
+	int otherSize = other.elements.size();
+	if(thisSize < otherSize) return true;
+	if(thisSize > otherSize) return false;
+	
+	auto thisIter = this->elements.begin();
+	auto otherIter = other.elements.begin();
+	for(; thisIter != this->elements.end(); thisIter++) {
+	  if(**thisIter < **otherIter) return true;
+	}
+	
+	return false;
+}
+
+bool Concatenation::operator==(const Concatenation& other) const {
+	if(this->elements.size() != other.elements.size()) return false;
+	
+	auto thisIter = this->elements.begin();
+	auto otherIter = other.elements.begin();
+	for(; thisIter != this->elements.end(); thisIter++) {
+	  if(**thisIter != **otherIter) return false;
+	}
+	
+	return true;
+}
+
+bool Concatenation::operator>(const Concatenation& other) const {
+	int thisSize = this->elements.size();
+	int otherSize = other.elements.size();
+	if(thisSize < otherSize) return false;
+	if(thisSize > otherSize) return true;
+	
+	auto thisIter = this->elements.begin();
+	auto otherIter = other.elements.begin();
+	for(; thisIter != this->elements.end(); thisIter++) {
+	  if(**thisIter > **otherIter) return true;
+	}
+	
+	return false;
+}
+
+bool Concatenation::operator>(const Alternation&) const {
+	return true;
+}
+
+bool Concatenation::operator>(const Iteration&) const {
+	return true;
+}
+
+bool Concatenation::operator>(const RegExpSymbol&) const {
+	return true;
+}
+
+bool Concatenation::operator>(const RegExpEpsilon&) const {
+	return true;
+}
+
+bool Concatenation::operator>(const RegExpEmpty&) const {
+	return true;
+}
+
 } /* namespace regexp */
diff --git a/alib/src/regexp/Concatenation.h b/alib/src/regexp/Concatenation.h
index 7acb02c2ee..38905280fd 100644
--- a/alib/src/regexp/Concatenation.h
+++ b/alib/src/regexp/Concatenation.h
@@ -38,6 +38,19 @@ public:
 	 */
 	RegExpElement* clone() const;
 
+	virtual bool operator<(const RegExpElement&) const;
+	virtual bool operator==(const RegExpElement&) const;
+	virtual bool operator>(const RegExpElement&) const;
+	
+	virtual bool operator<(const Concatenation&) const;
+	virtual bool operator==(const Concatenation&) const;
+	virtual bool operator>(const Concatenation&) const;
+	virtual bool operator>(const Alternation&) const;
+	virtual bool operator>(const Iteration&) const;
+	virtual bool operator>(const RegExpSymbol&) const;
+	virtual bool operator>(const RegExpEpsilon&) const;
+	virtual bool operator>(const RegExpEmpty&) const;
+
 };
 
 } /* namespace regexp */
diff --git a/alib/src/regexp/Iteration.cpp b/alib/src/regexp/Iteration.cpp
index f787e11bbd..8092c0a208 100644
--- a/alib/src/regexp/Iteration.cpp
+++ b/alib/src/regexp/Iteration.cpp
@@ -61,5 +61,50 @@ RegExpElement* Iteration::clone() const {
 	return new Iteration(*this);
 }
 
+
+bool Iteration::operator<(const RegExpElement& other) const {
+	return !(other > *this || other == *this);
+}
+
+bool Iteration::operator==(const RegExpElement& other) const {
+	return other == *this;
+}
+
+bool Iteration::operator>(const RegExpElement& other) const {
+	return !(other < *this || other == *this);
+}
+	
+bool Iteration::operator<(const Concatenation&) const {
+	return true;
+}
+
+bool Iteration::operator<(const Alternation&) const {
+	return true;
+}
+
+bool Iteration::operator<(const Iteration& other) const {
+	return *(this->element) < *(other.element);
+}
+
+bool Iteration::operator==(const Iteration& other) const {
+	return *(this->element) == *(other.element);
+}
+
+bool Iteration::operator>(const Iteration& other) const {
+	return *(this->element) > *(other.element);
+}
+
+bool Iteration::operator>(const RegExpSymbol&) const {
+	return true;
+}
+
+bool Iteration::operator>(const RegExpEpsilon&) const {
+	return true;
+}
+
+bool Iteration::operator>(const RegExpEmpty&) const {
+	return true;
+}
+
 } /* namespace regexp */
 
diff --git a/alib/src/regexp/Iteration.h b/alib/src/regexp/Iteration.h
index 1f1547b0a7..402cc32dbb 100644
--- a/alib/src/regexp/Iteration.h
+++ b/alib/src/regexp/Iteration.h
@@ -43,6 +43,20 @@ public:
 	 * @copydoc RegExpElement::clone() const
 	 */
 	RegExpElement* clone() const;
+	
+	virtual bool operator<(const RegExpElement&) const;
+	virtual bool operator==(const RegExpElement&) const;
+	virtual bool operator>(const RegExpElement&) const;
+	
+	virtual bool operator<(const Concatenation&) const;
+	virtual bool operator<(const Alternation&) const;
+	virtual bool operator<(const Iteration&) const;
+	virtual bool operator==(const Iteration&) const;
+	virtual bool operator>(const Iteration&) const;
+	virtual bool operator>(const RegExpSymbol&) const;
+	virtual bool operator>(const RegExpEpsilon&) const;
+	virtual bool operator>(const RegExpEmpty&) const;
+
 };
 
 } /* namespace regexp */
diff --git a/alib/src/regexp/RegExp.h b/alib/src/regexp/RegExp.h
index 73507c1fa4..ac17e4cc53 100644
--- a/alib/src/regexp/RegExp.h
+++ b/alib/src/regexp/RegExp.h
@@ -1,4 +1,4 @@
-/*
+    /*
  * RegExp.h
  *
  *  Created on: Nov 23, 2013
diff --git a/alib/src/regexp/RegExpElement.cpp b/alib/src/regexp/RegExpElement.cpp
index d7231b3c07..f6198be509 100644
--- a/alib/src/regexp/RegExpElement.cpp
+++ b/alib/src/regexp/RegExpElement.cpp
@@ -12,5 +12,84 @@ namespace regexp {
 RegExpElement::~RegExpElement() {
 }
 
+bool RegExpElement::operator!=(const RegExpElement& other) const {
+	return !(*this == other);
+}
+
+
+bool RegExpElement::operator<(const Concatenation& other) const {
+	return false;
+}
+
+bool RegExpElement::operator<(const Alternation& other) const {
+	return false;
+}
+
+bool RegExpElement::operator<(const Iteration& other) const {
+	return false;
+}
+
+bool RegExpElement::operator<(const RegExpSymbol& other) const {
+	return false;
+}
+
+bool RegExpElement::operator<(const RegExpEpsilon& other) const {
+	return false;
+}
+
+bool RegExpElement::operator<(const RegExpEmpty& other) const {
+	return false;
+}
+
+
+bool RegExpElement::operator==(const Concatenation& other) const {
+	return false;
+}
+
+bool RegExpElement::operator==(const Alternation& other) const {
+	return false;
+}
+
+bool RegExpElement::operator==(const Iteration& other) const {
+	return false;
+}
+
+bool RegExpElement::operator==(const RegExpSymbol& other) const {
+	return false;
+}
+
+bool RegExpElement::operator==(const RegExpEpsilon& other) const {
+	return false;
+}
+
+
+bool RegExpElement::operator==(const RegExpEmpty& other) const {
+	return false;
+}
+
+bool RegExpElement::operator>(const Concatenation& other) const {
+	return false;
+}
+
+bool RegExpElement::operator>(const Alternation& other) const {
+	return false;
+}
+
+bool RegExpElement::operator>(const Iteration& other) const {
+	return false;
+}
+
+bool RegExpElement::operator>(const RegExpSymbol& other) const {
+	return false;
+}
+
+bool RegExpElement::operator>(const RegExpEpsilon& other) const {
+	return false;
+}
+
+bool RegExpElement::operator>(const RegExpEmpty& other) const {
+	return false;
+}
+
 } /* namespace regexp */
 
diff --git a/alib/src/regexp/RegExpElement.h b/alib/src/regexp/RegExpElement.h
index 2cc340c6b3..6365c1da0a 100644
--- a/alib/src/regexp/RegExpElement.h
+++ b/alib/src/regexp/RegExpElement.h
@@ -12,6 +12,14 @@ namespace regexp {
 
 using namespace std;
 
+
+class Alternation;
+class Concatenation;
+class Iteration;
+class RegExpSymbol;
+class RegExpEmpty;
+class RegExpEpsilon;
+
 /**
  * Abstract class representing element in the regular expression. Can be operator or symbol.
  */
@@ -24,6 +32,34 @@ public:
 	 * @return copy of the element
 	 */
 	virtual RegExpElement* clone() const = 0;
+	
+	virtual bool operator<(const RegExpElement&) const = 0;
+	virtual bool operator==(const RegExpElement&) const = 0;
+	virtual bool operator>(const RegExpElement&) const = 0;
+	
+	virtual bool operator!=(const RegExpElement&) const;
+	
+	virtual bool operator<(const Concatenation&) const;
+	virtual bool operator<(const Alternation&) const;
+	virtual bool operator<(const Iteration&) const;
+	virtual bool operator<(const RegExpSymbol&) const;
+	virtual bool operator<(const RegExpEpsilon&) const;
+	virtual bool operator<(const RegExpEmpty&) const;
+	
+	virtual bool operator==(const Concatenation&) const;
+	virtual bool operator==(const Alternation&) const;
+	virtual bool operator==(const Iteration&) const;
+	virtual bool operator==(const RegExpSymbol&) const;
+	virtual bool operator==(const RegExpEpsilon&) const;
+	virtual bool operator==(const RegExpEmpty&) const;
+	
+	virtual bool operator>(const Concatenation&) const;
+	virtual bool operator>(const Alternation&) const;
+	virtual bool operator>(const Iteration&) const;
+	virtual bool operator>(const RegExpSymbol&) const;
+	virtual bool operator>(const RegExpEpsilon&) const;
+	virtual bool operator>(const RegExpEmpty&) const;
+	
 };
 
 } /* namespace regexp */
diff --git a/alib/src/regexp/RegExpEmpty.cpp b/alib/src/regexp/RegExpEmpty.cpp
index 3c5b5711ac..16c579eaef 100644
--- a/alib/src/regexp/RegExpEmpty.cpp
+++ b/alib/src/regexp/RegExpEmpty.cpp
@@ -16,5 +16,42 @@ RegExpElement* RegExpEmpty::clone() const {
 	return new RegExpEmpty();
 }
 
+bool RegExpEmpty::operator<(const RegExpElement& other) const {
+	return !(other > *this || other == *this);
+}
+
+bool RegExpEmpty::operator==(const RegExpElement& other) const {
+	return other == *this;
+}
+
+bool RegExpEmpty::operator>(const RegExpElement& other) const {
+	return !(other < *this || other == *this);
+}
+
+
+bool RegExpEmpty::operator<(const Concatenation&) const {
+	return true;
+}
+
+bool RegExpEmpty::operator<(const Alternation&) const {
+	return true;
+}
+
+bool RegExpEmpty::operator<(const Iteration&) const {
+	return true;
+}
+
+bool RegExpEmpty::operator<(const RegExpSymbol&) const {
+	return true;
+}
+
+bool RegExpEmpty::operator<(const RegExpEpsilon&) const {
+	return true;
+}
+
+bool RegExpEmpty::operator==(const RegExpEmpty&) const {
+	return true;
+}
+
 } /* namespace regexp */
 
diff --git a/alib/src/regexp/RegExpEmpty.h b/alib/src/regexp/RegExpEmpty.h
index 937d04e679..2c5ffb0827 100644
--- a/alib/src/regexp/RegExpEmpty.h
+++ b/alib/src/regexp/RegExpEmpty.h
@@ -25,6 +25,17 @@ public:
 	 * @copydoc RegExpElement::clone() const
 	 */
 	RegExpElement* clone() const;
+	
+	virtual bool operator<(const RegExpElement&) const;
+	virtual bool operator==(const RegExpElement&) const;
+	virtual bool operator>(const RegExpElement&) const;
+
+	virtual bool operator<(const Concatenation&) const;
+	virtual bool operator<(const Alternation&) const;
+	virtual bool operator<(const Iteration&) const;
+	virtual bool operator<(const RegExpSymbol&) const;
+	virtual bool operator<(const RegExpEpsilon&) const;
+	virtual bool operator==(const RegExpEmpty&) const;
 };
 
 } /* namespace regexp */
diff --git a/alib/src/regexp/RegExpEpsilon.cpp b/alib/src/regexp/RegExpEpsilon.cpp
index fff799e81c..11645bb8bd 100644
--- a/alib/src/regexp/RegExpEpsilon.cpp
+++ b/alib/src/regexp/RegExpEpsilon.cpp
@@ -16,5 +16,42 @@ RegExpElement* RegExpEpsilon::clone() const {
 	return new RegExpEpsilon();
 }
 
+bool RegExpEpsilon::operator<(const RegExpElement& other) const {
+	return !(other > *this || other == *this);
+}
+
+bool RegExpEpsilon::operator==(const RegExpElement& other) const {
+	return other == *this;
+}
+
+bool RegExpEpsilon::operator>(const RegExpElement& other) const {
+	return !(other < *this || other == *this);
+}
+
+
+bool RegExpEpsilon::operator<(const Alternation&) const {
+	  return true;
+}
+
+bool RegExpEpsilon::operator<(const Concatenation&) const {
+	  return true;
+}
+
+bool RegExpEpsilon::operator<(const Iteration&) const {
+	  return true;
+}
+
+bool RegExpEpsilon::operator<(const RegExpSymbol&) const {
+	  return true;
+}
+
+bool RegExpEpsilon::operator==(const RegExpEpsilon&) const {
+	  return true;
+}
+
+bool RegExpEpsilon::operator>(const RegExpEmpty&) const {
+	  return true;
+}
+  
 } /* namespace regexp */
 
diff --git a/alib/src/regexp/RegExpEpsilon.h b/alib/src/regexp/RegExpEpsilon.h
index 2d48546fcc..4c0f028251 100644
--- a/alib/src/regexp/RegExpEpsilon.h
+++ b/alib/src/regexp/RegExpEpsilon.h
@@ -27,6 +27,17 @@ public:
 	 * @copydoc RegExpElement::clone() const
 	 */
 	RegExpElement* clone() const;
+	
+	virtual bool operator<(const RegExpElement&) const;
+	virtual bool operator==(const RegExpElement&) const;
+	virtual bool operator>(const RegExpElement&) const;
+
+	virtual bool operator<(const Alternation&) const;
+	virtual bool operator<(const Concatenation&) const;
+	virtual bool operator<(const Iteration&) const;
+	virtual bool operator<(const RegExpSymbol&) const;
+	virtual bool operator==(const RegExpEpsilon&) const;
+	virtual bool operator>(const RegExpEmpty&) const;
 };
 
 } /* namespace regexp */
diff --git a/alib/src/regexp/RegExpSymbol.cpp b/alib/src/regexp/RegExpSymbol.cpp
index 95f5642371..1e524be73f 100644
--- a/alib/src/regexp/RegExpSymbol.cpp
+++ b/alib/src/regexp/RegExpSymbol.cpp
@@ -21,5 +21,50 @@ RegExpElement* RegExpSymbol::clone() const {
 	return new RegExpSymbol(this->symbol);
 }
 
+bool RegExpSymbol::operator<(const RegExpElement& other) const {
+	return !(other > *this || other == *this);
+}
+
+bool RegExpSymbol::operator==(const RegExpElement& other) const {
+	return other == *this;
+}
+
+bool RegExpSymbol::operator>(const RegExpElement& other) const {
+	return !(other < *this || other == *this);
+}
+
+
+bool RegExpSymbol::operator<(const Concatenation&) const {
+	return true;
+}
+
+bool RegExpSymbol::operator<(const Alternation&) const {
+	return true;
+}
+
+bool RegExpSymbol::operator<(const Iteration&) const {
+	return true;
+}
+
+bool RegExpSymbol::operator<(const RegExpSymbol& other) const {
+	return this->symbol < other.symbol;
+}
+
+bool RegExpSymbol::operator==(const RegExpSymbol& other) const {
+	return this->symbol == other.symbol;
+}
+
+bool RegExpSymbol::operator>(const RegExpSymbol& other) const {
+	return this->symbol > other.symbol;
+}
+
+bool RegExpSymbol::operator>(const RegExpEpsilon&) const {
+	return true;
+}
+
+bool RegExpSymbol::operator>(const RegExpEmpty&) const {
+	return true;
+}
+
 } /* namespace regexp */
 
diff --git a/alib/src/regexp/RegExpSymbol.h b/alib/src/regexp/RegExpSymbol.h
index bb6db856d9..ddd17e9d29 100644
--- a/alib/src/regexp/RegExpSymbol.h
+++ b/alib/src/regexp/RegExpSymbol.h
@@ -29,6 +29,20 @@ public:
 	 * @copydoc RegExpElement::clone() const
 	 */
 	RegExpElement* clone() const;
+	
+	virtual bool operator<(const RegExpElement&) const;
+	virtual bool operator==(const RegExpElement&) const;
+	virtual bool operator>(const RegExpElement&) const;
+	
+	virtual bool operator<(const Concatenation&) const;
+	virtual bool operator<(const Alternation&) const;
+	virtual bool operator<(const Iteration&) const;
+	virtual bool operator<(const RegExpSymbol&) const;
+	virtual bool operator==(const RegExpSymbol&) const;
+	virtual bool operator>(const RegExpSymbol&) const;
+	virtual bool operator>(const RegExpEpsilon&) const;
+	virtual bool operator>(const RegExpEmpty&) const;
+
 };
 
 } /* namespace regexp */
-- 
GitLab