import Lean

open Int.Linear

-- Convenient RArray literals
elab tk:"#R[" ts:term,* "]" : term => do
  let ts : Array Lean.Syntax := ts
  let es ← ts.mapM fun stx => Lean.Elab.Term.elabTerm stx none
  if h : 0 < es.size then
    return (Lean.RArray.toExpr (← Lean.Meta.inferType es[0]!) id (Lean.RArray.ofArray es h))
  else
    throwErrorAt tk "RArray cannot be empty"

example (x₁ x₂ : Int) :
  Expr.denote #R[x₁, x₂] (.add (.add (.var 0) (.var 1)) (.num 3))
  =
  x₁ + x₂ + 3  :=
  rfl

example (x₁ x₂ : Int) :
  Poly.denote #R[x₁, x₂] (.add 1 0 (.add 3 1 (.num 4)))
  =
  1 * x₁ + ((3 * x₂) + 4) :=
  rfl

example (x₁ x₂ : Int) :
  Expr.denote #R[x₁, x₂] (.sub (.add (.mulR (.var 0) 4) (.mulL 2 (.var 1))) (.num 3))
  =
  (x₁*4) + 2*x₂ - 3 :=
  rfl

example :
  Expr.toPoly (.add (.add (.var 1) (.var 1)) (.num 3))
  =
  Expr.toPoly (.add (.num 3) (.mulL 2 (.var 1))) :=
  rfl

example :
  Expr.toPoly (.add (.add (.add (.var 1) (.var 1)) (.num 3)) (.var 2))
  =
  Expr.toPoly (.add (.add (.num 3) (.var 2)) (.mulL 2 (.var 1))) :=
  rfl

example (x₁ x₂ x₃ : Int) :
  RawRelCnstr.denote #R[x₁, x₂, x₃] (.eq (.sub (.add (.mulR (.var 0) 4) (.mulL 2 (.var 1))) (.num 3)) (.sub (.var 1) (.var 2)))
  =
  ((x₁*4) + 2*x₂ - 3 = x₂ - x₃) :=
  rfl

example :
  RawRelCnstr.norm (.eq (.sub (.add (.mulR (.var 0) 4) (.mulL 2 (.var 1))) (.num 3)) (.sub (.var 1) (.var 2)))
  =
  RawRelCnstr.norm (.eq (.add (.var 2) (.add (.var 1) (.add (.mulL 4 (.var 0)) (.num (-3))))) (.num 0)) :=
  rfl

example (x₁ x₂ x₃ : Int) : (x₁ + x₂) + (x₂ + x₃) = x₃ + 2*x₂ + x₁ :=
  Expr.eq_of_toPoly_eq #R[x₁, x₂, x₃]
   (Expr.add (Expr.add (Expr.var 0) (Expr.var 1)) (Expr.add (Expr.var 1) (Expr.var 2)))
   (Expr.add (Expr.add (Expr.var 2) (Expr.mulL 2 (Expr.var 1))) (Expr.var 0))
   rfl

example :
  RawRelCnstr.norm
    (.eq (Expr.add (Expr.add (Expr.var 0) (Expr.var 1)) (Expr.add (Expr.var 1) (Expr.var 2)))
         (Expr.add (Expr.var 2) (Expr.var 1)))
  =
  RawRelCnstr.norm
    (.eq (Expr.add (Expr.var 0) (Expr.var 1))
         (Expr.num 0))
  :=
  rfl

example (x₁ x₂ x₃ : Int) : ((x₁ + x₂) + (x₂ + x₃) = x₃ + x₂) = (x₁ + x₂ = 0) :=
  RawRelCnstr.eq_of_norm_eq #R[x₃, x₂, x₁]
    (.eq (Expr.add (Expr.add (Expr.var 2) (Expr.var 1)) (Expr.add (Expr.var 1) (Expr.var 0)))
         (Expr.add (Expr.var 0) (Expr.var 1)))
    (.eq (.add 1 2 (.add 1 1 (.num 0))))
    rfl

example (x₁ x₂ x₃ : Int) : ((x₁ + x₂) + (x₂ + x₃) ≤ x₃ + x₂) = (x₁ + x₂ ≤ 0) :=
  RawRelCnstr.eq_of_norm_eq #R[x₃, x₂, x₁]
    (.le (Expr.add (Expr.add (Expr.var 2) (Expr.var 1)) (Expr.add (Expr.var 1) (Expr.var 0)))
         (Expr.add (Expr.var 0) (Expr.var 1)))
    (.le (Poly.add 1 2 (.add 1 1 (.num 0))))
    rfl
