/-
Copyright (c) 2016 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Leonardo de Moura

The integers, with addition, multiplication, and subtraction.
-/
prelude
import Init.Data.Nat.Basic
import Init.Data.List
import Init.Data.Repr
import Init.Data.ToString.Basic
open Nat

/- the Type, coercions, and notation -/

inductive Int : Type where
  | ofNat   : Nat → Int
  | negSucc : Nat → Int

attribute [extern "lean_nat_to_int"] Int.ofNat
attribute [extern "lean_int_neg_succ_of_nat"] Int.negSucc

instance : Coe Nat Int := ⟨Int.ofNat⟩

namespace Int
instance : Inhabited Int := ⟨ofNat 0⟩

def negOfNat : Nat → Int
  | 0      => 0
  | succ m => negSucc m

set_option bootstrap.gen_matcher_code false in
@[extern "lean_int_neg"]
protected def neg (n : @& Int) : Int :=
  match n with
  | ofNat n   => negOfNat n
  | negSucc n => succ n

def subNatNat (m n : Nat) : Int :=
  match (n - m : Nat) with
  | 0        => ofNat (m - n)  -- m ≥ n
  | (succ k) => negSucc k

set_option bootstrap.gen_matcher_code false in
@[extern "lean_int_add"]
  protected def add (m n : @& Int) : Int :=
  match m, n with
  | ofNat m,   ofNat n   => ofNat (m + n)
  | ofNat m,   negSucc n => subNatNat m (succ n)
  | negSucc m, ofNat n   => subNatNat n (succ m)
  | negSucc m, negSucc n => negSucc (succ (m + n))

set_option bootstrap.gen_matcher_code false in
@[extern "lean_int_mul"]
protected def mul (m n : @& Int) : Int :=
  match m, n with
  | ofNat m,   ofNat n   => ofNat (m * n)
  | ofNat m,   negSucc n => negOfNat (m * succ n)
  | negSucc m, ofNat n   => negOfNat (succ m * n)
  | negSucc m, negSucc n => ofNat (succ m * succ n)

instance : Neg Int where
  neg := Int.neg
instance : Add Int where
  add := Int.add
instance : Mul Int where
  mul := Int.mul

instance : HAdd Int Nat Int where
  hAdd a b := Int.add a b
instance : HAdd Nat Int Int where
  hAdd a b := Int.add a b

instance : HMul Int Nat Int where
  hMul a b := Int.mul a b
instance : HMul Nat Int Int where
  hMul a b := Int.mul a b

@[extern "lean_int_sub"]
protected def sub (m n : @& Int) : Int :=
  m + (- n)

instance : Sub Int where
  sub := Int.sub

instance : HSub Int Nat Int where
  hSub a b := Int.sub a b
instance : HSub Nat Int Int where
  hSub a b := Int.sub a b

inductive NonNeg : Int → Prop where
  | mk (n : Nat) : NonNeg (ofNat n)

protected def LessEq (a b : Int) : Prop := NonNeg (b - a)

instance : HasLessEq Int where
  LessEq := Int.LessEq

protected def Less (a b : Int) : Prop := (a + 1) ≤ b

instance : HasLess Int where
  Less := Int.Less

set_option bootstrap.gen_matcher_code false in
@[extern "lean_int_dec_eq"]
protected def decEq (a b : @& Int) : Decidable (a = b) :=
  match a, b with
  | ofNat a, ofNat b => match decEq a b with
    | isTrue h  => isTrue  <| h ▸ rfl
    | isFalse h => isFalse <| fun h' => Int.noConfusion h' (fun h' => absurd h' h)
  | negSucc a, negSucc b => match decEq a b with
    | isTrue h  => isTrue  <| h ▸ rfl
    | isFalse h => isFalse <| fun h' => Int.noConfusion h' (fun h' => absurd h' h)
  | ofNat a, negSucc b => isFalse <| fun h => Int.noConfusion h
  | negSucc a, ofNat b => isFalse <| fun h => Int.noConfusion h

instance : DecidableEq Int := Int.decEq

set_option bootstrap.gen_matcher_code false in
@[extern "lean_int_dec_nonneg"]
private def decNonneg (m : @& Int) : Decidable (NonNeg m) :=
  match m with
  | ofNat m   => isTrue <| NonNeg.mk m
  | negSucc m => isFalse <| fun h => nomatch h

@[extern "lean_int_dec_le"]
instance decLe (a b : @& Int) : Decidable (a ≤ b) :=
  decNonneg _

@[extern "lean_int_dec_lt"]
instance decLt (a b : @& Int) : Decidable (a < b) :=
  decNonneg _

set_option bootstrap.gen_matcher_code false in
@[extern "lean_nat_abs"]
def natAbs (m : @& Int) : Nat :=
  match m with
  | ofNat m   => m
  | negSucc m => m.succ

protected def repr : Int → String
  | ofNat m   => Nat.repr m
  | negSucc m => "-" ++ Nat.repr (succ m)

instance : Repr Int where
  repr := Int.repr

instance : ToString Int where
  toString := Int.repr

instance : OfNat Int n where
  ofNat := Int.ofNat n

@[extern "lean_int_div"]
def div : (@& Int) → (@& Int) → Int
  | ofNat m,   ofNat n   => ofNat (m / n)
  | ofNat m,   negSucc n => -ofNat (m / succ n)
  | negSucc m, ofNat n   => -ofNat (succ m / n)
  | negSucc m, negSucc n => ofNat (succ m / succ n)

@[extern "lean_int_mod"]
def mod : (@& Int) → (@& Int) → Int
  | ofNat m,   ofNat n   => ofNat (m % n)
  | ofNat m,   negSucc n => ofNat (m % succ n)
  | negSucc m, ofNat n   => -ofNat (succ m % n)
  | negSucc m, negSucc n => -ofNat (succ m % succ n)

instance : Div Int where
  div := Int.div

instance : Mod Int where
  mod := Int.mod

instance : HDiv Int Nat Int where
  hDiv a b := Int.div a b
instance : HDiv Nat Int Int where
  hDiv a b := Int.div a b

instance : HMod Int Nat Int where
  hMod a b := Int.mod a b
instance : HMod Nat Int Int where
  hMod a b := Int.mod a b

def toNat : Int → Nat
  | ofNat n   => n
  | negSucc n => 0

def natMod (m n : Int) : Nat := (m % n).toNat

protected def Int.pow (m : Int) : Nat → Int
  | 0      => 1
  | succ n => Int.pow m n * m

instance : HPow Int Nat Int where
  hPow := Int.pow

end Int

namespace String

def toInt? (s : String) : Option Int :=
  if s.get 0 = '-' then do
    let v ← (s.toSubstring.drop 1).toNat?;
    pure <| - Int.ofNat v
  else
   Int.ofNat <$> s.toNat?

def isInt (s : String) : Bool :=
  if s.get 0 = '-' then
    (s.toSubstring.drop 1).isNat
  else
    s.isNat

def toInt! (s : String) : Int :=
  match s.toInt? with
  | some v => v
  | none   => panic! "Int expected"

end String
