matrix-as-xyz 0.1.1.3 → 0.1.2.0
raw patch · 11 files changed
+769/−313 lines, 11 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Data.Matrix.AsXYZ.Parse: equivalentPositions :: Num a => ReadNum a -> CharParser () [[a]]
- Data.Matrix.AsXYZ.Parse: floating :: Floating a => Value -> Either String a
- Data.Matrix.AsXYZ.Parse: instance GHC.Base.Functor Data.Matrix.AsXYZ.Parse.Val
- Data.Matrix.AsXYZ.Parse: instance GHC.Base.Functor Data.Matrix.AsXYZ.Parse.Var
- Data.Matrix.AsXYZ.Parse: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Matrix.AsXYZ.Parse.Var a)
- Data.Matrix.AsXYZ.Parse: instance GHC.Show.Show a => GHC.Show.Show (Data.Matrix.AsXYZ.Parse.Val a)
- Data.Matrix.AsXYZ.Parse: instance GHC.Show.Show a => GHC.Show.Show (Data.Matrix.AsXYZ.Parse.Var a)
- Data.Matrix.AsXYZ.Parse: integral :: Integral a => Value -> Either String a
- Data.Matrix.AsXYZ.Parse: ratio :: Integral a => Value -> Either String (Ratio a)
- Data.Matrix.AsXYZ.Parse: transformPpABC :: Num a => ReadNum a -> CharParser () [[a]]
- Data.Matrix.AsXYZ.Parse: transformQqXYZ :: Num a => ReadNum a -> CharParser () [[a]]
- Data.Matrix.AsXYZ.Parse: type Value = Val String
+ Data.Matrix.AsXYZ: fromAB :: Integral a => String -> Matrix (Ratio a)
+ Data.Matrix.AsXYZ: fromXY :: Integral a => String -> Matrix (Ratio a)
+ Data.Matrix.AsXYZ: fromXY' :: Integral a => String -> Maybe (Matrix (Ratio a))
+ Data.Matrix.AsXYZ: prettyAB :: Integral a => Matrix (Ratio a) -> String
+ Data.Matrix.AsXYZ: prettyXY :: Integral a => Matrix (Ratio a) -> String
+ Data.Matrix.AsXYZ.Common: F :: a -> Val a
+ Data.Matrix.AsXYZ.Common: I :: a -> Val a
+ Data.Matrix.AsXYZ.Common: N :: a -> Sign a
+ Data.Matrix.AsXYZ.Common: P :: a -> Sign a
+ Data.Matrix.AsXYZ.Common: R :: a -> Val a
+ Data.Matrix.AsXYZ.Common: W :: a -> Var a
+ Data.Matrix.AsXYZ.Common: X :: a -> Var a
+ Data.Matrix.AsXYZ.Common: Y :: a -> Var a
+ Data.Matrix.AsXYZ.Common: Z :: a -> Var a
+ Data.Matrix.AsXYZ.Common: Zero :: Sign a
+ Data.Matrix.AsXYZ.Common: data Sign a
+ Data.Matrix.AsXYZ.Common: data Val a
+ Data.Matrix.AsXYZ.Common: data Var a
+ Data.Matrix.AsXYZ.Common: instance GHC.Base.Functor Data.Matrix.AsXYZ.Common.Val
+ Data.Matrix.AsXYZ.Common: instance GHC.Base.Functor Data.Matrix.AsXYZ.Common.Var
+ Data.Matrix.AsXYZ.Common: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Matrix.AsXYZ.Common.Sign a)
+ Data.Matrix.AsXYZ.Common: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Matrix.AsXYZ.Common.Var a)
+ Data.Matrix.AsXYZ.Common: instance GHC.Show.Show a => GHC.Show.Show (Data.Matrix.AsXYZ.Common.Sign a)
+ Data.Matrix.AsXYZ.Common: instance GHC.Show.Show a => GHC.Show.Show (Data.Matrix.AsXYZ.Common.Val a)
+ Data.Matrix.AsXYZ.Common: instance GHC.Show.Show a => GHC.Show.Show (Data.Matrix.AsXYZ.Common.Var a)
+ Data.Matrix.AsXYZ.Common: rowVars :: Integral a => [Ratio a] -> [Sign (Var (Ratio a))]
+ Data.Matrix.AsXYZ.ParseXY: equivalentPositions :: Num a => ReadNum a -> CharParser () [[a]]
+ Data.Matrix.AsXYZ.ParseXY: transformPpAB :: Num a => ReadNum a -> CharParser () [[a]]
+ Data.Matrix.AsXYZ.ParseXY: transformQqXY :: Num a => ReadNum a -> CharParser () [[a]]
+ Data.Matrix.AsXYZ.ParseXYZ: elementBody :: CharParser () Char -> ReadNum a -> CharParser () (Maybe a, Maybe Char)
+ Data.Matrix.AsXYZ.ParseXYZ: equivalentPositions :: Num a => ReadNum a -> CharParser () [[a]]
+ Data.Matrix.AsXYZ.ParseXYZ: floating :: Floating a => ReadNum a
+ Data.Matrix.AsXYZ.ParseXYZ: integral :: Integral a => ReadNum a
+ Data.Matrix.AsXYZ.ParseXYZ: minus :: Num a => Maybe Char -> a -> a
+ Data.Matrix.AsXYZ.ParseXYZ: overlap :: Eq a => [a] -> Bool
+ Data.Matrix.AsXYZ.ParseXYZ: ratio :: Integral a => ReadNum (Ratio a)
+ Data.Matrix.AsXYZ.ParseXYZ: sign :: CharParser () Char
+ Data.Matrix.AsXYZ.ParseXYZ: transformPpABC :: Num a => ReadNum a -> CharParser () [[a]]
+ Data.Matrix.AsXYZ.ParseXYZ: transformQqXYZ :: Num a => ReadNum a -> CharParser () [[a]]
+ Data.Matrix.AsXYZ.ParseXYZ: type ReadNum b = Value -> Either String b
+ Data.Matrix.AsXYZ.Plain: abcLabel :: String
+ Data.Matrix.AsXYZ.Plain: showAs :: Integral a => String -> Matrix (Ratio a) -> String
+ Data.Matrix.AsXYZ.Plain: showAs' :: Integral a => String -> Matrix (Ratio a) -> String
+ Data.Matrix.AsXYZ.Plain: xyzLabel :: String
Files
- README.md +31/−2
- matrix-as-xyz.cabal +12/−4
- src/Data/Matrix/AsXYZ.hs +71/−74
- src/Data/Matrix/AsXYZ/Common.hs +91/−0
- src/Data/Matrix/AsXYZ/Parse.hs +0/−229
- src/Data/Matrix/AsXYZ/ParseXY.hs +103/−0
- src/Data/Matrix/AsXYZ/ParseXYZ.hs +222/−0
- src/Data/Matrix/AsXYZ/Plain.hs +71/−0
- test/AsXYSpec.hs +92/−0
- test/AsXYZSpec.hs +74/−3
- test/doctests.hs +2/−1
README.md view
@@ -1,10 +1,39 @@ # matrix-as-xyz -Haskell General equivalent positions Library+[![Continuous Integration status][status-png]][status]+[![Hackage page (downloads and API reference)][hackage-png]][hackage]+[![Hackage-Deps][hackage-deps-png]][hackage-deps] -Treat matrix as xyz representation like International Tables for X-ray Crystallography+Haskell Jones-Faithful notation (also called coordinate triplet) Library +## Quickstart++```haskell+repl> :m Data.Matrix.AsXYZ+repl> fromXYZ "x,y,z"+┌ ┐+│ 1 % 1 0 % 1 0 % 1 0 % 1 │+│ 0 % 1 1 % 1 0 % 1 0 % 1 │+│ 0 % 1 0 % 1 1 % 1 0 % 1 │+│ 0 % 1 0 % 1 0 % 1 1 % 1 │+└ ┘+```++```haskell+repl> :m Data.Matrix Data.Matrix.AsXYZ+repl> prettyXYZ . identity $ 4+"x,y,z"+```+ ## License See the [LICENSE](https://raw.githubusercontent.com/narumij/matrix-as-xyz/master/LICENSE) file in the repository.++ [hackage]: http://hackage.haskell.org/package/matrix-as-xyz+ [hackage-png]: http://img.shields.io/hackage/v/matrix-as-xyz.svg+ [hackage-deps]: http://packdeps.haskellers.com/reverse/matrix-as-xyz+ [hackage-deps-png]: https://img.shields.io/hackage-deps/v/matrix-as-xyz.svg++ [status]: http://travis-ci.org/narumij/matrix-as-xyz?branch=master+ [status-png]: https://api.travis-ci.org/narumij/matrix-as-xyz.svg?branch=master
matrix-as-xyz.cabal view
@@ -4,11 +4,11 @@ -- -- see: https://github.com/sol/hpack ----- hash: defaa1a3cd8a332c2f80308c2d0d0183c9b16d442da980b984fc4b71f8bbb454+-- hash: 44baaebc9c92a8247f166d2e4ce54c025bca25e65992f5e10bdae3551bb1d2fd name: matrix-as-xyz-version: 0.1.1.3-synopsis: Read and Display representation of matrix like "x,y,z"+version: 0.1.2.0+synopsis: Read and Display Jones-Faithfull notation for spacegroup and planegroup description: Please see the README on GitHub at <https://github.com/narumij/matrix-as-xyz#readme> category: Chemistry homepage: https://github.com/narumij/matrix-as-xyz#readme@@ -29,7 +29,10 @@ library exposed-modules: Data.Matrix.AsXYZ- Data.Matrix.AsXYZ.Parse+ Data.Matrix.AsXYZ.Common+ Data.Matrix.AsXYZ.ParseXY+ Data.Matrix.AsXYZ.ParseXYZ+ Data.Matrix.AsXYZ.Plain Data.Ratio.ParseFloat Data.Ratio.Slash other-modules:@@ -38,6 +41,8 @@ src build-depends: base >=4.8 && <5+ , doctest+ , hspec , matrix >=0.3.5 && <1 , parsec >=3.1 && <4 default-language: Haskell2010@@ -51,6 +56,7 @@ build-depends: base >=4.8 && <5 , doctest+ , hspec , matrix >=0.3.5 && <1 , matrix-as-xyz , parsec >=3.1 && <4@@ -60,6 +66,7 @@ type: exitcode-stdio-1.0 main-is: Spec.hs other-modules:+ AsXYSpec AsXYZSpec SlashSpec Paths_matrix_as_xyz@@ -69,6 +76,7 @@ build-depends: QuickCheck , base >=4.8 && <5+ , doctest , hspec , matrix >=0.3.5 && <1 , matrix-as-xyz
src/Data/Matrix/AsXYZ.hs view
@@ -1,12 +1,12 @@ {- | Module : Data.Matrix.AsXYZ-Copyright : (c) Jun Narumi 2017-2018+Copyright : (c) Jun Narumi 2017-2020 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.)+Read and Display Jones-Faithfull notation for spacegroup (e.g. 'x,y,z') and planegroup (e.g. 'x,y') -} module Data.Matrix.AsXYZ (@@ -15,17 +15,25 @@ 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.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)+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 --@@ -44,11 +52,11 @@ -- > ( 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+fromXYZ input = unsafeGet $ makeMatrix <$> parse (XYZ.equivalentPositions XYZ.ratio) input input -- | Maybe version fromXYZ' :: Integral a => String -> Maybe (Matrix (Ratio a))-fromXYZ' input = get $ makeMatrix <$> parse (equivalentPositions ratio) input input+fromXYZ' input = get $ makeMatrix <$> parse (XYZ.equivalentPositions XYZ.ratio) input input -- | It's uses abc instead of xyz --@@ -57,7 +65,7 @@ -- > ( 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+fromABC input = unsafeGet $ makeMatrix <$> parse (XYZ.transformPpABC XYZ.ratio) input input makeMatrix :: Num a => [[a]] -> Matrix a makeMatrix m = fromLists m <-> fromLists [[0,0,0,1]]@@ -74,69 +82,7 @@ ---------------------------------- --- +または-が銭湯に必ずあるようにする-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+-- | Get the xyz string of matrix -- -- >>> prettyXYZ (identity 4 :: Matrix Rational) -- "x,y,z"@@ -148,14 +94,65 @@ prettyXYZ :: (Integral a) => Matrix (Ratio a) -- ^ 3x3, 3x4 or 4x4 matrix -> String-prettyXYZ = showAs xyzLabel-+prettyXYZ = Plain.showAs Plain.xyzLabel --- | It's uses abc instead of xyz+-- | 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 = showAs abcLabel+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 $ makeMatrix' <$> 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 $ makeMatrix' <$> 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 $ makeMatrix' <$> parse (XY.transformPpAB XY.ratio) input input++makeMatrix' :: Num a => [[a]] -> Matrix a+makeMatrix' m = 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
+ src/Data/Matrix/AsXYZ/Common.hs view
@@ -0,0 +1,91 @@+{- |+Module : Data.Matrix.AsXYZ.Common+Copyright : (c) Jun Narumi 2018-2020+License : BSD3+Maintainer : narumij@gmail.com+Stability : experimental+Portability : ?+-}+module Data.Matrix.AsXYZ.Common (+ Sign(..),+ Var(..),+ Val(..),+ rowVars,+ ) where++import Data.Ratio+import Numeric++data Sign a+ = P a+ | N a+ | Zero+ deriving (Show,Eq)++data Var a+ = X a+ | Y a+ | Z a+ | W a+ deriving (Show,Eq)++-- 数値の型情報+data Val a+ -- 整数+ = I a+ -- 浮動小数+ | F a+ -- 分数+ | R a+ deriving Show++instance Functor Var where+ fmap f (X a) = X (f a)+ fmap f (Y a) = Y (f a)+ fmap f (Z a) = Z (f a)+ fmap f (W a) = W (f a)++instance Functor Val where+ fmap f (I a) = I (f a)+ fmap f (F a) = F (f a)+ fmap f (R a) = R (f a)++rowVars :: Integral a => [Ratio a] -> [Sign (Var (Ratio a))]+rowVars = reduceVars . sortVars . toVars+ +toVars :: Integral a => [Ratio a] -> [Sign (Var (Ratio a))]+toVars = zipWith (\a b -> hoge a b) [X,Y,Z,W]+ where+ hoge f r | r < 0 = N $ f (r * (-1))+ | otherwise = P $ f r++sortVars :: Eq a => [Sign (Var a)] -> [Sign (Var a)]+sortVars parts | null hh = parts+ | otherwise = h: filter (/= h) parts+ where+ hh = filter isPrimary parts+ h = head hh++-- 正の係数がついた変数である+isPrimary :: Sign (Var a) -> Bool+isPrimary Zero = False+isPrimary (N _) = False+isPrimary (P (W _)) = False+isPrimary _ = True++reduceVars :: (Eq a, Num a) => [Sign (Var a)] -> [Sign (Var a)]+reduceVars rr = if null a then [Zero] else a+ where+ a = filter (not . isZero) rr++isZero :: (Eq a, Num a) => Sign (Var a) -> Bool+isZero (N v) = isZero' v+isZero (P v) = isZero' v+isZero Zero = True++isZero' :: (Eq a, Num a) => Var a -> Bool+isZero' (X n) = n == 0+isZero' (Y n) = n == 0+isZero' (Z n) = n == 0+isZero' (W n) = n == 0+
− src/Data/Matrix/AsXYZ/Parse.hs
@@ -1,229 +0,0 @@-{- |-Module : Data.Matrix.AsXYZ.Parse-Copyright : (c) Jun Narumi 2018-License : BSD3-Maintainer : narumij@gmail.com-Stability : experimental-Portability : ?--}-module Data.Matrix.AsXYZ.Parse (- Value,- equivalentPositions,- transformPpABC,- transformQqXYZ,- ratio,- integral,- floating,- ) where--import Control.Monad-import Data.Char-import Data.Maybe-import Data.List-import Text.ParserCombinators.Parsec--import Data.Ratio-import Data.Ratio.Slash--import Data.Ratio.ParseFloat (readFloatingPoint)--import Data.Matrix (fromList,fromLists,Matrix(..),joinBlocks,(<->))---- | General equivalent positions parser-equivalentPositions :: Num a =>- ReadNum a -- ^ use converter below- -> CharParser () [[a]]-equivalentPositions = components xyz---- | Same as equivalentPositions but uses abc instead of xyz-transformPpABC :: Num a => ReadNum a -> CharParser () [[a]]-transformPpABC = components abc---- | Alias of equivalentPositions-transformQqXYZ :: Num a => ReadNum a -> CharParser () [[a]]-transformQqXYZ = components xyz---- | Converter of 3 kind of number (int,float,ratio) string to rational------ Use it for equivalentPositions or something parseer-ratio :: Integral a => Value -> Either String (Ratio a)-ratio (I s) = Right $ getRatio . read $ s-ratio (R s) = Right $ getRatio . read $ s-ratio (F s) = Right $ readFloatingPoint s---- | Converter of integral number description to integral------ Use it for equivalentPositions or something parseer-integral :: Integral a => Value -> Either String a-integral (I s) = Right $ fromIntegral (read s :: Integer)-integral (R s) = Left $ "cannot convert to integer from " ++ s ++ "."-integral (F s) = Left $ "cannot convert to integer from " ++ s ++ "."---- | Converter of 3 kind of number description to floating point------ Use it for equivalentPositions or something parseer-floating :: Floating a => Value -> Either String a-floating v = fromRational <$> ratio v---- 数値の型情報-data Val a- -- 整数- = I a- -- 浮動小数- | F a- -- 分数- | R a- deriving Show--instance Functor Val where- fmap f (I a) = I (f a)- fmap f (F a) = F (f a)- fmap f (R a) = R (f a)---- | Type of numeric type information generated in the middle-type Value = Val String--data Var a- = X a- | Y a- | Z a- | W a- deriving (Show,Eq)--instance Functor Var where- fmap f (X a) = X (f a)- fmap f (Y a) = Y (f a)- fmap f (Z a) = Z (f a)- fmap f (W a) = W (f a)--v c = f $ toLower <$> c- where- f (Just 'x') = X- f (Just 'a') = X- f (Just 'y') = Y- f (Just 'b') = Y- f (Just 'z') = Z- f (Just 'c') = Z- f (Just 'Z') = Z- f Nothing = W--sign :: CharParser () Char-sign = oneOf "-+"--zero :: CharParser () String-zero = do- char '0'- return "0"--num :: CharParser () String-num = do- x <- oneOf "123456789"- xs <- many digit- return $ x : xs--int :: CharParser () String-int = zero <|> num--integer :: CharParser () Value-integer = do- i <- int- return (I i)--float :: CharParser () Value-float = do- i <- option "" int- char '.'- f <- many digit- return (F $ i ++ "." ++ f )--fract :: CharParser () Value-fract = do- n <- many1 digit- option () spaces- char '/'- option () spaces- d <- many1 digit- return (R $ n ++ "/" ++ d)--number' :: CharParser () Value-number'- = try fract- <|> try float- <|> integer---- | numRead関数のシグネチャの簡易表記-type ReadNum b = Value -> Either String b--number :: ReadNum b -> CharParser () b-number numRead = do- n <- number'- case numRead n of- Left s -> fail s- Right nn -> return nn--elementBody :: CharParser () Char -> ReadNum a -> CharParser () (Maybe a, Maybe Char)-elementBody var conv = do- n <- optionMaybe (number conv)- option () spaces- v <- optionMaybe var- option () spaces- guard (isJust n || isJust v)- return (n,v)--minus :: Num a => Maybe Char -> (a -> a)-minus (Just '-') = negate-minus (Just '+') = id-minus Nothing = id--one :: Num a => CharParser () Char -> ReadNum a -> CharParser () (Var a)-one var numRead = do- s <- optionMaybe sign- option () spaces- (n,l) <- elementBody var numRead- return $ v l . minus s . fromMaybe 1 $ n--other :: Num a => CharParser () Char -> ReadNum a -> CharParser () (Var a)-other var numRead = do- s <- sign- option () spaces- (n,l) <- elementBody var numRead- return $ v l . minus (Just s) . fromMaybe 1 $ n--overlap :: Eq a => [a] -> Bool-overlap n = (length . nub) n /= length n--constructRow :: Num a => [Var a] -> [a]-constructRow = map (fromMaybe 0 . listToMaybe . catMaybes) . transpose . map toArray- where- toArray (X n) = [Just n,Nothing,Nothing,Nothing]- toArray (Y n) = [Nothing,Just n,Nothing,Nothing]- toArray (Z n) = [Nothing,Nothing,Just n,Nothing]- toArray (W n) = [Nothing,Nothing,Nothing,Just n]--component :: Num b => CharParser () Char -> ReadNum b -> CharParser () [b]-component var numRead = do- option () spaces- x <- one var numRead- xs <- many (other var numRead)- option () spaces- let mm = x : xs- if overlap (map void mm)- then- fail "overlaps var type"- else- return (constructRow mm)--components :: Num a => CharParser () Char -> ReadNum a -> CharParser () [[a]]-components var conv = do- a <- component var conv- char ','- b <- component var conv- char ','- c <- component var conv- return [a,b,c]--xyz :: CharParser () Char-xyz = oneOf "xyzXYZ"--abc :: CharParser () Char-abc = oneOf "abcABC"
+ src/Data/Matrix/AsXYZ/ParseXY.hs view
@@ -0,0 +1,103 @@+{- |+Module : Data.Matrix.AsXYZ.ParseXY+Copyright : (c) Jun Narumi 2020-2020+License : BSD3+Maintainer : narumij@gmail.com+Stability : experimental+Portability : ?++Jones-Faithfull notation parser for planegroup.++-}+module Data.Matrix.AsXYZ.ParseXY (+ equivalentPositions,+ transformPpAB,+ transformQqXY,+ ) where++import Control.Monad+import Data.Char+import Data.Maybe+import Data.List+import Text.ParserCombinators.Parsec+import Data.Matrix.AsXYZ.ParseXYZ (ReadNum(..),overlap,sign,elementBody,minus,ratio,integral,floating)+import Data.Matrix.AsXYZ.Common (Var(..))++xy :: CharParser () Char+xy = oneOf "xyXY"++ab :: CharParser () Char+ab = oneOf "abAB"++-- | General equivalent positions parser+--+-- >>> parse (equivalentPositions integral) "" "x+1,y+2"+-- Right [[1,0,1],[0,1,2]]+equivalentPositions :: Num a =>+ ReadNum a -- ^ use converter below+ -> CharParser () [[a]]+equivalentPositions = components xy++-- | Same as equivalentPositions but uses abc instead of xyz+--+-- >>> parse (transformPpAB integral) "" "a+1,b+2"+-- Right [[1,0,1],[0,1,2]]+transformPpAB :: Num a => ReadNum a -> CharParser () [[a]]+transformPpAB = components ab++-- | Alias of equivalentPositions+--+-- >>> parse (transformQqXY integral) "" "x+1,y+2"+-- Right [[1,0,1],[0,1,2]]+transformQqXY :: Num a => ReadNum a -> CharParser () [[a]]+transformQqXY = components xy++v c = f $ toLower <$> c+ where+ f (Just 'x') = X+ f (Just 'a') = X+ f (Just 'y') = Y+ f (Just 'b') = Y+ f Nothing = Z++one :: Num a => CharParser () Char -> ReadNum a -> CharParser () (Var a)+one var numRead = do+ s <- optionMaybe sign+ option () spaces+ (n,l) <- elementBody var numRead+ return $ v l . minus s . fromMaybe 1 $ n++other :: Num a => CharParser () Char -> ReadNum a -> CharParser () (Var a)+other var numRead = do+ s <- sign+ option () spaces+ (n,l) <- elementBody var numRead+ return $ v l . minus (Just s) . fromMaybe 1 $ n++constructRow :: Num a => [Var a] -> [a]+constructRow = map (fromMaybe 0 . listToMaybe . catMaybes) . transpose . map toArray+ where+ toArray (X n) = [Just n,Nothing,Nothing]+ toArray (Y n) = [Nothing,Just n,Nothing]+ toArray (Z n) = [Nothing,Nothing,Just n]++component :: Num b => CharParser () Char -> ReadNum b -> CharParser () [b]+component var numRead = do+ option () spaces+ x <- one var numRead+ xs <- many (other var numRead)+ option () spaces+ let mm = x : xs+ if overlap (map void mm)+ then+ fail "overlaps var type"+ else+ return (constructRow mm)++components :: Num a => CharParser () Char -> ReadNum a -> CharParser () [[a]]+components var conv = do+ a <- component var conv+ char ','+ b <- component var conv+ return [a,b]+
+ src/Data/Matrix/AsXYZ/ParseXYZ.hs view
@@ -0,0 +1,222 @@+{- |+Module : Data.Matrix.AsXYZ.ParseXYZ+Copyright : (c) Jun Narumi 2018-2020+License : BSD3+Maintainer : narumij@gmail.com+Stability : experimental+Portability : ?++Jones-Faithfull notation parser for spacegroup.++-}+module Data.Matrix.AsXYZ.ParseXYZ (+ ReadNum(..),+ equivalentPositions,+ transformPpABC,+ transformQqXYZ,+ ratio,+ floating,+ integral,+ sign,+ minus,+ overlap,+ elementBody,+ ) where++import Control.Monad+import Data.Char+import Data.Maybe+import Data.List+import Text.ParserCombinators.Parsec++import Data.Ratio+import Data.Ratio.Slash++import Data.Ratio.ParseFloat (readFloatingPoint)++import Data.Matrix (fromList,fromLists,Matrix(..),joinBlocks,(<->))++import Data.Matrix.AsXYZ.Common (Var(..),Val(..))++-- | Jones-Faithful notation parser+--+-- >>> parse (equivalentPositions integral) "" "x+1,y+2,z+3"+-- Right [[1,0,0,1],[0,1,0,2],[0,0,1,3]]+equivalentPositions :: Num a =>+ ReadNum a -- ^ ratio or floating or integral+ -> CharParser () [[a]]+equivalentPositions = components xyz++-- | Same as equivalentPositions but uses abc instead of xyz+--+-- >>> parse (transformPpABC integral) "" "a+1,b+2,c+3"+-- Right [[1,0,0,1],[0,1,0,2],[0,0,1,3]]+transformPpABC :: Num a => ReadNum a -> CharParser () [[a]]+transformPpABC = components abc++-- | Alias of equivalentPositions+--+-- >>> parse (transformQqXYZ integral) "" "x+1,y+2,z+3"+-- Right [[1,0,0,1],[0,1,0,2],[0,0,1,3]]+transformQqXYZ :: Num a => ReadNum a -> CharParser () [[a]]+transformQqXYZ = components xyz++-- | Converter of 3 kind of number (int,float,ratio) string to rational+--+-- >>> parse (equivalentPositions ratio) "" "x+1,y+2,z+3"+-- Right [[1 % 1,0 % 1,0 % 1,1 % 1],[0 % 1,1 % 1,0 % 1,2 % 1],[0 % 1,0 % 1,1 % 1,3 % 1]]+ratio :: Integral a => ReadNum (Ratio a)+ratio (I s) = Right $ getRatio . read $ s+ratio (R s) = Right $ getRatio . read $ s+ratio (F s) = Right $ readFloatingPoint s++-- | Converter of integral number description to integral+--+-- This can not read ratio and floating string (e.g. '1/2', '0.1')+--+-- >>> parse (equivalentPositions integral) "" "x+1,y+2,z+3"+-- Right [[1,0,0,1],[0,1,0,2],[0,0,1,3]]+integral :: Integral a => ReadNum a+integral (I s) = Right $ fromIntegral (read s :: Integer)+integral (R s) = Left $ "cannot convert to integer from " ++ s ++ "."+integral (F s) = Left $ "cannot convert to integer from " ++ s ++ "."++-- | Converter of 3 kind of number description to floating point+--+-- >>> parse (equivalentPositions floating) "" "x+1,y+2,z+3"+-- Right [[1.0,0.0,0.0,1.0],[0.0,1.0,0.0,2.0],[0.0,0.0,1.0,3.0]]+floating :: Floating a => ReadNum a+floating v = fromRational <$> ratio v++-- | Type of numeric type information generated in the middle+type Value = Val String++v c = f $ toLower <$> c+ where+ f (Just 'x') = X+ f (Just 'a') = X+ f (Just 'y') = Y+ f (Just 'b') = Y+ f (Just 'z') = Z+ f (Just 'c') = Z+ f Nothing = W++sign :: CharParser () Char+sign = oneOf "-+"++zero :: CharParser () String+zero = do+ char '0'+ return "0"++num :: CharParser () String+num = do+ x <- oneOf "123456789"+ xs <- many digit+ return $ x : xs++int :: CharParser () String+int = zero <|> num++integer :: CharParser () Value+integer = do+ i <- int+ return (I i)++float :: CharParser () Value+float = do+ i <- option "" int+ char '.'+ f <- many digit+ return (F $ i ++ "." ++ f )++fract :: CharParser () Value+fract = do+ n <- many1 digit+ option () spaces+ char '/'+ option () spaces+ d <- many1 digit+ return (R $ n ++ "/" ++ d)++number' :: CharParser () Value+number'+ = try fract+ <|> try float+ <|> integer++type ReadNum b = Value -> Either String b++number :: ReadNum b -> CharParser () b+number numRead = do+ n <- number'+ case numRead n of+ Left s -> fail s+ Right nn -> return nn++elementBody :: CharParser () Char -> ReadNum a -> CharParser () (Maybe a, Maybe Char)+elementBody var conv = do+ n <- optionMaybe (number conv)+ option () spaces+ v <- optionMaybe var+ option () spaces+ guard (isJust n || isJust v)+ return (n,v)++minus :: Num a => Maybe Char -> (a -> a)+minus (Just '-') = negate+minus (Just '+') = id+minus Nothing = id++one :: Num a => CharParser () Char -> ReadNum a -> CharParser () (Var a)+one var numRead = do+ s <- optionMaybe sign+ option () spaces+ (n,l) <- elementBody var numRead+ return $ v l . minus s . fromMaybe 1 $ n++other :: Num a => CharParser () Char -> ReadNum a -> CharParser () (Var a)+other var numRead = do+ s <- sign+ option () spaces+ (n,l) <- elementBody var numRead+ return $ v l . minus (Just s) . fromMaybe 1 $ n++overlap :: Eq a => [a] -> Bool+overlap n = (length . nub) n /= length n++constructRow :: Num a => [Var a] -> [a]+constructRow = map (fromMaybe 0 . listToMaybe . catMaybes) . transpose . map toArray+ where+ toArray (X n) = [Just n,Nothing,Nothing,Nothing]+ toArray (Y n) = [Nothing,Just n,Nothing,Nothing]+ toArray (Z n) = [Nothing,Nothing,Just n,Nothing]+ toArray (W n) = [Nothing,Nothing,Nothing,Just n]++component :: Num b => CharParser () Char -> ReadNum b -> CharParser () [b]+component var numRead = do+ option () spaces+ x <- one var numRead+ xs <- many (other var numRead)+ option () spaces+ let mm = x : xs+ if overlap (map void mm)+ then+ fail "overlaps var type"+ else+ return (constructRow mm)++components :: Num a => CharParser () Char -> ReadNum a -> CharParser () [[a]]+components var conv = do+ a <- component var conv+ char ','+ b <- component var conv+ char ','+ c <- component var conv+ return [a,b,c]++xyz :: CharParser () Char+xyz = oneOf "xyzXYZ"++abc :: CharParser () Char+abc = oneOf "abcABC"
+ src/Data/Matrix/AsXYZ/Plain.hs view
@@ -0,0 +1,71 @@+{- |+Module : Data.Matrix.AsXYZ.ParseXYZ+Copyright : (c) Jun Narumi 2018-2020+License : BSD3+Maintainer : narumij@gmail.com+Stability : experimental+Portability : ?++Jones-Faithfull notation plain text printer.++-}+module Data.Matrix.AsXYZ.Plain (+ showAs,+ showAs',+ xyzLabel,+ abcLabel,+)where++import Data.Ratio (Ratio(..),numerator,denominator)+import Data.List (intercalate)+import Data.Matrix (Matrix(..),toLists)+import Numeric (showInt)+import Data.Matrix.AsXYZ.Common(Sign(..),Var(..),rowVars)++num :: Integral a => Ratio a -> String+num r | d == 1 = int n+ | otherwise = frac n d+ where+ nn = numerator r+ d = denominator r+ n = if nn >= 0 then nn else nn * (-1)+ showInt' = flip showInt "" -- Constraint Show class を回避するため+ int n = showInt' n+ frac n d = showInt' n ++ "/" ++ showInt' d++rowStr label (x:xs) = texP' label x ++ concatMap (texP label) xs++texP label (P var) = "+" ++ texV label var+texP label (N var) = "-" ++ texV label var+texP _ Zero = "0"++texP' label (P var) = texV label var+texP' label (N var) = "-" ++ texV label var+texP' _ Zero = "0"++texV _ (W n) = num $ n+texV labels var | 1 == n = label+ | otherwise = (++ label) . num $ n + where+ get (X n) = (0,n)+ get (Y n) = (1,n)+ get (Z n) = (2,n)+ t = get var+ n = snd t+ label = labels !! fst t:[]++showAs' :: (Integral a) => String -> Matrix (Ratio a) -> String+showAs' labels = intercalate "," . map (rowStr labels . rowVars . hoge) . take 2 . toLists+ where+ hoge (x:y:z) = x:y:0:z++showAs :: (Integral a) => String -> Matrix (Ratio a) -> String+showAs labels = intercalate "," . map (rowStr labels . rowVars) . take 3 . toLists++xyzLabel :: String+xyzLabel = "xyz"++abcLabel :: String+abcLabel = "abc"++
+ test/AsXYSpec.hs view
@@ -0,0 +1,92 @@+module AsXYSpec where++import Control.Exception (evaluate)++import Test.Hspec+import Data.Ratio+import Data.Matrix+import Data.Matrix.AsXYZ++readTest str mat = do+ it ("read " ++ str) $ do+ fromXY str `shouldBe` mat++readData = [+ ("x,y+1",+ {- shouldBe -}+ fromLists [+ [1,0,0],+ [0,1,1],+ [0,0,1]]),+ ("+x+y+2,+x+y+3",+ {- shouldBe -}+ fromLists [+ [1,1,2],+ [1,1,3],+ [0,0,1]])+ ]++curryM f (a,b) = do+ f a b++spec :: Spec+spec = do++ describe "Data.Matrix.AsXY.fromXY" $ do++ it "read empty throws exception" $ do+ evaluate (fromXY "") `shouldThrow` anyException++ it "read a,b throws exception" $ do+ evaluate (fromXY "a,b") `shouldThrow` anyException++ it "read x,y throws exception" $ do+ evaluate (fromAB "x,y") `shouldThrow` anyException++ it "read x,y" $ do+ fromXY "x,y" `shouldBe` (identity 3)++ it "read X,Y" $ do+ fromXY "X,Y" `shouldBe` (identity 3)++ mapM_ (curryM readTest) readData++ it "read a,b" $ do+ fromAB "a,b" `shouldBe` (identity 3)++ it "read A,B" $ do+ fromAB "A,B" `shouldBe` (identity 3)++ describe "Data.Matrix.AsXYZ.prettyXYZ" $ do++ it "show 0 (2x2)" $ do+ prettyXY (zero 2 2) `shouldBe` "0,0"++ it "show 0 (3x3)" $ do+ prettyXY (zero 3 3) `shouldBe` "0,0"++ it "show 0 (3x4)" $ do+ prettyXY (zero 2 3) `shouldBe` "0,0"++ it "show 1 (2x2)" $ do+ prettyXY (identity 2) `shouldBe` "x,y"++ it "show 1 (2x3)" $ do+ prettyXY (submatrix 1 2 1 3 $ identity 3) `shouldBe` "x,y"++ it "show 1 (3x3)" $ do+ prettyXY (identity 3) `shouldBe` "x,y"++ it "positive first" $ do+ prettyXY (fromLists [[1,-1,-1],[-1,1,-1]]) `shouldBe` "x-y-1,y-x-1"++ it "number last" $ do+ prettyXY (fromLists [[-1,-1,-1],[-1,-1,1]]) `shouldBe` "-x-y-1,-x-y+1"++ describe "Data.Matrix.AsXYZ.prettyABC" $ do++ it "show 0" $ do+ prettyAB (zero 4 4) `shouldBe` "0,0"++ it "show 1" $ do+ prettyAB (identity 4) `shouldBe` "a,b"
test/AsXYZSpec.hs view
@@ -7,6 +7,58 @@ import Data.Matrix import Data.Matrix.AsXYZ +readTest str mat = do+ it ("read " ++ str) $ do+ fromXYZ str `shouldBe` mat++curryM f (a,b) = do+ f a b++readData = [+ ("z,x,y",+ {- shouldBe -}+ fromLists [+ [0,0,1,0],+ [1,0,0,0],+ [0,1,0,0],+ [0,0,0,1]]),+ ("x+1,y+1,z+1",+ {- shouldBe -}+ fromLists [+ [1,0,0,1],+ [0,1,0,1],+ [0,0,1,1],+ [0,0,0,1]]),+ ("x-1,y-1,z-1",+ {- shouldBe -}+ fromLists [+ [1,0,0,-1],+ [0,1,0,-1],+ [0,0,1,-1],+ [0,0,0,1]]),+ ("x+1/2,y-2/3,z+3/4",+ {- shouldBe -}+ fromLists [+ [1,0,0, 1%2],+ [0,1,0,-2%3],+ [0,0,1, 3%4],+ [0,0,0, 1]]),+ ("x-5/8,y+7/16,z-9/32",+ {- shouldBe -}+ fromLists [+ [1,0,0,-5%8 ],+ [0,1,0, 7%16],+ [0,0,1,-9%32],+ [0,0,0, 1]]),+ ("1/2,1/2,1/2",+ {- shouldBe -}+ fromLists [+ [0,0,0,1%2],+ [0,0,0,1%2],+ [0,0,0,1%2],+ [0,0,0, 1]])+ ]+ spec :: Spec spec = do @@ -15,11 +67,20 @@ it "read empty throws exception" $ do evaluate (fromXYZ "") `shouldThrow` anyException + it "read a,b throws exception" $ do+ evaluate (fromXYZ "a,b,c") `shouldThrow` anyException++ it "read x,y throws exception" $ do+ evaluate (fromABC "x,y,z") `shouldThrow` anyException+ it "read x,y,z" $ do fromXYZ "x,y,z" `shouldBe` (identity 4) it "read X,Y,Z" $ do- fromXYZ "X,Y,Z" `shouldBe` (identity 4)+ fromXYZ "X,Y,Z"+ `shouldBe` (identity 4)+ + mapM_ (curryM readTest) readData it "read a,b,c" $ do fromABC "a,b,c" `shouldBe` (identity 4)@@ -47,11 +108,21 @@ it "show 1 (3x3)" $ do prettyXYZ (submatrix 1 3 1 3 $ identity 4) `shouldBe` "x,y,z" + it "integers" $ do+ prettyXYZ (fromLists [[1,2,3,4],[-5,-6,-7,-8],[9,10,11,12]])+ `shouldBe` "x+2y+3z+4,-5x-6y-7z-8,9x+10y+11z+12"++ it "rationals" $ do+ prettyXYZ (fromLists [[1%2,1%3,1%4,1%5],[2%3,2%5,2%7,2%9],[3%10,11%20,30%41,30%40]])+ `shouldBe` "1/2x+1/3y+1/4z+1/5,2/3x+2/5y+2/7z+2/9,3/10x+11/20y+30/41z+3/4"+ it "positive first" $ do- prettyXYZ (fromLists [[1,-1,-1],[-1,1,-1],[-1,-1,1]]) `shouldBe` "x-y-z,y-x-z,z-x-y"+ prettyXYZ (fromLists [[1,-1,-1],[-1,1,-1],[-1,-1,1]])+ `shouldBe` "x-y-z,y-x-z,z-x-y" it "number last" $ do- prettyXYZ (fromLists [[-1,-1,-1,-1],[-1,-1,-1,0],[-1,-1,-1,1]]) `shouldBe` "-x-y-z-1,-x-y-z,-x-y-z+1"+ prettyXYZ (fromLists [[-1,-1,-1,-1],[-1,-1,-1,0],[-1,-1,-1,1]])+ `shouldBe` "-x-y-z-1,-x-y-z,-x-y-z+1" describe "Data.Matrix.AsXYZ.prettyABC" $ do
test/doctests.hs view
@@ -4,8 +4,9 @@ main :: IO () main = doctest [- "--fast", "src/Data/Ratio/Slash.hs", "src/Data/Ratio/ParseFloat.hs",+ "src/Data/Matrix/AsXYZ/ParseXYZ.hs",+ "src/Data/Matrix/AsXYZ/ParseXY.hs", "src/Data/Matrix/AsXYZ.hs" ]