; =============== String rules

; Note that ?Seq is used for rules that can be used where Seq/String are
; interchangable, where note that String is assumed to be a macro for (Seq Char)
; according cvc5's implementation of RARE. Note however that Char is not
; accessible as a standalone type.

; ---

(define-cond-rule str-eq-ctn-false ((x1 ?Seq :list) (x ?Seq) (x2 ?Seq :list) (y ?Seq))
  (= (str.contains y x) false)
  (= (str.++ x1 x x2) y)
  false)

(define-cond-rule str-eq-ctn-full-false1 ((x ?Seq) (y ?Seq))
  (= (str.contains y x) false)
  (= x y)
  false)

(define-cond-rule str-eq-ctn-full-false2 ((x ?Seq) (y ?Seq))
  (= (str.contains x y) false)
  (= x y)
  false)

(define-cond-rule str-eq-len-false ((x String) (y String))
  (not (= (str.len x) (str.len y)))
  (= x y)
  false)

(define-cond-rule str-substr-empty-str ((x ?Seq) (n Int) (m Int))
  (= (str.len x) 0)
  (str.substr x n m)
  x)

(define-cond-rule str-substr-empty-range ((x ?Seq) (n Int) (m Int))
  (>= 0 m)
  (str.substr x n m)
  (@seq.empty_of_type (@type_of x)))

(define-cond-rule str-substr-empty-start ((x ?Seq) (n Int) (m Int))
  (>= n (str.len x))
  (str.substr x n m)
  (@seq.empty_of_type (@type_of x)))

(define-cond-rule str-substr-empty-start-neg ((x ?Seq) (n Int) (m Int)) 
  (< n 0)
  (str.substr x n m)
  (@seq.empty_of_type (@type_of x)))

(define-cond-rule str-substr-substr-start-geq-len ((x ?Seq) (n1 Int) (m1 Int) (n2 Int) (m2 Int))
  (>= n2 m1)
  (str.substr (str.substr x n1 m1) n2 m2)
  (@seq.empty_of_type (@type_of x)))

(define-cond-rule str-substr-eq-empty ((s ?Seq) (r ?Seq) (n Int) (m Int))
  (and (= n 0) (> m n) (= (str.len r) 0))
  (= (str.substr s n m) r)
  (= s r))

(define-cond-rule str-substr-z-eq-empty-leq ((s ?Seq) (r ?Seq) (m Int))
  (and (not (= (str.len s) 0)) (= (str.len r) 0))
  (= (str.substr s 0 m) r)
  (<= m 0))

(define-cond-rule str-substr-eq-empty-leq-len ((s ?Seq) (emp ?Seq) (n Int) (m Int))
  (and (>= n 0) (> m 0) (= (str.len emp) 0))
  (= (str.substr s n m) emp)
  (<= (str.len s) n))

(define-cond-rule str-len-replace-inv ((t ?Seq) (s ?Seq) (r ?Seq)) 
  (= (str.len s) (str.len r))
  (str.len (str.replace t s r))
  (str.len t))

(define-cond-rule str-len-replace-all-inv ((t ?Seq) (s ?Seq) (r ?Seq))
  (= (str.len s) (str.len r))
  (str.len (str.replace_all t s r))
  (str.len t))

(define-rule str-len-update-inv ((t ?Seq) (n Int) (r ?Seq)) 
  (str.len (str.update t n r))
  (str.len t))

(define-cond-rule str-update-in-first-concat ((t ?Seq) (ts ?Seq :list) (s ?Seq) (n Int) (tpre ?Seq) (tpost ?Seq))
  (and (>= n 0) (< (+ n (str.len s)) (str.len t))
       (= tpre (str.substr t 0 n))
       (= tpost (str.substr t (+ n (str.len s)) (str.len t))))
  (str.update (str.++ t ts) n s)
  (str.++ tpre s tpost ts))

(define-cond-rule str-len-substr-in-range ((s ?Seq) (n Int) (m Int))
  (and (>= n 0) (>= m 0) (>= (str.len s) (+ n m)))
  (str.len (str.substr s n m))
  m)

(define-cond-rule str-concat-clash ((s1 ?Seq) (s2 ?Seq :list) (t1 ?Seq) (t2 ?Seq :list))
  (and (not (= s1 t1)) (= (str.len s1) (str.len t1)))
  (= (str.++ s1 s2) (str.++ t1 t2))
  false)
(define-cond-rule str-concat-clash-rev ((s1 ?Seq) (s2 ?Seq :list) (t1 ?Seq) (t2 ?Seq :list))
  (and (not (= s1 t1)) (= (str.len s1) (str.len t1)))
  (= (str.++ s2 s1) (str.++ t2 t1))
  false)
(define-cond-rule str-concat-clash2 ((s1 ?Seq) (t1 ?Seq) (t2 ?Seq :list))
  (and (not (= s1 t1)) (= (str.len s1) (str.len t1)))
  (= s1 (str.++ t1 t2))
  false)
(define-cond-rule str-concat-clash2-rev ((s1 ?Seq) (t1 ?Seq) (t2 ?Seq :list))
  (and (not (= s1 t1)) (= (str.len s1) (str.len t1)))
  (= s1 (str.++ t2 t1))
  false)

(define-rule* str-concat-unify ((s1 ?Seq) (s2 ?Seq) (s3 ?Seq :list) (t2 ?Seq) (t3 ?Seq :list))
  (= (str.++ s1 s2 s3) (str.++ s1 t2 t3))
  (= (str.++ s2 s3) (str.++ t2 t3))
  _)
(define-rule* str-concat-unify-rev ((s1 ?Seq) (s2 ?Seq) (s3 ?Seq :list) (t2 ?Seq) (t3 ?Seq :list))
  (= (str.++ s2 s3 s1) (str.++ t2 t3 s1))
  (= (str.++ s2 s3) (str.++ t2 t3))
  _)

(define-rule str-concat-unify-base ((s ?Seq) (t1 ?Seq) (t2 ?Seq :list))
  (= s (str.++ s t1 t2))
  (= (@seq.empty_of_type (@type_of s)) (str.++ t1 t2)))
(define-rule str-concat-unify-base-rev ((s ?Seq) (t1 ?Seq) (t2 ?Seq :list))
  (= s (str.++ t1 t2 s))
  (= (@seq.empty_of_type (@type_of s)) (str.++ t1 t2)))

(define-rule str-prefixof-elim ((s ?Seq) (t ?Seq))
  (str.prefixof s t)
  (= s (str.substr t 0 (str.len s))))

(define-rule str-suffixof-elim ((s ?Seq) (t ?Seq))
  (str.suffixof s t)
  (= s (str.substr t (- (str.len t) (str.len s)) (str.len s))))

(define-cond-rule str-prefixof-eq ((s ?Seq) (t ?Seq))
  (>= (str.len s) (str.len t))
  (str.prefixof s t)
  (= s t))

(define-cond-rule str-suffixof-eq ((s ?Seq) (t ?Seq))
  (>= (str.len s) (str.len t))
  (str.suffixof s t)
  (= s t))

(define-cond-rule str-prefixof-one ((s ?Seq) (t ?Seq))
  (= (str.len t) 1)
  (str.prefixof s t)
  (str.contains t s))
(define-cond-rule str-suffixof-one ((s ?Seq) (t ?Seq))
  (= (str.len t) 1)
  (str.suffixof s t)
  (str.contains t s))

(define-cond-rule str-substr-combine1 ((s ?Seq) (n1 Int) (m1 Int) (n2 Int) (m2 Int))
 (and (>= n1 0) (>= n2 0) (>= (- m2 (- m1 n2)) 0))
  (str.substr (str.substr s n1 m1) n2 m2)
  (str.substr s (+ n1 n2) (- m1 n2)))

(define-cond-rule str-substr-combine2 ((s ?Seq) (n1 Int) (m1 Int) (n2 Int) (m2 Int))
 (and (>= n1 0) (>= n2 0) (>= (- (- m1 n2) m2) 0))
  (str.substr (str.substr s n1 m1) n2 m2)
  (str.substr s (+ n1 n2) m2))

(define-cond-rule str-substr-combine3 ((s ?Seq) (n1 Int) (m1 Int) (n2 Int) (m2 Int))
 (and (>= n1 0) (>= n2 0) (>= (str.len (str.substr s n1 m1)) (+ n2 m2)))
  (str.substr (str.substr s n1 m1) n2 m2)
  (str.substr s (+ n1 n2) m2))

(define-cond-rule str-substr-combine4 ((s ?Seq) (n1 Int) (m1 Int) (n2 Int) (m2 Int))
 (and (>= n1 0) (>= n2 0) (>= (+ n2 m2) (str.len (str.substr s n1 m1))))
  (str.substr (str.substr s n1 m1) n2 m2)
  (str.substr s (+ n1 n2) (- m1 n2)))


(define-cond-rule str-substr-concat1 ((s1 ?Seq) (s2 ?Seq :list) (n Int) (m Int))
  (and (>= n 0) (>= (str.len s1) (+ n m)))
  (str.substr (str.++ s1 s2) n m)
  (str.substr s1 n m))

(define-cond-rule str-substr-concat2 ((s1 ?Seq) (s2 ?Seq) (s3 ?Seq :list) (n Int) (m Int))
  (>= n (str.len s1))
  (str.substr (str.++ s1 s2 s3) n m)
  (str.substr (str.++ s2 s3) (- n (str.len s1)) m))

(define-cond-rule str-substr-replace ((s ?Seq) (t ?Seq) (r ?Seq) (n Int))
  (and (= (str.len t) (str.len r)) (= (str.len t) 1))
  (str.substr (str.replace s t r) 0 n)
  (str.replace (str.substr s 0 n) t r))

(define-cond-rule str-substr-full ((s ?Seq) (n Int))
  (>= n (str.len s))
  (str.substr s 0 n)
  s)
  
(define-cond-rule str-substr-full-eq ((s ?Seq) (n Int))
  (= (str.len s) n)
  (str.substr s 0 n)
  s)

(define-rule str-contains-refl ((x ?Seq)) (str.contains x x) true)

(define-cond-rule str-contains-concat-find ((xs ?Seq :list) (z ?Seq) (y ?Seq) (zs ?Seq :list))
  (str.contains z y)
  (str.contains (str.++ xs z zs) y)
  true)

(define-cond-rule str-contains-concat-find-contra ((xs ?Seq :list) (z ?Seq) (y ?Seq) (zs ?Seq :list))
  (not (str.contains y z))
  (str.contains y (str.++ xs z zs))
  false)

(define-cond-rule str-contains-split-char ((x ?Seq) (y ?Seq) (z ?Seq :list) (w ?Seq))
  (= (str.len w) 1)
  (str.contains (str.++ x y z) w)
  (or (str.contains x w) (str.contains (str.++ y z) w)))

(define-cond-rule str-contains-leq-len-eq ((x ?Seq) (y ?Seq))
  (>= (str.len y) (str.len x))
  (str.contains x y)
  (= x y))

(define-cond-rule str-contains-emp ((x ?Seq) (y ?Seq))
  (= (str.len y) 0)
  (str.contains x y)
  true)

(define-cond-rule str-contains-char ((x ?Seq) (y ?Seq))
  (= (str.len x) 1)
  (str.contains x y)
  (or (= (@seq.empty_of_type (@type_of x)) y) (= x y)))

(define-rule str-at-elim ((x ?Seq) (n Int)) (str.at x n) (str.substr x n 1))

; not effective since due to not proving inequalities on length
;(define-cond-rule str-replace-all-to-self ((t ?Seq) (s ?Seq)) 
;  (>= (str.len s) (str.len t))
;  (str.replace t s t)
;  t)

(define-rule str-replace-self ((t ?Seq) (s ?Seq))
  (str.replace t t s)
  s)

(define-rule str-replace-id ((t ?Seq) (s ?Seq))
  (str.replace t s s)
  t)

(define-rule str-replace-prefix ((t1 ?Seq) (t2 ?Seq) (r ?Seq :list) (s ?Seq))
  (str.replace (str.++ t1 t2 r) t1 s)
  (str.++ s t2 r))

(define-cond-rule str-replace-no-contains ((t ?Seq) (s ?Seq) (r ?Seq)) 
  (not (str.contains t s))
  (str.replace t s r)
  t)

(define-cond-rule str-replace-find-base ((t ?Seq) (s ?Seq) (r ?Seq) (tpre ?Seq) (tpost ?Seq))
  (def (n (str.indexof t s 0)))
  (and (>= n 0)
       (= tpre (str.substr t 0 n))
       (= tpost (str.substr t (+ n (str.len s)) (str.len t))))
  (str.replace t s r)
  (str.++ tpre r tpost))

(define-cond-rule str-replace-find-first-concat ((t ?Seq) (ts ?Seq :list) (s ?Seq) (r ?Seq) (tpre ?Seq) (tpost ?Seq))
  (def (n (str.indexof t s 0)))
  (and (>= n 0)
       (= tpre (str.substr t 0 n))
       (= tpost (str.substr t (+ n (str.len s)) (str.len t))))
  (str.replace (str.++ t ts) s r)
  (str.++ tpre r tpost ts))

(define-cond-rule str-replace-empty ((t ?Seq) (s ?Seq) (r ?Seq))
  (= (str.len r) 0)
  (str.replace t r s)
  (str.++ s t))

(define-cond-rule str-replace-one-pre ((t ?Seq) (s ?Seq) (r ?Seq) (ts ?Seq :list) (ss ?Seq :list))
  (= (str.len s) 1)
  (str.replace (str.++ ts t ss t) s r)
  (str.++ (str.replace (str.++ ts t ss) s r) t))

(define-rule str-replace-find-pre ((t ?Seq) (r ?Seq) (ts ?Seq :list) (ss ?Seq :list))
  (str.replace (str.++ ts t ss) t r)
  (str.++ (str.replace (str.++ ts t) t r) ss))

(define-cond-rule str-replace-all-no-contains ((t ?Seq) (s ?Seq) (r ?Seq))
  (not (str.contains t s))
  (str.replace_all t s r)
  t)

(define-cond-rule str-replace-all-empty ((t ?Seq) (s ?Seq) (r ?Seq))
  (= (str.len s) 0)
  (str.replace_all t s r)
  t)

(define-rule str-replace-re-none ((t ?Seq) (r ?Seq))
  (str.replace_re t re.none r)
  t)

(define-rule str-replace-re-all-none ((t ?Seq) (r ?Seq))
  (str.replace_re_all t re.none r)
  t)

(define-rule* str-len-concat-rec ((s1 ?Seq) (s2 ?Seq) (s3 ?Seq :list))
  (str.len (str.++ s1 s2 s3))
  (str.len (str.++ s2 s3))
  (+ (str.len s1) _))

(define-rule* str-len-eq-zero-concat-rec ((s1 ?Seq) (s2 ?Seq) (s3 ?Seq :list))
  (= (str.len (str.++ s1 s2 s3)) 0)
  (= (str.len (str.++ s2 s3)) 0)
  (and (= s1 (@seq.empty_of_type (@type_of s1))) _))

(define-rule str-len-eq-zero-base ((s1 ?Seq))
  (= (str.len s1) 0)
  (= s1 (@seq.empty_of_type (@type_of s1))))

(define-rule str-indexof-self ((t ?Seq) (n Int))
  (def (emp (@seq.empty_of_type (@type_of t))))
  (str.indexof t t n)
  (str.indexof emp emp n))

(define-cond-rule str-indexof-no-contains ((t ?Seq) (s ?Seq) (n Int))
  (not (str.contains (str.substr t n (str.len t)) s))
  (str.indexof t s n)
  (- 1))

(define-cond-rule str-indexof-oob ((t ?Seq) (s ?Seq) (n Int))
  (> n (str.len t))
  (str.indexof t s n)
  (- 1))

(define-cond-rule str-indexof-oob2 ((t ?Seq) (s ?Seq) (n Int))
  (> 0 n)
  (str.indexof t s n)
  (- 1))

(define-cond-rule str-indexof-contains-pre ((t1 ?Seq) (t2 ?Seq :list) (s ?Seq) (n Int))
  (and (> (str.len s) 0) (str.contains (str.substr t1 n (str.len t1)) s))
  (str.indexof (str.++ t1 t2) s n)
  (str.indexof t1 s n))

(define-rule str-indexof-contains-concat-pre ((t1 ?Seq :list) (t ?Seq) (t2 ?Seq :list) (s ?Seq))
  (str.indexof (str.++ t1 t s t2) t 0)
  (str.indexof (str.++ t1 t) t 0))

(define-cond-rule str-indexof-find-emp ((t ?Seq) (emp ?Seq) (n Int))
  (and (= (str.len emp) 0) (>= (str.len t) n) (>= n 0))
  (str.indexof t emp n)
  n)

(define-cond-rule str-indexof-eq-irr ((t ?Seq) (s ?Seq) (r ?Seq) (n Int))
  (and (<= n (str.len t)) (<= n (str.len r)) (= (str.substr t n (str.len t)) (str.substr r n (str.len r))))
  (= (str.indexof t s n) (str.indexof r s n))
  true)

(define-rule str-indexof-re-none ((t String) (n Int))
  (str.indexof_re t re.none n)
  (- 1))

(define-cond-rule str-indexof-re-emp-re ((t String) (r RegLan) (n Int))
  (and (str.in_re "" r) (>= (str.len t) n))
  (str.indexof_re t r n)
  n)

(define-rule* str-to-lower-concat ((s1 String) (s2 String) (s3 String :list))
  (str.to_lower (str.++ s1 s2 s3)) 
  (str.to_lower (str.++ s2 s3))
  (str.++ (str.to_lower s1) _))

(define-rule* str-to-upper-concat ((s1 String) (s2 String) (s3 String :list))
  (str.to_upper (str.++ s1 s2 s3)) 
  (str.to_upper (str.++ s2 s3))
  (str.++ (str.to_upper s1) _))

(define-rule str-to-lower-upper ((s String))
  (str.to_lower (str.to_upper s))
  (str.to_lower s))

(define-rule str-to-upper-lower ((s String))
  (str.to_upper (str.to_lower s))
  (str.to_upper s))

(define-rule str-to-lower-len ((s String))
  (str.len (str.to_lower s))
  (str.len s))

(define-rule str-to-upper-len ((s String))
  (str.len (str.to_upper s))
  (str.len s))

(define-rule str-to-lower-from-int ((n Int))
  (str.to_lower (str.from_int n))
  (str.from_int n))

(define-rule str-to-upper-from-int ((n Int))
  (str.to_upper (str.from_int n))
  (str.from_int n))

(define-cond-rule str-to-int-concat-neg-one ((s1 String :list) (s2 String) (s3 String :list))
  (= (str.to_int s2) (- 1))
  (str.to_int (str.++ s1 s2 s3))
  (- 1))

(define-rule str-is-digit-elim ((s String))
  (str.is_digit s)
  (and (<= 48 (str.to_code s)) (<= (str.to_code s) 57)))

(define-rule str-leq-empty ((s String))
  (str.<= "" s)
  true)

(define-rule str-leq-empty-eq ((s String))
  (str.<= s "")
  (= s ""))

(define-cond-rule str-leq-concat-false ((s String :list) (t1 String) (s1 String) (t2 String :list) (s2 String :list))
  (and (= (str.len t1) (str.len s1)) (= (str.<= t1 s1) false))
  (str.<= (str.++ s t1 t2) (str.++ s s1 s2))
  false)

(define-cond-rule str-leq-concat-true ((s String :list) (t1 String) (s1 String) (t2 String :list) (s2 String :list))
  (and (= (str.len t1) (str.len s1)) (not (= t1 s1)) (= (str.<= t1 s1) true))
  (str.<= (str.++ s t1 t2) (str.++ s s1 s2))
  true)

(define-cond-rule str-leq-concat-base-1 ((t1 String) (t2 String :list) (s String))
  (and (= (str.len t1) (str.len s)) (not (= t1 s)))
  (str.<= (str.++ t1 t2) s)
  (str.<= t1 s))

(define-cond-rule str-leq-concat-base-2 ((t String) (s1 String) (s2 String :list))
  (and (= (str.len t) (str.len s1)) (not (= t s1)))
  (str.<= t (str.++ s1 s2))
  (str.<= t s1))

(define-rule str-lt-elim ((s String) (t String))
  (str.< s t)
  (and (not (= s t)) (str.<= s t)))

; if t contains only digits, it cannot contain a string s containing a non-digit
(define-cond-rule str-from-int-no-ctn-nondigit ((n Int) (s String))
  (and (not (= s "")) (= (str.to_int s) (- 1)))
  (str.contains (str.from_int n) s)
  false)

(define-cond-rule str-substr-ctn-contra ((t ?Seq) (s ?Seq) (n Int) (m Int))
  (not (str.contains t s))
  (str.contains (str.substr t n m) s)
  false)

(define-rule str-substr-ctn ((s ?Seq) (n Int) (m Int))
  (str.contains s (str.substr s n m))
  true)

(define-cond-rule str-replace-dual-ctn ((s ?Seq) (t ?Seq) (r ?Seq) (u ?Seq))
  (and (str.contains s u) (str.contains r u))
  (str.contains (str.replace s t r) u)
  true)

(define-cond-rule str-replace-dual-ctn-false ((s ?Seq) (t ?Seq) (r ?Seq) (u ?Seq))
  (and (not (str.contains s t)) (not (str.contains s u)))
  (str.contains s (str.replace t r u))
  false)

(define-rule str-replace-self-ctn-simp ((s ?Seq) (t ?Seq))
  (str.contains s (str.replace t s t))
  (str.contains s t))

(define-cond-rule str-replace-emp-ctn-src ((s ?Seq) (t ?Seq) (emp ?Seq))
  (= (str.len emp) 0)
  (str.contains s (str.replace emp s t))
  (= emp (str.replace emp s t)))

(define-cond-rule str-substr-char-start-eq-len ((x ?Seq) (n Int))
  (>= 1 (str.len x))
  (str.substr x n n)
  (@seq.empty_of_type (@type_of x)))

(define-cond-rule str-contains-repl-char ((x ?Seq) (y ?Seq) (z ?Seq) (w ?Seq))
  (and (= (str.len w) 1) (not (str.contains y w)))
  (str.contains (str.replace x y z) w)
  (or (str.contains x w) (and (str.contains x y) (str.contains z w))))

(define-cond-rule str-contains-repl-self-tgt-char ((x ?Seq) (y ?Seq) (w ?Seq))
  (= (str.len w) 1)
  (str.contains (str.replace x y x) w)
  (str.contains x w))

(define-rule str-contains-repl-self ((x ?Seq) (y ?Seq))
  (str.contains (str.replace x y x) y)
  (str.contains x y))

(define-rule str-contains-repl-tgt ((x ?Seq) (y ?Seq) (z ?Seq))
  (str.contains (str.replace x y z) z)
  (or (str.contains x y) (str.contains x z)))

(define-cond-rule str-repl-repl-len-id ((x ?Seq) (y ?Seq))
  (>= (str.len y) (str.len x))
  (str.replace x y x)
  x)

(define-cond-rule str-repl-repl-src-tgt-no-ctn ((x ?Seq) (y ?Seq) (z ?Seq) (w ?Seq))
  (not (str.contains z w))
  (str.replace x w (str.replace z x y))
  (str.replace x w z))

(define-rule str-repl-repl-tgt-self ((x ?Seq) (y ?Seq))
  (str.replace x y (str.replace y x y))
  x)

(define-cond-rule str-repl-repl-tgt-no-ctn ((x ?Seq) (y ?Seq) (z ?Seq) (w ?Seq))
  (not (str.contains x z))
  (str.replace x y (str.replace y z w))
  x)

(define-rule str-repl-repl-src-self ((x ?Seq) (y ?Seq) (z ?Seq))
  (str.replace x (str.replace y x y) z)
  (str.replace x y z))

(define-cond-rule str-repl-repl-src-inv-no-ctn1 ((x ?Seq) (y ?Seq) (z ?Seq))
  (not (str.contains y z))
  (str.replace x (str.replace y x z) y)
  (str.replace x y y))

(define-cond-rule str-repl-repl-src-inv-no-ctn2 ((x ?Seq) (y ?Seq) (z ?Seq))
  (not (str.contains y z))
  (str.replace x (str.replace y x z) x)
  (str.replace x y x))

(define-cond-rule str-repl-repl-src-inv-no-ctn3 ((x ?Seq) (y ?Seq) (z ?Seq) (w ?Seq) (u ?Seq))
  (and (not (str.contains x z)) (not (str.contains x w)))
  (str.replace x (str.replace y z w) u)
  (str.replace x y u))

(define-rule str-repl-repl-dual-self ((x ?Seq) (y ?Seq))
  (str.replace x (str.replace x y x) x)
  x)

(define-cond-rule str-repl-repl-dual-ite1 ((x ?Seq) (y ?Seq) (z ?Seq) (w ?Seq))
  (not (str.contains x z))
  (str.replace x (str.replace x y z) w)
  (ite (str.contains x y) x w))

(define-cond-rule str-repl-repl-dual-ite2 ((x ?Seq) (y ?Seq) (z ?Seq) (w ?Seq))
  (and (not (str.contains y z)) (not (str.contains z y)))
  (str.replace x (str.replace x y z) w)
  (ite (str.contains x y) x w))

(define-cond-rule str-repl-repl-lookahead-id-simp ((y ?Seq) (z ?Seq) (w ?Seq))
  (and (not (= w z)) (>= (str.len w) (str.len z)))
  (str.replace (str.replace y w y) y z)
  (str.replace (str.replace y w z) y z))

; =============== Regular expression rules

(define-rule re-all-elim () re.all (re.* re.allchar))

(define-rule re-opt-elim ((x RegLan)) (re.opt x) (re.union (str.to_re "") x))

(define-rule re-diff-elim ((x RegLan) (y RegLan)) (re.diff x y) (re.inter x (re.comp y)))

(define-rule re-plus-elim ((x RegLan)) (re.+ x) (re.++ x (re.* x)))

(define-rule re-repeat-elim ((n Int) (x RegLan)) (re.^ n x) (re.loop n n x))

(define-rule re-concat-star-swap ((xs RegLan :list) (r RegLan) (ys RegLan :list)) (re.++ xs (re.* r) r ys) (re.++ xs r (re.* r) ys))

(define-rule re-concat-star-repeat ((xs RegLan :list) (r RegLan) (ys RegLan :list)) (re.++ xs (re.* r) (re.* r) ys) (re.++ xs (re.* r) ys))

(define-rule re-concat-star-subsume1 ((xs RegLan :list) (r RegLan) (ys RegLan :list))
  (re.++ xs (re.* r) (re.* re.allchar) ys)
  (re.++ xs (re.* re.allchar) ys))

(define-rule re-concat-star-subsume2 ((xs RegLan :list) (r RegLan) (ys RegLan :list))
  (re.++ xs (re.* re.allchar) (re.* r) ys)
  (re.++ xs (re.* re.allchar) ys))

(define-rule* re-concat-merge ((xs RegLan :list) (s String) (t String) (ys RegLan :list)) 
  (re.++ xs (str.to_re s) (str.to_re t) ys)
  (re.++ xs (str.to_re (str.++ s t)) ys)
  _)

(define-rule re-union-all ((xs RegLan :list) (ys RegLan :list)) (re.union xs (re.* re.allchar) ys) (re.* re.allchar))

; used for macro expansion
(define-cond-rule re-union-const-elim ((r RegLan) (s String))
  (str.in_re s r)
  (re.union (str.to_re s) r)
  r)

(define-rule* re-inter-all ((xs RegLan :list) (ys RegLan :list)) (re.inter xs (re.* re.allchar) ys) (re.inter xs ys))

(define-rule re-star-none () (re.* re.none) (str.to_re ""))

(define-rule re-star-emp () (re.* (str.to_re "")) (str.to_re ""))

(define-rule re-star-star ((x RegLan)) (re.* (re.* x)) (re.* x))

(define-cond-rule re-range-non-singleton-1 ((s String) (t String))
  (not (= (str.len s) 1))
  (re.range s t)
  re.none)
  
(define-cond-rule re-range-non-singleton-2 ((s String) (t String))
  (not (= (str.len t) 1))
  (re.range s t)
  re.none)

(define-rule re-star-union-drop-emp ((x RegLan :list) (y RegLan :list))
  (re.* (re.union x (str.to_re "") y))
  (re.* (re.union x y)))

(define-cond-rule re-loop-neg ((n Int) (m Int) (r RegLan))
  (> n m)
  (re.loop n m r)
  re.none)

(define-cond-rule re-inter-cstring ((x RegLan) (ys RegLan :list) (s String))
  (str.in_re s (re.inter (str.to_re s) x ys))
  (re.inter (str.to_re s) x ys) 
  (str.to_re s))

(define-cond-rule re-inter-cstring-neg ((x RegLan) (ys RegLan :list) (s String))
  (not (str.in_re s (re.inter (str.to_re s) x ys)))
  (re.inter (str.to_re s) x ys)
  re.none)

(define-cond-rule str-substr-len-include ((s1 ?Seq) (s2 ?Seq :list) (n Int) (m Int))
  (>= (str.len s1) (+ n m))
  (str.substr (str.++ s1 s2) n m)
  (str.substr s1 n m)
)

(define-cond-rule str-substr-len-include-pre ((s1 ?Seq) (s2 ?Seq) (s3 ?Seq :list) (n Int))
  (>= n (str.len s1))
  (str.substr (str.++ s1 s2 s3) 0 n)
  (str.++ s1 (str.substr (str.++ s2 s3) 0 (- n (str.len s1))))
)

(define-cond-rule str-substr-len-norm ((s ?Seq) (n Int) (m Int))
  (>= m (str.len s))
  (str.substr s n m)
  (str.substr s n (str.len s))
)

(define-rule seq-len-rev ((x ?Seq))
  (str.len (str.rev x))
  (str.len x))

(define-rule seq-rev-rev ((x ?Seq))
  (str.rev (str.rev x))
  x)

(define-rule* seq-rev-concat ((x ?Seq) (y ?Seq :list) (z ?Seq))
  (str.rev (str.++ x y z))
  (str.rev (str.++ x y))
  (str.++ (str.rev z) _))


(define-cond-rule str-eq-repl-self-emp ((x ?Seq) (y ?Seq) (emp ?Seq))
  (= (str.len emp) 0)
  (= (str.replace x y x) emp)
  (= x emp))

(define-cond-rule str-eq-repl-no-change ((x ?Seq) (y ?Seq) (z ?Seq))
  (not (= y z))
  (= (str.replace x y z) x)
  (not (str.contains x y)))

(define-cond-rule str-eq-repl-tgt-eq-len ((x ?Seq) (y ?Seq) (z ?Seq))
  (= (str.len y) (str.len z))
  (= (str.replace x y z) z)
  (or (= x y) (= x z)))

(define-cond-rule str-eq-repl-len-one-emp-prefix ((x ?Seq) (y ?Seq) (emp ?Seq))
  (and (= (str.len emp) 0) (= (str.len y) 1))
  (= (str.replace x y emp) emp)
  (str.prefixof x y))

(define-cond-rule str-eq-repl-emp-tgt-nemp ((x ?Seq) (y ?Seq) (z ?Seq) (emp ?Seq))
  (and (= (str.len emp) 0) (not (= z emp)))
  (= (str.replace x y z) emp)
  (and (= x emp) (not (= y emp))))


(define-cond-rule str-eq-repl-nemp-src-emp ((x ?Seq) (y ?Seq) (z ?Seq) (emp ?Seq))
  (and (= (str.len emp) 0) (not (= z emp)))
  (= (str.replace emp x y) z)
  (and (= x emp) (= y z)))

(define-rule str-eq-repl-self-src ((x ?Seq) (y ?Seq))
  (= (str.replace x y x) y)
  (= x y))

; =============== Sequences-specific rules

(define-rule seq-len-unit ((x ?)) (str.len (seq.unit x)) 1)
(define-rule seq-nth-unit ((x ?)) (seq.nth (seq.unit x) 0) x)
(define-rule seq-rev-unit ((x ?)) (str.rev (seq.unit x)) (seq.unit x))

