diff --git a/btypes_lazy/src/main/rust/btypes/Cargo.toml b/btypes_lazy/src/main/rust/btypes/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..f1c1a2a93fe25128e266521f7468465569ade379
--- /dev/null
+++ b/btypes_lazy/src/main/rust/btypes/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "btypes"
+version = "0.1.0"
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+rand = "0.8.3"
+im = "15.0.0"
+lazy_static = "1.4.0"
\ No newline at end of file
diff --git a/btypes_lazy/src/main/rust/btypes/src/bboolean.rs b/btypes_lazy/src/main/rust/btypes/src/bboolean.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b1188ebb86908343e28a29db304c0f18e0e8b531
--- /dev/null
+++ b/btypes_lazy/src/main/rust/btypes/src/bboolean.rs
@@ -0,0 +1,40 @@
+use crate::bobject::BObject;
+
+pub type BBoolean = bool;
+
+pub trait IntoBool {
+    #![allow(non_snake_case)]
+    fn booleanValue(&self) -> bool;
+}
+
+pub trait BBooleanT: BObject + IntoBool + Copy {
+    fn new(val: bool) -> Self;
+    fn or(&self, other: &Self) -> Self;
+    fn xor(&self, other: &Self) -> Self;
+    fn and(&self, other: &Self) -> Self;
+    fn not(&self) -> Self;
+    fn implies(&self, other: &Self) -> Self;
+    fn equivalent(&self, other: &Self) -> Self;
+    fn equal(&self, other: &Self) -> Self;
+    fn unequal(&self, other: &Self) -> Self;
+}
+
+impl IntoBool for bool {
+    fn booleanValue(&self) -> bool {
+        return *self;
+    }
+}
+
+impl BObject for bool {}
+
+impl BBooleanT for bool {
+    fn new(val: bool) -> Self { val }
+    fn or(&self, other: &Self) -> Self { *self || *other }
+    fn xor(&self, other: &Self) -> Self { *self ^ *other }
+    fn and(&self, other: &Self) -> Self { *self && *other }
+    fn not(&self) -> Self { !*self }
+    fn implies(&self, other: &Self) -> Self { !*self || *other }
+    fn equivalent(&self, other: &Self) -> Self { *self == *other }
+    fn equal(&self, other: &Self) -> Self { *self == *other }
+    fn unequal(&self, other: &Self) -> Self { *self != *other }
+}
diff --git a/btypes_lazy/src/main/rust/btypes/src/binteger.rs b/btypes_lazy/src/main/rust/btypes/src/binteger.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e3e70874cc1fb28f3648ae9d3f66291047221c42
--- /dev/null
+++ b/btypes_lazy/src/main/rust/btypes/src/binteger.rs
@@ -0,0 +1,138 @@
+use std::convert::TryInto;
+use crate::bboolean::{BBoolean, BBooleanT};
+use crate::bobject::BObject;
+
+use std::fmt;
+use std::hash::Hash;
+
+pub trait BInt: BObject { fn get_binteger_value(&self) -> BInteger { panic!("get_integer_value not implemented!"); }}
+pub trait FromBInt { fn from<T: BInt>(value: &T) -> Self; }
+
+#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct BInteger {
+    val: i64,
+}
+
+impl BObject for BInteger {}
+impl BInt for BInteger {
+    fn get_binteger_value(&self) -> BInteger { return *self; }
+}
+
+impl FromBInt for BInteger {
+    fn from<T: BInt>(value: &T) -> Self {
+        return value.get_binteger_value();
+    }
+}
+
+impl BInteger {
+    #![allow(non_snake_case, dead_code)]
+
+    pub const fn new(init: i64) -> BInteger {
+        return BInteger {
+            val: init,
+        }
+    }
+
+    pub fn get_val(&self) -> i64 { return self.val; }
+
+    pub fn compareTo(&self, o: &BInteger) -> i64 {
+        return self.val - o.val;
+    }
+
+    pub fn lessEqual(&self, o: &BInteger) -> BBoolean {
+        return BBoolean::new(self.val <= o.val);
+    }
+
+    pub fn greaterEqual(&self, o: &BInteger) -> BBoolean {
+        return BBoolean::new(self.val >= o.val);
+    }
+
+    pub fn less(&self, o: &BInteger) -> BBoolean {
+        return BBoolean::new(self.val < o.val);
+    }
+
+    pub fn greater(&self, o: &BInteger) -> BBoolean {
+        return BBoolean::new(self.val > o.val);
+    }
+
+    pub fn equal(&self, o: &BInteger) -> BBoolean {
+        return BBoolean::new(self.val == o.val);
+    }
+
+    pub fn unequal(&self, o: &BInteger) -> BBoolean {
+        return BBoolean::new(self.val != o.val);
+    }
+
+    pub fn plus(&self, v: &BInteger) -> BInteger {
+        return BInteger::new(self.val + v.val);
+    }
+
+    pub fn minus(&self, v: &BInteger) -> BInteger {
+        return BInteger::new(self.val - v.val);
+    }
+
+    pub fn multiply(&self, v: &BInteger) -> BInteger {
+        return BInteger::new(self.val * v.val);
+    }
+
+    pub fn power(&self, v: &BInteger) -> BInteger {
+        if v.val < 0 { panic!("Power with negative exponent!") }
+        let exp: u32 = v.val.unsigned_abs().try_into().unwrap();
+        return BInteger::new(self.val.pow(exp));
+    }
+
+    pub fn divide(&self, v: &BInteger) -> BInteger {
+        return BInteger::new(self.val / v.val);
+    }
+
+    pub fn modulo(&self, v: &BInteger) -> BInteger {
+        //return BInteger::new(self.val.rem_euclid(v.val));
+        return BInteger::new(self.val % v.val);
+    }
+
+    pub fn succ(&self) -> BInteger {
+        return BInteger::new(self.val + 1);
+    }
+
+    pub fn pred(&self) -> BInteger {
+        return BInteger::new(self.val - 1);
+    }
+
+    pub fn negative(&self) -> BInteger {
+        return BInteger::new(-self.val);
+    }
+
+    pub fn positive(&self) -> BInteger {
+        return *self;
+    }
+
+    pub fn isInteger(&self) -> BBoolean {
+        return BBoolean::new(true);
+    }
+
+    pub fn isNotInteger(&self) -> BBoolean {
+        return BBoolean::new(false);
+    }
+
+    pub fn isNatural(&self) -> BBoolean {
+        return BBoolean::new(self.val >= 0);
+    }
+
+    pub fn isNotNatural(&self) -> BBoolean {
+        return BBoolean::new(self.val < 0);
+    }
+
+    pub fn isNatural1(&self) -> BBoolean {
+        return BBoolean::new(self.val > 0);
+    }
+
+    pub fn isNotNatural1(&self) -> BBoolean {
+        return BBoolean::new(self.val < 1);
+    }
+}
+
+impl fmt::Display for BInteger {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", self.val)
+    }
+}
\ No newline at end of file
diff --git a/btypes_lazy/src/main/rust/btypes/src/bobject.rs b/btypes_lazy/src/main/rust/btypes/src/bobject.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a913ee366977ce5b49bbec8353c0e8363623e3cb
--- /dev/null
+++ b/btypes_lazy/src/main/rust/btypes/src/bobject.rs
@@ -0,0 +1,8 @@
+use std::hash::Hash;
+use std::clone::Clone;
+use std::fmt::{Debug, Display};
+use crate::bboolean::{BBoolean, BBooleanT};
+
+pub trait BObject: Eq + Hash + Ord + Clone + Display + Debug {
+    fn is_struct(&self) -> BBoolean { BBoolean::new(false) }
+}
\ No newline at end of file
diff --git a/btypes_lazy/src/main/rust/btypes/src/brelation.rs b/btypes_lazy/src/main/rust/btypes/src/brelation.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d1eab3dffa1067b36e38952b53c5f77198e25e49
--- /dev/null
+++ b/btypes_lazy/src/main/rust/btypes/src/brelation.rs
@@ -0,0 +1,652 @@
+#![ allow( dead_code, non_snake_case) ]
+
+use std::collections::hash_map::DefaultHasher;
+use std::collections::LinkedList;
+use std::cell::RefCell;
+use std::cmp::Ordering;
+use crate::bobject::BObject;
+use crate::binteger::{BInt, BInteger, FromBInt};
+use crate::btuple::BTuple;
+
+use std::fmt;
+use std::hash::{Hash, Hasher};
+use std::convert::TryInto;
+use rand::prelude::IteratorRandom;
+use crate::orderedhashset::OrderedHashSet as OrdSet; //TODO try OrdMap instead
+use im::OrdMap as HashMap;
+use crate::bboolean::{BBoolean, BBooleanT};
+use crate::brelation::CombiningType::{DIFFERENCE, INTERSECTION, UNION};
+use crate::bset::{BSet, SetLike, TBSet};
+use crate::bstring::TBString;
+use crate::bstruct::BStruct;
+
+enum CombiningType {
+    DIFFERENCE,
+    INTERSECTION,
+    UNION
+}
+
+#[derive(Default, Debug, Eq, Clone)]
+pub struct BRelation<L: BObject, R: BObject> {
+    map: HashMap<L, OrdSet<R>>,
+    hash_cache: RefCell<Option<u64>>,
+}
+
+impl<L: BObject, R: BObject> PartialOrd for BRelation<L, R> {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> { self.map.partial_cmp(&other.map) }
+    fn lt(&self, other: &Self) -> bool { self.map.lt(&other.map) }
+    fn le(&self, other: &Self) -> bool { self.map.le(&other.map) }
+    fn gt(&self, other: &Self) -> bool { self.map.gt(&other.map) }
+    fn ge(&self, other: &Self) -> bool { self.map.ge(&other.map) }
+}
+
+impl<L: BObject, R: BObject> Ord for BRelation<L, R> {
+    fn cmp(&self, other: &Self) -> Ordering { self.map.cmp(&other.map) }
+    fn max(self, other: Self) -> Self {
+        match self.cmp(&other) {
+            Ordering::Less => other,
+            Ordering::Greater => self,
+            Ordering::Equal => other,
+        }
+    }
+    fn min(self, other: Self) -> Self {
+        match self.cmp(&other) {
+            Ordering::Less => self,
+            Ordering::Greater => other,
+            Ordering::Equal => self,
+        }
+    }
+    fn clamp(self, min: Self, max: Self) -> Self {
+        if self < min {
+            min
+        } else if self > max {
+            max
+        } else {
+            self
+        }
+    }
+}
+
+//TODO: check if replacing cache with mutex works and does not impact permormance too much
+unsafe impl<L: BObject, R: BObject> Sync for BRelation<L, R> {}
+
+impl<L: BObject, R: BObject> PartialEq for BRelation<L, R> {
+    fn eq(&self, other: &BRelation<L, R>) -> bool {
+        self.map.eq(&other.map)
+    }
+}
+
+impl<L: BObject, R: BObject> Hash for BRelation<L, R> {
+    fn hash<H: Hasher>(self: &BRelation<L, R>, state: &mut H) {
+        let cache = self.hash_cache.clone().take();
+        let hash: u64;
+
+        if cache.is_none() {
+            let mut hasher = DefaultHasher::new();
+            self.map.hash(&mut hasher);
+            /*
+            let mut kvs = self.map.iter().collect::<Vec<(&L, &OrdSet<R>)>>();
+            kvs.sort_by(|(k1, _v1), (k2, _v2)| k1.cmp(k2));
+            for (key, value) in kvs {
+                key.hash(&mut hasher);
+                value.iter().for_each(|v| v.hash(&mut hasher));
+            }
+            */
+            hash = hasher.finish();
+            //println!("BRelation: cache miss");
+            self.hash_cache.replace(Option::Some(hash));
+        } else {
+            //println!("BRelation: cache hit");
+            hash = cache.unwrap();
+        }
+        hash.hash(state);
+    }
+}
+
+pub trait TBRelation {
+    type Left: BObject;
+    type Right: BObject;
+
+    fn get_as_brelation(&self) -> &BRelation<Self::Left, Self::Right>;
+
+    fn isPartialInteger(&self) -> BBoolean { return BBoolean::new(false); }
+    fn checkDomainInteger(&self) -> BBoolean { return BBoolean::new(false); }
+    fn isPartialNatural(&self) -> BBoolean { return BBoolean::new(false); }
+    fn checkDomainNatural(&self) -> BBoolean { return BBoolean::new(false); }
+    fn isPartialNatural1(&self) -> BBoolean { return BBoolean::new(false); }
+    fn checkDomainNatural1(&self) -> BBoolean { return BBoolean::new(false); }
+    fn checkRangeInteger(&self) -> BBoolean { return BBoolean::new(false); }
+    fn checkRangeNatural(&self) -> BBoolean { return BBoolean::new(false); }
+    fn checkRangeNatural1(&self) -> BBoolean { return BBoolean::new(false); }
+    fn checkDomainString(&self) -> BBoolean { return BBoolean::new(false); }
+    fn isPartialString(&self) -> BBoolean { return BBoolean::new(false); }
+    fn checkRangeString(&self) -> BBoolean { return BBoolean::new(false); }
+    fn checkDomainStruct(&self) -> BBoolean { return BBoolean::new(false); }
+    fn isPartialStruct(&self) -> BBoolean { return BBoolean::new(false); }
+    fn checkRangeStruct(&self) -> BBoolean { return BBoolean::new(false); }
+}
+
+impl<L: BObject, R: BObject> fmt::Display for BRelation<L, R> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let mut result = "{".to_owned();
+        let mut first = true;
+        for (key, range) in self.map.iter() {
+            for value in range.iter() {
+                if !first { result = result + ", " }
+                else { first = false; }
+                result = result + &format!("({} |-> {})", key, value).to_string();
+            }
+        }
+        result = result + "}";
+        return write!(f, "{}", result);
+    }
+}
+impl<L: BObject, R: BObject> TBRelation for BRelation<L, R> {
+    type Left = L;
+    type Right = R;
+    fn get_as_brelation(&self) -> &BRelation<Self::Left, Self::Right> { self }
+}
+impl<L: BObject, R: BObject> BObject for BRelation<L, R> {}
+
+impl<L: 'static + BObject, R: 'static + BObject> BRelation<L, R> {
+    pub fn new(mut args: Vec<BTuple<L, R>>) -> BRelation<L,R> {
+        let mut ret: BRelation<L, R> = BRelation {map: HashMap::new(), hash_cache: RefCell::new(Option::None) };
+        while !args.is_empty() {
+            let current_tuple = args.remove(0);
+            ret.insert(&current_tuple);
+        }
+        return ret;
+    }
+
+    pub fn fromSet(set: BSet<BTuple<L, R>>) -> BRelation<L, R> {
+        let mut ret: BRelation<L, R> = BRelation {map: HashMap::new(), hash_cache: RefCell::new(Option::None)};
+        set.iter().for_each(|current_tuple| ret.insert(current_tuple));
+        return ret;
+    }
+
+    fn insert(&mut self, tuple: &BTuple<L, R>) {
+        let set = self.map.get(&tuple.projection1());
+        let new_set: OrdSet<R>;
+        if set.is_some() {
+            new_set = set.unwrap().update(tuple.projection2());
+        } else {
+            new_set = OrdSet::from(vec![tuple.projection2()]);
+        }
+        self.map.insert(tuple.projection1(), new_set);
+        self.hash_cache.replace(Option::None);
+    }
+
+    fn update(&self, key: L, value: OrdSet<R>) -> Self {
+        BRelation{ map: self.map.update(key, value), hash_cache: RefCell::new(Option::None) }
+    }
+
+    fn update_unit(&self, key: L, value: R) -> Self {
+        self.update(key, OrdSet::unit(value))
+    }
+
+    pub fn card(&self) -> BInteger {
+        return self.size();
+    }
+
+    pub fn size(&self) -> BInteger {
+        let mut size = 0;
+        for val in self.map.values() {
+            size += val.len();
+        }
+        return BInteger::new(size.try_into().unwrap());
+    }
+
+    pub fn intersect(&self, relation: &BRelation<L, R>) -> BRelation<L, R> {
+        return self.relation_combine(relation, INTERSECTION)
+    }
+
+    pub fn difference(&self, relation: &BRelation<L, R>) -> BRelation<L, R> {
+        return self.relation_combine(relation, DIFFERENCE);
+    }
+
+    pub fn _union(&self, relation: &BRelation<L, R>) -> BRelation<L, R> {
+        return self.relation_combine(relation, UNION);
+    }
+
+    fn relation_combine(&self, relation: &BRelation<L, R>, comb_type: CombiningType) -> BRelation<L, R> {
+        let other_map = &relation.map;
+        let other_domain: OrdSet<L> = OrdSet::from(relation.map.keys().cloned().collect::<Vec<L>>());
+        let this_domain: OrdSet<L> = OrdSet::from(self.map.keys().cloned().collect::<Vec<L>>());
+        let intersection_domain = this_domain.clone().intersection(other_domain.clone());
+        let difference_domain = this_domain.relative_complement(other_domain.clone());
+
+        let loop1_set;
+        let loop2_set;
+        match comb_type {
+            CombiningType::DIFFERENCE => {loop1_set = difference_domain; loop2_set = intersection_domain;}
+            CombiningType::INTERSECTION => {loop1_set = intersection_domain; loop2_set = difference_domain;}
+            CombiningType::UNION => {loop1_set = other_domain; loop2_set = OrdSet::new();}
+        }
+
+        let empty_map = OrdSet::new();
+        let mut result_map = BRelation{map: self.map.clone(), hash_cache: RefCell::new(Option::None)};
+        for domain_element in loop1_set {
+            let this_range_set = self.map.get(&domain_element).unwrap_or(&empty_map).clone();
+            let other_range_set = other_map.get(&domain_element).unwrap_or(&empty_map).clone();
+            let new_range_set;
+            match comb_type {
+                CombiningType::DIFFERENCE => {new_range_set = this_range_set.relative_complement(other_range_set);}
+                CombiningType::INTERSECTION => {new_range_set = this_range_set.intersection(other_range_set);}
+                CombiningType::UNION => {new_range_set = this_range_set.union(other_range_set);}
+            }
+            if new_range_set.is_empty() {
+                result_map.map.remove(&domain_element);
+            } else {
+                result_map.map.insert(domain_element, new_range_set);
+            }
+        }
+
+        for domain_element in loop2_set {
+            result_map.map.remove(&domain_element);
+        }
+
+        return result_map;
+    }
+
+    pub fn equal(&self, o: &BRelation<L, R>) -> BBoolean {
+        return BBoolean::new(self.eq(o));
+    }
+
+    pub fn unequal(&self, o: &BRelation<L, R>) -> BBoolean {
+        return BBoolean::new(!self.eq(o));
+    }
+
+    pub fn elementOf(&self, element: &BTuple<L, R>) -> BBoolean {
+        let prj1 = element.projection1();
+        let prj2 = element.projection2();
+        return BBoolean::new(self.map.get(&prj1).unwrap_or(&OrdSet::new()).contains(&prj2));
+    }
+
+    pub fn notElementOf(&self, element: &BTuple<L, R>) -> BBoolean {
+        let prj1 = element.projection1();
+        let prj2 = element.projection2();
+        return BBoolean::new(!self.map.get(&prj1).unwrap_or(&OrdSet::new()).contains(&prj2));
+    }
+
+    pub fn relationImage(&self, domain: &BSet<L>) -> BSet<R> {
+        let result_set = OrdSet::unions(domain.iter().map(|e| self.map.get(&e).unwrap_or(&OrdSet::new()).clone()).into_iter());
+        return BSet::fromOrdSet(result_set);
+    }
+
+    pub fn functionCall(&self, arg: &L) -> R {
+        let range = self.map.get(arg);
+        if range.is_none() {
+            panic!("Argument is not in the domain of this relation");
+        }
+
+        return range.unwrap().iter().next().unwrap_or_else(|| panic!("Argument is not in the domain of this relation")).clone();
+    }
+
+    pub fn pow(&self) -> BSet<BRelation<L, R>> { return self._pow(true); }
+    pub fn fin(&self) -> BSet<BRelation<L, R>> { return self.pow(); }
+    pub fn pow1(&self) -> BSet<BRelation<L, R>> { return self._pow(false); }
+    pub fn fin1(&self) -> BSet<BRelation<L, R>> { return self.pow1(); }
+
+    fn _pow(&self, with_empty_set: bool) -> BSet<BRelation<L, R>> {
+        let this_map = &self.map;
+
+        let mut result: BSet<BRelation<L, R>> = BSet::new(vec![]);
+        let start: BRelation<L, R> = BRelation::new(vec![]);
+        let mut queue: LinkedList<BRelation<L, R>> = LinkedList::new();
+        queue.push_back(start.clone());
+        if with_empty_set { result = result._union(&BSet::<BRelation<L, R>>::new(vec![start])); }
+        while !queue.is_empty() {
+            let current_set = queue.pop_front().unwrap();
+
+            for domain_element in self.map.keys() {
+                let range_option = this_map.get(&domain_element);
+                if range_option.is_none() { break; }
+                let mut range = range_option.unwrap().clone();
+                while !range.is_empty() {
+                    let range_element = range.remove_min().unwrap();
+                    let next_relation = current_set._union(&BRelation::fromSet(BSet::new(vec![BTuple::new(domain_element.clone(), range_element)])));
+                    let previous_size = result.size();
+                    result = result._union(&BSet::new(vec![next_relation.clone()]));
+                    if previous_size < result.size() { queue.push_back(next_relation) }
+                }
+            }
+        }
+        return result;
+    }
+
+    pub fn domain(&self) -> BSet<L> {
+        let result_set: Vec<L> = self.map.iter().filter_map(|(k, v)| return if !v.is_empty() { Some(k.clone()) } else { None }).collect();
+        return BSet::new(result_set);
+    }
+
+    pub fn range(&self) -> BSet<R> {
+        if self.map.is_empty() {
+            return BSet::<R>::new(vec![]);
+        } else {
+            return BSet::fromOrdSet(self.map.values().cloned().reduce(|v1, v2| v1.union(v2)).unwrap());
+        }
+    }
+
+    pub fn inverse(&self) -> BRelation<R, L> {
+        if self.map.is_empty() {
+            return BRelation::new(vec![]);
+        } else {
+            return self.map.iter().fold(BRelation::<R, L>::new(vec![]),
+                                        |mut map, (k, v)| {
+                                            v.iter().for_each(|cv| map.insert(&BTuple::new(cv.clone(), k.clone())));
+                                            return map
+                                        });
+        }
+    }
+
+    pub fn domainRestriction(&self, arg: &BSet<L>) -> BRelation<L, R> {
+        return self.domainSubstraction(&self.domain().difference(&arg));
+    }
+
+    pub fn domainSubstraction(&self, arg: &BSet<L>) -> BRelation<L, R> {
+        return BRelation {map: arg.iter().fold(self.map.clone(), |map, e| map.without(e)), hash_cache: RefCell::new(Option::None)}
+    }
+
+    pub fn rangeRestriction(&self, arg: &BSet<R>) -> BRelation<L, R> {
+        //return self.rangeSubstraction(self.range().difference(arg));
+        return self.map.iter().filter_map(|(key, value)| {
+            let resut_range = BSet::fromOrdSet(value.clone()).intersect(arg);
+            if resut_range.isEmpty() { Option::None } else { Option::Some((key, resut_range)) }
+        }).fold(BRelation::<L, R>::new(vec![]), |rel, (key, val)| rel.update(key.clone(), val.as_ord_set()));
+    }
+
+    pub fn rangeSubstraction(&self, arg: &BSet<R>) -> BRelation<L, R> {
+        return self.map.iter().filter_map(|(key, value)| {
+                let resut_range = BSet::fromOrdSet(value.clone()).difference(arg);
+                if resut_range.isEmpty() { Option::None } else { Option::Some((key, resut_range)) }
+            }).fold(BRelation::<L, R>::new(vec![]), |rel, (key, val)| rel.update(key.clone(), val.as_ord_set()));
+    }
+
+    pub fn subset(&self, arg: &BRelation<L, R>) -> BBoolean {
+        let emptySet = OrdSet::new();
+        for (k, v) in self.map.clone() {
+            let arg_v = arg.map.get(&k).unwrap_or(&emptySet).clone();
+            if !v.is_subset(arg_v) { return BBoolean::new(false) }
+        }
+        return BBoolean::new(true);
+    }
+
+    pub fn notSubset(&self, arg: &BRelation<L, R>) -> BBoolean {
+        return self.subset(arg).not();
+    }
+
+    pub fn _override(&self, arg: &BRelation<L, R>) -> BRelation<L, R> {
+        return BRelation { map: arg.map.clone().union(self.map.clone()), hash_cache: RefCell::new(Option::None)}
+    }
+
+    pub fn directProduct<ArgR: 'static + BObject>(&self, arg: &BRelation<L, ArgR>) -> BRelation<L, BTuple<R, ArgR>> {
+        self.map.iter()
+            .fold(BRelation::<L, BTuple<R, ArgR>>::new(vec![]),
+                  |mut rel, (k, v)| {
+                      let option = arg.map.get(k);
+                      if option.is_some() {
+                          rel.map.insert(k.clone(), BSet::<R>::cartesian::<R, ArgR>(v, option.unwrap()));
+                      }
+                      return rel;
+                  })
+    }
+
+    pub fn parallelProduct<ArgL, ArgR>(&self, arg: &BRelation<ArgL, ArgR>)
+        -> BRelation<BTuple<L, ArgL>, BTuple<R, ArgR>>
+    where ArgL: 'static + BObject,
+          ArgR: 'static + BObject {
+        let mut result_relation: BRelation<BTuple<L, ArgL>, BTuple<R, ArgR>> = BRelation::new(vec![]);
+        for (this_key, this_range) in self.map.clone() {
+            for (that_key, that_range) in arg.map.clone() {
+                result_relation.map.insert(BTuple::new(this_key.clone(), that_key),
+                                           BSet::<R>::cartesian::<R, ArgR>(&this_range, &that_range));
+            }
+        }
+        return result_relation; //TODO?
+    }
+
+    pub fn composition<NewR: 'static + BObject>(&self, arg: &BRelation<R, NewR>) -> BRelation<L, NewR> {
+        let mut result_set: BRelation<L, NewR> = BRelation::new(vec![]);
+        let empty_set = OrdSet::<NewR>::new();
+        for (this_key, this_range) in self.map.iter() {
+            let new_range = this_range.iter().fold(OrdSet::<NewR>::new(),
+                                                   |set, element| set.union(arg.map.get(element).unwrap_or(&empty_set).clone()));
+            if !new_range.is_empty() { result_set.map.insert(this_key.clone(), new_range); }
+        }
+        return result_set;
+    }
+
+    pub fn projection1(arg1: &BSet<L>, arg2: &BSet<R>) -> BRelation<BTuple<L,R>,L> {
+        return arg1.iter().fold(BRelation::new(vec![]), |rel, element1|
+            arg2.iter().cloned().fold(rel, |nrel, element2|
+                nrel.update_unit(BTuple::new(element1.clone(), element2), element1.clone())));
+    }
+
+    pub fn projection2(arg1: &BSet<L>, arg2: &BSet<R>) -> BRelation<BTuple<L,R>,R> {
+        return arg1.iter().fold(BRelation::new(vec![]), |rel, element1|
+            arg2.iter().cloned().fold(rel, |nrel, element2|
+                nrel.update_unit(BTuple::new(element1.clone(), element2.clone()), element2)));
+    }
+
+    pub fn fnc(&self) -> BRelation<L, BSet<R>> {
+        return self.map.iter().fold(BRelation::new(vec![]), |rel, (key, range)|
+            rel.update_unit(key.clone(), BSet::fromOrdSet(range.clone())))
+    }
+
+    pub fn cartesianProduct(set_a: &BSet<L>, set_b: &BSet<R>) -> BRelation<L, R> {
+        // slightly inefficient due to double iteration
+        return BSet::<L>::cartesian::<L, R>(&set_a.as_ord_set(), &set_b.as_ord_set()).iter()
+            .fold(BRelation::new(vec![]), |mut rel, tuple| { rel.insert(tuple); return rel; });
+    }
+
+    pub fn nondeterminism(&self) -> BTuple<L, R> {
+        let mut rng = rand::thread_rng();
+        let tuple = self.map.iter().choose(&mut rng).unwrap();
+        return BTuple::new(tuple.0.clone(), tuple.1.iter().choose(&mut rng).unwrap().clone());
+    }
+
+    pub fn isTotal(&self, domain: &BSet<L>) -> BBoolean { return self.domain().equal(domain); }
+    pub fn isTotalInteger(&self) -> BBoolean { return BBoolean::new(false); }
+    pub fn isTotalNatural(&self) -> BBoolean { return BBoolean::new(false); }
+    pub fn isTotalNatural1(&self) -> BBoolean { return BBoolean::new(false); }
+    pub fn isTotalString(&self) -> BBoolean { return BBoolean::new(false); }
+    pub fn isTotalStruct(&self) -> BBoolean { return BBoolean::new(false); }
+    pub fn isPartial(&self, domain: &BSet<L>) -> BBoolean { return self.domain().subset(domain); }
+    pub fn checkDomain(&self, domain: &BSet<L>) -> BBoolean { return self.domain().subset(domain); }
+    pub fn checkRange(&self, range: &BSet<R>) -> BBoolean { return self.range().subset(range); }
+
+    pub fn isRelation(&self) -> BBoolean { return BBoolean::new(true); }
+
+    pub fn isFunction(&self) -> BBoolean {
+        return BBoolean::new(self.map.values().find(|set| set.len() > 1).is_none());
+    }
+
+    pub fn isSurjection(&self, range: &BSet<R>) -> BBoolean { return self.range().equal(range); }
+    pub fn isSurjectionInteger(&self) -> BBoolean { return BBoolean::new(false); }
+    pub fn isSurjectionNatural(&self) -> BBoolean { return BBoolean::new(false); }
+    pub fn isSurjectionNatural1(&self) -> BBoolean { return BBoolean::new(false); }
+    pub fn isSurjectionString(&self) -> BBoolean { return BBoolean::new(false); }
+    pub fn isSurjectionStruct(&self) -> BBoolean { return BBoolean::new(false); }
+
+    pub fn isInjection(&self) -> BBoolean {
+        if self.map.is_empty() { return BBoolean::new(true); }
+        let mut ranges = self.map.values().cloned();
+        let mut checked = ranges.next().unwrap();
+        for to_check in ranges {
+            //current range "hits" and element that was already "hit" before
+            if !checked.clone().intersection(to_check.clone()).is_empty() { return BBoolean::new(false); }
+            checked = checked.union(to_check);
+        }
+        return BBoolean::new(true);
+    }
+
+    pub fn isBijection(&self, range: &BSet<R>) -> BBoolean { return self.isSurjection(range).and(&self.isInjection()); }
+    pub fn isBijectionInteger(&self) -> BBoolean { return BBoolean::new(false); }
+    pub fn isBijectionNatural(&self) -> BBoolean { return BBoolean::new(false); }
+    pub fn isBijectionNatural1(&self) -> BBoolean { return BBoolean::new(false); }
+    pub fn isBijectionString(&self) -> BBoolean { return BBoolean::new(false); }
+    pub fn isBijectionStruct(&self) -> BBoolean { return BBoolean::new(false); }
+}
+
+
+impl<L: 'static + BObject> BRelation<L, L> {
+    pub fn identity(set: &BSet<L>) -> BRelation<L, L> {
+        return set.iter().fold(BRelation::<L, L>::new(vec![]), |rel, v| rel.update_unit(v.clone(), v.clone()));
+    }
+
+    pub fn iterate(&self, n: &BInteger) -> BRelation<L, L> {
+        return (0..n.get_val()).fold(BRelation::identity(&self.domain()._union(&self.range())),
+                                     |rel, _| rel.composition(self));
+    }
+
+    pub fn closure(&self) -> BRelation<L, L> {
+        return self.closure_closure1(false);
+    }
+
+    pub fn closure1(&self) -> BRelation<L, L> {
+        return self.closure_closure1(true);
+    }
+
+    fn closure_closure1(&self, is_closure1: bool) -> BRelation<L, L> {
+        let mut result = if is_closure1 { BRelation::new(vec![]) } else { self.iterate(&BInteger::new(0)) };
+        let mut current_iteration = self.iterate(&BInteger::new(1));
+        let mut next_result = result._union(&current_iteration);
+        while !result.eq(&next_result) {
+            result = next_result;
+            current_iteration = current_iteration.composition(self);
+            next_result = result._union(&current_iteration);
+        }
+        return result;
+    }
+}
+
+//sequence
+impl<L, R> BRelation<L, R>
+where L: 'static + BInt + FromBInt,
+      R: 'static + BObject {
+    //this actually works, ridiculous...
+    pub fn first(&self) -> R {
+        return self.functionCall(&L::from(&BInteger::new(1)));
+    }
+
+    pub fn last(&self) -> R {
+        return self.functionCall(&L::from(&self.card()));
+    }
+
+    pub fn reverse(&self) -> BRelation<L, R> {
+        let size = self.card().succ();
+        return BRelation {
+            map: self.map.iter().fold(HashMap::<L, OrdSet<R>>::new(),
+                                      |result, (k, v)|
+                                          result.update(L::from(&size.minus(&k.get_binteger_value())), v.clone())),
+            hash_cache: RefCell::new(Option::None)
+        }
+    }
+
+    pub fn front(&self) -> BRelation<L, R> {
+        return self.domainSubstraction(&BSet::new(vec![L::from(&self.card())]));
+    }
+
+    pub fn tail(&self) -> BRelation<L, R> {
+        return self.drop(&BInteger::new(1))
+    }
+
+    pub fn take(&self, n: &BInteger) -> BRelation<L, R> {
+        return self.domainRestriction(&BSet::new((1..n.get_val()+1).map(|i| L::from(&BInteger::new(i))).collect()));
+    }
+
+    pub fn drop(&self, n: &BInteger) -> BRelation<L, R> {
+        return BSet::<BInteger>::interval(&n.succ(), &self.card()).iter().map(|i| (L::from(&i.minus(n)), L::from(i)))
+            .fold(BRelation::<L, R>::new(vec![]), |rel, (i, i2)| rel.update(i, self.map.get(&i2).unwrap().clone()));
+    }
+
+    pub fn concat(&self, arg: &BRelation<L,R>) -> BRelation<L, R> {
+        return arg.map.iter().fold(self.clone(), |rel, (k, v)|
+            rel.update(L::from(&k.get_binteger_value().plus(&self.card())), v.clone()));
+    }
+
+    pub fn append(&self, arg: &R) -> BRelation<L, R> {
+        return self.update_unit(L::from(&self.card().succ()), arg.clone());
+    }
+
+    pub fn prepend(&self, arg: &R) -> BRelation<L, R> {
+        return self.map.iter().fold(self.clone(), |rel, (k, v)|
+                                    rel.update(L::from(&k.get_binteger_value().succ()), v.clone()))
+                   .update_unit(L::from(&self.card().succ()), arg.clone());
+    }
+
+    pub fn isPartialInteger(&self) -> BBoolean { return BBoolean::new(true); }
+    pub fn checkDomainInteger(&self) -> BBoolean { return self.isPartialInteger(); }
+    pub fn isPartialNatural(&self) -> BBoolean { return self.domain().subsetOfNatural(); }
+    pub fn checkDomainNatural(&self) -> BBoolean { return self.isPartialNatural(); }
+    pub fn isPartialNatural1(&self) -> BBoolean { return self.domain().subsetOfNatural1(); }
+    pub fn checkDomainNatural1(&self) -> BBoolean { return self.isPartialNatural1(); }
+}
+
+impl<L, R> BRelation<L, R>
+    where L: 'static + BInt + FromBInt,
+          R: 'static + BObject + TBRelation,
+          R::Left: 'static + BInt + FromBInt {
+
+    pub fn conc(&self) -> BRelation<R::Left, R::Right> {
+        return self.map.values().map(|set| set.iter().next().unwrap().get_as_brelation())
+            .fold(BRelation::<R::Left, R::Right>::new(vec![]),
+                  |result, next| result.concat(next));
+    }
+}
+
+impl<L, R> BRelation<L, R>
+    where L: 'static + BObject,
+          R: 'static + BInt + FromBInt {
+
+    pub fn checkRangeInteger(&self) -> BBoolean { return BBoolean::new(true); }
+    pub fn checkRangeNatural(&self) -> BBoolean { return self.range().subsetOfNatural(); }
+    pub fn checkRangeNatural1(&self) -> BBoolean { return self.range().subsetOfNatural1(); }
+}
+
+impl<L, R> BRelation<L, R>
+where L: 'static + BObject,
+      R: 'static + BObject + TBSet {
+
+    pub fn rel(&self) -> BRelation<L, R::Item> {
+        return self.map.iter().fold(BRelation::<L, R::Item>::new(vec![]), |rel, (key, range)|
+            rel.update(key.clone(), range.iter().next().unwrap().as_ord_set()));
+    }
+
+}
+
+impl<L, R> BRelation<L, R>
+    where L: 'static + BObject + TBString,
+          R: 'static + BObject {
+
+    pub fn checkDomainString(&self) -> BBoolean { return BBoolean::new(true); }
+    pub fn isPartialString(&self) -> BBoolean { return self.checkDomainString(); }
+}
+
+impl<L, R> BRelation<L, R>
+    where L: 'static + BObject,
+          R: 'static + BObject + TBString {
+
+    pub fn checkRangeString(&self) -> BBoolean { return BBoolean::new(true); }
+}
+
+impl<L, R> BRelation<L, R>
+where L: 'static + BObject + BStruct,
+      R: 'static + BObject {
+
+    pub fn checkDomainStruct(&self) -> BBoolean { return BBoolean::new(true); }
+    pub fn isPartialStruct(&self) -> BBoolean { return self.checkDomainStruct(); }
+}
+
+impl<L, R> BRelation<L, R>
+    where L: 'static + BObject,
+          R: 'static + BObject + BStruct {
+
+    pub fn checkRangeStruct(&self) -> BBoolean { return BBoolean::new(true); }
+}
+
+impl<L: 'static + BObject, R: 'static + BObject> SetLike for BRelation<L, R> {
+    fn get_empty() -> Self { BRelation::<L, R>::new(vec![]) }
+    fn _union(&self, other: &Self) -> Self { self._union(other) }
+    fn intersect(&self, other: &Self) -> Self { self.intersect(other) }
+}
\ No newline at end of file
diff --git a/btypes_lazy/src/main/rust/btypes/src/bset.rs b/btypes_lazy/src/main/rust/btypes/src/bset.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b374eb142b63ca789ef1245823fde3f575ade86e
--- /dev/null
+++ b/btypes_lazy/src/main/rust/btypes/src/bset.rs
@@ -0,0 +1,399 @@
+#![ allow( dead_code, non_snake_case) ]
+
+use crate::bboolean::{IntoBool, BBoolean, BBooleanT};
+use crate::binteger::{BInt, BInteger};
+use crate::bstring::BString;
+use crate::bobject::BObject;
+use crate::btuple::BTuple;
+use crate::orderedhashset::OrderedHashSet as OrdSet;
+
+use std::any::TypeId;
+use std::borrow::Borrow;
+use std::convert::TryInto;
+use im::ordset::Iter;
+use std::hash::{Hash, Hasher};
+use std::collections::LinkedList;
+use std::fmt;
+use std::fmt::Debug;
+use rand::Rng;
+use crate::lazy_ops::set_ops::setops::{SetOp, SetOpTraits};
+
+pub trait TBSet: BObject {
+    type Item: BObject;
+
+    fn as_ord_set(&self) -> OrdSet<Self::Item>;
+    fn as_bset(&self) -> &BSet<Self::Item>;
+}
+
+pub trait SetLike: BObject {
+    fn get_empty() -> Self;
+    fn _union(&self, other: &Self) -> Self;
+    fn intersect(&self, other: &Self) -> Self;
+}
+
+#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
+pub struct BSet<T: BObject> {
+    set: OrdSet<T>,
+    transformation: Option<Box<dyn SetOp<Item = T>>>,
+}
+
+impl<T: BObject> SetOp for BSet<T> {
+    type Item = T;
+
+    fn compute(&self, _: &BSet<Self::Item>) -> BSet<Self::Item> {
+        match self.transformation.borrow() {
+            Some(trans) => trans.compute(&self) , //todo save computation?
+            None => self.clone(),
+        }
+    }
+
+    fn clone_box(&self) -> Box<dyn SetOp<Item=Self::Item>> {
+        todo!()
+    }
+}
+
+impl<T: BObject> SetOpTraits for BSet<T> {
+
+}
+
+impl<I: BObject> Hash for BSet<I> {
+    fn hash<H: Hasher>(self: &BSet<I>, state: &mut H) { self.set.hash(state); }
+}
+
+impl<T: BObject> BObject for BSet<T> {}
+
+impl<T: BObject> TBSet for BSet<T> {
+    type Item = T;
+    fn as_ord_set(&self) -> OrdSet<Self::Item> { self.set.clone() }
+    fn as_bset(&self) -> &BSet<Self::Item> { self }
+}
+
+impl<T: BObject> fmt::Display for BSet<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let mut result = "{".to_owned();
+        let mut first = true;
+        for e in self.set.iter() {
+            if !first { result = result + ", " }
+            else { first = false; }
+            result = result + &format!("{}", e).to_string();
+        }
+        result = result + "}";
+        return write!(f, "{}", result);
+    }
+}
+
+impl<'a, T: 'static + BObject> IntoIterator for &'a BSet<T>
+where
+    T: 'a + Ord,
+{
+    type Item = &'a T;
+    type IntoIter = im::ordset::Iter<'a, T>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.iter()
+    }
+}
+
+//TODO: check if replacing cache with mutex works and does not impact permormance too much
+unsafe impl<T: BObject> Sync for BSet<T> {}
+
+impl<T: 'static + BObject> BSet<T> {
+
+    pub fn new(mut args: Vec<T>) -> BSet<T> {
+        let mut ret: BSet<T> = BSet {
+            set: OrdSet::new(),
+            transformation: Option::None
+        };
+        while !args.is_empty() {
+            ret.set.insert(args.remove(0));
+        }
+        return ret;
+    }
+
+    pub fn fromOrdSet(set: OrdSet<T>) -> BSet<T> {
+        return BSet { set: set, transformation: Option::None };
+    }
+
+
+    pub fn iter(&self) -> Iter<'_, T> {
+        return self.set.iter();
+    }
+
+    pub fn size(&self) -> usize {
+        return self.set.len();
+    }
+
+    pub fn isEmpty(&self) -> bool {
+        return self.set.is_empty();
+    }
+
+    pub fn contains(&self, o: &T) -> bool {
+        return self.set.contains(o);
+    }
+
+    pub fn intersect(&self, set: &BSet<T>) -> BSet<T> {
+        return BSet{ set: self.set.clone().intersection(set.set.clone()), transformation: Option::None };
+    }
+
+    pub fn difference(&self, set: &BSet<T>) -> BSet<T> {
+        return BSet{ set: self.set.clone().relative_complement(set.set.clone()), transformation: Option::None };
+    }
+
+    pub fn _union(&self, set: &BSet<T>) -> BSet<T> {
+        return BSet{ set: self.set.clone().union(set.set.clone()), transformation: Option::None };
+    }
+
+    pub fn interval(a: &BInteger, b: &BInteger) -> BSet<BInteger> {
+        let mut result: BSet<BInteger> = BSet::new(vec![]);
+        for i in a.get_val()..b.get_val()+1 {
+            result.set.insert(BInteger::new(i));
+        }
+        return result;
+    }
+
+    pub fn card(&self) -> BInteger {
+        return self._size();
+    }
+
+    pub fn _size(&self) -> BInteger {
+        return BInteger::new(self.set.len().try_into().unwrap());
+    }
+
+    pub fn elementOf(&self, object: &T) -> BBoolean {
+        return BBoolean::new(self.contains(object));
+    }
+
+    pub fn notElementOf(&self, object: &T) -> BBoolean {
+        return BBoolean::new(!self.contains(object));
+    }
+
+    pub fn subset(&self, set: &BSet<T>) -> BBoolean {
+        return BBoolean::new(self.set.is_subset(&set.set));
+    }
+
+    pub fn notSubset(&self, set: &BSet<T>) -> BBoolean {
+        return BBoolean::new(!self.set.is_subset(&set.set));
+    }
+
+    pub fn strictSubset(&self, set: &BSet<T>) -> BBoolean {
+        return BBoolean::new(self.set.len() < set.set.len() && self.set.is_subset(&set.set));
+    }
+
+    pub fn strictNotSubset(&self, set: &BSet<T>) -> BBoolean {
+        return BBoolean::new(self.set.len() == set.set.len() || !self.set.is_subset(&set.set));
+    }
+
+    pub fn fin(&self) -> BSet<BSet<T>> {
+        return self.pow();
+    }
+
+    pub fn pow(&self) -> BSet<BSet<T>> {
+        return self._pow(true);
+    }
+
+    pub fn fin1(&self) -> BSet<BSet<T>> {
+        return self.pow1();
+    }
+
+    pub fn pow1(&self) -> BSet<BSet<T>> {
+        return self._pow(false);
+    }
+
+    fn _pow(&self, with_empty_set: bool) -> BSet<BSet<T>> {
+        let mut result: BSet<BSet<T>> = BSet::new(vec![]);
+        let start: BSet<T> = BSet::new(vec![]);
+        let mut queue: LinkedList<BSet<T>> = LinkedList::new();
+        queue.push_back(start.clone());
+        if with_empty_set { result = result._union(&BSet::new(vec![start])); }
+
+        while !queue.is_empty() {
+            let current_set: BSet<T> = queue.pop_front().unwrap();
+            for element in self.set.iter() {
+                let next_set: BSet<T> = current_set._union(&BSet::new(vec![element.clone()]));
+                let previous_size = result.size();
+                result = result._union(&BSet::new(vec![next_set.clone()]));
+                if previous_size < result.size() {
+                    queue.push_back(next_set);
+                }
+            }
+        }
+
+        return result;
+    }
+
+    pub fn nondeterminism(&self) -> T {
+        let mut rng = rand::thread_rng();
+        return self.set.iter().nth(rng.gen_range(0..self.set.len())).unwrap().clone();
+    }
+
+    pub fn equal(&self, other: &BSet<T>) -> BBoolean {
+        return BBoolean::new(self.set.eq(&other.set));
+    }
+
+    pub fn unequal(&self, other: &BSet<T>) -> BBoolean {
+        return BBoolean::new(!self.set.eq(&other.set));
+    }
+
+    pub fn subsetOfInteger(&self) -> BBoolean {
+        return BBoolean::new(TypeId::of::<BInteger>() == TypeId::of::<T>());
+    }
+
+    pub fn strictSubsetOfInteger(&self) -> BBoolean {
+        return self.subsetOfInteger();
+    }
+
+    pub fn notSubsetOfInteger(&self) -> BBoolean {
+        return self.subsetOfInteger().not();
+    }
+
+    pub fn equalInteger(&self) -> BBoolean {
+        return BBoolean::new(false);
+    }
+
+    pub fn unequalInteger(&self) -> BBoolean {
+        return BBoolean::new(true);
+    }
+
+    pub fn equalNatural(&self) -> BBoolean {
+        return BBoolean::new(false);
+    }
+
+    pub fn unequalNatural(&self) -> BBoolean {
+        return BBoolean::new(true);
+    }
+
+    pub fn equalNatural1(&self) -> BBoolean {
+        return BBoolean::new(false);
+    }
+
+    pub fn unequalNatural1(&self) -> BBoolean {
+        return BBoolean::new(true);
+    }
+
+    pub fn equalString(&self) -> BBoolean {
+        return BBoolean::new(false);
+    }
+
+    pub fn unequalString(&self) -> BBoolean {
+        return BBoolean::new(true);
+    }
+
+    pub fn equalStruct(&self) -> BBoolean {
+        return BBoolean::new(false);
+    }
+
+    pub fn unequalStruct(&self) -> BBoolean {
+        return BBoolean::new(true);
+    }
+
+    pub fn subsetOfString(&self) -> BBoolean {
+        return BBoolean::new(TypeId::of::<BString>() == TypeId::of::<T>());
+    }
+
+    pub fn strictSubsetOfString(&self) -> BBoolean {
+        return self.subsetOfString();
+    }
+
+    pub fn notSubsetOfString(&self) -> BBoolean {
+        return self.subsetOfString().not();
+    }
+
+    pub fn notStrictSubsetOfString(&self) -> BBoolean {
+        return self.strictSubsetOfString().not();
+    }
+
+    pub fn subsetOfStruct(&self) -> BBoolean {
+        return self.is_struct();
+    }
+
+    pub fn strictsubsetOfStruct(&self) -> BBoolean {
+        return self.subsetOfStruct();
+    }
+
+    pub fn notsubsetOfStruct(&self) -> BBoolean {
+        return self.subsetOfStruct().not();
+    }
+
+    pub fn notStrictsubsetOfStruct(&self) -> BBoolean {
+        return self.strictsubsetOfStruct().not();
+    }
+
+    //rust specific
+    pub fn cartesian<T1: 'static + BObject, T2: 'static + BObject>(set_a: &OrdSet<T1>, set_b: &OrdSet<T2>) -> OrdSet<BTuple<T1, T2>> {
+        if set_a.is_empty() || set_b.is_empty() {return OrdSet::<BTuple<T1, T2>>::new();}
+        return set_a.iter()
+            .fold(OrdSet::<BTuple<T1, T2>>::new(),
+                  |set, lhs| set_b.iter().cloned()
+                                                         .fold(set,
+                                                               |tset, rhs| tset.update(BTuple::new(lhs.clone(), rhs))))
+    }
+}
+
+impl<T: 'static + BInt> BSet<T> {
+
+    pub fn notStrictSubsetOfInteger(&self) -> BBoolean {
+        return self.strictSubsetOfInteger().not();
+    }
+
+    pub fn subsetOfNatural(&self) -> BBoolean {
+        if self.subsetOfInteger().booleanValue() {
+            return BBoolean::new(self.set.iter().find(|i| i.get_binteger_value().isNotNatural().booleanValue()).is_none());
+        }
+        return BBoolean::new(false);
+    }
+
+    pub fn strictSubsetOfNatural(&self) -> BBoolean {
+        return self.subsetOfNatural();
+    }
+
+    pub fn notSubsetOfNatural(&self) -> BBoolean {
+        return self.subsetOfNatural().not();
+    }
+
+    pub fn notStrictSubsetOfNatural(&self) -> BBoolean {
+        return self.strictSubsetOfNatural().not();
+    }
+
+    pub fn subsetOfNatural1(&self) -> BBoolean {
+        if self.subsetOfInteger().booleanValue() {
+            return BBoolean::new(self.set.iter().find(|i| i.get_binteger_value().isNotNatural1().booleanValue()).is_none());
+        }
+        return BBoolean::new(false);
+    }
+
+    pub fn strictSubsetOfNatural1(&self) -> BBoolean {
+        return self.subsetOfNatural();
+    }
+
+    pub fn notSubsetOfNatural1(&self) -> BBoolean {
+        return self.subsetOfNatural1().not();
+    }
+
+    pub fn notStrictSubsetOfNatural1(&self) -> BBoolean {
+        return self.strictSubsetOfNatural1().not();
+    }
+
+    pub fn _min(self: &BSet<T>) -> BInteger {
+        return self.set.get_min().unwrap().get_binteger_value();
+    }
+
+    pub fn _max(self: &BSet<T>) -> BInteger {
+        return self.set.get_max().unwrap().get_binteger_value();
+    }
+}
+
+impl<T: 'static + BObject> SetLike for BSet<T> {
+    fn get_empty() -> Self { BSet::<T>::new(vec![]) }
+    fn _union(&self, other: &Self) -> Self { self._union(other) }
+    fn intersect(&self, other: &Self) -> Self { self.intersect(other) }
+}
+
+impl<T: 'static + SetLike> BSet<T> {
+    pub fn unary__union(&self) -> T {
+        return self.iter().fold(T::get_empty(), |result, next| result._union(next));
+    }
+
+    pub fn unary_intersect(&self) -> T {
+        return self.iter().fold(T::get_empty(), |result, next| result.intersect(next));
+    }
+}
\ No newline at end of file
diff --git a/btypes_lazy/src/main/rust/btypes/src/bstring.rs b/btypes_lazy/src/main/rust/btypes/src/bstring.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e49302b41c87b2d8b511fd4bad1b10456db9e2e8
--- /dev/null
+++ b/btypes_lazy/src/main/rust/btypes/src/bstring.rs
@@ -0,0 +1,19 @@
+use std::fmt::{Display, Formatter};
+use crate::bobject::BObject;
+
+#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct BString {
+    val: String,
+}
+
+pub trait TBString {}
+
+impl TBString for BString {}
+
+impl Display for BString {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.val)
+    }
+}
+
+impl BObject for BString {}
\ No newline at end of file
diff --git a/btypes_lazy/src/main/rust/btypes/src/bstruct.rs b/btypes_lazy/src/main/rust/btypes/src/bstruct.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ca151047b3a4cf9c69f159a08cd1f9a00a7aa1a6
--- /dev/null
+++ b/btypes_lazy/src/main/rust/btypes/src/bstruct.rs
@@ -0,0 +1,6 @@
+use crate::bboolean::{BBoolean, BBooleanT};
+use crate::bobject::BObject;
+
+pub trait BStruct: BObject{
+    fn is_struct(&self) -> BBoolean { BBoolean::new(true) }
+}
\ No newline at end of file
diff --git a/btypes_lazy/src/main/rust/btypes/src/btuple.rs b/btypes_lazy/src/main/rust/btypes/src/btuple.rs
new file mode 100644
index 0000000000000000000000000000000000000000..31779d4b33902a8e1a064477a9aca0159d74a9d7
--- /dev/null
+++ b/btypes_lazy/src/main/rust/btypes/src/btuple.rs
@@ -0,0 +1,35 @@
+use crate::bobject::BObject;
+use std::fmt;
+
+#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct BTuple<L: BObject, R: BObject>{
+    left_val: L,
+    right_val: R,
+}
+
+impl<L: BObject, R: BObject> fmt::Display for BTuple<L, R> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        return write!(f, "({} |-> {})", self.left_val, self.right_val);
+    }
+}
+
+impl<L: BObject, R:BObject> BObject for BTuple<L, R>{}
+
+impl<L: BObject, R: BObject> BTuple<L, R> {
+    pub fn new(left: L, right: R) -> BTuple<L, R> {
+        return BTuple {left_val: left, right_val: right,};
+    }
+
+    pub fn from_refs(left: &L, right: &R) -> BTuple<L, R> {
+        return BTuple {left_val: left.clone(), right_val: right.clone(),};
+    }
+
+    pub fn projection1(&self) -> L {
+        return self.left_val.clone();
+    }
+
+    pub fn projection2(&self) -> R {
+        return self.right_val.clone();
+    }
+
+}
diff --git a/btypes_lazy/src/main/rust/btypes/src/butils.rs b/btypes_lazy/src/main/rust/btypes/src/butils.rs
new file mode 100644
index 0000000000000000000000000000000000000000..dc5e4835e4e5689e41450769c411309f7667f415
--- /dev/null
+++ b/btypes_lazy/src/main/rust/btypes/src/butils.rs
@@ -0,0 +1,6 @@
+use crate::bboolean::{BBoolean, BBooleanT};
+use crate::bset::BSet;
+
+lazy_static! {
+    pub static ref BOOL: BSet<BBoolean> = BSet::new(vec![BBoolean::new(true), BBoolean::new(false)]);
+}
\ No newline at end of file
diff --git a/btypes_lazy/src/main/rust/btypes/src/lazy_ops/mod.rs b/btypes_lazy/src/main/rust/btypes/src/lazy_ops/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..032fa0acb3fa7131be974169637c97365c39e609
--- /dev/null
+++ b/btypes_lazy/src/main/rust/btypes/src/lazy_ops/mod.rs
@@ -0,0 +1 @@
+pub mod set_ops;
\ No newline at end of file
diff --git a/btypes_lazy/src/main/rust/btypes/src/lazy_ops/set_ops/mod.rs b/btypes_lazy/src/main/rust/btypes/src/lazy_ops/set_ops/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c4442fe7273818eb198fbb935b45dd7847d8b3bb
--- /dev/null
+++ b/btypes_lazy/src/main/rust/btypes/src/lazy_ops/set_ops/mod.rs
@@ -0,0 +1 @@
+pub mod setops;
\ No newline at end of file
diff --git a/btypes_lazy/src/main/rust/btypes/src/lazy_ops/set_ops/setops.rs b/btypes_lazy/src/main/rust/btypes/src/lazy_ops/set_ops/setops.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d72d55426bbe28e297419037cdbc87f3b7cb4dac
--- /dev/null
+++ b/btypes_lazy/src/main/rust/btypes/src/lazy_ops/set_ops/setops.rs
@@ -0,0 +1,102 @@
+use std::cmp::Ordering;
+use std::fmt;
+use std::fmt::{Debug, Formatter};
+use std::marker::PhantomData;
+use crate::bobject::BObject;
+use crate::bset::BSet;
+
+pub trait SetOp: SetOpTraits + Debug {
+    type Item: BObject;
+
+    fn compute(&self, lhs: &BSet<Self::Item>) -> BSet<Self::Item>;
+    fn clone_box(&self) -> Box<dyn SetOp<Item = Self::Item>>;
+
+    //PartialEq
+    fn eq(&self, other: &Box<dyn SetOp<Item = Self::Item>>) -> bool;
+
+    //PartialOrd
+    fn partial_cmp(&self, other: &Box<dyn SetOp<Item = Self::Item>>) -> Option<Ordering>;
+
+    //Ord
+    fn cmp(&self, other: &Box<dyn SetOp<Item = Self::Item>>) -> Ordering;
+    fn max(&self, other: &Box<dyn SetOp<Item = Self::Item>>) -> Box<dyn SetOp<Item = Self::Item>>;
+    fn min(&self, other: &Box<dyn SetOp<Item = Self::Item>>) -> Box<dyn SetOp<Item = Self::Item>>;
+    fn clamp(&self, min: &Box<dyn SetOp<Item = Self::Item>>, max: &Box<dyn SetOp<Item = Self::Item>>) -> Box<dyn SetOp<Item = Self::Item>>;
+}
+
+pub trait SetOpTraits {
+    //type Bla: BObject;
+
+    //fn clone_box(&self) -> Box<dyn SetOp<Item = Self::Bla>>;
+    //fn default_box() -> Box<dyn SetOp>;
+}
+/*
+impl<T> SetOpTraits for T
+where T: 'static + SetOp + Clone,
+{
+    fn clone_box<S: BObject>(&self) -> Box<dyn SetOp<Item = S>> {
+        Box::new(self.clone())
+    }
+}
+*/
+impl<T: BObject> Clone for Box<dyn SetOp<Item = T>> {
+    fn clone(&self) -> Self {
+        self.clone_box()
+    }
+}
+
+impl<T: BObject> Eq for Box<dyn SetOp<Item=T>> {}
+
+impl<T: BObject> PartialEq<Self> for Box<dyn SetOp<Item=T>> {
+    fn eq(&self, other: &Self) -> bool {
+        self.eq(other)
+    }
+}
+
+impl<T: BObject> PartialOrd<Self> for Box<dyn SetOp<Item=T>> {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        self.partial_cmp(other)
+    }
+}
+
+impl<T: BObject> Ord for Box<dyn SetOp<Item = T>> {
+    fn cmp(&self, other: &Self) -> Ordering {
+        self.cmp(other)
+    }
+}
+
+impl<T: 'static +  BObject> dyn SetOp<Item = T> {
+    fn default() -> Box<Self> {
+        Box::new(Identity {phantom: PhantomData})
+    }
+}
+
+
+
+
+
+
+#[derive(Clone)]
+pub struct Identity<T: BObject> {
+    phantom: PhantomData<T>,
+}
+
+impl<T: BObject> Debug for Identity<T> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        write!(f, "Identity()")
+    }
+}
+
+impl<T: 'static +  BObject> SetOp for Identity<T> {
+    type Item = T;
+
+    fn compute(&self, lhs: &BSet<T>) -> BSet<T> {
+        lhs.clone()
+    }
+
+    fn clone_box(&self) -> Box<dyn SetOp<Item=Self::Item>> {
+        Box::new(Identity{phantom: PhantomData})
+    }
+}
+
+impl<T: 'static +  BObject> SetOpTraits for Identity<T>{}
\ No newline at end of file
diff --git a/btypes_lazy/src/main/rust/btypes/src/lib.rs b/btypes_lazy/src/main/rust/btypes/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..67e1bf40dc81246dc1ddce46c9ac766da5f579eb
--- /dev/null
+++ b/btypes_lazy/src/main/rust/btypes/src/lib.rs
@@ -0,0 +1,14 @@
+#[macro_use]
+extern crate lazy_static;
+
+pub mod bboolean;
+pub mod binteger;
+pub mod bobject;
+pub mod bstruct;
+pub mod bset;
+pub mod bstring;
+pub mod btuple;
+pub mod brelation;
+pub mod butils;
+pub mod orderedhashset;
+pub mod lazy_ops;
diff --git a/btypes_lazy/src/main/rust/btypes/src/orderedhashset.rs b/btypes_lazy/src/main/rust/btypes/src/orderedhashset.rs
new file mode 100644
index 0000000000000000000000000000000000000000..2680018b8716deff8f20ad27b0f484f7ef949a51
--- /dev/null
+++ b/btypes_lazy/src/main/rust/btypes/src/orderedhashset.rs
@@ -0,0 +1,148 @@
+use std::borrow::Borrow;
+use std::collections::hash_map::DefaultHasher;
+use std::cell::RefCell;
+use std::cmp::Ordering;
+use std::hash::{Hash, Hasher};
+
+use im::OrdSet;
+use im::ordset::Iter;
+use crate::bobject::BObject;
+
+#[derive(Default, Debug, Eq)]
+pub struct OrderedHashSet<I: BObject> {
+    set: OrdSet<I>,
+    hash_cache: RefCell<Option<u64>>,
+}
+
+impl<I: BObject> Clone for OrderedHashSet<I> {
+    fn clone(&self) -> Self {
+        OrderedHashSet { set: self.set.clone(), hash_cache: RefCell::new(self.hash_cache.borrow().clone()) }
+    }
+}
+
+impl<I: BObject> PartialOrd for OrderedHashSet<I> {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> { self.set.partial_cmp(&other.set) }
+    fn lt(&self, other: &Self) -> bool { self.set.lt(&other.set) }
+    fn le(&self, other: &Self) -> bool { self.set.le(&other.set) }
+    fn gt(&self, other: &Self) -> bool { self.set.gt(&other.set) }
+    fn ge(&self, other: &Self) -> bool { self.set.ge(&other.set) }
+}
+
+impl<I: BObject> Ord for OrderedHashSet<I> {
+    fn cmp(&self, other: &Self) -> Ordering { self.set.cmp(&other.set) }
+    fn max(self, other: Self) -> Self {
+        match self.cmp(&other) {
+            Ordering::Less => other,
+            Ordering::Greater => self,
+            Ordering::Equal => other,
+        }
+    }
+    fn min(self, other: Self) -> Self {
+        match self.cmp(&other) {
+            Ordering::Less => self,
+            Ordering::Greater => other,
+            Ordering::Equal => self,
+        }
+    }
+    fn clamp(self, min: Self, max: Self) -> Self {
+        if self < min {
+            min
+        } else if self > max {
+            max
+        } else {
+            self
+        }
+    }
+}
+
+//TODO: check if replacing cache with mutex works and does not impact permormance too much
+unsafe impl<T: BObject> Sync for OrderedHashSet<T> {}
+
+impl<I: BObject> PartialEq for OrderedHashSet<I> {
+    fn eq(&self, other: &Self) -> bool {
+        self.set.eq(&other.set)
+    }
+}
+
+impl<I: BObject> Hash for OrderedHashSet<I> {
+    fn hash<H: Hasher>(self: &OrderedHashSet<I>, state: &mut H) {
+        let cache = self.hash_cache.borrow().clone();
+        let hash: u64;
+
+        if cache.is_none() {
+            let mut hasher = DefaultHasher::new();
+            self.set.hash(&mut hasher);
+            hash = hasher.finish();
+            self.hash_cache.replace(Option::Some(hash));
+        } else {
+            hash = cache.unwrap();
+        }
+        hash.hash(state);
+    }
+}
+
+impl<A: BObject> IntoIterator for OrderedHashSet<A> {
+    type Item = A;
+    type IntoIter = im::ordset::ConsumingIter<A>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.set.into_iter()
+    }
+}
+
+impl<I: BObject> OrderedHashSet<I> {
+    pub fn new() -> Self { Self::from_set(OrdSet::new()) }
+
+    pub fn from_set(val: OrdSet<I>) -> Self { OrderedHashSet {set: val, hash_cache: RefCell::new(Option::None)} }
+
+    pub fn unit(item: I) -> Self { Self::from_set(OrdSet::unit(item)) }
+
+    pub fn from(items: Vec<I>) -> Self { Self::from_set(OrdSet::from(items)) }
+
+    pub fn update(&self, item: I) -> Self { Self::from_set(self.set.update(item)) }
+
+    pub fn unions<A: IntoIterator<Item = Self>>(a: A) -> Self {
+        a.into_iter().fold(Self::new(), Self::union)
+    }
+
+    pub fn iter(&self) -> Iter<'_, I> { self.set.iter() }
+
+    pub fn len(&self) -> usize { self.set.len() }
+    pub fn is_empty(&self) -> bool { self.set.is_empty() }
+
+    pub fn contains(&self, value: &I) -> bool { self.set.contains(value) }
+    pub fn is_subset<RS: Borrow<Self>>(&self, other: RS) -> bool {
+        let other = other.borrow();
+        self.set.is_subset(&other.set)
+    }
+
+    pub fn insert(&mut self, value: I) -> Option<I> {
+        self.hash_cache.replace(Option::None);
+        return self.set.insert(value);
+    }
+
+    pub fn remove_min(&mut self) -> Option<I> {
+        self.hash_cache.replace(Option::None);
+        return self.set.remove_min()
+    }
+
+    pub fn get_min(&self) -> Option<&I> { self.set.get_min() }
+    pub fn get_max(&self) -> Option<&I> { self.set.get_max() }
+
+    pub fn union(self, other: Self) -> Self {
+        self.hash_cache.replace(Option::None);
+        return Self::from_set(self.set.union(other.set));
+    }
+
+    pub fn intersection(self, other: Self) -> Self {
+        self.hash_cache.replace(Option::None);
+        return Self::from_set(self.set.intersection(other.set));
+    }
+
+    pub fn relative_complement(self, other: Self) -> Self {
+        self.hash_cache.replace(Option::None);
+        return Self::from_set(self.set.relative_complement(other.set));
+    }
+
+    pub fn get_set(self) -> OrdSet<I> { return self.set }
+}
\ No newline at end of file