From Coq Require Import Lia ZArith.
Local Open Scope Z_scope.

Module Z.
  Lemma push_minus_add a b : -(-a + b) = a + -b. Proof. lia. Qed.

  Ltac clean_neg_step _ :=
    match goal with
    | [ H : (-?x) < 0 |- _ ] => assert (0 < x) by lia; clear H
    | [ H : 0 > (-?x) |- _ ] => assert (0 < x) by lia; clear H
    | [ H : 0 <> -?x |- _ ] => assert (0 <> x) by lia; clear H
    | [ H : -?x <> 0 |- _ ] => assert (x <> 0) by lia; clear H
    | [ H : (-?x) <= 0 |- _ ] => assert (0 <= x) by lia; clear H
    | [ H : 0 >= (-?x) |- _ ] => assert (0 <= x) by lia; clear H
    | [ H : -?x <= -?y |- _ ] => rewrite <- Z.opp_le_mono in H
    | [ |- -?x <= -?y ] => rewrite <- Z.opp_le_mono
    | [ H : -?x < -?y |- _ ] => rewrite <- Z.opp_lt_mono in H
    | [ |- -?x < -?y ] => rewrite <- Z.opp_lt_mono
    | _ => progress rewrite <- Z.opp_le_mono in *
    | [ H : context[_ * _] |- _ ]
      => first [ rewrite !Z.mul_opp_opp in H
               | rewrite !Z.mul_opp_l in H
               | rewrite !Z.mul_opp_r in H ]
    | [ |- context[_ * _] ]
      => first [ rewrite !Z.mul_opp_opp
               | rewrite !Z.mul_opp_l
               | rewrite !Z.mul_opp_r ]
    | [ H : context[- - _] |- _ ] => rewrite !Z.opp_involutive in H
    | [ |- context[- - _] ] => rewrite !Z.opp_involutive
    | [ H : context[-_ + -_] |- _ ] => rewrite <- !Z.opp_add_distr in H
    | [ |- context[-_ + -_] ] => rewrite <- !Z.opp_add_distr
    | [ H : context[-(-?a + ?b)] |- _ ] => rewrite !push_minus_add in H
    | [ |- context[-(-?a + ?b)] ] => rewrite !push_minus_add
    | [ H : 0 <= ?x, H' : 0 <= ?y, H'' : -?x <= ?y |- _ ] => clear H''
    | [ H : 0 < ?x, H' : 0 <= ?y, H'' : -?x <= ?y |- _ ] => clear H''
    | [ H : 0 <= ?x, H' : 0 < ?y, H'' : -?x <= ?y |- _ ] => clear H''
    | [ H : 0 < ?x, H' : 0 < ?y, H'' : -?x <= ?y |- _ ] => clear H''
    | [ H : 0 < ?x, H' : 0 <= ?y, H'' : -?x < ?y |- _ ] => clear H''
    | [ H : 0 <= ?x, H' : 0 < ?y, H'' : -?x < ?y |- _ ] => clear H''
    | [ H : 0 < ?x, H' : 0 < ?y, H'' : -?x < ?y |- _ ] => clear H''
    | [ |- -_ = -_ ] => rewrite Z.opp_inj_wd
    | [ H : -_ = -_ |- _ ] => rewrite Z.opp_inj_wd in H
    end.
  Ltac clean_neg := repeat clean_neg_step ().
  Ltac replace_with_neg x :=
    assert (x = -(-x)) by (symmetry; apply Z.opp_involutive); generalize dependent (-x);
    let x' := fresh in
    rename x into x'; intro x; intros; subst x';
    clean_neg.
  Ltac replace_all_neg_with_pos :=
    repeat match goal with
           | [ H : ?x < 0 |- _ ] => replace_with_neg x
           | [ H : 0 > ?x |- _ ] => replace_with_neg x
           | [ H : ?x <= 0 |- _ ] => replace_with_neg x
           | [ H : 0 >= ?x |- _ ] => replace_with_neg x
           end.
End Z.
