add rewrite_not (#263)

diff --git a/crates/iceberg/src/expr/predicate.rs b/crates/iceberg/src/expr/predicate.rs
index 4ab9aae..67a46e2 100644
--- a/crates/iceberg/src/expr/predicate.rs
+++ b/crates/iceberg/src/expr/predicate.rs
@@ -478,6 +478,47 @@
             )),
         }
     }
+    /// Simplifies the expression by removing `NOT` predicates,
+    /// directly negating the inner expressions instead. The transformation
+    /// applies logical laws (such as De Morgan's laws) to
+    /// recursively negate and simplify inner expressions within `NOT`
+    /// predicates.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// use iceberg::expr::{LogicalExpression, Predicate, Reference};
+    /// use iceberg::spec::Datum;
+    /// use std::ops::Not;
+    ///
+    /// let expression = Reference::new("a").less_than(Datum::long(5)).not();
+    /// let result = expression.rewrite_not();
+    ///
+    /// assert_eq!(&format!("{result}"), "a >= 5");
+    /// ```
+    pub fn rewrite_not(self) -> Predicate {
+        match self {
+            Predicate::And(expr) => {
+                let [left, right] = expr.inputs;
+                let new_left = Box::new(left.rewrite_not());
+                let new_right = Box::new(right.rewrite_not());
+                Predicate::And(LogicalExpression::new([new_left, new_right]))
+            }
+            Predicate::Or(expr) => {
+                let [left, right] = expr.inputs;
+                let new_left = Box::new(left.rewrite_not());
+                let new_right = Box::new(right.rewrite_not());
+                Predicate::Or(LogicalExpression::new([new_left, new_right]))
+            }
+            Predicate::Not(expr) => {
+                let [inner] = expr.inputs;
+                inner.negate()
+            }
+            Predicate::Unary(expr) => Predicate::Unary(expr),
+            Predicate::Binary(expr) => Predicate::Binary(expr),
+            Predicate::Set(expr) => Predicate::Set(expr),
+        }
+    }
 }
 
 impl Not for Predicate {
@@ -569,6 +610,73 @@
     use crate::spec::{NestedField, PrimitiveType, Schema, SchemaRef, Type};
 
     #[test]
+    fn test_logical_or_rewrite_not() {
+        let expression = Reference::new("b")
+            .less_than(Datum::long(5))
+            .or(Reference::new("c").less_than(Datum::long(10)))
+            .not();
+
+        let expected = Reference::new("b")
+            .greater_than_or_equal_to(Datum::long(5))
+            .and(Reference::new("c").greater_than_or_equal_to(Datum::long(10)));
+
+        let result = expression.rewrite_not();
+
+        assert_eq!(result, expected);
+    }
+
+    #[test]
+    fn test_logical_and_rewrite_not() {
+        let expression = Reference::new("b")
+            .less_than(Datum::long(5))
+            .and(Reference::new("c").less_than(Datum::long(10)))
+            .not();
+
+        let expected = Reference::new("b")
+            .greater_than_or_equal_to(Datum::long(5))
+            .or(Reference::new("c").greater_than_or_equal_to(Datum::long(10)));
+
+        let result = expression.rewrite_not();
+
+        assert_eq!(result, expected);
+    }
+
+    #[test]
+    fn test_set_rewrite_not() {
+        let expression = Reference::new("a")
+            .is_in([Datum::int(5), Datum::int(6)])
+            .not();
+
+        let expected = Reference::new("a").is_not_in([Datum::int(5), Datum::int(6)]);
+
+        let result = expression.rewrite_not();
+
+        assert_eq!(result, expected);
+    }
+
+    #[test]
+    fn test_binary_rewrite_not() {
+        let expression = Reference::new("a").less_than(Datum::long(5)).not();
+
+        let expected = Reference::new("a").greater_than_or_equal_to(Datum::long(5));
+
+        let result = expression.rewrite_not();
+
+        assert_eq!(result, expected);
+    }
+
+    #[test]
+    fn test_unary_rewrite_not() {
+        let expression = Reference::new("a").is_null().not();
+
+        let expected = Reference::new("a").is_not_null();
+
+        let result = expression.rewrite_not();
+
+        assert_eq!(result, expected);
+    }
+
+    #[test]
     fn test_predicate_negate_and() {
         let expression = Reference::new("b")
             .less_than(Datum::long(5))