diff --git a/btypes_lazy/src/main/rust/bmachine/Cargo.toml b/btypes_lazy/src/main/rust/bmachine/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..1f4c46e38bde5648b94b4d5e195236ccd4c1d26d
--- /dev/null
+++ b/btypes_lazy/src/main/rust/bmachine/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "bmachine"
+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"
+threadpool = "1.8.1"
+derivative = "2.2.0"
+dashmap = "5.1.0"
+btypes = { path = "../btypes" }
+
+[profile.release]
+opt-level = 3
+
+[profile.dev]
+opt-level = 0
diff --git a/btypes_lazy/src/main/rust/btypes/src/bset.rs b/btypes_lazy/src/main/rust/btypes/src/bset.rs
index 5d7771a7879e889cf5bf555b02c305c288e5e8fd..ea8a2fe8eb4866129b722ae63e10fe961eab7596 100644
--- a/btypes_lazy/src/main/rust/btypes/src/bset.rs
+++ b/btypes_lazy/src/main/rust/btypes/src/bset.rs
@@ -6,6 +6,8 @@ use crate::bstring::BString;
 use crate::bobject::BObject;
 use crate::btuple::BTuple;
 use crate::orderedhashset::OrderedHashSet as OrdSet;
+use crate::lazy_ops::set_ops::setops::{SetOp, SetOpTraits};
+use crate::lazy_ops::set_ops::union::Union;
 
 use std::any::TypeId;
 use std::borrow::Borrow;
@@ -16,7 +18,6 @@ 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;
@@ -41,9 +42,7 @@ impl<T: BObject> BSet<T> {
     const OP_NAME: &'static str = "set";
 }
 
-impl<T: BObject> SetOp for BSet<T> {
-    type Item = T;
-
+impl<T: 'static + BObject> SetOp for BSet<T> {
     fn compute(&self, _: &BSet<Self::Item>) -> BSet<Self::Item> {
         match self.transformation.borrow() {
             Some(trans) => trans.compute(&self) , //todo save computation?
@@ -52,45 +51,80 @@ impl<T: BObject> SetOp for BSet<T> {
     }
 
     fn clone_box(&self) -> Box<dyn SetOp<Item=Self::Item>> {
-        todo!()
+        Box::new(self.clone())
     }
 
     fn get_op_name(&self) -> &str {
         return BSet::<T>::OP_NAME;
     }
 
-    fn get_rhs(&self) -> Option<&Box<dyn SetOp<Item=Self::Item>>> {
-        return self.transformation.as_ref();
+    fn get_rhs(&self) -> Option<Box<dyn SetOp<Item=Self::Item>>> {
+        return self.transformation.clone();
+    }
+
+    fn to_string(&self, _lhs: Option<&str>) -> String {
+        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 + "}";
+        match &self.transformation {
+            Some(op) => return op.to_string(Option::Some(result.as_str())),
+            None => return result,
+        }
     }
 }
 
-impl<T: BObject> SetOpTraits for BSet<T> {
+impl<T: 'static + BObject> SetOpTraits for BSet<T> {
+    type Item = T;
 
+    fn iter_lazy(&self, _lhs: &BSet<Self::Item>) -> Iter<'_, T> {
+        match &self.transformation {
+            Some(op) => op.iter_lazy(self),
+            None => self.iter_directly(),
+        }
+    }
+
+    fn contains_lazy(&self, _lhs: &BSet<Self::Item>, o: &Self::Item) -> bool {
+        match &self.transformation {
+            Some(op) => op.contains_lazy(self, o),
+            None => self.contains_directly(o),
+        }
+    }
+
+    fn is_empty_lazy(&self, _lhs: &BSet<Self::Item>) -> bool {
+        match &self.transformation {
+            Some(op) => op.is_empty_lazy(self),
+            None => self.is_empty_directly(),
+        }
+    }
+
+    fn size_lazy(&self, _lhs: &BSet<Self::Item>) -> usize {
+        match &self.transformation {
+            Some(op) => op.size_lazy(self),
+            None => self.size_directly(),
+        }
+    }
 }
 
 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: 'static + BObject> BObject for BSet<T> {}
 
-impl<T: BObject> TBSet for BSet<T> {
+impl<T: 'static + 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> {
+impl<T: 'static + 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);
+        return write!(f, "{}", <BSet<T> as SetOp>::to_string(self, Option::None));
     }
 }
 
@@ -126,20 +160,20 @@ impl<T: 'static + BObject> BSet<T> {
         return BSet { set: set, transformation: Option::None };
     }
 
+    pub fn iter(&self) -> Iter<'_, T> { self.iter_lazy(self) }
 
-    pub fn iter(&self) -> Iter<'_, T> {
-        return self.set.iter();
-    }
+    pub fn iter_directly(&self) -> Iter<'_, T> { self.set.iter() }
 
-    pub fn size(&self) -> usize {
+    pub fn size(&self) -> usize { self.size_lazy(self) }
+    pub fn size_directly(&self) -> usize {
         return self.set.len();
     }
 
-    pub fn isEmpty(&self) -> bool {
-        return self.set.is_empty();
-    }
+    pub fn isEmpty(&self) -> bool { return self.is_empty_lazy(self); }
+    pub fn is_empty_directly(&self) -> bool { return self.set.is_empty() }
 
-    pub fn contains(&self, o: &T) -> bool {
+    pub fn contains(&self, o: &T) -> bool { self.contains_lazy(self, o) }
+    pub fn contains_directly(&self, o: &T) -> bool {
         return self.set.contains(o);
     }
 
@@ -148,10 +182,15 @@ impl<T: 'static + BObject> BSet<T> {
     }
 
     pub fn difference(&self, set: &BSet<T>) -> BSet<T> {
-        return BSet{ set: self.set.clone().relative_complement(set.set.clone()), transformation: Option::None };
+        let result: OrdSet<T> = self.iter().fold(OrdSet::new(), |r_set, element| if set.contains(element) { r_set } else { r_set.update(element.clone()) });
+        return BSet::fromOrdSet(result);
     }
 
     pub fn _union(&self, set: &BSet<T>) -> BSet<T> {
+        return BSet { set: self.set.clone(), transformation: Option::Some(Box::new(Union::new(set.clone())))}
+    }
+
+    pub fn real_union(&self, set: &BSet<T>) -> BSet<T> {
         return BSet{ set: self.set.clone().union(set.set.clone()), transformation: Option::None };
     }
 
@@ -168,7 +207,7 @@ impl<T: 'static + BObject> BSet<T> {
     }
 
     pub fn _size(&self) -> BInteger {
-        return BInteger::new(self.set.len().try_into().unwrap());
+        return BInteger::new(self.size().try_into().unwrap());
     }
 
     pub fn elementOf(&self, object: &T) -> BBoolean {
@@ -184,15 +223,15 @@ impl<T: 'static + BObject> BSet<T> {
     }
 
     pub fn notSubset(&self, set: &BSet<T>) -> BBoolean {
-        return BBoolean::new(!self.set.is_subset(&set.set));
+        return self.subset(set).not();
     }
 
     pub fn strictSubset(&self, set: &BSet<T>) -> BBoolean {
-        return BBoolean::new(self.set.len() < set.set.len() && self.set.is_subset(&set.set));
+        return BBoolean::new(self.size() < set.size() && self.subset(set));
     }
 
     pub fn strictNotSubset(&self, set: &BSet<T>) -> BBoolean {
-        return BBoolean::new(self.set.len() == set.set.len() || !self.set.is_subset(&set.set));
+        return BBoolean::new(self.size() >= set.size() || !self.subset(set));
     }
 
     pub fn fin(&self) -> BSet<BSet<T>> {
@@ -235,7 +274,7 @@ impl<T: 'static + BObject> BSet<T> {
 
     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();
+        return self.iter().nth(rng.gen_range(0..self.set.len())).unwrap().clone();
     }
 
     pub fn equal(&self, other: &BSet<T>) -> BBoolean {
diff --git a/btypes_lazy/src/main/rust/btypes/src/lazy_ops/set_ops/identity.rs b/btypes_lazy/src/main/rust/btypes/src/lazy_ops/set_ops/identity.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e88fb8ae9cc262aad73fa1def659a40b05b221b7
--- /dev/null
+++ b/btypes_lazy/src/main/rust/btypes/src/lazy_ops/set_ops/identity.rs
@@ -0,0 +1,50 @@
+use crate::bobject::BObject;
+use crate::bset::BSet;
+use crate::lazy_ops::set_ops::setops::{SetOp, SetOpTraits};
+
+use std::fmt;
+use std::fmt::{Debug, Formatter};
+use std::marker::PhantomData;
+
+
+#[derive(Clone)]
+pub struct Identity<T: BObject> {
+    phantom: PhantomData<T>,
+}
+
+impl<T: BObject> Identity<T> {
+    const OP_NAME: &'static str = "Identity";
+}
+
+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> {
+
+    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})
+    }
+
+    fn get_op_name(&self) -> &str {
+        return Identity::<T>::OP_NAME;
+    }
+
+    fn get_rhs(&self) -> Option<Box<dyn SetOp<Item=Self::Item>>> {
+        return Option::None;
+    }
+}
+
+impl<T: 'static +  BObject> SetOpTraits for Identity<T>{
+    type Item = T;
+
+    fn contains_lazy(&self, lhs: &BSet<Self::Item>, o: &Self::Item) -> bool { lhs.contains_directly(o) }
+    fn is_empty_lazy(&self, lhs: &BSet<Self::Item>) -> bool { lhs.is_empty_directly() }
+    fn size_lazy(&self, lhs: &BSet<Self::Item>) -> usize { lhs.size_directly() }
+}
\ 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
index c4442fe7273818eb198fbb935b45dd7847d8b3bb..6ee6fe5523a65fca3f8fb0bbc15d23715008ceac 100644
--- 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
@@ -1 +1,3 @@
-pub mod setops;
\ No newline at end of file
+pub mod setops;
+//pub mod identity;
+pub mod union;
\ 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
index 787ae5c0bef310d8d8426f39a33390acd7b97688..faceceac4efc2b60360b07df9929f4932e689a44 100644
--- 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
@@ -1,18 +1,37 @@
 use std::cmp::Ordering;
-use std::fmt;
-use std::fmt::{Debug, Formatter};
-use std::marker::PhantomData;
+use std::fmt::{Debug};
+use std::iter::Chain;
+use im::ordset::Iter;
+
 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>>;
 
     fn get_op_name(&self) -> &str;
-    fn get_rhs(&self) -> Option<&Box<dyn SetOp<Item = Self::Item>>>;
+    fn get_rhs(&self) -> Option<Box<dyn SetOp<Item = Self::Item>>>;
+
+    fn to_string(&self, lhs: Option<&str>) -> String {
+        let mut result = format!("{}(", self.get_op_name());
+        let mut has_lhs = false;
+        if lhs.is_some() {
+            result.push_str(lhs.unwrap());
+            has_lhs = true;
+        }
+        match self.get_rhs() {
+            Some(v) => {
+                if has_lhs { result.push_str(", "); }
+                result.push_str(format!("{}", v.to_string(Option::None)).as_str())
+            },
+            None => {},
+        }
+        result.push_str(")");
+
+        return result;
+    }
 
     //PartialEq
     fn eq_box(&self, other: &Box<dyn SetOp<Item = Self::Item>>) -> bool {
@@ -71,12 +90,25 @@ pub trait SetOp: SetOpTraits + Debug {
 }
 
 pub trait SetOpTraits {
-    //type Bla: BObject;
+    type Item: BObject;
 
-    //fn clone_box(&self) -> Box<dyn SetOp<Item = Self::Bla>>;
-    //fn default_box() -> Box<dyn SetOp>;
+    fn iter_lazy(&self, lhs: &BSet<Self::Item>) -> Box<dyn Iterator<Item=Self::Item> + '_>;
+    fn contains_lazy(&self, lhs: &BSet<Self::Item>, o:&Self::Item) -> bool;
+    fn is_empty_lazy(&self, lhs: &BSet<Self::Item>) -> bool;
+    fn size_lazy(&self, lhs: &BSet<Self::Item>) -> usize;
 }
 /*
+struct IterWrapper<T: BObject> {
+    chain_iter: Option<Chain<Iter<T>, Iter<T>>>,
+}
+
+impl<T: BObject> IterWrapper<T> {
+    pub fn from_chain_iter(iter: Chain<Iter<T>, Iter<T>>) {
+        IterWrapper { chain_iter: Option::Some(iter) }
+    }
+}
+*/
+/*
 impl<T> SetOpTraits for T
 where T: 'static + SetOp + Clone,
 {
@@ -116,45 +148,4 @@ impl<T: 'static +  BObject> Default for Box<dyn SetOp<Item = T>> {
         Box::new(Identity {phantom: PhantomData})
     }
 }
-*/
-
-
-
-
-
-#[derive(Clone)]
-pub struct Identity<T: BObject> {
-    phantom: PhantomData<T>,
-}
-
-impl<T: BObject> Identity<T> {
-    const OP_NAME: &'static str = "Identity";
-}
-
-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})
-    }
-
-    fn get_op_name(&self) -> &str {
-        return Identity::<T>::OP_NAME;
-    }
-
-    fn get_rhs(&self) -> Option<&Box<dyn SetOp<Item=Self::Item>>> {
-        return Option::None;
-    }
-}
-
-impl<T: 'static +  BObject> SetOpTraits for Identity<T>{}
\ No newline at end of file
+*/
\ No newline at end of file
diff --git a/btypes_lazy/src/main/rust/btypes/src/lazy_ops/set_ops/union.rs b/btypes_lazy/src/main/rust/btypes/src/lazy_ops/set_ops/union.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a8117a7d1900f66065b6e9338851c53ebf3ae3d1
--- /dev/null
+++ b/btypes_lazy/src/main/rust/btypes/src/lazy_ops/set_ops/union.rs
@@ -0,0 +1,77 @@
+use std::cell::RefCell;
+use std::convert::TryInto;
+use crate::bobject::BObject;
+use crate::bset::BSet;
+use crate::lazy_ops::set_ops::setops::{SetOp, SetOpTraits};
+
+use std::fmt;
+use std::fmt::{Debug, Formatter};
+use std::iter::Chain;
+use std::ops::Not;
+use im::ordset::Iter;
+
+#[derive(Clone)]
+pub struct Union<T: BObject> {
+    rhs: RefCell<BSet<T>>,
+    rhs_is_reduced: RefCell<bool>,
+}
+
+impl<T: BObject> Union<T> {
+    const OP_NAME: &'static str = "Union";
+}
+
+impl<T: 'static + BObject> Debug for Union<T> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        write!(f, "Union({})", self.rhs.borrow())
+    }
+}
+
+impl<T: BObject> Union<T> {
+    pub fn new(rhs: BSet<T>) -> Union<T> {
+        Union {rhs: RefCell::new(rhs), rhs_is_reduced: RefCell::new(false)}
+    }
+}
+
+impl<T: 'static +  BObject> SetOpTraits for Union<T>{
+    type Item = T;
+
+    fn iter_lazy(&self, lhs: &BSet<Self::Item>) -> Chain<Iter<T>, Iter<T>> {
+        return lhs.iter_directly().chain(self.rhs.borrow().iter());
+    }
+
+    fn contains_lazy(&self, lhs: &BSet<Self::Item>, o: &Self::Item) -> bool {
+        lhs.contains_directly(o) || self.rhs.borrow().contains_directly(o)
+    }
+
+    fn is_empty_lazy(&self, lhs: &BSet<Self::Item>) -> bool {
+        lhs.is_empty_directly() && self.rhs.borrow().is_empty_directly()
+    }
+
+    fn size_lazy(&self, lhs: &BSet<Self::Item>) -> usize {
+        if (*self.rhs_is_reduced.borrow()).not() {
+            let new_rhs = self.rhs.borrow().difference(lhs);
+            self.rhs.replace(new_rhs);
+            self.rhs_is_reduced.replace(true);
+        }
+        return lhs.size_directly() + self.rhs.borrow().size_directly();
+    }
+}
+
+impl<T: 'static +  BObject> SetOp for Union<T> {
+    fn compute(&self, lhs: &BSet<T>) -> BSet<T> {
+        lhs.real_union(&self.rhs.borrow())
+    }
+
+    fn clone_box(&self) -> Box<dyn SetOp<Item=Self::Item>> {
+        Box::new(Union{rhs: RefCell::new(self.rhs.borrow().clone()),
+                          rhs_is_reduced: RefCell::new(*self.rhs_is_reduced.borrow())})
+    }
+
+    fn get_op_name(&self) -> &str {
+        return Union::<T>::OP_NAME;
+    }
+
+    fn get_rhs(&self) -> Option<Box<dyn SetOp<Item=Self::Item>>> {
+        return Option::Some(self.rhs.borrow().clone_box());
+    }
+}
\ No newline at end of file