{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE TypeFamilies #-}

module Data.Dir where

import Data.Bits
import qualified Data.Vector.Generic as G
import qualified Data.Vector.Generic.Mutable as GM
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Unboxed.Mutable as UM
import Data.Word

data Dir = L | R | U | D
  deriving (Dir -> Dir -> Bool
(Dir -> Dir -> Bool) -> (Dir -> Dir -> Bool) -> Eq Dir
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Dir -> Dir -> Bool
== :: Dir -> Dir -> Bool
$c/= :: Dir -> Dir -> Bool
/= :: Dir -> Dir -> Bool
Eq, Eq Dir
Eq Dir =>
(Dir -> Dir -> Ordering)
-> (Dir -> Dir -> Bool)
-> (Dir -> Dir -> Bool)
-> (Dir -> Dir -> Bool)
-> (Dir -> Dir -> Bool)
-> (Dir -> Dir -> Dir)
-> (Dir -> Dir -> Dir)
-> Ord Dir
Dir -> Dir -> Bool
Dir -> Dir -> Ordering
Dir -> Dir -> Dir
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Dir -> Dir -> Ordering
compare :: Dir -> Dir -> Ordering
$c< :: Dir -> Dir -> Bool
< :: Dir -> Dir -> Bool
$c<= :: Dir -> Dir -> Bool
<= :: Dir -> Dir -> Bool
$c> :: Dir -> Dir -> Bool
> :: Dir -> Dir -> Bool
$c>= :: Dir -> Dir -> Bool
>= :: Dir -> Dir -> Bool
$cmax :: Dir -> Dir -> Dir
max :: Dir -> Dir -> Dir
$cmin :: Dir -> Dir -> Dir
min :: Dir -> Dir -> Dir
Ord, Int -> Dir
Dir -> Int
Dir -> [Dir]
Dir -> Dir
Dir -> Dir -> [Dir]
Dir -> Dir -> Dir -> [Dir]
(Dir -> Dir)
-> (Dir -> Dir)
-> (Int -> Dir)
-> (Dir -> Int)
-> (Dir -> [Dir])
-> (Dir -> Dir -> [Dir])
-> (Dir -> Dir -> [Dir])
-> (Dir -> Dir -> Dir -> [Dir])
-> Enum Dir
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
$csucc :: Dir -> Dir
succ :: Dir -> Dir
$cpred :: Dir -> Dir
pred :: Dir -> Dir
$ctoEnum :: Int -> Dir
toEnum :: Int -> Dir
$cfromEnum :: Dir -> Int
fromEnum :: Dir -> Int
$cenumFrom :: Dir -> [Dir]
enumFrom :: Dir -> [Dir]
$cenumFromThen :: Dir -> Dir -> [Dir]
enumFromThen :: Dir -> Dir -> [Dir]
$cenumFromTo :: Dir -> Dir -> [Dir]
enumFromTo :: Dir -> Dir -> [Dir]
$cenumFromThenTo :: Dir -> Dir -> Dir -> [Dir]
enumFromThenTo :: Dir -> Dir -> Dir -> [Dir]
Enum, Dir
Dir -> Dir -> Bounded Dir
forall a. a -> a -> Bounded a
$cminBound :: Dir
minBound :: Dir
$cmaxBound :: Dir
maxBound :: Dir
Bounded, ReadPrec [Dir]
ReadPrec Dir
Int -> ReadS Dir
ReadS [Dir]
(Int -> ReadS Dir)
-> ReadS [Dir] -> ReadPrec Dir -> ReadPrec [Dir] -> Read Dir
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
$creadsPrec :: Int -> ReadS Dir
readsPrec :: Int -> ReadS Dir
$creadList :: ReadS [Dir]
readList :: ReadS [Dir]
$creadPrec :: ReadPrec Dir
readPrec :: ReadPrec Dir
$creadListPrec :: ReadPrec [Dir]
readListPrec :: ReadPrec [Dir]
Read, Int -> Dir -> ShowS
[Dir] -> ShowS
Dir -> [Char]
(Int -> Dir -> ShowS)
-> (Dir -> [Char]) -> ([Dir] -> ShowS) -> Show Dir
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Dir -> ShowS
showsPrec :: Int -> Dir -> ShowS
$cshow :: Dir -> [Char]
show :: Dir -> [Char]
$cshowList :: [Dir] -> ShowS
showList :: [Dir] -> ShowS
Show)

instance U.IsoUnbox Dir Word8 where
  toURepr :: Dir -> Word8
toURepr = Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word8) -> (Dir -> Int) -> Dir -> Word8
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Dir -> Int
forall a. Enum a => a -> Int
fromEnum
  {-# INLINE toURepr #-}
  fromURepr :: Word8 -> Dir
fromURepr = Int -> Dir
forall a. Enum a => Int -> a
toEnum (Int -> Dir) -> (Word8 -> Int) -> Word8 -> Dir
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral
  {-# INLINE fromURepr #-}

newtype instance UM.MVector s Dir = MV_Dir (UM.MVector s Word8)
newtype instance U.Vector Dir = V_Dir (U.Vector Word8)
deriving via (Dir `U.As` Word8) instance GM.MVector U.MVector Dir
deriving via (Dir `U.As` Word8) instance G.Vector U.Vector Dir
instance U.Unbox Dir

{- |
>>> enumerateDir
[L,R,U,D]
-}
enumerateDir :: [Dir]
enumerateDir :: [Dir]
enumerateDir = [Dir
L, Dir
R, Dir
U, Dir
D]

{- |
>>> opposite L
R
>>> opposite R
L
>>> opposite U
D
>>> opposite D
U
-}
opposite :: Dir -> Dir
opposite :: Dir -> Dir
opposite = Int -> Dir
forall a. Enum a => Int -> a
toEnum (Int -> Dir) -> (Dir -> Int) -> Dir -> Dir
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int -> Int
forall a. Bits a => a -> a -> a
xor Int
1 (Int -> Int) -> (Dir -> Int) -> Dir -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Dir -> Int
forall a. Enum a => a -> Int
fromEnum
{-# INLINE opposite #-}

dirToChar :: Dir -> Char
dirToChar :: Dir -> Char
dirToChar Dir
L = Char
'L'
dirToChar Dir
R = Char
'R'
dirToChar Dir
U = Char
'U'
dirToChar Dir
D = Char
'D'

charToDir :: Char -> Dir
charToDir :: Char -> Dir
charToDir Char
'L' = Dir
L
charToDir Char
'R' = Dir
R
charToDir Char
'U' = Dir
U
charToDir Char
'D' = Dir
D
charToDir Char
c = [Char] -> Dir
forall a. HasCallStack => [Char] -> a
error ([Char] -> Dir) -> [Char] -> Dir
forall a b. (a -> b) -> a -> b
$ [Char]
"invalid dir: " [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [Char
c]

{- |
>>> dirMove (2, 3) U
(1,3)
>>> dirMove (0, 0) U
(-1,0)
>>> dirMove (0, 0) R
(0,1)
>>> map (dirMove (2, 3)) enumerateDir
[(2,2),(2,4),(1,3),(3,3)]
-}
dirMove :: (Int, Int) -> Dir -> (Int, Int)
dirMove :: (Int, Int) -> Dir -> (Int, Int)
dirMove (!Int
x, !Int
y) Dir
L = let !y' :: Int
y' = Int
y Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1 in (Int
x, Int
y')
dirMove (!Int
x, !Int
y) Dir
R = let !y' :: Int
y' = Int
y Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1 in (Int
x, Int
y')
dirMove (!Int
x, !Int
y) Dir
U = let !x' :: Int
x' = Int
x Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1 in (Int
x', Int
y)
dirMove (!Int
x, !Int
y) Dir
D = let !x' :: Int
x' = Int
x Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1 in (Int
x', Int
y)

{- |
>>> dir4Moves (2, 3)
[(2,2),(2,4),(1,3),(3,3)]
>>> dir4Moves (0, 1)
[(0,0),(0,2),(-1,1),(1,1)]
-}
dir4Moves :: (Int, Int) -> [(Int, Int)]
dir4Moves :: (Int, Int) -> [(Int, Int)]
dir4Moves (Int
x, Int
y) = [(Int
x, Int
y Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1), (Int
x, Int
y Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1), (Int
x Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1, Int
y), (Int
x Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1, Int
y)]
{-# INLINE dir4Moves #-}

{- |
>>> dirDiff U
(-1,0)
>>> dirDiff R
(0,1)
-}
dirDiff :: Dir -> (Int, Int)
dirDiff :: Dir -> (Int, Int)
dirDiff Dir
L = (Int
0, -Int
1)
dirDiff Dir
R = (Int
0, Int
1)
dirDiff Dir
U = (-Int
1, Int
0)
dirDiff Dir
D = (Int
1, Int
0)

{- |
>>> moveToDir (0, 1) (0, 2)
R
>>> moveToDir (1, 2) (0, 2)
U
>>> moveToDir (0, 0) (1, 1)
*** Exception: invalid move: (1,1)
-}
moveToDir :: (Int, Int) -> (Int, Int) -> Dir
moveToDir :: (Int, Int) -> (Int, Int) -> Dir
moveToDir (Int
x, Int
y) (Int
x', Int
y') = case (Int
x' Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
x, Int
y' Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
y) of
  (Int
0, Int
dy)
    | Int
dy Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 -> Dir
L
    | Int
dy Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0 -> Dir
R
  (Int
dx, Int
0)
    | Int
dx Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 -> Dir
U
    | Int
dx Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0 -> Dir
D
  (Int
dx, Int
dy) -> [Char] -> Dir
forall a. HasCallStack => [Char] -> a
error ([Char] -> Dir) -> [Char] -> Dir
forall a b. (a -> b) -> a -> b
$ [Char]
"invalid move: " [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> (Int, Int) -> [Char]
forall a. Show a => a -> [Char]
show (Int
dx, Int
dy)