module Rubik.Cube.Cubie.Internal where
import Rubik.Cube.Facelet.Internal as F
import Rubik.Misc
import Control.Applicative
import Control.Exception
import Control.Monad
import Data.Function ( on )
import Data.List
import Data.Maybe
import Data.Monoid
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Unboxed.Mutable as MU
newtype CornerPermu = CornerPermu { fromCornerPermu :: Vector Int }
deriving (Eq, Show)
newtype CornerOrien = CornerOrien { fromCornerOrien :: Vector Int }
deriving (Eq, Show)
data Corner = Corner
{ cPermu :: CornerPermu
, cOrien :: CornerOrien }
deriving (Eq, Show)
cornerPermu :: Vector Int -> Maybe CornerPermu
cornerPermu v = CornerPermu <$> mfilter check (Just v)
where check v = U.length v == numCorners
&& isPermutationVector v
unsafeCornerPermu = CornerPermu
unsafeCornerPermu' = CornerPermu . U.fromList
cornerOrien :: Vector Int -> Maybe CornerOrien
cornerOrien v = do
guard $ U.length v == numCorners
&& U.all (\o -> 0 <= o && o < 6) v
return (CornerOrien v)
unsafeCornerOrien = CornerOrien
unsafeCornerOrien' = CornerOrien . U.fromList
newtype EdgePermu = EdgePermu { fromEdgePermu :: Vector Int }
deriving (Eq, Show)
newtype EdgeOrien = EdgeOrien { fromEdgeOrien :: Vector Int }
deriving (Eq, Show)
data Edge = Edge
{ ePermu :: EdgePermu
, eOrien :: EdgeOrien }
deriving (Eq, Show)
edgePermu :: Vector Int -> Maybe EdgePermu
edgePermu v = do
guard $ U.length v == numEdges
&& isPermutationVector v
return (EdgePermu v)
unsafeEdgePermu = EdgePermu
unsafeEdgePermu' = EdgePermu . U.fromList
edgeOrien :: Vector Int -> Maybe EdgeOrien
edgeOrien v = do
guard $ U.length v == numEdges
&& U.all (`elem` [0, 1]) v
return (EdgeOrien v)
unsafeEdgeOrien = EdgeOrien
unsafeEdgeOrien' = EdgeOrien . U.fromList
data Cube = Cube
{ corner :: Corner
, edge :: Edge }
deriving (Eq, Show)
class FromCube a where
fromCube :: Cube -> a
instance (FromCube a, FromCube b) => FromCube (a, b) where
fromCube c = (fromCube c, fromCube c)
class CubeAction a where
cubeAction :: a -> Cube -> a
instance (CubeAction a, CubeAction b) => CubeAction (a, b) where
cubeAction (a, b) c = (cubeAction a c, cubeAction b c)
cube :: Vector Int -> Vector Int -> Vector Int -> Vector Int -> Maybe Cube
cube cp co ep eo = Cube <$> c <*> e
where c = Corner <$> cornerPermu cp <*> cornerOrien co
e = Edge <$> edgePermu ep <*> edgeOrien eo
cube' :: [Int] -> [Int] -> [Int] -> [Int] -> Maybe Cube
cube' cp co ep eo = cube (f cp) (f co) (f ep) (f eo)
where f = U.fromList
unsafeCube :: Vector Int -> Vector Int -> Vector Int -> Vector Int -> Cube
unsafeCube cp co ep eo = Cube c e
where c = Corner (CornerPermu cp) (CornerOrien co)
e = Edge (EdgePermu ep) (EdgeOrien eo)
unsafeCube' :: [Int] -> [Int] -> [Int] -> [Int] -> Cube
unsafeCube' cp co ep eo = unsafeCube (f cp) (f co) (f ep) (f eo)
where f = U.fromList
instance FromCube Corner where
fromCube = corner
instance FromCube CornerPermu where
fromCube = cPermu . corner
instance FromCube CornerOrien where
fromCube = cOrien . corner
instance FromCube Edge where
fromCube = edge
instance FromCube EdgePermu where
fromCube = ePermu . edge
instance FromCube EdgeOrien where
fromCube = eOrien . edge
numCorners = 8 :: Int
numEdges = 12 :: Int
o `oPlus` o' | o < 3 && o' < 3 = (o + o') `mod` 3
| o < 3 = 3 + ((o'+ o) `mod` 3)
| o' < 3 = 3 + ((o o') `mod` 3)
| otherwise = (o o') `mod` 3
oInv o | o == 0 = 0
| o < 3 = 3 o
| otherwise = o
instance Monoid CornerPermu where
mempty = CornerPermu $ idVector numCorners
mappend (CornerPermu b) (CornerPermu c) = CornerPermu $ composeVector b c
instance Group CornerPermu where
inverse (CornerPermu a) = CornerPermu $ inverseVector a
instance Monoid EdgePermu where
mempty = EdgePermu $ idVector numEdges
mappend (EdgePermu b) (EdgePermu c) = EdgePermu $ composeVector b c
instance Group EdgePermu where
inverse (EdgePermu a) = EdgePermu $ inverseVector a
instance CubeAction CornerPermu where
cubeAction cp_ = (cp_ <>) . fromCube
instance CubeAction EdgePermu where
cubeAction ep_ = (ep_ <>) . fromCube
actionCorner :: CornerOrien -> Corner -> CornerOrien
actionCorner (CornerOrien o) (Corner (CornerPermu gp) (CornerOrien go))
= CornerOrien $ U.zipWith oPlus (U.backpermute o gp) go
actionEdge :: EdgeOrien -> Edge -> EdgeOrien
actionEdge (EdgeOrien o) (Edge (EdgePermu gp) (EdgeOrien go))
= EdgeOrien $ U.zipWith (((`mod` 2) .) . (+)) (U.backpermute o gp) go
instance CubeAction CornerOrien where
cubeAction co_ = actionCorner co_ . corner
instance CubeAction EdgeOrien where
cubeAction eo_ = actionEdge eo_ . edge
instance CubeAction Corner where
cubeAction (Corner cp co) c =
Corner (cp `cubeAction` c) (co `cubeAction` c)
instance CubeAction Edge where
cubeAction (Edge ep eo) c =
Edge (ep `cubeAction` c) (eo `cubeAction` c)
instance Monoid Corner where
mempty = Corner iden idCornerO
where idCornerO = CornerOrien $ U.replicate numCorners 0
mappend (Corner bp_ bo_)
c@(Corner cp_ co_)
= Corner dp_ do_
where dp_ = bp_ <> cp_
do_ = bo_ `actionCorner` c
instance Group Corner where
inverse (Corner ap_ (CornerOrien ao))
= Corner ap_' (CornerOrien ao')
where ap_'@(CornerPermu ap') = inverse ap_
ao' = U.map oInv . U.backpermute ao $ ap'
instance Monoid Edge where
mempty = Edge iden idEdgeO
where idEdgeO = EdgeOrien $ U.replicate numEdges 0
mappend (Edge bp_ bo_)
c@(Edge cp_ co_)
= Edge dp_ do_
where dp_ = bp_ <> cp_
do_ = bo_ `actionEdge` c
instance Group Edge where
inverse (Edge ap_ (EdgeOrien ao))
= Edge ap_' (EdgeOrien ao')
where ap_'@(EdgePermu ap') = inverse ap_
ao' = U.backpermute ao ap'
instance Monoid Cube where
mempty = Cube iden iden
mappend (Cube cA eA) (Cube cB eB) = Cube (cA <> cB) (eA <> eB)
instance Group Cube where
inverse (Cube c e) = Cube (inverse c) (inverse e)
solvable :: Cube -> Bool
solvable (Cube (Corner (CornerPermu cp) (CornerOrien co))
(Edge (EdgePermu ep) (EdgeOrien eo))) =
signPermutationVector cp == signPermutationVector ep
&& U.sum co `mod` 3 == 0
&& U.all (< 3) co
&& U.sum eo `mod` 2 == 0
symRotate :: Int -> [Int] -> [Int]
symRotate o
| o < 3 = rotate o
| otherwise = rotate (5 o) . sym
where sym [a,b,c] = [a,c,b]
toFacelet :: Cube -> Facelets
toFacelet
(Cube
{ corner = Corner (CornerPermu cp) (CornerOrien co)
, edge = Edge (EdgePermu ep) (EdgeOrien eo) })
= unsafeFacelets $ U.create (do
v <- MU.new F.numFacelets
setFacelets v cp co cornerFacelets
setFacelets v ep eo edgeFacelets
forM_ [4, 13 .. 49] (\x -> MU.write v x x)
return v)
where
setFacelets v p o f
= forM_
((zip `on` concat) f orientedFaces)
. uncurry $ MU.write v
where
orientedFaces = zipWith symRotate (U.toList o) cubieFacelets
cubieFacelets = map (f !!) (U.toList p)
colorFaceletsToCube :: ColorFacelets -> Either [Int] (Maybe Cube)
colorFaceletsToCube (fromColorFacelets -> c) = do
(co, cp) <- pack <$> zipWithM findCorner (colorsOfC cornerFacelets) cornerFacelets
(eo, ep) <- pack <$> zipWithM findEdge (colorsOfC edgeFacelets) edgeFacelets
Right $ cube cp co ep eo
where
pack = U.unzip . U.fromList
colorsOfC = (((c U.!) <$>) <$>)
findCorner = findPos cornerColors [0 .. 5]
findEdge = findPos edgeColors [0, 1]
cornerColors = (colorOf <$>) <$> cornerFacelets
edgeColors = (colorOf <$>) <$> edgeFacelets
findPos :: [[Int]] -> [Int] -> [Int] -> e -> Either e (Int, Int)
findPos xs os x e
= case join . find isJust $
map
(\o -> (,) o <$> elemIndex x (map (symRotate o) xs))
os
of
Nothing -> Left e
Just x -> Right x
stringOfCubeColors :: Cube -> String
stringOfCubeColors = stringOfColorFacelets' . toFacelet
newtype UDSlicePermu = UDSlicePermu { fromUDSlicePermu :: Vector Int }
deriving (Eq, Show)
newtype UDSlice = UDSlice { fromUDSlice :: Vector Int }
deriving (Eq, Show)
newtype UDSlicePermu2 = UDSlicePermu2 { fromUDSlicePermu2 :: Vector Int }
deriving (Eq, Show)
newtype UDEdgePermu2 = UDEdgePermu2 { fromUDEdgePermu2 :: Vector Int }
deriving (Eq, Show)
type FlipUDSlice = (UDSlice, EdgeOrien)
type FlipUDSlicePermu = (UDSlicePermu, EdgeOrien)
numUDSliceEdges = 4 :: Int
unsafeUDSlicePermu = UDSlicePermu
unsafeUDSlicePermu' = UDSlicePermu . U.fromList
uDSlicePermu :: Vector Int -> Maybe UDSlicePermu
uDSlicePermu v = do
guard $ U.length v == numUDSliceEdges
&& U.all (liftA2 (&&) (0 <=) (< numEdges)) v
&& (length . nub . U.toList) v == numUDSliceEdges
return (UDSlicePermu v)
uDSlice :: Vector Int -> Maybe UDSlice
uDSlice v = do
guard $ U.length v == numUDSliceEdges
&& U.and (U.zipWith (<) ((1) `U.cons` v) (v `U.snoc` 12))
return (UDSlice v)
unsafeUDSlice = UDSlice
unsafeUDSlice' = UDSlice . U.fromList
uDSlicePermu2 :: Vector Int -> Maybe UDSlicePermu2
uDSlicePermu2 v = do
guard $ U.length v == numUDSliceEdges
&& isPermutationVector v
return (UDSlicePermu2 v)
unsafeUDSlicePermu2 = UDSlicePermu2
unsafeUDSlicePermu2' = UDSlicePermu2 . U.fromList
uDEdgePermu2 :: Vector Int -> Maybe UDEdgePermu2
uDEdgePermu2 v = do
guard $ U.length v == numEdges numUDSliceEdges
&& isPermutationVector v
return (UDEdgePermu2 v)
unsafeUDEdgePermu2 = UDEdgePermu2
unsafeUDEdgePermu2' = UDEdgePermu2 . U.fromList
vSort = U.fromList . sort . U.toList
unpermuUDSlice :: UDSlicePermu -> UDSlice
unpermuUDSlice = UDSlice . vSort . fromUDSlicePermu
edgePermu2 :: UDSlicePermu2 -> UDEdgePermu2 -> EdgePermu
edgePermu2 (UDSlicePermu2 sp) (UDEdgePermu2 ep)
= EdgePermu (ep U.++ U.map (+8) sp)
neutralUDSlicePermu = UDSlicePermu $ U.enumFromN 8 numUDSliceEdges
neutralUDSlice = UDSlice $ U.enumFromN 8 numUDSliceEdges
neutralUDSlicePermu2 = UDSlicePermu2 $ U.enumFromN 0 numUDSliceEdges
neutralUDEdgePermu2 = UDEdgePermu2 $ U.enumFromN 0 (numEdges numUDSliceEdges)
actionUDSlicePermu' :: EdgePermu -> Vector Int -> Vector Int
actionUDSlicePermu' (EdgePermu ep) = U.map (fromJust . flip U.elemIndex ep)
actionUDSlicePermu :: UDSlicePermu -> EdgePermu -> UDSlicePermu
actionUDSlicePermu (UDSlicePermu p) ep
= UDSlicePermu (actionUDSlicePermu' ep p)
actionUDSlice :: UDSlice -> EdgePermu -> UDSlice
actionUDSlice (UDSlice s) ep = UDSlice (act s)
where
act = vSort . actionUDSlicePermu' ep
actionUDSlicePermu2 :: UDSlicePermu2 -> EdgePermu -> UDSlicePermu2
actionUDSlicePermu2 (UDSlicePermu2 sp) (EdgePermu ep) =
UDSlicePermu2 $ sp `composeVector` U.map (subtract 8) (U.drop 8 ep)
actionUDEdgePermu2 :: UDEdgePermu2 -> EdgePermu -> UDEdgePermu2
actionUDEdgePermu2 (UDEdgePermu2 ep') (EdgePermu ep) =
UDEdgePermu2 $ ep' `composeVector` U.take 8 ep
instance CubeAction UDSlicePermu where
cubeAction p = actionUDSlicePermu p . fromCube
instance CubeAction UDSlice where
cubeAction s = actionUDSlice s . fromCube
instance CubeAction UDSlicePermu2 where
cubeAction sp = actionUDSlicePermu2 sp . fromCube
instance CubeAction UDEdgePermu2 where
cubeAction e = actionUDEdgePermu2 e . fromCube
instance FromCube UDSlicePermu where
fromCube = cubeAction neutralUDSlicePermu
instance FromCube UDSlice where
fromCube = cubeAction neutralUDSlice
instance FromCube UDSlicePermu2 where
fromCube = cubeAction neutralUDSlicePermu2
instance FromCube UDEdgePermu2 where
fromCube = cubeAction neutralUDEdgePermu2
conjugateFlipUDSlice :: Cube -> FlipUDSlice -> FlipUDSlice
conjugateFlipUDSlice c = assert conjugable conjugate
where
(EdgeOrien eo_c, EdgePermu ep_c) = fromCube c
conjugable
= let fromCube_c = UDSlice . vSort . U.drop 8 $ ep_c
in fromCube_c == neutralUDSlice
&& isConstant (U.take 8 eo_c)
&& isConstant (U.drop 8 eo_c)
isConstant v = U.init v == U.tail v
udsO = eo_c U.! 8
altO = eo_c U.! 0
conjugate (uds_@(UDSlice uds), EdgeOrien eo) = (uds_', EdgeOrien eo')
where
eo' = U.zipWith
(\o p -> (o + eo U.! p + bool altO udsO (p `U.elem` uds)) `mod` 2)
eo_c
ep_c
uds_' = cubeAction uds_ c
conjugateFlipUDSlicePermu :: Cube -> FlipUDSlicePermu -> FlipUDSlicePermu
conjugateFlipUDSlicePermu c = assert conjugable conjugate
where
(EdgeOrien eo_c, EdgePermu ep_c) = fromCube c
udsp_c = U.drop 8 ep_c
conjugable
= UDSlicePermu (vSort udsp_c) == neutralUDSlicePermu
&& isConstant (U.take 8 eo_c)
&& isConstant (U.drop 8 eo_c)
isConstant v = U.init v == U.tail v
conjugate fuds@(udsp, _)
= (conjugateUDSlicePermu c udsp, conjugateEdgeOrien' c fuds)
conjugateEdgeOrien' :: Cube -> FlipUDSlicePermu -> EdgeOrien
conjugateEdgeOrien' c (UDSlicePermu udsp, EdgeOrien eo)
= EdgeOrien $ U.zipWith
(\o p -> (o + eo U.! p + bool altO udsO (p `U.elem` udsp)) `mod` 2)
eo_c
ep_c
where
(EdgeOrien eo_c, EdgePermu ep_c) = fromCube c
udsO = eo_c U.! 8
altO = eo_c U.! 0
conjugateUDSlicePermu :: Cube -> UDSlicePermu -> UDSlicePermu
conjugateUDSlicePermu c (UDSlicePermu udsp)
= cubeAction (UDSlicePermu $ U.map (\i -> udsp U.! (i 8)) udsp_c) c
where
EdgePermu ep_c = fromCube c
udsp_c = U.drop 8 . fromEdgePermu $ fromCube c
conjugateCornerOrien :: Cube -> CornerOrien -> CornerOrien
conjugateCornerOrien c (CornerOrien co) = cubeAction (CornerOrien (U.map (oPlus (oInv o)) co)) c
where
CornerOrien co_c = fromCube c
o = U.head co_c