{- | Module : Data.Matrix.AsXYZ Copyright : (c) Jun Narumi 2017-2020 License : BSD3 Maintainer : narumij@gmail.com Stability : experimental Portability : ? Read and Display Jones-Faithful notation for spacegroup (e.g. 'x,y,z') and planegroup (e.g. 'x,y') -} module Data.Matrix.AsXYZ ( fromXYZ, fromXYZ', fromABC, prettyXYZ, prettyABC, fromXY, fromXY', fromAB, prettyXY, prettyAB, ) where import Control.Monad (join) import Data.Char (isAlpha) import Data.List (intercalate) import Data.Ratio (Ratio,(%)) import Data.Matrix (Matrix,fromList,fromLists,toLists,identity,zero,(<->),submatrix) import Text.ParserCombinators.Parsec (parse,ParseError) import Data.Ratio.Slash (getRatio,Slash(..)) import qualified Data.Matrix.AsXYZ.ParseXYZ as XYZ(equivalentPositions,transformPpABC,ratio) import qualified Data.Matrix.AsXYZ.ParseXY as XY (equivalentPositions,transformPpAB) import qualified Data.Matrix.AsXYZ.ParseXYZ as XY(ratio) import qualified Data.Matrix.AsXYZ.Plain as Plain (showAs,showAs',xyzLabel,abcLabel) -- | Create a matirx from xyz coordinate string of general equivalent position -- -- > ( 1 % 1 0 % 1 0 % 1 0 % 1 ) -- > ( 0 % 1 1 % 1 0 % 1 0 % 1 ) -- > ( 0 % 1 0 % 1 1 % 1 0 % 1 ) -- > fromXYZ "x,y,z" :: Matrix Rational = ( 0 % 1 0 % 1 0 % 1 1 % 1 ) -- > -- > ( 1 % 1 0 % 1 0 % 1 1 % 2 ) -- > ( 0 % 1 1 % 1 0 % 1 1 % 3 ) -- > ( 0 % 1 0 % 1 1 % 1 1 % 4 ) -- > fromXYZ "x+1/2,y+1/3,z+1/4" :: Matrix Rational = ( 0 % 1 0 % 1 0 % 1 1 % 1 ) -- > -- > ( 1 2 3 4 ) -- > ( 5 6 7 8 ) -- > ( 9 10 11 12 ) -- > fromXYZ "x+2y+3z+4,5x+6y+7z+8,9x+10y+11z+12" :: Matrix Int = ( 0 0 0 1 ) fromXYZ :: Integral a => String -> Matrix (Ratio a) fromXYZ input = unsafeGet $ makeMatrixS <$> parse (XYZ.equivalentPositions XYZ.ratio) input input -- | Maybe version fromXYZ' :: Integral a => String -> Maybe (Matrix (Ratio a)) fromXYZ' input = get $ makeMatrixS <$> parse (XYZ.equivalentPositions XYZ.ratio) input input -- | It's uses abc instead of xyz -- -- > ( 1 % 1 0 % 1 0 % 1 0 % 1 ) -- > ( 0 % 1 1 % 1 0 % 1 0 % 1 ) -- > ( 0 % 1 0 % 1 1 % 1 0 % 1 ) -- > fromXYZ "a,b,c" :: Matrix Rational = ( 0 % 1 0 % 1 0 % 1 1 % 1 ) fromABC :: Integral a => String -> Matrix (Ratio a) fromABC input = unsafeGet $ makeMatrixS <$> parse (XYZ.transformPpABC XYZ.ratio) input input makeMatrixS :: Num a => [[a]] -> Matrix a makeMatrixS m = (submatrix 1 3 1 4 . fromLists) m <-> fromLists [[0,0,0,1]] unsafeGet :: Either ParseError a -> a unsafeGet e = case e of Left s -> error $ show s Right m -> m get :: Either ParseError a -> Maybe a get e = case e of Left s -> Nothing Right m -> Just m ---------------------------------- -- | Get the xyz string of matrix -- -- >>> prettyXYZ (identity 4 :: Matrix Rational) -- "x,y,z" -- -- > ( 0 % 1 0 % 1 0 % 1 1 % 2 ) -- > ( 0 % 1 0 % 1 0 % 1 2 % 3 ) -- > ( 0 % 1 0 % 1 0 % 1 4 % 5 ) -- > prettyXYZ ( 0 % 1 0 % 1 0 % 1 1 % 1 ) = "1/2,2/3,4/5" prettyXYZ :: (Integral a) => Matrix (Ratio a) -- ^ 3x3, 3x4 or 4x4 matrix -> String prettyXYZ = Plain.showAs Plain.xyzLabel -- | It's uses abc instead of xyz as text format -- -- >>> prettyABC (identity 4 :: Matrix Rational) -- "a,b,c" prettyABC :: (Integral a) => Matrix (Ratio a) -- ^ 3x3, 3x4 or 4x4 matrix -> String prettyABC = Plain.showAs Plain.abcLabel -- | Create a matirx from xyz coordinate string of general equivalent position -- -- >>> toLists . fromXY $ "x,y" -- [[1 % 1,0 % 1,0 % 1],[0 % 1,1 % 1,0 % 1],[0 % 1,0 % 1,1 % 1]] fromXY :: Integral a => String -> Matrix (Ratio a) fromXY input = unsafeGet $ makeMatrixP <$> parse (XY.equivalentPositions XY.ratio) input input -- | Maybe version -- -- >>> toLists <$> fromXY' "x,y" -- Just [[1 % 1,0 % 1,0 % 1],[0 % 1,1 % 1,0 % 1],[0 % 1,0 % 1,1 % 1]] fromXY' :: Integral a => String -> Maybe (Matrix (Ratio a)) fromXY' input = get $ makeMatrixP <$> parse (XY.equivalentPositions XY.ratio) input input -- | It's uses abc instead of xyz -- -- >>> toLists . fromAB $ "a,b" -- [[1 % 1,0 % 1,0 % 1],[0 % 1,1 % 1,0 % 1],[0 % 1,0 % 1,1 % 1]] fromAB :: Integral a => String -> Matrix (Ratio a) fromAB input = unsafeGet $ makeMatrixP <$> parse (XY.transformPpAB XY.ratio) input input makeMatrixP :: Num a => [[a]] -> Matrix a makeMatrixP m = (submatrix 1 2 1 3 . fromLists) m <-> fromLists [[0,0,1]] -- | Get the xyz string of matrix -- -- >>> prettyXY (identity 4 :: Matrix Rational) -- "x,y" prettyXY :: (Integral a) => Matrix (Ratio a) -- ^ 2x2, 2x3 or 3x3 matrix -> String prettyXY = Plain.showAs' Plain.xyzLabel -- | It's uses abc instead of xyz as text format -- -- >>> prettyAB (identity 4 :: Matrix Rational) -- "a,b" prettyAB :: (Integral a) => Matrix (Ratio a) -- ^ 2x2, 2x3 or 3x3 matrix -> String prettyAB = Plain.showAs' Plain.abcLabel