----------------------------------------------------------------------------- -- | -- Module : HCube.Utility -- Copyright : (c) Todd Wegner 2012 -- License : BSD-style (see the LICENSE file) -- -- Maintainer : echbar137@yahoo.co.in -- Stability : provisional -- Portability : portable -- -- Common utility functions, simple linear algebra. ----------------------------------------------------------------------------- {-# LANGUAGE Safe #-} module HCube.Utility where import Control.Monad (foldM, (>=>), liftM, liftM2) import Data.List import HCube.Data import HCube.Common(padL) -- | Returns square root of argument if argument is a perfect square. squareRoot :: Int -> Maybe Int squareRoot nn = f 1 nn where f lo hi | lo > hi = Nothing | nn == h = Just g | nn < h = f lo (g - 1) | otherwise = f (g + 1) h where g = (hi + lo) `div` 2 h = g * g -- | Multiple a matrix on the left side of a vector. (|*|) :: Matrix -> Vec -> Vec (|*|)(Matrix (a,d,g) (b,e,h) (c,f,i)) (x,y,z) = (a*x + b*y + c*z, d*x + e*y + f*z, g*x + h*y + i*z) -- | Multiple two matrices. (|**|) :: Matrix -> Matrix -> Matrix (|**|)(Matrix (a,d,g) (b,e,h) (c,f,i)) (Matrix (j,m,p) (k,n,q) (l,o,r)) = Matrix (a*j + b*m + c*p, d*j + e*m + f*p, g*j + h*m + i*p) (a*k + b*n + c*q, d*k + e*n + f*q, g*k + h*n + i*q) (a*l + b*l + c*r, d*l + e*o + f*r, g*l + h*o + i*r ) -- | Multiple a matrix by a scalar. multMatrix :: Numb -> Matrix -> Matrix multMatrix nm (Matrix (a,d,g) (b,e,h) (c,f,i)) = Matrix (nm*a,nm*d,nm*g) (nm*b,nm*e,h) (nm*c,nm*f,nm*i) -- | The cofactor of a matrix. cofactors :: Matrix -> Matrix cofactors (Matrix (a,d,g) (b,e,h) (c,f,i)) = Matrix (e*i-f*h, c*h-b*i, b*f-c*e) (f*g-d*i, a*i-c*g, c*d-a*f) (d*h-e*g, b*g-a*h, a*e-b*d) -- | Transpose of a matrix. transposeM :: Matrix -> Matrix transposeM (Matrix (a,d,g) (b,e,h) (c,f,i)) = Matrix (a,b,c) (d,e,f) (g,h,i) -- | Inverse of a matrix. inverse :: Matrix -> Matrix inverse mx = multMatrix (det mx) . transposeM $ cofactors mx -- | Display a matrix. showM :: Matrix -> String showM (Matrix (a,d,g) (b,e,h) (c,f,i)) = concat y where y = [z a, z d, z g, " ", z b, z e, z h, " ", z c, z f, z i] z = padL 3 . show doM :: Monad m => (a -> Bool) -> (a -> m a) -> a -> m a doM te lp = lp >=> \s-> if te s then doM te lp s else return s -- Inject operator infixl 1 ~> (~>) :: Monad m => m a -> (a -> b -> m b) -> b -> m b (~>) op fu st = op >>= \s -> fu s st (~|) :: Monad m => (a -> m b) -> a -> m a (~|) fu a = fu a >> return a infixl 4 <* (<*) :: Monad m => m a -> m b -> m a (<*) = liftM2 const concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b] concatMapM bbf = liftM concat . mapM bbf listToMaybe :: [a] -> Maybe a listToMaybe [] = Nothing listToMaybe (a:_) = Just a -- | A safe form of read. maybeRead :: String -> Maybe Int maybeRead = fmap fst . listToMaybe . reads -- | Applies a function on the domain of Side x Side. spanFaces :: (Enum a, Num a, Ord b) => (Side -> Side -> b) -> [(a, b)] spanFaces fn = zip [1..] $ sort $ f where f = [fn a b | a <- sides, b <- sides, a /=b] -- | List of the sides. sides = [UpS,DownS,FrontS,BackS,LeftS,RightS] -- | List of the vectors. vecs = [(1,0,0),(0,1,0),(0,0,1),(-1,0,0),(0,-1,0),(0,0,-1)] :: [Vec] -- | Multiple a vector by a scalar. multVec :: Numb -> Vec -> Vec multVec nm (v1,v2,v3) = (nm*v1, nm*v2, nm*v3) -- | Convert a function with two vector arguments to one accepting a list of vectors. mapVec :: (Vec -> Vec -> a) -> [Vec] -> a mapVec fu (v1:v2:[]) = fu v1 v2 -- | The determinate of a matrix. det :: Matrix -> Int det (Matrix (a,d,g) (b,e,h) (c,f,i)) = a*e*i + b*f*g + c*d*h - c*e*g - b*d*i - a*f*h -- | Calculate the determinate of a matrix constructed by three column vectors. vecDet :: Vec -> Vec -> Vec -> Int vecDet v1 v2 v3 = det $ Matrix v1 v2 v3 -- | Multiple a matrix by a scalar. matrixMult :: Matrix -> Int -> Matrix matrixMult (Matrix v1 v2 v3) ct = Matrix (f v1) (f v2) (f v3) where f = multVec ct -- |The cross product of two vectors. cross :: Vec -> Vec -> Vec cross (a,b,c) (x,y,z) = (b*z-y*c,a*z-c*x,a*y-b*x) -- |Vector multipled by scalar -1. minus :: Vec -> Vec minus (a,b,c) = (-a,-b,-c) -- |Vectors we are interested in only have one non zero component. vcomp :: Vec -> Int vcomp (vl,0,0) = vl vcomp (0,vl,0) = vl vcomp (0,0,vl) = vl vcomp bd = error . show $ bd -- |Position of non-zero vector component. vpos :: Vec -> Int vpos (_,0,0) = 1 vpos (0,_,0) = 2 vpos (0,0,_) = 3 vpos bd = error . show $ bd -- | The dot product of two vectors. dot :: Vec -> Vec -> Numb dot (a,b,c) (x,y,z) = a*x + b*y + c*z gateMinus :: Numb -> Vec -> Vec gateMinus ct = f $ ct < 0 where f True = minus f False = id modMinus :: Numb -> Numb -> Vec -> Vec modMinus cd ts = f $ cd `mod` ts where f 0 = minus f _ = id modNot :: Numb -> Numb -> Numb modNot cd ts = f $ cd `mod` ts where f 0 = 1 f _ = 0