import Data.Vect

strange : (n : Nat) -> if n == 0 then Int else String
strange Z = 0
strange (S k) = "positive"

strangeLength : (s : String) -> if length s == 0 then Int else String
strangeLength s = strange (length s)

{-
-- Doesn't compile, because Idris enforces parametricity.
replicateWithWart : (n : Nat) -> a -> Vect n a
replicateWithWart {a} n x =
  case a of
    Int => replicate n 0
    _   => replicate n x
-}

useTortured : a -> b -> (Type, a, b)
useTortured {a} {b} x y = tortured [a, b] x y

-- Doesn't compile
useTorturedBroken : a -> b -> (Type, a, b)
useTorturedBroken {a} {b} x y = tortured [a, b] x x

{-
-- Doesn't compile
useTorturedBroken : (x : a) -> (y : b) -> (Type, a, b)
useTorturedBroken {a} {b} x y = tortured [a, b] x x
-}

{-
-- Doesn't compile
strangeLengthBroken : (s : String) -> if length s == 0 then Int else String
strangeLengthBroken s = strange (length s + 1)
-}

{-
zipReplicasBroken : (numAs : Nat) -> a -> (numBs : Nat) -> b -> Vect numAs (a, b)
zipReplicasBroken numAs valA numBs valB =
  let aVec = replicate numAs valA
      bVec = replicate numBs valB
  in Vect.zip aVec bVec
-}
zipReplicas : (n : Nat) -> a -> b -> Vect n (a, b)
zipReplicas n valA valB =
  let aVec = replicate n valA
      bVec = replicate n valB
  in Vect.zip aVec bVec

{-
zipReplicasSuccessorBroken : (n : Nat) -> a -> b -> Vect (n + 1) (a, b)
zipReplicasSuccessorBroken n valA valB =
  let aVec = replicate (n + 1) valA
      bVec = valB :: replicate n valB
  in Vect.zip aVec bVec
~-}
zipReplicasSuccessor : (n : Nat) -> a -> b -> Vect (1 + n) (a, b)
zipReplicasSuccessor n valA valB =
  let aVec = replicate (1 + n) valA
      bVec = valB :: replicate n valB
  in Vect.zip aVec bVec

zipReplicasSuccessor2 : (n : Nat) -> a -> b -> Vect (n + 1) (a, b)
zipReplicasSuccessor2 n valA valB =
  let aVec = replicate (n + 1) valA
      bVec = valB :: replicate n valB
  in Vect.zip aVec (rewrite plusCommutative n 1 in bVec)


> module Nat.GCDOperations

> import Nat.GCD
> import Nat.Divisor

> %default total
> %access public export
> %auto_implicits on

> |||
> gcdDivisorFst : GCD d m n -> d `Divisor` m
> gcdDivisorFst {d} (MkGCD dDm dDn dG) = dDm

> |||
> gcdDivisorSnd : GCD d m n -> d `Divisor` n
> gcdDivisorSnd {d} (MkGCD dDm dDn dG) = dDn

> |||
> gcdDivisorGreatest : GCD d m n -> ((d' : Nat) -> d' `Divisor` m -> d' `Divisor` n -> d' `Divisor` d)
> gcdDivisorGreatest {d} (MkGCD dDm dDn dG) = dG


module Main

-- from http://docs.idris-lang.org/en/latest/tutorial/theorems.html?highlight=fiveIsFive#equality

-- = is defined in sourse code of idris
-- https://groups.google.com/forum/#!topic/idris-lang/-tmOaux6DFI

-- proudo-definition
-- data (=) : a -> b -> Type where
--   refl : x = x

fiveIsFive : 5 = 5
fiveIsFive = Refl

twoPlusTwo : 2 + 2 = 4
twoPlusTwo = Refl

-- twoPlusTwo : 2 + 2 = 5 -- raise error
-- twoPlusTwo = Refl

--- (=) defined ourselves

using (a : Type, b : Type, x : a)
  data Equus : a -> b -> Type where
    --  will raise error `Main.eqRefl has a name which may be implicitly bound.`
    --  implicitly bound variable - implicit forall

    -- eqRefl : Equus x x

    EqRefl : Equus x x -- right definition

twoPlusTwo' : 2 + 2 `Equus` 4
twoPlusTwo' = EqRefl

heterogeneous : 2 + 2 ~=~ 4
heterogeneous = Refl

-- heterogeneous : 2 + 2 ~=~ 5 -- raise error
-- heterogeneous = Refl


module Injection

import Data.Vect
import Data.Nat

%default total

|||Is a Nat different from all the Nats in a vector?
public export
isDifferent : Nat -> Vect n Nat -> Bool
isDifferent n [] = True
isDifferent n (x :: xs) = (n /= x) && isDifferent n xs

|||Are all the elements of a Nat vector different?
public export
allDifferent : Vect n Nat -> Bool
allDifferent [] = True
allDifferent (x :: xs) = isDifferent x xs && allDifferent xs

|||Are all the elements in a vector smaller than a given Nat?
public export
allSmaller : Vect n Nat -> Nat -> Bool
allSmaller [] m = True
allSmaller (x :: xs) m = (x < m) && allSmaller xs m

public export
isInjective : Nat -> Vect n Nat -> Bool
isInjective m v = allSmaller v m && allDifferent v

|||Returns the element at index k in a vector
public export
index : (k : Nat) -> Vect n Nat -> {auto prf : (k < n) = True} -> Nat
index Z     (x::_)  = x
index (S k) (_::xs) = index k xs

|||Returns the vector [1,2,...,n]
public export
rangeVect : (startIndex : Nat) -> (length : Nat) -> Vect length Nat
rangeVect k Z = []
rangeVect k (S i) = k :: rangeVect (S k) i