diff --git a/btypes_primitives/src/main/python_magicstack_immutable/btypes/BBoolean.py b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BBoolean.py
new file mode 100644
index 0000000000000000000000000000000000000000..cb95d7e65cbaf1b495ffcc5f8fa053b9b7684ff8
--- /dev/null
+++ b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BBoolean.py
@@ -0,0 +1,60 @@
+class BBoolean:
+    def __init__(self, value):
+        if type(value) is bool:
+            self.__value = value
+        elif type(value) is str:
+            self.__value = str(value).lower() == "true"
+
+    def __and__(self, other: 'BBoolean') -> 'BBoolean':
+        return BBoolean(self.__value and other.__value)
+
+    def __or__(self, other: 'BBoolean') -> 'BBoolean':
+        return BBoolean(self.__value or other.__value)
+
+    def __rand__(self, other: 'BBoolean') -> 'BBoolean':
+        return BBoolean(self.__value and other.__value)
+
+    def __str__(self) -> 'str':
+        return str(self.__value).lower()
+
+    def __ror__(self, other: 'BBoolean') -> 'BBoolean':
+        return BBoolean(self.__value or other.__value)
+
+    def __rxor__(self, other: 'BBoolean') -> 'BBoolean':
+        return BBoolean(self.__value ^ other.__value)
+
+    def __xor__(self, other: 'BBoolean') -> 'BBoolean':
+        return BBoolean(self.__value ^ other.__value)
+
+    def __eq__(self, other: 'BBoolean') -> 'BBoolean':
+        return BBoolean(self.__value == other.__value)
+
+    def __ne__(self, other: 'BBoolean') -> 'BBoolean':
+        return BBoolean(self.__value != other.__value)
+
+    def _and(self, other:  'BBoolean') -> 'BBoolean':
+        return self.__and__(other)
+
+    def _or(self, other:  'BBoolean') -> 'BBoolean':
+        return self.__or__(other)
+
+    def _not(self) -> 'BBoolean':
+        return BBoolean(not self.__value)
+
+    def implies(self, other:  'BBoolean') -> 'BBoolean':
+        return self._not()._or(other)
+
+    def equivalent(self, other:  'BBoolean') -> 'BBoolean':
+        return self.implies(other)._and(other.implies(self))
+
+    def equal(self, other:  'BBoolean') -> 'BBoolean':
+        return self.__eq__(other)
+
+    def unequal(self, other:  'BBoolean') -> 'BBoolean':
+        return self.__ne__(other)
+
+    def booleanValue(self) -> 'bool':
+        return self.__value
+
+    def __hash__(self):
+        return hash(self.__value)
\ No newline at end of file
diff --git a/btypes_primitives/src/main/python_magicstack_immutable/btypes/BCouple.py b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BCouple.py
new file mode 100644
index 0000000000000000000000000000000000000000..76a5de9a4cc474a694ffebcf401c6f0016e47d95
--- /dev/null
+++ b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BCouple.py
@@ -0,0 +1,20 @@
+class BCouple:
+
+    def __init__(self, first, second):
+        self.__first = first
+        self.__second = second
+
+    def get_first(self):
+        return self.__first
+
+    def get_second(self):
+        return self.__second
+
+    def __eq__(self, other):
+        return self.__first == other.__first and self.__second == other.__second
+
+    def equal(self, other):
+        return self == other
+
+    def unequal(self, other):
+        return self != other
\ No newline at end of file
diff --git a/btypes_primitives/src/main/python_magicstack_immutable/btypes/BInteger.py b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BInteger.py
new file mode 100644
index 0000000000000000000000000000000000000000..ab76c84f3bce631c1adde5d08d605ebe901f551b
--- /dev/null
+++ b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BInteger.py
@@ -0,0 +1,124 @@
+from btypes.BBoolean import *
+
+class BInteger:
+
+    def __init__(self, value):
+        self.__value = value
+
+    def __add__(self, other: 'BInteger') -> 'BInteger':
+        if type(other) == str:
+            return str(self) + other
+        return BInteger(self.__value + other.__value)
+
+    def __sub__(self, other: 'BInteger') -> 'BInteger':
+        return BInteger(self.__value - other.__value)
+
+    def __mul__(self, other: 'BInteger') -> 'BInteger':
+        return BInteger(self.__value * other.__value)
+
+    def __mod__(self, other: 'BInteger') -> 'BInteger':
+        return BInteger(self.__value % other.__value)
+
+    def __div__(self, other: 'BInteger') -> 'BInteger':
+        return BInteger(self.__value // other.__value)
+
+    def __neg__(self) -> 'BInteger':
+        return BInteger(-self.__value)
+
+    def __lt__(self, other: 'BInteger') -> 'bool':
+        return self.__value < other.__value
+
+    def __le__(self, other: 'BInteger') -> 'bool':
+        return self.__value <= other.__value
+
+    def __eq__(self, other: 'BInteger') -> 'bool':
+        if not isinstance(other, BInteger):
+            return False
+        return self.__value == other.__value
+
+    def __ne__(self, other: 'BInteger') -> 'bool':
+        return self.__value != other.__value
+
+    def __gt__(self, other: 'BInteger') -> 'bool':
+        return self.__value > other.__value
+
+    def __ge__(self, other: 'BInteger') -> 'bool':
+        return self.__value >= other.__value
+
+    def __pow__(self, other: 'BInteger') -> 'BInteger':
+        return self.__value ** other.__value
+
+    def __str__(self) -> 'str':
+        return str(self.__value)
+
+    def plus(self, other: 'BInteger') -> 'BInteger':
+        return self.__add__(other)
+
+    def minus(self, other: 'BInteger') -> 'BInteger':
+        return self.__sub__(other)
+
+    def multiply(self, other: 'BInteger') -> 'BInteger':
+        return self.__mul__(other)
+
+    def modulo(self, other: 'BInteger') -> 'BInteger':
+        return self.__mod__(other)
+
+    def divide(self, other: 'BInteger') -> 'BInteger':
+        return self.__div__(other)
+
+    def negative(self) -> 'BInteger':
+        return self.__neg__()
+
+    def less(self, other: 'BInteger') -> 'BBoolean':
+        return BBoolean(self.__value < other.__value)
+
+    def lessEqual(self, other: 'BInteger') -> 'BBoolean':
+        return BBoolean(self.__value <= other.__value)
+
+    def equal(self, other: 'BInteger') -> 'BBoolean':
+        return BBoolean(self.__value == other.__value)
+
+    def unequal(self, other: 'BInteger') -> 'BBoolean':
+        return BBoolean(self.__value != other.__value)
+
+    def greater(self, other: 'BInteger') -> 'BBoolean':
+        return BBoolean(self.__value > other.__value)
+
+    def greaterEqual(self, other: 'BInteger') -> 'BBoolean':
+        return BBoolean(self.__value >= other.__value)
+
+    def succ(self) -> 'BInteger':
+        return BInteger(self.__value + 1)
+
+    def pred(self) -> 'BInteger':
+        return BInteger(self.__value - 1)
+
+    def power(self, other: 'BInteger') -> 'BInteger':
+        return self.__pow__(other)
+
+    def positive(self):
+        return self
+
+    def intValue(self):
+        return self.__value
+
+    def isInteger(self) -> 'BBoolean':
+        return BBoolean(true)
+
+    def isNotInteger(self) -> 'BBoolean':
+        return BBoolean(false)
+
+    def isNatural(self) -> 'BBoolean':
+        return self.greaterEqual(BInteger(0))
+
+    def isNotNatural(self) -> 'BBoolean':
+        return self.isNatural()._not()
+
+    def isNatural1(self) -> 'BBoolean':
+        return self.greater(BInteger(0))
+
+    def isNotNatural1(self) -> 'BBoolean':
+        return self.isNatural1()._not()
+
+    def __hash__(self):
+        return hash(31 + self.__value)
\ No newline at end of file
diff --git a/btypes_primitives/src/main/python_magicstack_immutable/btypes/BObject.py b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BObject.py
new file mode 100644
index 0000000000000000000000000000000000000000..327a2e93bbe98f46d78b5d0f1fe37e651003ff5e
--- /dev/null
+++ b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BObject.py
@@ -0,0 +1,2 @@
+class BObject:
+    pass
\ No newline at end of file
diff --git a/btypes_primitives/src/main/python_magicstack_immutable/btypes/BRelation.py b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BRelation.py
new file mode 100644
index 0000000000000000000000000000000000000000..d05ac35442e91bd0a328bb1c2cb32eae88460467
--- /dev/null
+++ b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BRelation.py
@@ -0,0 +1,767 @@
+from btypes.BInteger import *
+from btypes.BBoolean import *
+from btypes.BInteger import BInteger
+from btypes.BTuple import *
+from btypes.BSet import *
+
+import immutables
+from copy import deepcopy
+from functools import reduce
+import random
+
+
+class BRelation:
+
+	def __init__(self, *args):
+		if len(args) == 0:
+			self.map = immutables.Map()
+		elif len(args) == 1 and type(args[0]) == immutables.Map:
+			self.map = args[0]
+		else:
+			self.map = immutables.Map()
+			for e in args:
+				key = e.projection1()
+				value = e.projection2()
+				_set = self.map.get(key)
+				if not _set:
+					_set = frozenset()
+				_set = _set.union({value})
+				self.map=self.map.set(key, _set)
+
+	@staticmethod
+	def fromSet(_set: 'BSet') -> 'BRelation':
+		result_map = immutables.Map()
+		for e in _set:
+			key = e.projection1()
+			value = e.projection2()
+			_range = result_map.get(key, None)
+			if _range is None:
+				_range = {value}
+				result_map = result_map.set(key, _range)
+			else:
+				_range = _range.union(frozenset([value]))
+				result_map = result_map.set(key, _range)
+
+		return BRelation(result_map)
+
+	def __eq__(self, other: 'BRelation') -> 'bool':
+		if self is other:
+			return True
+		if other is None or type(self) != type(other):
+			return False
+		if not self.map == other.map:
+			return False
+		return True
+
+	def equals(self, other):
+		return self.__eq__(other)
+
+	def __hash__(self):
+		return hash(frozenset(self.map))
+
+	def intersect(self, relation: 'BRelation') -> 'BRelation':
+		other_map = relation.map
+		other_domain = set(relation.map.keys())
+		this_domain = set(self.map.keys())
+		intersection_domain = this_domain.intersection(other_domain)
+		difference_domain = this_domain.difference(other_domain)
+
+		result_map = self.map
+		for obj in intersection_domain:
+			domain_element = obj
+			this_range_set = self.map[domain_element]
+			other_range_set = other_map[domain_element]
+			result_map = result_map.set(domain_element, this_range_set.union(other_range_set))
+
+		for obj in difference_domain:
+			domain_element = obj
+			result_map = result_map.delete(domain_element)
+
+		return BRelation(result_map)
+
+	def difference(self, relation: 'BRelation') -> 'BRelation':
+		other_map = relation.map
+		other_domain = set(other_map.keys())
+		this_domain = set(self.map.keys())
+		difference_domain = this_domain.difference(other_domain)
+		rest_domain = this_domain.difference(difference_domain)
+
+		result_map = self.map
+		for obj in difference_domain:
+			domain_element = obj
+			this_range_set = self.map[domain_element]
+			other_range_set = other_map[domain_element]
+			result_map = result_map.set(domain_element, this_range_set.difference(other_range_set))
+
+		for obj in rest_domain:
+			domain_element = obj
+			result_map = result_map.delete(domain_element)
+			
+		return BRelation(result_map)
+
+	def union(self, relation: 'BRelation') -> 'BRelation':
+		other_map = relation.map
+		other_domain = other_map.keys()
+
+		result_map = deepcopy(self.map)
+		for obj in other_domain:
+			domain_element = obj
+			this_range_set = self.map.get(domain_element)
+			other_range_set = relation.map.get(domain_element, None)
+			if this_range_set is None:
+				this_range_set = set()
+			result_map = result_map.set(domain_element, this_range_set.union(other_range_set))
+			
+		return BRelation(result_map)
+
+	def size(self) -> 'int':
+		size = 0
+		this_domain = self.map.keys()
+
+		for obj in this_domain:
+			domain_element = obj
+			this_range_set = self.map[domain_element]
+			size += len(this_range_set)
+			
+		return size
+
+	def card(self) -> 'BInteger':
+		return BInteger(self.size())
+
+	def _size(self) -> 'BInteger':
+		return BInteger(self.size())
+
+	def equal(self, other: 'BRelation') -> 'BBoolean':
+		return BBoolean(self.equals(other))
+
+	def unequal(self, other: 'BRelation') -> 'BBoolean':
+		return BBoolean(not self.equals(other))
+
+	def elementOf(self, obj: 'BTuple') -> 'BBoolean':
+		prj1 = obj.projection1()
+		prj2 = obj.projection2()
+
+		domain = self.map
+
+		if prj1 not in domain:
+			return BBoolean(False)
+
+		_range = self.map[prj1]
+
+		return BBoolean(prj2 in _range)
+
+	def notElementOf(self, obj: 'BTuple') -> 'BBoolean':
+		prj1 = obj.projection1()
+		prj2 = obj.projection2()
+
+		domain = self.map.keys()
+
+		if prj1 not in domain:
+			return BBoolean(True)
+
+		_range = self.map[prj1]
+
+		return BBoolean(prj2 not in _range)
+	
+	def relationImage(self, domain: 'BSet') -> 'BSet':
+		result_set = set()
+		for domain_element in domain:
+			this_range_set = self.map.get(domain_element)
+			if this_range_set is None:
+				continue
+			result_set = result_set.union(this_range_set)
+		return BSet(*result_set)
+
+	def functionCall(self, arg):
+		_range = self.map.get(arg)
+		if _range is None:
+			raise Exception("Argument is not in the domain of this relation")
+		
+		for element in _range:
+			return element
+		raise Exception("Argument is not in the domain of this relation")
+	
+	def pow(self) -> 'BSet':
+		this_map = self.map
+		this_domain = this_map.keys()
+
+		start = BRelation()
+		queue = [start]
+		result = BSet(start)
+		while not len(queue) == 0:
+			current_set = queue.pop(0)
+
+			for e1 in this_domain:
+				domain_element = e1
+				_range = this_map[domain_element]
+				for e2 in _range:
+					range_element = e2
+					next_relation = current_set.union(BRelation.fromSet(BSet(BTuple(domain_element, range_element))))
+					previous_size = result.size()
+					result = result.union(BSet(next_relation))
+					if previous_size < result.size():
+						queue.append(next_relation)
+						
+		return result
+
+	def pow1(self) -> 'BSet':
+		return self.pow().difference(BSet(BRelation()))
+
+	def fin(self) -> 'BSet':
+		return self.pow()
+
+	def fin1(self) -> 'BSet':
+		return self.pow1()
+
+	def domain(self) -> 'BSet':
+		this_map = self.map
+		_set = self.map.keys()
+		result_set = _set
+		for obj in _set:
+			domain_element = obj
+			_range = this_map[domain_element]
+			if len(_range) == 0:
+				result_set = result_set.difference([domain_element])
+		return BSet(*list(result_set))
+
+	def _range(self) -> 'BSet':
+		_set = reduce(lambda a, b: a.union(b), self.map.values(), set())
+		return BSet(*list(_set))
+
+	def inverse(self) -> 'BRelation':
+		this_map = self.map
+		keys = this_map.keys()
+
+		result_map = immutables.Map()
+		for e1 in keys:
+			domain_element = e1
+			_range = this_map[domain_element]
+			for e2 in _range:
+				range_element = e2
+				current_range = result_map.get(range_element)
+				if current_range is None:
+					current_range = set()
+				current_range = current_range.union([domain_element])
+				result_map = result_map.set(range_element, current_range)
+		return BRelation(result_map)
+
+	def domainRestriction(self, arg: 'BSet') -> 'BRelation':
+		_set = set(self.map.keys())
+		other_set = arg.getSet()
+		result_set = _set.difference(other_set)
+		result_map = self.map
+		for obj in result_set:
+			result_map = result_map.delete(obj)
+		return BRelation(result_map)
+
+	def domainSubstraction(self, arg: 'BSet') -> 'BRelation':
+		_set = self.map.keys()
+		other_set = arg.getSet()
+		result_map = self.map
+		for obj in other_set:
+			if obj in result_map:
+				result_map = result_map.delete(obj)
+		return BRelation(result_map)
+	
+	def rangeRestriction(self, arg: 'BSet') -> 'BRelation':
+		other_set = arg.getSet()
+		this_domain = self.map.keys()
+
+		result_map = self.map
+		for obj in this_domain:
+			domain_element = obj
+			this_range_set = self.map[domain_element]
+			result_map = result_map.set(domain_element, this_range_set.intersection(other_set))
+		return BRelation(result_map)
+
+	def rangeSubstraction(self, arg: 'BSet') -> 'BRelation':
+		other_set = arg.getSet()
+		this_domain = self.map.keys()
+
+		result_map = self.map
+		for obj in this_domain:
+			domain_element = obj
+			this_range_set = self.map[domain_element]
+			result_map = result_map.set(domain_element, this_range_set.difference(other_set))
+		return BRelation(result_map)
+
+	def override(self, arg: 'BRelation') -> 'BRelation':
+		other_map = arg.map
+
+		other_domain = other_map.keys()
+
+		result_map = self.map
+		for obj in other_domain:
+			domain_element = obj
+			_range = other_map[domain_element]
+			result_map = result_map.set(domain_element, _range)
+
+		return BRelation(result_map)
+	
+	def first(self):
+		return self.functionCall(BInteger(1))
+
+	def last(self):
+		return self.functionCall(self.card())
+
+	def reverse(self) -> 'BRelation':
+		size = self.card()
+		result_map = immutables.Map()
+		for i in map(BInteger, range(1, size.intValue() + 1)):
+			range_element = self.functionCall(size.minus(i).succ())
+			result_map = result_map.set(i, frozenset([range_element]))
+		return BRelation(result_map)
+
+	def front(self) -> 'BRelation':
+		return self.domainSubstraction(BSet(self.card()))
+
+	def tail(self) -> 'BRelation':
+		size = self._size().intValue()
+		result_map = immutables.Map()
+		for i in map(BInteger, range(2, size+1)):
+			range_element = self.functionCall(i)
+			result_map = result_map.set(i.pred(), frozenset([range_element]))
+		return BRelation(result_map)
+	
+	def take(self, n: 'BInteger') -> 'BRelation':
+		size = self._size()
+		if n.greaterEqual(size).booleanValue():
+			return BRelation(self.map)
+		result_map = self.map
+		# Remove sets with index greater than n
+		i: BInteger
+		for i in map(BInteger, range(n.intValue() + 1, size.intValue() + 1)):
+			result_map = result_map.delete(i)
+		return BRelation(result_map)
+
+	def drop(self, n: 'BInteger') -> 'BRelation':
+		size = self._size().intValue()
+		this_map = self.map
+		result_map = immutables.Map()
+		for i in map(BInteger, range(n.intValue() + 1, size + 1)):
+			current_set = this_map[i]
+			result_map = result_map.set(i.minus(n), current_set)
+		return BRelation(result_map)
+
+	def concat(self, arg: 'BRelation') -> 'BRelation':
+		result_map = self.map
+		other_map = arg.map
+		size = self.card()
+		i: BInteger
+		for i in map(BInteger, range(1, arg._size().intValue() + 1)):
+			result_map = result_map.set(size.plus(i), other_map[i])
+		return BRelation(result_map)
+
+	def conc(self) -> 'BRelation':
+		result = BRelation()
+		size = self.card().intValue()
+		for i in map(BInteger, range(1, size + 1)):
+			result = result.concat(self.functionCall(i))
+		return result
+
+	def append(self, arg) -> 'BRelation':
+		result_map = self.map
+		result_map = result_map.set(self.card().succ(), frozenset([arg]))
+		return BRelation(result_map)
+
+	def prepend(self, arg) -> 'BRelation':
+		result_map = immutables.Map()
+		this_map = self.map
+		size = self._size().intValue()
+		for i in map(BInteger, range(1, size + 1)):
+			result_map = result_map.set(i.succ(), this_map.get(i))
+		result_map = result_map.set(BInteger(1), frozenset([arg]))
+		return BRelation(result_map)
+
+	def directProduct(self, arg: 'BRelation') -> 'BRelation':
+		this_map = self.map
+		this_domain = this_map.keys()
+		other_map = arg.map
+
+		result_map = immutables.Map()
+		for obj in this_domain:
+			domain_element = obj
+			this_range = this_map.get(domain_element, None)
+			other_range = other_map.get(domain_element, None)
+			if other_range is None:
+				continue
+			result_range = set()
+			for lhs in this_range:
+				for rhs in other_range:
+					lhs_element = lhs
+					rhs_element = rhs
+					result_range = result_range.union([BTuple(lhs_element, rhs_element)])
+			result_map = result_map.set(domain_element, result_range)
+		return BRelation(result_map)
+	
+	def parallelProduct(self, arg: 'BRelation') -> 'BRelation':
+		this_map = self.map
+		this_domain = this_map.keys()
+
+		other_map = arg.map
+		other_domain = other_map.keys()
+
+		result_map = immutables.Map()
+
+		for domain_elementThis in this_domain:
+			for domaineElementOther in other_domain:
+				domain_element_this_element = domain_elementThis
+				domain_element_other_element = domaineElementOther
+
+				this_range = this_map[domain_element_this_element]
+				other_range = other_map[domain_element_other_element]
+
+				result_range = set()
+				for lhs in this_range:
+					for rhs in other_range:
+						lhs_element = lhs
+						rhs_element = rhs
+						result_range = result_range.union([BTuple(lhs_element, rhs_element)])
+				_tuple = BTuple(domain_element_this_element, domain_element_other_element)
+				result_map = result_map.set(_tuple, result_range)
+		return BRelation(result_map)
+
+	def composition(self, arg: 'BRelation') -> 'BRelation':
+		this_map = self.map
+		other_map = arg.map
+
+		this_domain = this_map.keys()
+
+		result_map = immutables.Map()
+
+		for e1 in this_domain:
+			domain_element = e1
+			_range = this_map[domain_element]
+
+			_set = set()
+			for e2 in _range:
+				range_element = e2
+				union_element = other_map.get(range_element)
+				if union_element is None:
+					union_element = set()
+				_set = _set.union(union_element)
+			result_map = result_map.set(domain_element, _set)
+		return BRelation(result_map)
+
+	def iterate(self, n: 'BInteger') -> 'BRelation':
+		this_relation = self
+		result = self.identity(self.domain())
+		for _ in map(BInteger, range(1, n.intValue() + 1)):
+			result = result.union(result.composition(this_relation))
+		return result
+
+	def closure(self) -> 'BRelation':
+		this_relation = self
+		result = self.identity(self.domain())
+		next_result = result.composition(this_relation)
+		while True:
+			last_result = deepcopy(result)
+			result = result.union(next_result)
+			next_result = result.composition(this_relation)
+			if result.equal(last_result).booleanValue():
+				break
+		return result
+
+	def closure1(self) -> 'BRelation':
+		this_relation = self
+		result = self
+		next_result = result.composition(this_relation)
+		while True:
+			last_result = deepcopy(result)
+			result = result.union(next_result)
+			next_result = result.composition(this_relation)
+			if result.equal(last_result).booleanValue():
+				break
+		return result
+
+	@staticmethod
+	def projection1(arg1: 'BSet', arg2: 'BSet') -> 'BRelation':
+		arg_set_1 = arg1.getSet()
+		arg_set_2 = arg2.getSet()
+
+		result_map = immutables.Map()
+		for e1 in arg_set_1:
+			for e2 in arg_set_2:
+				element1 = e1
+				element2 = e2
+
+				_tuple = BTuple(element1, element2)
+				result_map = result_map.set(_tuple, frozenset([element1]))
+		return BRelation(result_map)
+
+	@staticmethod
+	def projection2(arg1: 'BSet', arg2: 'BSet') -> 'BRelation':
+		arg_set_1 = arg1.getSet()
+		arg_set_2 = arg2.getSet()
+
+		result_map = immutables.Map()
+		for e1 in arg_set_1:
+			for e2 in arg_set_2:
+				element1 = e1
+				element2 = e2
+
+				_tuple = BTuple(element1, element2)
+				result_map = result_map.set(_tuple, frozenset([element2]))
+		return BRelation(result_map)
+	
+	def fnc(self) -> 'BRelation':
+		this_map = self.map
+		domain = self.domain().getSet()
+
+		result_map = immutables.Map()
+		for e in domain:
+			domain_element = e
+			_range = this_map[e]
+			range_set = BSet(_range)
+			result_map = result_map.set(domain_element, frozenset([range_set]))
+		return BRelation(result_map)
+
+	def rel(self) -> 'BRelation':
+		domain = self.domain()
+
+		result_map = immutables.Map()
+		for domain_element in domain:
+			_range = self.functionCall(domain_element)
+			range_set = _range.getSet()
+			result_map = result_map.set(domain_element, range_set)
+		return BRelation(result_map)
+
+	@staticmethod
+	def identity(arg: 'BSet') -> 'BRelation':
+		result_map = immutables.Map()
+		for e in arg:
+			result_map = result_map.set(e, frozenset([e]))
+		return BRelation(result_map)
+
+	@staticmethod
+	def cartesianProduct(arg1: 'BSet', arg2: 'BSet') -> 'BRelation':
+		result_map = immutables.Map()
+		for e1 in arg1:
+			result_map = result_map.set(e1, arg2.getSet())
+		return BRelation(result_map)
+	
+	def nondeterminism(self) -> 'BTuple':
+		domain = self.map.keys()
+
+		index = random.choice(range(len(domain)))
+		i = 0
+		domain_element = None
+		for obj in domain:
+			if i == index:
+				domain_element = obj
+				break
+			i = i + 1
+
+		_range = self.map[domain_element]
+		index = random.choice(range(_range.size()))
+		i = 0
+		for obj in _range:
+			if i == index:
+				return BTuple(domain_element, obj)
+			i = i + 1
+		return None
+
+	def isTotal(self, domain: 'BSet'):
+		return self.domain().equal(domain)
+
+	def isTotalInteger(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isTotalNatural(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isTotalNatural1(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isTotalString(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isTotalStruct(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isPartial(self, domain: 'BSet'):
+		return self.domain().strictSubset(domain)
+
+	def isPartialInteger(self):
+		for e in self.domain():
+			if(isinstance(e, BInteger)):
+				return BBoolean(True)
+			else:
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def isPartialNatural(self):
+		for e in self.domain():
+			if(isinstance(e, BInteger) and not e.isNatural().booleanValue()):
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def isPartialNatural1(self):
+		for e in self.domain():
+			if isinstance(e, BInteger) and not e.isNatural1().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def isPartialString(self):
+		for e in self.domain():
+			if isinstance(e, BString) and not e.isString().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def isPartialStruct(self):
+		for e in self.domain():
+			if isinstance(e, BStruct) and not e.isRecord().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkDomain(self, domain: 'BSet'):
+		return self.domain().subset(domain)
+
+	def checkDomainInteger(self):
+		for e in self.domain():
+			if isinstance(e, BInteger):
+				return BBoolean(True)
+			else:
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkDomainNatural(self):
+		for e in self.domain():
+			if isinstance(e, BInteger) and not e.isNatural().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkDomainNatural1(self):
+		for e in self.domain():
+			if isinstance(e, BInteger) and not e.isNatural1().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkDomainString(self):
+		for e in self.domain():
+			if isinstance(e, BString) and not e.isString().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkDomainStruct(self):
+		for e in self.domain():
+			if isinstance(e, BStruct) and not e.isRecord().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkRange(self, _range: 'BSet'):
+		return self._range().subset(_range)
+
+	def checkRangeInteger(self):
+		for e in self._range():
+			if isinstance(e, BInteger):
+				return BBoolean(True)
+			else:
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkRangeNatural(self):
+		for e in self._range():
+			if isinstance(e, BInteger) and not e.isNatural().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkRangeNatural1(self):
+		for e in self._range():
+			if isinstance(e, BInteger) and not e.isNatural1().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkRangeString(self):
+		for e in self._range():
+			if isinstance(e, BString) and not e.isString().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkRangeStruct(self) -> 'BBoolean':
+		for e in self._range():
+			if isinstance(e, BStruct) and not e.isRecord().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def isRelation(self) -> 'BBoolean':
+		return BBoolean(True)
+
+	def isFunction(self) -> 'BBoolean':
+		for element in self.domain():
+			_range = self.map[element]
+			if _range.size() > 1:
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def isSurjection(self, _range: 'BSet'):
+		return self._range().equal(_range)
+
+	def isSurjectionInteger(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isSurjectionNatural(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isSurjectionNatural1(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isSurjectionString(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isSurjectionStruct(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isInjection(self) -> 'BBoolean':
+		visited = set()
+		for element in self.domain():
+			_range = self.map[element]
+			for e in _range:
+				range_element = e
+				if range_element in visited:
+					return BBoolean(False)
+				visited = visited.union([range_element])
+		return BBoolean(True)
+
+	def isBijection(self, _range: 'BSet'):
+		return self.isSurjection(_range) and self.isInjection()
+
+	def isBijectionInteger(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isBijectionNatural(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isBijectionNatural1(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isBijectionString(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isBijectionStruct(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def __str__(self) -> 'str':
+
+		this_map = self.map
+		domain = this_map.keys()
+
+		size = self.size()
+		i = 0
+
+		sb = "{"
+		for e1 in domain:
+			domain_element = e1
+			_range = this_map[domain_element]
+			for e2 in _range:
+				range_element = e2
+				sb += "("
+				sb += str(domain_element)
+				sb += " |-> "
+				sb += str(range_element)
+				sb += ")"
+				if i+1 < size:
+					sb += ", "
+				i = i + 1
+		sb += "}"
+		return sb
diff --git a/btypes_primitives/src/main/python_magicstack_immutable/btypes/BSet.py b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BSet.py
new file mode 100644
index 0000000000000000000000000000000000000000..6c423ad993523a26098a7a7d5ae4405c12fb3f09
--- /dev/null
+++ b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BSet.py
@@ -0,0 +1,259 @@
+from btypes.BInteger import *
+from btypes.BBoolean import *
+from btypes.BString import *
+from btypes.BStruct import *
+
+import immutables
+
+
+class BSet:
+
+    def __init__(self, *args):
+        if len(args) == 1 and type(args[0]) is immutables.Map:
+            self.__set = args[0]
+        else:
+            _set = {x: x for x in args}
+            self.__set = immutables.Map(_set)
+
+    def __str__(self) -> 'str':
+        return '{' + ', '.join([str(x) for x in self.__set]) + '}'
+
+    def __eq__(self, other):
+        if self is other:
+            return True
+        if other is None or type(self) != type(other):
+            return False
+        if not self.__set == other.__set:
+            return False
+        return True
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __len__(self):
+        return len(self.__set)
+
+    def union(self, other=None):
+        if other is None:
+            if len(self.__set) == 0:
+                return BSet()
+            elif type(next(iter(self.__set))) == BSet:
+                return reduce(lambda a, e: a.update(e), self, BSet())
+            elif type(next(iter(self.__set))) == BRelation:
+                return reduce(lambda a, e: a.update(e), self, BRelation())
+
+        return BSet(self.__set.update(other.getSet()))
+
+    def intersect(self, other=None):
+        if other is None:
+            if len(self.__set) == 0:
+                return BSet()
+            elif type(next(iter(self.__set))) == BSet:
+                return reduce(lambda a, e: a.intersect(e), self, BSet())
+            elif type(next(iter(self.__set))) == BRelation:
+                return reduce(lambda a, e: a.intersect(e), self, BRelation())
+
+        _set = self.__set
+        for element in _set:
+            if not element in other.__set:
+                _set = _set.delete(element)
+        return BSet(_set)
+
+    def difference(self, other: 'BSet') -> 'BSet':
+        _set = self.__set
+        for element in other.__set:
+            if element in _set:
+                _set = _set.delete(element)
+        return BSet(_set)
+
+    def card(self) -> 'BInteger':
+        return BInteger(len(self.__set))
+
+    def size(self) -> 'BInteger':
+        return BInteger(len(self.__set))
+
+    def elementOf(self, obj) -> 'BBoolean':
+        return BBoolean(obj in self.__set)
+
+    def notElementOf(self, obj) -> 'BBoolean':
+        return BBoolean(obj not in self.__set)
+
+    def subset(self, other: 'BSet') -> 'BBoolean':
+        return BBoolean(other.containsAll(self.__set))
+
+    def notSubset(self, other: 'BSet') -> 'BBoolean':
+        return BBoolean(not other.containsAll(self.__set))
+
+    def strictSubset(self, other: 'BSet') -> 'BBoolean':
+        return BBoolean(other.size() != self.size() and other.containsAll(self.__set))
+
+    def strictNotSubset(self, other: 'BSet') -> 'BBoolean':
+        return BBoolean(other.size() == self.size() and not other.containsAll(self.__set))
+
+    def contains(self, other):
+        return other in self.__set
+
+    def containsAll(self, other):
+        for o in other:
+            if o not in self.__set:
+                return False
+        return True
+
+    def isEmpty(self) -> 'int':
+        return len(self.__set) == 0
+
+    def equal(self, other) -> 'bool':
+        return self.__eq__(other)
+
+    def unequal(self, other) -> 'bool':
+        return self.__ne__(other)
+
+    def nondeterminism(self):
+        return random.choice(self.__set)
+
+    def min(self):
+        return min(self.__set)
+
+    def max(self):
+        return max(self.__set)
+
+    def pow(self) -> 'BSet':
+        result = BSet()
+        start = BSet()
+        queue = [start]
+        result = result.union(BSet(start))
+        while not len(queue) == 0:
+            currentSet = queue.pop()
+            for element in self.__set:
+                nextSet = currentSet.union(BSet(element))
+                previousSize = result.size()
+                result = result.union(BSet(nextSet))
+                if previousSize < result.size():
+                    queue.append(nextSet)
+
+        return result
+        # s = list(self.__set)
+        # return BSet(frozenset(chain.from_iterable(BSet(combinations(s, r)) for r in range(len(s) + 1))))
+
+    def pow1(self) -> 'BSet':
+        return self.pow().difference(BSet(BSet()))
+
+    # Only finite subsets are supported so fin = pow
+    def fin(self) -> 'BSet':
+        return self.pow()
+
+    def fin1(self) -> 'BSet':
+        return self.pow1()
+
+    def subsetOfInteger(self) -> 'bool':
+        for element in self.__set:
+            if not isinstance(element, BInteger):
+                return False
+        return True
+
+    def strictSubsetOfInteger(self) -> 'bool':
+        return self.subsetOfInteger()
+
+    def notSubsetOfInteger(self) -> 'bool':
+        return not self.subsetOfInteger()
+
+    def notStrictSubsetOfInteger(self) -> 'bool':
+        return not self.strictSubsetOfInteger()
+
+    def subsetOfNatural(self) -> 'bool':
+        for element in self.__set:
+            if not (isinstance(element, BInteger) and element.isNatural().booleanValue()):
+                return False
+        return True
+
+    def strictSubsetOfNatural(self) -> 'bool':
+        return self.subsetOfNatural()
+
+    def notSubsetOfNatural(self) -> 'bool':
+        return not self.subsetOfNatural()
+
+    def notStrictSubsetOfNatural(self) -> 'bool':
+        return not self.strictSubsetOfNatural()
+
+    def subsetOfNatural1(self) -> 'bool':
+        for element in self.__set:
+            if not (isinstance(element, BInteger) and element.isNatural1().booleanValue()):
+                return False
+        return True
+
+    def subsetOfString(self) -> 'bool':
+        for element in self.__set:
+            if not isinstance(element, BString):
+                return False
+        return True
+
+    def strictSubsetOfString(self) -> 'bool':
+        return self.subsetOfString()
+
+    def notSubsetOfString(self) -> 'bool':
+        return not self.subsetOfString()
+
+    def notStrictSubsetOfString(self) -> 'bool':
+        return not self.strictSubsetOfString()
+
+    def subsetOfStruct(self) -> 'bool':
+        for element in self.__set:
+            if not isinstance(element, BStruct):
+                return False
+        return True
+
+    def strictSubsetOfStruct(self) -> 'bool':
+        return self.subsetOfStruct()
+
+    def notSubsetOfStruct(self) -> 'bool':
+        return not self.subsetOfStruct()
+
+    def notStrictSubsetOfStruct(self) -> 'bool':
+        return not self.strictSubsetOfStruct()
+
+    def equalInteger(self) -> 'BBoolean':
+        return BBoolean(False)
+
+    def unequalInteger(self) -> 'BBoolean':
+        return BBoolean(True)
+
+    def equalNatural(self) -> 'BBoolean':
+        return BBoolean(False)
+
+    def unequalNatural(self) -> 'BBoolean':
+        return BBoolean(True)
+
+    def equalNatural1(self) -> 'BBoolean':
+        return BBoolean(False)
+
+    def unequalNatural1(self) -> 'BBoolean':
+        return BBoolean(True)
+
+    def equalString(self) -> 'BBoolean':
+        return BBoolean(False)
+
+    def unequalString(self) -> 'BBoolean':
+        return BBoolean(True)
+
+    def equalStruct(self) -> 'BBoolean':
+        return BBoolean(False)
+
+    def unequalStruct(self) -> 'BBoolean':
+        return BBoolean(True)
+
+    def getSet(self):
+        return self.__set
+
+    @staticmethod
+    def interval(a: 'BInteger', b: 'BInteger') -> 'BSet':
+        r = list(map(BInteger, range(a.intValue(), b.intValue() + 1)))
+        return BSet(*r)
+
+    def __hash__(self):
+        return hash(self.__set)
+
+    def __iter__(self):
+        return iter(self.__set)
+
+
+from btypes.BRelation import *
diff --git a/btypes_primitives/src/main/python_magicstack_immutable/btypes/BString.py b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BString.py
new file mode 100755
index 0000000000000000000000000000000000000000..6403d7e95d942301c6d43d1957975ca460a5f195
--- /dev/null
+++ b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BString.py
@@ -0,0 +1,38 @@
+from btypes.BBoolean import *
+
+class BString:
+
+	def __init__(self, value):
+		self.__value = value
+
+	def getValue(self):
+		return value
+
+	def __eq__(self, other: 'BString') -> 'BBoolean':
+		return BBoolean(self.__value == other.__value)
+
+	def equals(self, other) -> 'bool':
+		return self.__eq__(other)
+
+	def length(self) -> 'int':
+		return len(self.__value)
+
+	def isEmpty(self) -> 'bool':
+		return not self.__value
+
+	def hashCode(self):
+		hash(self.__value)
+
+
+
+	def toString(self) -> 'str':
+		return '"' + self.__value + '"'
+
+	def isCase(self, other) -> 'bool':
+		return self.__value.equals(other)
+
+	def isString(self) -> 'BBoolean':
+		return BBoolean(True)
+
+	def isNotString(self) -> 'BBoolean':
+		return BBoolean(False)
diff --git a/btypes_primitives/src/main/python_magicstack_immutable/btypes/BStruct.py b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BStruct.py
new file mode 100755
index 0000000000000000000000000000000000000000..4015efa460b8f36bb832a48730f4413a8bf4bbc0
--- /dev/null
+++ b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BStruct.py
@@ -0,0 +1,10 @@
+from btypes.BBoolean import *
+
+class BStruct:
+
+    def isRecord(self):
+        return BBoolean(True)
+
+    def isNotRecord(self):
+        return BBoolean(False)
+
diff --git a/btypes_primitives/src/main/python_magicstack_immutable/btypes/BTuple.py b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BTuple.py
new file mode 100755
index 0000000000000000000000000000000000000000..318be351a49b990ad68937c1d35b3f48db4b8b4c
--- /dev/null
+++ b/btypes_primitives/src/main/python_magicstack_immutable/btypes/BTuple.py
@@ -0,0 +1,42 @@
+from btypes.BObject import *
+from btypes.BBoolean import *
+
+
+class BTuple(BObject):
+
+    def __init__(self, first, second):
+        if first is None or second is None:
+            raise ValueError()
+        self.first = first
+        self.second = second
+
+    def __eq__(self, other):
+        return self.equals(other)
+
+    def equals(self, other) -> 'bool':
+        if self is other:
+            return True
+        if other is None or type(self) != type(other):
+            return False
+
+        b_objects = other
+        # elements is never None
+        return b_objects.projection1().__eq__(self.first) and b_objects.projection2().__eq__(self.second)
+
+    def __hash__(self):
+        return hash(hash(self.first) + hash(self.second))
+
+    def projection1(self):
+        return self.first
+
+    def projection2(self):
+        return self.second
+
+    def __str__(self):
+        return "(" + str(self.projection1()) + " |-> " + str(self.projection2()) + ')'
+
+    def equal(self, other: 'BTuple') -> 'BBoolean':
+        return BBoolean(self.equals(other))
+
+    def unequal(self, other: 'BTuple') -> 'BBoolean':
+        return BBoolean(not self.equals(other))
diff --git a/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BBoolean.py b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BBoolean.py
new file mode 100644
index 0000000000000000000000000000000000000000..cb95d7e65cbaf1b495ffcc5f8fa053b9b7684ff8
--- /dev/null
+++ b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BBoolean.py
@@ -0,0 +1,60 @@
+class BBoolean:
+    def __init__(self, value):
+        if type(value) is bool:
+            self.__value = value
+        elif type(value) is str:
+            self.__value = str(value).lower() == "true"
+
+    def __and__(self, other: 'BBoolean') -> 'BBoolean':
+        return BBoolean(self.__value and other.__value)
+
+    def __or__(self, other: 'BBoolean') -> 'BBoolean':
+        return BBoolean(self.__value or other.__value)
+
+    def __rand__(self, other: 'BBoolean') -> 'BBoolean':
+        return BBoolean(self.__value and other.__value)
+
+    def __str__(self) -> 'str':
+        return str(self.__value).lower()
+
+    def __ror__(self, other: 'BBoolean') -> 'BBoolean':
+        return BBoolean(self.__value or other.__value)
+
+    def __rxor__(self, other: 'BBoolean') -> 'BBoolean':
+        return BBoolean(self.__value ^ other.__value)
+
+    def __xor__(self, other: 'BBoolean') -> 'BBoolean':
+        return BBoolean(self.__value ^ other.__value)
+
+    def __eq__(self, other: 'BBoolean') -> 'BBoolean':
+        return BBoolean(self.__value == other.__value)
+
+    def __ne__(self, other: 'BBoolean') -> 'BBoolean':
+        return BBoolean(self.__value != other.__value)
+
+    def _and(self, other:  'BBoolean') -> 'BBoolean':
+        return self.__and__(other)
+
+    def _or(self, other:  'BBoolean') -> 'BBoolean':
+        return self.__or__(other)
+
+    def _not(self) -> 'BBoolean':
+        return BBoolean(not self.__value)
+
+    def implies(self, other:  'BBoolean') -> 'BBoolean':
+        return self._not()._or(other)
+
+    def equivalent(self, other:  'BBoolean') -> 'BBoolean':
+        return self.implies(other)._and(other.implies(self))
+
+    def equal(self, other:  'BBoolean') -> 'BBoolean':
+        return self.__eq__(other)
+
+    def unequal(self, other:  'BBoolean') -> 'BBoolean':
+        return self.__ne__(other)
+
+    def booleanValue(self) -> 'bool':
+        return self.__value
+
+    def __hash__(self):
+        return hash(self.__value)
\ No newline at end of file
diff --git a/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BCouple.py b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BCouple.py
new file mode 100644
index 0000000000000000000000000000000000000000..76a5de9a4cc474a694ffebcf401c6f0016e47d95
--- /dev/null
+++ b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BCouple.py
@@ -0,0 +1,20 @@
+class BCouple:
+
+    def __init__(self, first, second):
+        self.__first = first
+        self.__second = second
+
+    def get_first(self):
+        return self.__first
+
+    def get_second(self):
+        return self.__second
+
+    def __eq__(self, other):
+        return self.__first == other.__first and self.__second == other.__second
+
+    def equal(self, other):
+        return self == other
+
+    def unequal(self, other):
+        return self != other
\ No newline at end of file
diff --git a/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BInteger.py b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BInteger.py
new file mode 100644
index 0000000000000000000000000000000000000000..ab76c84f3bce631c1adde5d08d605ebe901f551b
--- /dev/null
+++ b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BInteger.py
@@ -0,0 +1,124 @@
+from btypes.BBoolean import *
+
+class BInteger:
+
+    def __init__(self, value):
+        self.__value = value
+
+    def __add__(self, other: 'BInteger') -> 'BInteger':
+        if type(other) == str:
+            return str(self) + other
+        return BInteger(self.__value + other.__value)
+
+    def __sub__(self, other: 'BInteger') -> 'BInteger':
+        return BInteger(self.__value - other.__value)
+
+    def __mul__(self, other: 'BInteger') -> 'BInteger':
+        return BInteger(self.__value * other.__value)
+
+    def __mod__(self, other: 'BInteger') -> 'BInteger':
+        return BInteger(self.__value % other.__value)
+
+    def __div__(self, other: 'BInteger') -> 'BInteger':
+        return BInteger(self.__value // other.__value)
+
+    def __neg__(self) -> 'BInteger':
+        return BInteger(-self.__value)
+
+    def __lt__(self, other: 'BInteger') -> 'bool':
+        return self.__value < other.__value
+
+    def __le__(self, other: 'BInteger') -> 'bool':
+        return self.__value <= other.__value
+
+    def __eq__(self, other: 'BInteger') -> 'bool':
+        if not isinstance(other, BInteger):
+            return False
+        return self.__value == other.__value
+
+    def __ne__(self, other: 'BInteger') -> 'bool':
+        return self.__value != other.__value
+
+    def __gt__(self, other: 'BInteger') -> 'bool':
+        return self.__value > other.__value
+
+    def __ge__(self, other: 'BInteger') -> 'bool':
+        return self.__value >= other.__value
+
+    def __pow__(self, other: 'BInteger') -> 'BInteger':
+        return self.__value ** other.__value
+
+    def __str__(self) -> 'str':
+        return str(self.__value)
+
+    def plus(self, other: 'BInteger') -> 'BInteger':
+        return self.__add__(other)
+
+    def minus(self, other: 'BInteger') -> 'BInteger':
+        return self.__sub__(other)
+
+    def multiply(self, other: 'BInteger') -> 'BInteger':
+        return self.__mul__(other)
+
+    def modulo(self, other: 'BInteger') -> 'BInteger':
+        return self.__mod__(other)
+
+    def divide(self, other: 'BInteger') -> 'BInteger':
+        return self.__div__(other)
+
+    def negative(self) -> 'BInteger':
+        return self.__neg__()
+
+    def less(self, other: 'BInteger') -> 'BBoolean':
+        return BBoolean(self.__value < other.__value)
+
+    def lessEqual(self, other: 'BInteger') -> 'BBoolean':
+        return BBoolean(self.__value <= other.__value)
+
+    def equal(self, other: 'BInteger') -> 'BBoolean':
+        return BBoolean(self.__value == other.__value)
+
+    def unequal(self, other: 'BInteger') -> 'BBoolean':
+        return BBoolean(self.__value != other.__value)
+
+    def greater(self, other: 'BInteger') -> 'BBoolean':
+        return BBoolean(self.__value > other.__value)
+
+    def greaterEqual(self, other: 'BInteger') -> 'BBoolean':
+        return BBoolean(self.__value >= other.__value)
+
+    def succ(self) -> 'BInteger':
+        return BInteger(self.__value + 1)
+
+    def pred(self) -> 'BInteger':
+        return BInteger(self.__value - 1)
+
+    def power(self, other: 'BInteger') -> 'BInteger':
+        return self.__pow__(other)
+
+    def positive(self):
+        return self
+
+    def intValue(self):
+        return self.__value
+
+    def isInteger(self) -> 'BBoolean':
+        return BBoolean(true)
+
+    def isNotInteger(self) -> 'BBoolean':
+        return BBoolean(false)
+
+    def isNatural(self) -> 'BBoolean':
+        return self.greaterEqual(BInteger(0))
+
+    def isNotNatural(self) -> 'BBoolean':
+        return self.isNatural()._not()
+
+    def isNatural1(self) -> 'BBoolean':
+        return self.greater(BInteger(0))
+
+    def isNotNatural1(self) -> 'BBoolean':
+        return self.isNatural1()._not()
+
+    def __hash__(self):
+        return hash(31 + self.__value)
\ No newline at end of file
diff --git a/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BObject.py b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BObject.py
new file mode 100644
index 0000000000000000000000000000000000000000..327a2e93bbe98f46d78b5d0f1fe37e651003ff5e
--- /dev/null
+++ b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BObject.py
@@ -0,0 +1,2 @@
+class BObject:
+    pass
\ No newline at end of file
diff --git a/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BRelation.py b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BRelation.py
new file mode 100644
index 0000000000000000000000000000000000000000..d05ac35442e91bd0a328bb1c2cb32eae88460467
--- /dev/null
+++ b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BRelation.py
@@ -0,0 +1,767 @@
+from btypes.BInteger import *
+from btypes.BBoolean import *
+from btypes.BInteger import BInteger
+from btypes.BTuple import *
+from btypes.BSet import *
+
+import immutables
+from copy import deepcopy
+from functools import reduce
+import random
+
+
+class BRelation:
+
+	def __init__(self, *args):
+		if len(args) == 0:
+			self.map = immutables.Map()
+		elif len(args) == 1 and type(args[0]) == immutables.Map:
+			self.map = args[0]
+		else:
+			self.map = immutables.Map()
+			for e in args:
+				key = e.projection1()
+				value = e.projection2()
+				_set = self.map.get(key)
+				if not _set:
+					_set = frozenset()
+				_set = _set.union({value})
+				self.map=self.map.set(key, _set)
+
+	@staticmethod
+	def fromSet(_set: 'BSet') -> 'BRelation':
+		result_map = immutables.Map()
+		for e in _set:
+			key = e.projection1()
+			value = e.projection2()
+			_range = result_map.get(key, None)
+			if _range is None:
+				_range = {value}
+				result_map = result_map.set(key, _range)
+			else:
+				_range = _range.union(frozenset([value]))
+				result_map = result_map.set(key, _range)
+
+		return BRelation(result_map)
+
+	def __eq__(self, other: 'BRelation') -> 'bool':
+		if self is other:
+			return True
+		if other is None or type(self) != type(other):
+			return False
+		if not self.map == other.map:
+			return False
+		return True
+
+	def equals(self, other):
+		return self.__eq__(other)
+
+	def __hash__(self):
+		return hash(frozenset(self.map))
+
+	def intersect(self, relation: 'BRelation') -> 'BRelation':
+		other_map = relation.map
+		other_domain = set(relation.map.keys())
+		this_domain = set(self.map.keys())
+		intersection_domain = this_domain.intersection(other_domain)
+		difference_domain = this_domain.difference(other_domain)
+
+		result_map = self.map
+		for obj in intersection_domain:
+			domain_element = obj
+			this_range_set = self.map[domain_element]
+			other_range_set = other_map[domain_element]
+			result_map = result_map.set(domain_element, this_range_set.union(other_range_set))
+
+		for obj in difference_domain:
+			domain_element = obj
+			result_map = result_map.delete(domain_element)
+
+		return BRelation(result_map)
+
+	def difference(self, relation: 'BRelation') -> 'BRelation':
+		other_map = relation.map
+		other_domain = set(other_map.keys())
+		this_domain = set(self.map.keys())
+		difference_domain = this_domain.difference(other_domain)
+		rest_domain = this_domain.difference(difference_domain)
+
+		result_map = self.map
+		for obj in difference_domain:
+			domain_element = obj
+			this_range_set = self.map[domain_element]
+			other_range_set = other_map[domain_element]
+			result_map = result_map.set(domain_element, this_range_set.difference(other_range_set))
+
+		for obj in rest_domain:
+			domain_element = obj
+			result_map = result_map.delete(domain_element)
+			
+		return BRelation(result_map)
+
+	def union(self, relation: 'BRelation') -> 'BRelation':
+		other_map = relation.map
+		other_domain = other_map.keys()
+
+		result_map = deepcopy(self.map)
+		for obj in other_domain:
+			domain_element = obj
+			this_range_set = self.map.get(domain_element)
+			other_range_set = relation.map.get(domain_element, None)
+			if this_range_set is None:
+				this_range_set = set()
+			result_map = result_map.set(domain_element, this_range_set.union(other_range_set))
+			
+		return BRelation(result_map)
+
+	def size(self) -> 'int':
+		size = 0
+		this_domain = self.map.keys()
+
+		for obj in this_domain:
+			domain_element = obj
+			this_range_set = self.map[domain_element]
+			size += len(this_range_set)
+			
+		return size
+
+	def card(self) -> 'BInteger':
+		return BInteger(self.size())
+
+	def _size(self) -> 'BInteger':
+		return BInteger(self.size())
+
+	def equal(self, other: 'BRelation') -> 'BBoolean':
+		return BBoolean(self.equals(other))
+
+	def unequal(self, other: 'BRelation') -> 'BBoolean':
+		return BBoolean(not self.equals(other))
+
+	def elementOf(self, obj: 'BTuple') -> 'BBoolean':
+		prj1 = obj.projection1()
+		prj2 = obj.projection2()
+
+		domain = self.map
+
+		if prj1 not in domain:
+			return BBoolean(False)
+
+		_range = self.map[prj1]
+
+		return BBoolean(prj2 in _range)
+
+	def notElementOf(self, obj: 'BTuple') -> 'BBoolean':
+		prj1 = obj.projection1()
+		prj2 = obj.projection2()
+
+		domain = self.map.keys()
+
+		if prj1 not in domain:
+			return BBoolean(True)
+
+		_range = self.map[prj1]
+
+		return BBoolean(prj2 not in _range)
+	
+	def relationImage(self, domain: 'BSet') -> 'BSet':
+		result_set = set()
+		for domain_element in domain:
+			this_range_set = self.map.get(domain_element)
+			if this_range_set is None:
+				continue
+			result_set = result_set.union(this_range_set)
+		return BSet(*result_set)
+
+	def functionCall(self, arg):
+		_range = self.map.get(arg)
+		if _range is None:
+			raise Exception("Argument is not in the domain of this relation")
+		
+		for element in _range:
+			return element
+		raise Exception("Argument is not in the domain of this relation")
+	
+	def pow(self) -> 'BSet':
+		this_map = self.map
+		this_domain = this_map.keys()
+
+		start = BRelation()
+		queue = [start]
+		result = BSet(start)
+		while not len(queue) == 0:
+			current_set = queue.pop(0)
+
+			for e1 in this_domain:
+				domain_element = e1
+				_range = this_map[domain_element]
+				for e2 in _range:
+					range_element = e2
+					next_relation = current_set.union(BRelation.fromSet(BSet(BTuple(domain_element, range_element))))
+					previous_size = result.size()
+					result = result.union(BSet(next_relation))
+					if previous_size < result.size():
+						queue.append(next_relation)
+						
+		return result
+
+	def pow1(self) -> 'BSet':
+		return self.pow().difference(BSet(BRelation()))
+
+	def fin(self) -> 'BSet':
+		return self.pow()
+
+	def fin1(self) -> 'BSet':
+		return self.pow1()
+
+	def domain(self) -> 'BSet':
+		this_map = self.map
+		_set = self.map.keys()
+		result_set = _set
+		for obj in _set:
+			domain_element = obj
+			_range = this_map[domain_element]
+			if len(_range) == 0:
+				result_set = result_set.difference([domain_element])
+		return BSet(*list(result_set))
+
+	def _range(self) -> 'BSet':
+		_set = reduce(lambda a, b: a.union(b), self.map.values(), set())
+		return BSet(*list(_set))
+
+	def inverse(self) -> 'BRelation':
+		this_map = self.map
+		keys = this_map.keys()
+
+		result_map = immutables.Map()
+		for e1 in keys:
+			domain_element = e1
+			_range = this_map[domain_element]
+			for e2 in _range:
+				range_element = e2
+				current_range = result_map.get(range_element)
+				if current_range is None:
+					current_range = set()
+				current_range = current_range.union([domain_element])
+				result_map = result_map.set(range_element, current_range)
+		return BRelation(result_map)
+
+	def domainRestriction(self, arg: 'BSet') -> 'BRelation':
+		_set = set(self.map.keys())
+		other_set = arg.getSet()
+		result_set = _set.difference(other_set)
+		result_map = self.map
+		for obj in result_set:
+			result_map = result_map.delete(obj)
+		return BRelation(result_map)
+
+	def domainSubstraction(self, arg: 'BSet') -> 'BRelation':
+		_set = self.map.keys()
+		other_set = arg.getSet()
+		result_map = self.map
+		for obj in other_set:
+			if obj in result_map:
+				result_map = result_map.delete(obj)
+		return BRelation(result_map)
+	
+	def rangeRestriction(self, arg: 'BSet') -> 'BRelation':
+		other_set = arg.getSet()
+		this_domain = self.map.keys()
+
+		result_map = self.map
+		for obj in this_domain:
+			domain_element = obj
+			this_range_set = self.map[domain_element]
+			result_map = result_map.set(domain_element, this_range_set.intersection(other_set))
+		return BRelation(result_map)
+
+	def rangeSubstraction(self, arg: 'BSet') -> 'BRelation':
+		other_set = arg.getSet()
+		this_domain = self.map.keys()
+
+		result_map = self.map
+		for obj in this_domain:
+			domain_element = obj
+			this_range_set = self.map[domain_element]
+			result_map = result_map.set(domain_element, this_range_set.difference(other_set))
+		return BRelation(result_map)
+
+	def override(self, arg: 'BRelation') -> 'BRelation':
+		other_map = arg.map
+
+		other_domain = other_map.keys()
+
+		result_map = self.map
+		for obj in other_domain:
+			domain_element = obj
+			_range = other_map[domain_element]
+			result_map = result_map.set(domain_element, _range)
+
+		return BRelation(result_map)
+	
+	def first(self):
+		return self.functionCall(BInteger(1))
+
+	def last(self):
+		return self.functionCall(self.card())
+
+	def reverse(self) -> 'BRelation':
+		size = self.card()
+		result_map = immutables.Map()
+		for i in map(BInteger, range(1, size.intValue() + 1)):
+			range_element = self.functionCall(size.minus(i).succ())
+			result_map = result_map.set(i, frozenset([range_element]))
+		return BRelation(result_map)
+
+	def front(self) -> 'BRelation':
+		return self.domainSubstraction(BSet(self.card()))
+
+	def tail(self) -> 'BRelation':
+		size = self._size().intValue()
+		result_map = immutables.Map()
+		for i in map(BInteger, range(2, size+1)):
+			range_element = self.functionCall(i)
+			result_map = result_map.set(i.pred(), frozenset([range_element]))
+		return BRelation(result_map)
+	
+	def take(self, n: 'BInteger') -> 'BRelation':
+		size = self._size()
+		if n.greaterEqual(size).booleanValue():
+			return BRelation(self.map)
+		result_map = self.map
+		# Remove sets with index greater than n
+		i: BInteger
+		for i in map(BInteger, range(n.intValue() + 1, size.intValue() + 1)):
+			result_map = result_map.delete(i)
+		return BRelation(result_map)
+
+	def drop(self, n: 'BInteger') -> 'BRelation':
+		size = self._size().intValue()
+		this_map = self.map
+		result_map = immutables.Map()
+		for i in map(BInteger, range(n.intValue() + 1, size + 1)):
+			current_set = this_map[i]
+			result_map = result_map.set(i.minus(n), current_set)
+		return BRelation(result_map)
+
+	def concat(self, arg: 'BRelation') -> 'BRelation':
+		result_map = self.map
+		other_map = arg.map
+		size = self.card()
+		i: BInteger
+		for i in map(BInteger, range(1, arg._size().intValue() + 1)):
+			result_map = result_map.set(size.plus(i), other_map[i])
+		return BRelation(result_map)
+
+	def conc(self) -> 'BRelation':
+		result = BRelation()
+		size = self.card().intValue()
+		for i in map(BInteger, range(1, size + 1)):
+			result = result.concat(self.functionCall(i))
+		return result
+
+	def append(self, arg) -> 'BRelation':
+		result_map = self.map
+		result_map = result_map.set(self.card().succ(), frozenset([arg]))
+		return BRelation(result_map)
+
+	def prepend(self, arg) -> 'BRelation':
+		result_map = immutables.Map()
+		this_map = self.map
+		size = self._size().intValue()
+		for i in map(BInteger, range(1, size + 1)):
+			result_map = result_map.set(i.succ(), this_map.get(i))
+		result_map = result_map.set(BInteger(1), frozenset([arg]))
+		return BRelation(result_map)
+
+	def directProduct(self, arg: 'BRelation') -> 'BRelation':
+		this_map = self.map
+		this_domain = this_map.keys()
+		other_map = arg.map
+
+		result_map = immutables.Map()
+		for obj in this_domain:
+			domain_element = obj
+			this_range = this_map.get(domain_element, None)
+			other_range = other_map.get(domain_element, None)
+			if other_range is None:
+				continue
+			result_range = set()
+			for lhs in this_range:
+				for rhs in other_range:
+					lhs_element = lhs
+					rhs_element = rhs
+					result_range = result_range.union([BTuple(lhs_element, rhs_element)])
+			result_map = result_map.set(domain_element, result_range)
+		return BRelation(result_map)
+	
+	def parallelProduct(self, arg: 'BRelation') -> 'BRelation':
+		this_map = self.map
+		this_domain = this_map.keys()
+
+		other_map = arg.map
+		other_domain = other_map.keys()
+
+		result_map = immutables.Map()
+
+		for domain_elementThis in this_domain:
+			for domaineElementOther in other_domain:
+				domain_element_this_element = domain_elementThis
+				domain_element_other_element = domaineElementOther
+
+				this_range = this_map[domain_element_this_element]
+				other_range = other_map[domain_element_other_element]
+
+				result_range = set()
+				for lhs in this_range:
+					for rhs in other_range:
+						lhs_element = lhs
+						rhs_element = rhs
+						result_range = result_range.union([BTuple(lhs_element, rhs_element)])
+				_tuple = BTuple(domain_element_this_element, domain_element_other_element)
+				result_map = result_map.set(_tuple, result_range)
+		return BRelation(result_map)
+
+	def composition(self, arg: 'BRelation') -> 'BRelation':
+		this_map = self.map
+		other_map = arg.map
+
+		this_domain = this_map.keys()
+
+		result_map = immutables.Map()
+
+		for e1 in this_domain:
+			domain_element = e1
+			_range = this_map[domain_element]
+
+			_set = set()
+			for e2 in _range:
+				range_element = e2
+				union_element = other_map.get(range_element)
+				if union_element is None:
+					union_element = set()
+				_set = _set.union(union_element)
+			result_map = result_map.set(domain_element, _set)
+		return BRelation(result_map)
+
+	def iterate(self, n: 'BInteger') -> 'BRelation':
+		this_relation = self
+		result = self.identity(self.domain())
+		for _ in map(BInteger, range(1, n.intValue() + 1)):
+			result = result.union(result.composition(this_relation))
+		return result
+
+	def closure(self) -> 'BRelation':
+		this_relation = self
+		result = self.identity(self.domain())
+		next_result = result.composition(this_relation)
+		while True:
+			last_result = deepcopy(result)
+			result = result.union(next_result)
+			next_result = result.composition(this_relation)
+			if result.equal(last_result).booleanValue():
+				break
+		return result
+
+	def closure1(self) -> 'BRelation':
+		this_relation = self
+		result = self
+		next_result = result.composition(this_relation)
+		while True:
+			last_result = deepcopy(result)
+			result = result.union(next_result)
+			next_result = result.composition(this_relation)
+			if result.equal(last_result).booleanValue():
+				break
+		return result
+
+	@staticmethod
+	def projection1(arg1: 'BSet', arg2: 'BSet') -> 'BRelation':
+		arg_set_1 = arg1.getSet()
+		arg_set_2 = arg2.getSet()
+
+		result_map = immutables.Map()
+		for e1 in arg_set_1:
+			for e2 in arg_set_2:
+				element1 = e1
+				element2 = e2
+
+				_tuple = BTuple(element1, element2)
+				result_map = result_map.set(_tuple, frozenset([element1]))
+		return BRelation(result_map)
+
+	@staticmethod
+	def projection2(arg1: 'BSet', arg2: 'BSet') -> 'BRelation':
+		arg_set_1 = arg1.getSet()
+		arg_set_2 = arg2.getSet()
+
+		result_map = immutables.Map()
+		for e1 in arg_set_1:
+			for e2 in arg_set_2:
+				element1 = e1
+				element2 = e2
+
+				_tuple = BTuple(element1, element2)
+				result_map = result_map.set(_tuple, frozenset([element2]))
+		return BRelation(result_map)
+	
+	def fnc(self) -> 'BRelation':
+		this_map = self.map
+		domain = self.domain().getSet()
+
+		result_map = immutables.Map()
+		for e in domain:
+			domain_element = e
+			_range = this_map[e]
+			range_set = BSet(_range)
+			result_map = result_map.set(domain_element, frozenset([range_set]))
+		return BRelation(result_map)
+
+	def rel(self) -> 'BRelation':
+		domain = self.domain()
+
+		result_map = immutables.Map()
+		for domain_element in domain:
+			_range = self.functionCall(domain_element)
+			range_set = _range.getSet()
+			result_map = result_map.set(domain_element, range_set)
+		return BRelation(result_map)
+
+	@staticmethod
+	def identity(arg: 'BSet') -> 'BRelation':
+		result_map = immutables.Map()
+		for e in arg:
+			result_map = result_map.set(e, frozenset([e]))
+		return BRelation(result_map)
+
+	@staticmethod
+	def cartesianProduct(arg1: 'BSet', arg2: 'BSet') -> 'BRelation':
+		result_map = immutables.Map()
+		for e1 in arg1:
+			result_map = result_map.set(e1, arg2.getSet())
+		return BRelation(result_map)
+	
+	def nondeterminism(self) -> 'BTuple':
+		domain = self.map.keys()
+
+		index = random.choice(range(len(domain)))
+		i = 0
+		domain_element = None
+		for obj in domain:
+			if i == index:
+				domain_element = obj
+				break
+			i = i + 1
+
+		_range = self.map[domain_element]
+		index = random.choice(range(_range.size()))
+		i = 0
+		for obj in _range:
+			if i == index:
+				return BTuple(domain_element, obj)
+			i = i + 1
+		return None
+
+	def isTotal(self, domain: 'BSet'):
+		return self.domain().equal(domain)
+
+	def isTotalInteger(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isTotalNatural(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isTotalNatural1(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isTotalString(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isTotalStruct(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isPartial(self, domain: 'BSet'):
+		return self.domain().strictSubset(domain)
+
+	def isPartialInteger(self):
+		for e in self.domain():
+			if(isinstance(e, BInteger)):
+				return BBoolean(True)
+			else:
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def isPartialNatural(self):
+		for e in self.domain():
+			if(isinstance(e, BInteger) and not e.isNatural().booleanValue()):
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def isPartialNatural1(self):
+		for e in self.domain():
+			if isinstance(e, BInteger) and not e.isNatural1().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def isPartialString(self):
+		for e in self.domain():
+			if isinstance(e, BString) and not e.isString().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def isPartialStruct(self):
+		for e in self.domain():
+			if isinstance(e, BStruct) and not e.isRecord().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkDomain(self, domain: 'BSet'):
+		return self.domain().subset(domain)
+
+	def checkDomainInteger(self):
+		for e in self.domain():
+			if isinstance(e, BInteger):
+				return BBoolean(True)
+			else:
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkDomainNatural(self):
+		for e in self.domain():
+			if isinstance(e, BInteger) and not e.isNatural().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkDomainNatural1(self):
+		for e in self.domain():
+			if isinstance(e, BInteger) and not e.isNatural1().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkDomainString(self):
+		for e in self.domain():
+			if isinstance(e, BString) and not e.isString().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkDomainStruct(self):
+		for e in self.domain():
+			if isinstance(e, BStruct) and not e.isRecord().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkRange(self, _range: 'BSet'):
+		return self._range().subset(_range)
+
+	def checkRangeInteger(self):
+		for e in self._range():
+			if isinstance(e, BInteger):
+				return BBoolean(True)
+			else:
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkRangeNatural(self):
+		for e in self._range():
+			if isinstance(e, BInteger) and not e.isNatural().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkRangeNatural1(self):
+		for e in self._range():
+			if isinstance(e, BInteger) and not e.isNatural1().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkRangeString(self):
+		for e in self._range():
+			if isinstance(e, BString) and not e.isString().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def checkRangeStruct(self) -> 'BBoolean':
+		for e in self._range():
+			if isinstance(e, BStruct) and not e.isRecord().booleanValue():
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def isRelation(self) -> 'BBoolean':
+		return BBoolean(True)
+
+	def isFunction(self) -> 'BBoolean':
+		for element in self.domain():
+			_range = self.map[element]
+			if _range.size() > 1:
+				return BBoolean(False)
+		return BBoolean(True)
+
+	def isSurjection(self, _range: 'BSet'):
+		return self._range().equal(_range)
+
+	def isSurjectionInteger(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isSurjectionNatural(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isSurjectionNatural1(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isSurjectionString(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isSurjectionStruct(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isInjection(self) -> 'BBoolean':
+		visited = set()
+		for element in self.domain():
+			_range = self.map[element]
+			for e in _range:
+				range_element = e
+				if range_element in visited:
+					return BBoolean(False)
+				visited = visited.union([range_element])
+		return BBoolean(True)
+
+	def isBijection(self, _range: 'BSet'):
+		return self.isSurjection(_range) and self.isInjection()
+
+	def isBijectionInteger(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isBijectionNatural(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isBijectionNatural1(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isBijectionString(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def isBijectionStruct(self) -> 'BBoolean':
+		return BBoolean(False)
+
+	def __str__(self) -> 'str':
+
+		this_map = self.map
+		domain = this_map.keys()
+
+		size = self.size()
+		i = 0
+
+		sb = "{"
+		for e1 in domain:
+			domain_element = e1
+			_range = this_map[domain_element]
+			for e2 in _range:
+				range_element = e2
+				sb += "("
+				sb += str(domain_element)
+				sb += " |-> "
+				sb += str(range_element)
+				sb += ")"
+				if i+1 < size:
+					sb += ", "
+				i = i + 1
+		sb += "}"
+		return sb
diff --git a/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BSet.py b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BSet.py
new file mode 100644
index 0000000000000000000000000000000000000000..92ddd92c5944bf150a7bac47ccd396ca1df5bc7a
--- /dev/null
+++ b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BSet.py
@@ -0,0 +1,254 @@
+from btypes.BInteger import *
+from btypes.BBoolean import *
+from btypes.BString import *
+from btypes.BStruct import *
+
+from pyrsistent import pset
+from pyrsistent import PSet
+
+
+class BSet:
+
+    def __init__(self, *args):
+        if len(args) == 1 and type(args[0]) is PSet:
+            self.__set = args[0]
+        else:
+            self.__set = pset(args)
+
+    def __str__(self) -> 'str':
+        return '{' + ', '.join([str(x) for x in self.__set]) + '}'
+
+    def __eq__(self, other):
+        if self is other:
+            return True
+        if other is None or type(self) != type(other):
+            return False
+        if not self.__set == other.__set:
+            return False
+        return True
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def __len__(self):
+        return len(self.__set)
+
+    def union(self, other=None):
+        if other is None:
+            if len(self.__set) == 0:
+                return BSet()
+            elif type(next(iter(self.__set))) == BSet:
+                return reduce(lambda a, e: a.union(e), self, BSet())
+            elif type(next(iter(self.__set))) == BRelation:
+                return reduce(lambda a, e: a.union(e), self, BRelation())
+
+        return BSet(self.__set.union(other.getSet()))
+
+    def intersect(self, other=None):
+        if other is None:
+            if len(self.__set) == 0:
+                return BSet()
+            elif type(next(iter(self.__set))) == BSet:
+                return reduce(lambda a, e: a.intersect(e), self, BSet())
+            elif type(next(iter(self.__set))) == BRelation:
+                return reduce(lambda a, e: a.intersect(e), self, BRelation())
+
+        return BSet(self.__set.intersection(other.getSet()))
+
+    def difference(self, other: 'BSet') -> 'BSet':
+        return BSet(self.__set.difference(other.__set))
+
+    def complement(self, other: 'BSet') -> 'BSet':
+        return BSet(self.__set.difference(other.__set))
+
+    def card(self) -> 'BInteger':
+        return BInteger(len(self.__set))
+
+    def size(self) -> 'BInteger':
+        return BInteger(len(self.__set))
+
+    def elementOf(self, obj) -> 'BBoolean':
+        return BBoolean(obj in self.__set)
+
+    def notElementOf(self, obj) -> 'BBoolean':
+        return BBoolean(obj not in self.__set)
+
+    def subset(self, other: 'BSet') -> 'BBoolean':
+        return BBoolean(self.__set.issubset(other.__set))
+
+    def notSubset(self, other: 'BSet') -> 'BBoolean':
+        return BBoolean(not self.__set.issubset(other.__set))
+
+    def strictSubset(self, other: 'BSet') -> 'BBoolean':
+        return BBoolean(other.size() != self.size() and self.__set.issubset(other.__set))
+
+    def strictNotSubset(self, other: 'BSet') -> 'BBoolean':
+        return BBoolean(other.size() == self.size() and not self.__set.issubset(other.__set))
+
+    def contains(self, other):
+        return other in self.__set
+
+    def containsAll(self, other):
+        for o in other:
+            if o not in self.__set:
+                return False
+        return True
+
+    def isEmpty(self) -> 'int':
+        return len(self.__set) == 0
+
+    def equal(self, other) -> 'bool':
+        return self.__eq__(other)
+
+    def unequal(self, other) -> 'bool':
+        return self.__ne__(other)
+
+    def nondeterminism(self):
+        return random.choice(self.__set)
+
+    def min(self):
+        return min(self.__set)
+
+    def max(self):
+        return max(self.__set)
+
+    def pow(self) -> 'BSet':
+        result = BSet()
+        start = BSet()
+        queue = [start]
+        result = result.union(BSet(start))
+        while not len(queue) == 0:
+            currentSet = queue.pop()
+            for element in self.__set:
+                nextSet = currentSet.union(BSet(element))
+                previousSize = result.size()
+                result = result.union(BSet(nextSet))
+                if previousSize < result.size():
+                    queue.append(nextSet)
+
+        return result
+        # s = list(self.__set)
+        # return BSet(frozenset(chain.from_iterable(BSet(combinations(s, r)) for r in range(len(s) + 1))))
+
+    def pow1(self) -> 'BSet':
+        return self.pow().difference(BSet(BSet()))
+
+    # Only finite subsets are supported so fin = pow
+    def fin(self) -> 'BSet':
+        return self.pow()
+
+    def fin1(self) -> 'BSet':
+        return self.pow1()
+
+    def subsetOfInteger(self) -> 'bool':
+        for element in self.__set:
+            if not isinstance(element, BInteger):
+                return False
+        return True
+
+    def strictSubsetOfInteger(self) -> 'bool':
+        return self.subsetOfInteger()
+
+    def notSubsetOfInteger(self) -> 'bool':
+        return not self.subsetOfInteger()
+
+    def notStrictSubsetOfInteger(self) -> 'bool':
+        return not self.strictSubsetOfInteger()
+
+    def subsetOfNatural(self) -> 'bool':
+        for element in self.__set:
+            if not (isinstance(element, BInteger) and element.isNatural().booleanValue()):
+                return False
+        return True
+
+    def strictSubsetOfNatural(self) -> 'bool':
+        return self.subsetOfNatural()
+
+    def notSubsetOfNatural(self) -> 'bool':
+        return not self.subsetOfNatural()
+
+    def notStrictSubsetOfNatural(self) -> 'bool':
+        return not self.strictSubsetOfNatural()
+
+    def subsetOfNatural1(self) -> 'bool':
+        for element in self.__set:
+            if not (isinstance(element, BInteger) and element.isNatural1().booleanValue()):
+                return False
+        return True
+
+    def subsetOfString(self) -> 'bool':
+        for element in self.__set:
+            if not isinstance(element, BString):
+                return False
+        return True
+
+    def strictSubsetOfString(self) -> 'bool':
+        return self.subsetOfString()
+
+    def notSubsetOfString(self) -> 'bool':
+        return not self.subsetOfString()
+
+    def notStrictSubsetOfString(self) -> 'bool':
+        return not self.strictSubsetOfString()
+
+    def subsetOfStruct(self) -> 'bool':
+        for element in self.__set:
+            if not isinstance(element, BStruct):
+                return False
+        return True
+
+    def strictSubsetOfStruct(self) -> 'bool':
+        return self.subsetOfStruct()
+
+    def notSubsetOfStruct(self) -> 'bool':
+        return not self.subsetOfStruct()
+
+    def notStrictSubsetOfStruct(self) -> 'bool':
+        return not self.strictSubsetOfStruct()
+
+    def equalInteger(self) -> 'BBoolean':
+        return BBoolean(False)
+
+    def unequalInteger(self) -> 'BBoolean':
+        return BBoolean(True)
+
+    def equalNatural(self) -> 'BBoolean':
+        return BBoolean(False)
+
+    def unequalNatural(self) -> 'BBoolean':
+        return BBoolean(True)
+
+    def equalNatural1(self) -> 'BBoolean':
+        return BBoolean(False)
+
+    def unequalNatural1(self) -> 'BBoolean':
+        return BBoolean(True)
+
+    def equalString(self) -> 'BBoolean':
+        return BBoolean(False)
+
+    def unequalString(self) -> 'BBoolean':
+        return BBoolean(True)
+
+    def equalStruct(self) -> 'BBoolean':
+        return BBoolean(False)
+
+    def unequalStruct(self) -> 'BBoolean':
+        return BBoolean(True)
+
+    def getSet(self):
+        return self.__set
+
+    @staticmethod
+    def interval(a: 'BInteger', b: 'BInteger') -> 'BSet':
+        r = list(map(BInteger, range(a.intValue(), b.intValue() + 1)))
+        return BSet(*r)
+
+    def __hash__(self):
+        return hash(self.__set)
+
+    def __iter__(self):
+        return iter(self.__set)
+
+
+from btypes.BRelation import *
diff --git a/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BString.py b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BString.py
new file mode 100755
index 0000000000000000000000000000000000000000..6403d7e95d942301c6d43d1957975ca460a5f195
--- /dev/null
+++ b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BString.py
@@ -0,0 +1,38 @@
+from btypes.BBoolean import *
+
+class BString:
+
+	def __init__(self, value):
+		self.__value = value
+
+	def getValue(self):
+		return value
+
+	def __eq__(self, other: 'BString') -> 'BBoolean':
+		return BBoolean(self.__value == other.__value)
+
+	def equals(self, other) -> 'bool':
+		return self.__eq__(other)
+
+	def length(self) -> 'int':
+		return len(self.__value)
+
+	def isEmpty(self) -> 'bool':
+		return not self.__value
+
+	def hashCode(self):
+		hash(self.__value)
+
+
+
+	def toString(self) -> 'str':
+		return '"' + self.__value + '"'
+
+	def isCase(self, other) -> 'bool':
+		return self.__value.equals(other)
+
+	def isString(self) -> 'BBoolean':
+		return BBoolean(True)
+
+	def isNotString(self) -> 'BBoolean':
+		return BBoolean(False)
diff --git a/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BStruct.py b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BStruct.py
new file mode 100755
index 0000000000000000000000000000000000000000..4015efa460b8f36bb832a48730f4413a8bf4bbc0
--- /dev/null
+++ b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BStruct.py
@@ -0,0 +1,10 @@
+from btypes.BBoolean import *
+
+class BStruct:
+
+    def isRecord(self):
+        return BBoolean(True)
+
+    def isNotRecord(self):
+        return BBoolean(False)
+
diff --git a/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BTuple.py b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BTuple.py
new file mode 100755
index 0000000000000000000000000000000000000000..318be351a49b990ad68937c1d35b3f48db4b8b4c
--- /dev/null
+++ b/btypes_primitives/src/main/python_pyrsistant_immutable/btypes/BTuple.py
@@ -0,0 +1,42 @@
+from btypes.BObject import *
+from btypes.BBoolean import *
+
+
+class BTuple(BObject):
+
+    def __init__(self, first, second):
+        if first is None or second is None:
+            raise ValueError()
+        self.first = first
+        self.second = second
+
+    def __eq__(self, other):
+        return self.equals(other)
+
+    def equals(self, other) -> 'bool':
+        if self is other:
+            return True
+        if other is None or type(self) != type(other):
+            return False
+
+        b_objects = other
+        # elements is never None
+        return b_objects.projection1().__eq__(self.first) and b_objects.projection2().__eq__(self.second)
+
+    def __hash__(self):
+        return hash(hash(self.first) + hash(self.second))
+
+    def projection1(self):
+        return self.first
+
+    def projection2(self):
+        return self.second
+
+    def __str__(self):
+        return "(" + str(self.projection1()) + " |-> " + str(self.projection2()) + ')'
+
+    def equal(self, other: 'BTuple') -> 'BBoolean':
+        return BBoolean(self.equals(other))
+
+    def unequal(self, other: 'BTuple') -> 'BBoolean':
+        return BBoolean(not self.equals(other))