Skip to content

Commit

Permalink
Sync cvc5 main arithmetic subtyping fixes. (#154)
Browse files Browse the repository at this point in the history
  • Loading branch information
abdoo8080 authored Dec 5, 2024
1 parent 0e0e28c commit 39b2782
Show file tree
Hide file tree
Showing 9 changed files with 307 additions and 144 deletions.
5 changes: 5 additions & 0 deletions Smt/Reconstruct/Builtin.lean
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ def reconstructRewrite (pf : cvc5.Proof) : ReconstructM (Option Expr) := do
addThm q($t = $t') (.app q(@of_decide_eq_true ($t = $t') $h) q(Eq.refl true))
| .ACI_NORM =>
addTac (← reconstructTerm pf.getResult) Meta.AC.rewriteUnnormalizedTop
| .ENCODE_EQ_INTRO =>
let (u, (α : Q(Sort u))) ← reconstructSortLevelAndSort pf.getResult[0]!.getSort
let x : Q($α) ← reconstructTerm pf.getResult[0]!
let y : Q($α) ← reconstructTerm pf.getResult[1]!
addThm q($x = $y) q(Eq.refl $y)
| .DSL_REWRITE
| .THEORY_REWRITE => reconstructRewrite pf
| .ITE_ELIM1 =>
Expand Down
71 changes: 32 additions & 39 deletions Smt/Reconstruct/Int.lean
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,36 @@ where
else
return ha

def reconstructArithPolyNormRel (pf : cvc5.Proof) : ReconstructM (Option Expr) := do
let cx : Int := pf.getChildren[0]!.getResult[0]![0]!.getIntegerValue!
let cy : Int := pf.getChildren[0]!.getResult[1]![0]!.getIntegerValue!
let x₁ : Q(Int) ← reconstructTerm pf.getResult[0]![0]!
let x₂ : Q(Int) ← reconstructTerm pf.getResult[0]![1]!
let y₁ : Q(Int) ← reconstructTerm pf.getResult[1]![0]!
let y₂ : Q(Int) ← reconstructTerm pf.getResult[1]![1]!
let h : Q($cx * ($x₁ - $x₂) = $cy * ($y₁ - $y₂)) ← reconstructProof pf.getChildren[0]!
let k := pf.getResult[0]!.getKind
let (hcx, hcy) :=
if k == .EQUAL then (q(@of_decide_eq_true ($cx ≠ 0) _), q(@of_decide_eq_true ($cy ≠ 0) _))
else if cx > 0 then (q(@of_decide_eq_true ($cx > 0) _), q(@of_decide_eq_true ($cy > 0) _))
else (q(@of_decide_eq_true ($cx < 0) _), q(@of_decide_eq_true ($cy < 0) _))
let hcx := .app hcx q(Eq.refl true)
let hcy := .app hcy q(Eq.refl true)
let n ← getThmName k (cx > 0)
return mkApp9 (.const n []) x₁ x₂ y₁ y₂ q($cx) q($cy) hcx hcy h
where
getThmName (k : cvc5.Kind) (sign : Bool) : ReconstructM Name :=
if k == .LT && sign == true then pure ``Int.lt_of_sub_eq_pos
else if k == .LT && sign == false then pure ``Int.lt_of_sub_eq_neg
else if k == .LEQ && sign == true then pure ``Int.le_of_sub_eq_pos
else if k == .LEQ && sign == false then pure ``Int.le_of_sub_eq_neg
else if k == .EQUAL then pure ``Int.eq_of_sub_eq
else if k == .GEQ && sign == true then pure ``Int.ge_of_sub_eq_pos
else if k == .GEQ && sign == false then pure ``Int.ge_of_sub_eq_neg
else if k == .GT && sign == true then pure ``Int.gt_of_sub_eq_pos
else if k == .GT && sign == false then pure ``Int.gt_of_sub_eq_neg
else throwError "[arith_poly_norm_rel]: invalid combination of kind and sign: {k}, {sign}"

@[smt_proof_reconstruct] def reconstructIntProof : ProofReconstructor := fun pf => do match pf.getRule with
| .DSL_REWRITE
| .THEORY_REWRITE => reconstructRewrite pf
Expand Down Expand Up @@ -385,45 +415,8 @@ where
let b : Q(Int) ← reconstructTerm pf.getResult[1]!
addTac q($a = $b) Int.nativePolyNorm
| .ARITH_POLY_NORM_REL =>
if !pf.getResult[0]![0]!.getSort.isInteger then return none
let cx : Int := pf.getChildren[0]!.getResult[0]![0]!.getIntegerValue!
let x₁ : Q(Int) ← reconstructTerm pf.getResult[0]![0]!
let x₂ : Q(Int) ← reconstructTerm pf.getResult[0]![1]!
let cy : Int := pf.getChildren[0]!.getResult[1]![0]!.getIntegerValue!
let y₁ : Q(Int) ← reconstructTerm pf.getResult[1]![0]!
let y₂ : Q(Int) ← reconstructTerm pf.getResult[1]![1]!
let h : Q($cx * ($x₁ - $x₂) = $cy * ($y₁ - $y₂)) ← reconstructProof pf.getChildren[0]!
let k := pf.getResult[0]!.getKind
if k == .EQUAL then
let hcx : Q($cx ≠ 0) := .app q(@of_decide_eq_true ($cx ≠ 0) _) q(Eq.refl true)
let hcy : Q($cy ≠ 0) := .app q(@of_decide_eq_true ($cy ≠ 0) _) q(Eq.refl true)
addThm q(($x₁ = $x₂) = ($y₁ = $y₂)) q(Int.eq_of_sub_eq $hcx $hcy $h)
else if cx > 0 then
let hcx : Q($cx > 0) := .app q(@of_decide_eq_true ($cx > 0) _) q(Eq.refl true)
let hcy : Q($cy > 0) := .app q(@of_decide_eq_true ($cy > 0) _) q(Eq.refl true)
match k with
| .LT =>
addThm q(($x₁ < $x₂) = ($y₁ < $y₂)) q(Int.lt_of_sub_eq_pos $hcx $hcy $h)
| .LEQ =>
addThm q(($x₁ ≤ $x₂) = ($y₁ ≤ $y₂)) q(Int.le_of_sub_eq_pos $hcx $hcy $h)
| .GEQ =>
addThm q(($x₁ ≥ $x₂) = ($y₁ ≥ $y₂)) q(Int.ge_of_sub_eq_pos $hcx $hcy $h)
| .GT =>
addThm q(($x₁ > $x₂) = ($y₁ > $y₂)) q(Int.gt_of_sub_eq_pos $hcx $hcy $h)
| _ => return none
else
let hcx : Q($cx < 0) := .app q(@of_decide_eq_true ($cx < 0) _) q(Eq.refl true)
let hcy : Q($cy < 0) := .app q(@of_decide_eq_true ($cy < 0) _) q(Eq.refl true)
match k with
| .LT =>
addThm q(($x₁ < $x₂) = ($y₁ < $y₂)) q(Int.lt_of_sub_eq_neg $hcx $hcy $h)
| .LEQ =>
addThm q(($x₁ ≤ $x₂) = ($y₁ ≤ $y₂)) q(Int.le_of_sub_eq_neg $hcx $hcy $h)
| .GEQ =>
addThm q(($x₁ ≥ $x₂) = ($y₁ ≥ $y₂)) q(Int.ge_of_sub_eq_neg $hcx $hcy $h)
| .GT =>
addThm q(($x₁ > $x₂) = ($y₁ > $y₂)) q(Int.gt_of_sub_eq_neg $hcx $hcy $h)
| _ => return none
if !pf.getChildren[0]!.getResult[0]![0]!.getSort.isInteger then return none
reconstructArithPolyNormRel pf
| .ARITH_MULT_SIGN =>
if !pf.getResult[1]![0]!.getSort.isInteger then return none
reconstructMulSign pf
Expand Down
91 changes: 51 additions & 40 deletions Smt/Reconstruct/Rat.lean
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,55 @@ where
else
return ha

def reconstructArithPolyNormRel (pf : cvc5.Proof) : ReconstructM (Option Expr) := do
let lcx : Lean.Rat := pf.getChildren[0]!.getResult[0]![0]!.getRationalValue!
let cx : Q(Rat) ← reconstructTerm pf.getChildren[0]!.getResult[0]![0]!
let cy : Q(Rat) ← reconstructTerm pf.getChildren[0]!.getResult[1]![0]!
let x₁ : Q(Rat) ← reconstructTerm pf.getResult[0]![0]!
let x₂ : Q(Rat) ← reconstructTerm pf.getResult[0]![1]!
let y₁ : Q(Rat) ← reconstructTerm pf.getResult[1]![0]!
let y₂ : Q(Rat) ← reconstructTerm pf.getResult[1]![1]!
let h : Q($cx * ($x₁ - $x₂) = $cy * ($y₁ - $y₂)) ← reconstructProof pf.getChildren[0]!
let k := pf.getResult[0]!.getKind
let (hcx, hcy) :=
if k == .EQUAL then (q(@of_decide_eq_true ($cx ≠ 0) _), q(@of_decide_eq_true ($cy ≠ 0) _))
else if lcx > 0 then (q(@of_decide_eq_true ($cx > 0) _), q(@of_decide_eq_true ($cy > 0) _))
else (q(@of_decide_eq_true ($cx < 0) _), q(@of_decide_eq_true ($cy < 0) _))
let hcx := .app hcx q(Eq.refl true)
let hcy := .app hcy q(Eq.refl true)
let n ← getThmName k pf.getResult[0]![0]!.getSort.isInteger pf.getResult[1]![0]!.getSort.isInteger (lcx > 0)
return mkApp9 (.const n []) x₁ x₂ y₁ y₂ cx cy hcx hcy h
where
getThmName (k : cvc5.Kind) (il ir sign : Bool) : ReconstructM Name :=
if k == .LT && il == false && ir == false && sign == true then pure ``Rat.lt_of_sub_eq_pos
else if k == .LT && il == false && ir == false && sign == false then pure ``Rat.lt_of_sub_eq_neg
else if k == .LT && il == false && ir == true && sign == true then pure ``Rat.lt_of_sub_eq_pos_int_right
else if k == .LT && il == false && ir == true && sign == false then pure ``Rat.lt_of_sub_eq_neg_int_right
else if k == .LT && il == true && ir == false && sign == true then pure ``Rat.lt_of_sub_eq_pos_int_left
else if k == .LT && il == true && ir == false && sign == false then pure ``Rat.lt_of_sub_eq_neg_int_left
else if k == .LEQ && il == false && ir == false && sign == true then pure ``Rat.le_of_sub_eq_pos
else if k == .LEQ && il == false && ir == false && sign == false then pure ``Rat.le_of_sub_eq_neg
else if k == .LEQ && il == false && ir == true && sign == true then pure ``Rat.le_of_sub_eq_pos_int_right
else if k == .LEQ && il == false && ir == true && sign == false then pure ``Rat.le_of_sub_eq_neg_int_right
else if k == .LEQ && il == true && ir == false && sign == true then pure ``Rat.le_of_sub_eq_pos_int_left
else if k == .LEQ && il == true && ir == false && sign == false then pure ``Rat.le_of_sub_eq_neg_int_left
else if k == .EQUAL && il == false && ir == false then pure ``Rat.eq_of_sub_eq
else if k == .EQUAL && il == false && ir == true then pure ``Rat.eq_of_sub_eq_int_right
else if k == .EQUAL && il == true && ir == false then pure ``Rat.eq_of_sub_eq_int_left
else if k == .GEQ && il == false && ir == false && sign == true then pure ``Rat.ge_of_sub_eq_pos
else if k == .GEQ && il == false && ir == false && sign == false then pure ``Rat.ge_of_sub_eq_neg
else if k == .GEQ && il == false && ir == true && sign == true then pure ``Rat.ge_of_sub_eq_pos_int_right
else if k == .GEQ && il == false && ir == true && sign == false then pure ``Rat.ge_of_sub_eq_neg_int_right
else if k == .GEQ && il == true && ir == false && sign == true then pure ``Rat.ge_of_sub_eq_pos_int_left
else if k == .GEQ && il == true && ir == false && sign == false then pure ``Rat.ge_of_sub_eq_neg_int_left
else if k == .GT && il == false && ir == false && sign == true then pure ``Rat.gt_of_sub_eq_pos
else if k == .GT && il == false && ir == false && sign == false then pure ``Rat.gt_of_sub_eq_neg
else if k == .GT && il == false && ir == true && sign == true then pure ``Rat.gt_of_sub_eq_pos_int_right
else if k == .GT && il == false && ir == true && sign == false then pure ``Rat.gt_of_sub_eq_neg_int_right
else if k == .GT && il == true && ir == false && sign == true then pure ``Rat.gt_of_sub_eq_pos_int_left
else if k == .GT && il == true && ir == false && sign == false then pure ``Rat.gt_of_sub_eq_neg_int_left
else throwError "[arith_poly_norm_rel]: invalid combination of kind, integer operands, and sign: {k}, {il}, {ir}, {sign}"

@[smt_proof_reconstruct] def reconstructRatProof : ProofReconstructor := fun pf => do match pf.getRule with
| .DSL_REWRITE
| .THEORY_REWRITE => reconstructRewrite pf
Expand Down Expand Up @@ -370,46 +419,8 @@ where
let b : Q(Rat) ← reconstructTerm pf.getResult[1]!
addTac q($a = $b) Rat.nativePolyNorm
| .ARITH_POLY_NORM_REL =>
if pf.getResult[0]![0]!.getSort.isInteger then return none
let lcx : Lean.Rat := pf.getChildren[0]!.getResult[0]![0]!.getRationalValue!
let cx : Q(Rat) ← reconstructTerm pf.getChildren[0]!.getResult[0]![0]!
let x₁ : Q(Rat) ← reconstructTerm pf.getResult[0]![0]!
let x₂ : Q(Rat) ← reconstructTerm pf.getResult[0]![1]!
let cy : Q(Rat) ← reconstructTerm pf.getChildren[0]!.getResult[1]![0]!
let y₁ : Q(Rat) ← reconstructTerm pf.getResult[1]![0]!
let y₂ : Q(Rat) ← reconstructTerm pf.getResult[1]![1]!
let h : Q($cx * ($x₁ - $x₂) = $cy * ($y₁ - $y₂)) ← reconstructProof pf.getChildren[0]!
let k := pf.getResult[0]!.getKind
if k == .EQUAL then
let hcx : Q($cx ≠ 0) := .app q(@of_decide_eq_true ($cx ≠ 0) _) q(Eq.refl true)
let hcy : Q($cy ≠ 0) := .app q(@of_decide_eq_true ($cy ≠ 0) _) q(Eq.refl true)
addThm q(($x₁ = $x₂) = ($y₁ = $y₂)) q(Rat.eq_of_sub_eq $hcx $hcy $h)
else if lcx > 0 then
let hcx : Q($cx > 0) := .app q(@of_decide_eq_true ($cx > 0) _) q(Eq.refl true)
let hcy : Q($cy > 0) := .app q(@of_decide_eq_true ($cy > 0) _) q(Eq.refl true)
match k with
| .LT =>
addThm q(($x₁ < $x₂) = ($y₁ < $y₂)) q(Rat.lt_of_sub_eq_pos $hcx $hcy $h)
| .LEQ =>
addThm q(($x₁ ≤ $x₂) = ($y₁ ≤ $y₂)) q(Rat.le_of_sub_eq_pos $hcx $hcy $h)
| .GEQ =>
addThm q(($x₁ ≥ $x₂) = ($y₁ ≥ $y₂)) q(Rat.ge_of_sub_eq_pos $hcx $hcy $h)
| .GT =>
addThm q(($x₁ > $x₂) = ($y₁ > $y₂)) q(Rat.gt_of_sub_eq_pos $hcx $hcy $h)
| _ => return none
else
let hcx : Q($cx < 0) := .app q(@of_decide_eq_true ($cx < 0) _) q(Eq.refl true)
let hcy : Q($cy < 0) := .app q(@of_decide_eq_true ($cy < 0) _) q(Eq.refl true)
match k with
| .LT =>
addThm q(($x₁ < $x₂) = ($y₁ < $y₂)) q(Rat.lt_of_sub_eq_neg $hcx $hcy $h)
| .LEQ =>
addThm q(($x₁ ≤ $x₂) = ($y₁ ≤ $y₂)) q(Rat.le_of_sub_eq_neg $hcx $hcy $h)
| .GEQ =>
addThm q(($x₁ ≥ $x₂) = ($y₁ ≥ $y₂)) q(Rat.ge_of_sub_eq_neg $hcx $hcy $h)
| .GT =>
addThm q(($x₁ > $x₂) = ($y₁ > $y₂)) q(Rat.gt_of_sub_eq_neg $hcx $hcy $h)
| _ => return none
if pf.getChildren[0]!.getResult[0]![0]!.getSort.isInteger then return none
reconstructArithPolyNormRel pf
| .ARITH_MULT_SIGN =>
if pf.getResult[1]![0]!.getSort.isInteger then return none
reconstructMulSign pf
Expand Down
54 changes: 54 additions & 0 deletions Smt/Reconstruct/Rat/Lemmas.lean
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,60 @@ theorem gt_of_sub_eq_pos {c₁ c₂ : Rat} (hc₁ : c₁ > 0) (hc₂ : c₂ > 0)
theorem gt_of_sub_eq_neg {c₁ c₂ : Rat} (hc₁ : c₁ < 0) (hc₂ : c₂ < 0) (h : c₁ * (a₁ - a₂) = c₂ * (b₁ - b₂)) : (a₁ > a₂) = (b₁ > b₂) := by
sorry

theorem lt_of_sub_eq_pos_int_left {a₁ a₂ : Int} {b₁ b₂ c₁ c₂ : Rat} (hc₁ : c₁ > 0) (hc₂ : c₂ > 0) (h : c₁ * ↑(a₁ - a₂) = c₂ * (b₁ - b₂)) : (a₁ < a₂) = (b₁ < b₂) :=
sorry

theorem lt_of_sub_eq_neg_int_left {a₁ a₂ : Int} {b₁ b₂ c₁ c₂ : Rat} (hc₁ : c₁ < 0) (hc₂ : c₂ < 0) (h : c₁ * ↑(a₁ - a₂) = c₂ * (b₁ - b₂)) : (a₁ < a₂) = (b₁ < b₂) := by
sorry

theorem le_of_sub_eq_pos_int_left {a₁ a₂ : Int} {b₁ b₂ c₁ c₂ : Rat} (hc₁ : c₁ > 0) (hc₂ : c₂ > 0) (h : c₁ * ↑(a₁ - a₂) = c₂ * (b₁ - b₂)) : (a₁ ≤ a₂) = (b₁ ≤ b₂) := by
sorry

theorem le_of_sub_eq_neg_int_left {a₁ a₂ : Int} {b₁ b₂ c₁ c₂ : Rat} (hc₁ : c₁ < 0) (hc₂ : c₂ < 0) (h : c₁ * ↑(a₁ - a₂) = c₂ * (b₁ - b₂)) : (a₁ ≤ a₂) = (b₁ ≤ b₂) := by
sorry

theorem eq_of_sub_eq_int_left {a₁ a₂ : Int} {b₁ b₂ c₁ c₂ : Rat} (hc₁ : c₁ ≠ 0) (hc₂ : c₂ ≠ 0) (h : c₁ * ↑(a₁ - a₂) = c₂ * (b₁ - b₂)) : (a₁ = a₂) = (b₁ = b₂) := by
sorry

theorem ge_of_sub_eq_pos_int_left {a₁ a₂ : Int} {b₁ b₂ c₁ c₂ : Rat} (hc₁ : c₁ > 0) (hc₂ : c₂ > 0) (h : c₁ * ↑(a₁ - a₂) = c₂ * (b₁ - b₂)) : (a₁ ≥ a₂) = (b₁ ≥ b₂) := by
sorry

theorem ge_of_sub_eq_neg_int_left {a₁ a₂ : Int} {b₁ b₂ c₁ c₂ : Rat} (hc₁ : c₁ < 0) (hc₂ : c₂ < 0) (h : c₁ * ↑(a₁ - a₂) = c₂ * (b₁ - b₂)) : (a₁ ≥ a₂) = (b₁ ≥ b₂) := by
sorry

theorem gt_of_sub_eq_pos_int_left {a₁ a₂ : Int} {b₁ b₂ c₁ c₂ : Rat} (hc₁ : c₁ > 0) (hc₂ : c₂ > 0) (h : c₁ * ↑(a₁ - a₂) = c₂ * (b₁ - b₂)) : (a₁ > a₂) = (b₁ > b₂) := by
sorry

theorem gt_of_sub_eq_neg_int_left {a₁ a₂ : Int} {b₁ b₂ c₁ c₂ : Rat} (hc₁ : c₁ < 0) (hc₂ : c₂ < 0) (h : c₁ * ↑(a₁ - a₂) = c₂ * (b₁ - b₂)) : (a₁ > a₂) = (b₁ > b₂) := by
sorry

theorem lt_of_sub_eq_pos_int_right {a₁ a₂ : Rat} {b₁ b₂ : Int} {c₁ c₂ : Rat} (hc₁ : c₁ > 0) (hc₂ : c₂ > 0) (h : c₁ * (a₁ - a₂) = c₂ * ↑(b₁ - b₂)) : (a₁ < a₂) = (b₁ < b₂) :=
sorry

theorem lt_of_sub_eq_neg_int_right {a₁ a₂ : Rat} {b₁ b₂ : Int} {c₁ c₂ : Rat} (hc₁ : c₁ < 0) (hc₂ : c₂ < 0) (h : c₁ * (a₁ - a₂) = c₂ * ↑(b₁ - b₂)) : (a₁ < a₂) = (b₁ < b₂) := by
sorry

theorem le_of_sub_eq_pos_int_right {a₁ a₂ : Rat} {b₁ b₂ : Int} {c₁ c₂ : Rat} (hc₁ : c₁ > 0) (hc₂ : c₂ > 0) (h : c₁ * (a₁ - a₂) = c₂ * ↑(b₁ - b₂)) : (a₁ ≤ a₂) = (b₁ ≤ b₂) := by
sorry

theorem le_of_sub_eq_neg_int_right {a₁ a₂ : Rat} {b₁ b₂ : Int} {c₁ c₂ : Rat} (hc₁ : c₁ < 0) (hc₂ : c₂ < 0) (h : c₁ * (a₁ - a₂) = c₂ * ↑(b₁ - b₂)) : (a₁ ≤ a₂) = (b₁ ≤ b₂) := by
sorry

theorem eq_of_sub_eq_int_right {a₁ a₂ : Rat} {b₁ b₂ : Int} {c₁ c₂ : Rat} (hc₁ : c₁ ≠ 0) (hc₂ : c₂ ≠ 0) (h : c₁ * (a₁ - a₂) = c₂ * ↑(b₁ - b₂)) : (a₁ = a₂) = (b₁ = b₂) := by
sorry

theorem ge_of_sub_eq_pos_int_right {a₁ a₂ : Rat} {b₁ b₂ : Int} {c₁ c₂ : Rat} (hc₁ : c₁ > 0) (hc₂ : c₂ > 0) (h : c₁ * (a₁ - a₂) = c₂ * ↑(b₁ - b₂)) : (a₁ ≥ a₂) = (b₁ ≥ b₂) := by
sorry

theorem ge_of_sub_eq_neg_int_right {a₁ a₂ : Rat} {b₁ b₂ : Int} {c₁ c₂ : Rat} (hc₁ : c₁ < 0) (hc₂ : c₂ < 0) (h : c₁ * (a₁ - a₂) = c₂ * ↑(b₁ - b₂)) : (a₁ ≥ a₂) = (b₁ ≥ b₂) := by
sorry

theorem gt_of_sub_eq_pos_int_right {a₁ a₂ : Rat} {b₁ b₂ : Int} {c₁ c₂ : Rat} (hc₁ : c₁ > 0) (hc₂ : c₂ > 0) (h : c₁ * (a₁ - a₂) = c₂ * ↑(b₁ - b₂)) : (a₁ > a₂) = (b₁ > b₂) := by
sorry

theorem gt_of_sub_eq_neg_int_right {a₁ a₂ : Rat} {b₁ b₂ : Int} {c₁ c₂ : Rat} (hc₁ : c₁ < 0) (hc₂ : c₂ < 0) (h : c₁ * (a₁ - a₂) = c₂ * ↑(b₁ - b₂)) : (a₁ > a₂) = (b₁ > b₂) := by
sorry

theorem mul_sign₁ (ha : a < 0) (hb : b < 0) : a * b > 0 := by
sorry

Expand Down
Loading

0 comments on commit 39b2782

Please sign in to comment.