{- | Module : Data.Matrix.AsXYZ Copyright : (c) Jun Narumi 2017-2018 License : BSD3 Maintainer : narumij@gmail.com Stability : experimental Portability : ? Read and Display matrix with xyz reperesentation. (like general equivalnet position of International tables of Crystallography.) -} module Data.Matrix.AsXYZ ( fromXYZ, fromXYZ', fromABC, prettyXYZ, prettyABC, ) 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,(<->)) import Text.ParserCombinators.Parsec (parse,ParseError) import Data.Ratio.Slash (getRatio,Slash(..)) import Data.Matrix.AsXYZ.Parse (equivalentPositions,transformPpABC,ratio) -- | 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 $ makeMatrix <$> parse (equivalentPositions ratio) input input -- | Maybe version fromXYZ' :: Integral a => String -> Maybe (Matrix (Ratio a)) fromXYZ' input = get $ makeMatrix <$> parse (equivalentPositions 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 $ makeMatrix <$> parse (transformPpABC ratio) input input makeMatrix :: Num a => [[a]] -> Matrix a makeMatrix m = 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 ---------------------------------- -- +または-が銭湯に必ずあるようにする addPlusSign :: String -> String addPlusSign xs@('-':_) = xs addPlusSign xs = '+' : xs -- 符号付きの数値文字列にする numStr :: (Integral a) => Ratio a -> String numStr = addPlusSign . show . Slash varString :: (Integral a) => Ratio a -> String -> String varString num label  -- 0の場合省略 | num == 0 = "" -- 4番目の項目で、変数が付かない場合、数値文字列化 | null label = numStr num -- 数値が1で変数がある場合、数値を省略 | num == 1 = "+" ++ label -- 数値が-1で変数がある場合、数値を省略 | num == -1 = "-" ++ label -- それ以外では数値と変数を文字列化 | otherwise = numStr num ++ label -- 正の係数がついた変数である isPrimary :: String -> Bool isPrimary x = (hasLetter . reverse) x && isPositive x hasLetter :: String -> Bool hasLetter (x:_) = isAlpha x hasLetter _ = False isPositive :: String -> Bool isPositive ('+':_) = True isPositive _ = False -- 正の係数がついた変数を先頭にする varSort :: [String] -> [String] varSort parts = filter isPrimary parts ++ filter (not . isPrimary) parts row :: (Integral a) => [String] -> [Ratio a] -> String row labels line = join . varSort $ zipWith varString line labels refineRow :: String -> String refineRow s -- 全ての項目が省略されていると空文字列になっているので、0 | null s = "0" -- 先頭の項目が正の場合、+記号を省略できるので削る | head s == '+' = tail s | otherwise = s rowString :: (Integral a) => [String] -> [Ratio a] -> String rowString labels line = refineRow (row labels line) xyzLabel :: [String] xyzLabel = ["x","y","z",""] abcLabel :: [String] abcLabel = ["a","b","c",""] showAs :: (Integral a) => [String] -> Matrix (Ratio a) -> String showAs labels = intercalate "," . map (rowString labels) . take 3 . toLists -- | Get the xyz representation 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 = showAs xyzLabel -- | It's uses abc instead of xyz -- -- >>> prettyABC (identity 4 :: Matrix Rational) -- "a,b,c" prettyABC :: (Integral a) => Matrix (Ratio a) -- ^ 3x3, 3x4 or 4x4 matrix -> String prettyABC = showAs abcLabel