packages feed

matrix-as-xyz (empty) → 0.1.1.0

raw patch · 13 files changed

+802/−0 lines, 13 filesdep +QuickCheckdep +basedep +doctestsetup-changed

Dependencies added: QuickCheck, base, doctest, hspec, matrix, matrix-as-xyz, parsec

Files

+ ChangeLog.md view
@@ -0,0 +1,13 @@+# Revision history for matrix-as-xyz++## 0.1.1.0  -- 2018-06-05++* CabalからStackに変更+* API変更+* 挙動の変更+* Documentの整備+* その他色々++## 0.1.0.0  -- 2017-03-11++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2017, narumij++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of narumij nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,10 @@+# matrix-as-xyz++Haskell General equivalent positions Library++Treat matrix as xyz representation like International Tables for X-ray Crystallography++## License++See the [LICENSE](https://github.com/narumij/matrix-as-xyz/LICENSE)+file in the repository.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ matrix-as-xyz.cabal view
@@ -0,0 +1,85 @@+-- This file has been generated from package.yaml by hpack version 0.28.2.+--+-- see: https://github.com/sol/hpack+--+-- hash: 19899810c3d6696ddbcceefa6fcb228661c87238cefcfc1bcdde512fba3dd8ba++name:           matrix-as-xyz+version:        0.1.1.0+synopsis:       Read and Display representation of matrix like "x,y,z"+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+bug-reports:    https://github.com/narumij/matrix-as-xyz/issues+author:         Jun Narumi+maintainer:     narumij@gmail.com+copyright:      Jun Narumi+license:        BSD3+license-file:   LICENSE+build-type:     Simple+cabal-version:  >= 1.10+extra-source-files:+    ChangeLog.md+    README.md++source-repository head+  type: git+  location: https://github.com/narumij/matrix-as-xyz++library+  exposed-modules:+      Data.Matrix.AsXYZ+      Data.Matrix.AsXYZ.Parse+      Data.Ratio.ParseFloat+      Data.Ratio.Slash+  other-modules:+      Paths_matrix_as_xyz+  hs-source-dirs:+      src+  build-depends:+      QuickCheck+    , base >=4.7 && <5+    , hspec+    , matrix+    , parsec+  default-language: Haskell2010++test-suite matrix-as-xyz-doctest+  type: exitcode-stdio-1.0+  main-is: doctests.hs+  other-modules:+      AsXYZSpec+      SlashSpec+      Spec+      Paths_matrix_as_xyz+  hs-source-dirs:+      test+  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      QuickCheck+    , base >=4.7 && <5+    , doctest+    , hspec+    , matrix+    , matrix-as-xyz+    , parsec+  default-language: Haskell2010++test-suite matrix-as-xyz-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      AsXYZSpec+      SlashSpec+      Paths_matrix_as_xyz+  hs-source-dirs:+      test+  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      QuickCheck+    , base >=4.7 && <5+    , hspec+    , matrix+    , matrix-as-xyz+    , parsec+  default-language: Haskell2010
+ src/Data/Matrix/AsXYZ.hs view
@@ -0,0 +1,161 @@+{- |+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
+ src/Data/Matrix/AsXYZ/Parse.hs view
@@ -0,0 +1,225 @@+{- |+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.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 (Just 'x') = X+v (Just 'a') = X+v (Just 'y') = Y+v (Just 'b') = Y+v (Just 'z') = Z+v (Just 'c') = Z+v 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/Ratio/ParseFloat.hs view
@@ -0,0 +1,117 @@+{- |+Module      : Data.Ratio.ParseFloat+Copyright   : (c) Jun Narumi 2018+License     : BSD3+Maintainer  : narumij@gmail.com+Stability   : experimental+Portability : ?++Floating point parser++Temporary solution to the problem below++> ghci> realToFrac (read "1.1" :: Double) :: Rational+> 2476979795053773 % 2251799813685248++-}+module Data.Ratio.ParseFloat (+  readFloatingPoint,+  floating,+  ) where++import Data.Ratio+import Text.ParserCombinators.Parsec++-- このような一見、車輪の再発明に思えるコードをわざわざ書いたのは+-- ghci> realToFrac (read "1.1" :: Double) :: Rational+-- 2476979795053773 % 2251799813685248+-- という問題に対処するため。+-- これ以外に良い方法、良い書き方が分かれば、削除します。++-- | Obtain fractions from floating point representation string+--+-- >>> readFloatingPoint "1.1"+-- 11 % 10+-- >>> readFloatingPoint "0.5"+-- 1 % 2+-- >>> readFloatingPoint ".5"+-- 1 % 2+-- >>> readFloatingPoint "10."+-- 10 % 1+-- >>> readFloatingPoint "10"+-- 10 % 1+-- >>> readFloatingPoint "10.2"+-- 51 % 5+-- >>> readFloatingPoint "1e-1"+-- 1 % 10+-- >>> readFloatingPoint "-0.5e-1"+-- (-1) % 20+-- >>> readFloatingPoint "5e2"+-- 500 % 1+-- >>> readFloatingPoint "5e+2"+-- 500 % 1+readFloatingPoint :: Integral a => String -> Ratio a+readFloatingPoint s = case parse floating s s of+  Left  e -> error $ show e+  Right r -> r++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++sign :: CharParser () Char+sign = oneOf "-+"++decimal :: CharParser () String+decimal = do+  char '.'+  many digit++exponent' :: Integral a => CharParser () a+exponent' = do+  oneOf "eE"+  s <- optionMaybe sign+  e <- int+  return $ signPart s $ read' e++-- | Parser section+floating :: Integral a => CharParser () (Ratio a)+floating = do+  s <- optionMaybe sign+  i <- option "" int+  f <- option "" decimal+  e <- optionMaybe exponent'+  return $ signPart s . expPart e $ intPart i + decimalPart f++signPart :: Num a => Maybe Char -> a -> a+signPart (Just '-') = negate+signPart _          = id++intPart :: Integral a => String -> Ratio a+intPart "" = 0+intPart i  = read' i % 1++decimalPart :: Integral a => String -> Ratio a+decimalPart "" = 0+decimalPart f  | read f == 0 = 0+               | otherwise = read' f % (10 ^ length f)++-- 整数型のread instanceを隠蔽している+read' :: Integral a => String -> a+read' s = fromIntegral (read s :: Integer)++expPart :: Integral a => Maybe Integer -> Ratio a -> Ratio a+expPart Nothing      = id+expPart (Just s)     | s == 0    = id+                     | s < 0     = flip (/) (10 ^ abs s)+                     | otherwise = flip (*) (10 ^ s)
+ src/Data/Ratio/Slash.hs view
@@ -0,0 +1,67 @@+{- |+Module      : Data.Ratio.Slash+Copyright   : (c) Jun Narumi 2018+License     : BSD3+Maintainer  : narumij@gmail.com+Stability   : experimental+Portability : ?++Handle fractions described in /, not%++-}++module Data.Ratio.Slash (+  Slash(..),+  ) where++import Control.Monad (guard)+import Control.Applicative ((<|>))+import Data.Ratio+import Numeric++-- | Type of read and show slash form rational+--+-- >>> getRatio . read $ "1/2"+-- 1 % 2+--+-- >>> map getRatio . read $ "[1/2,3/4,5/6]"+-- [1 % 2,3 % 4,5 % 6]+--+-- >>> Slash (1 % 2)+-- 1/2+--+-- >>> map Slash [1%2,3%4,5%6]+-- [1/2,3/4,5/6]+--+newtype Slash a+  = Slash {+    getRatio :: Ratio a+    } deriving (Eq,Ord)++instance (Integral a) => Show (Slash a) where+  showsPrec _ (Slash n)+    | denominator n == 1+      = showSigned showInt 0 (numerator n)+    | otherwise+      = showSigned showInt 0 (numerator n) . showString "/" . showInt (denominator n)++instance (Integral a) => Read (Slash a) where+  readsPrec _ n = do+    ( ( n, d ), st ) <- slashOrInteger n+    guard $ d /= 0+    return ( Slash $ n % d , st )++integer :: (Integral a) => ReadS (a,a)+integer n = do+  ( n, st ) <- readSigned readDec n+  return ( ( n, 1 ), st )++slash :: (Integral a) => ReadS (a,a)+slash n = do+  ( numer, st  )  <- readSigned readDec n+  ( "/"  , st1 ) <- lex st+  ( denom, st2 ) <- readDec st1+  return ( ( numer, denom ), st2)++slashOrInteger :: (Integral a) => ReadS (a,a)+slashOrInteger n = take 1 $ slash n <|> integer n
+ test/AsXYZSpec.hs view
@@ -0,0 +1,54 @@+module AsXYZSpec where++import Control.Exception (evaluate)++import Test.Hspec+import Data.Ratio+import Data.Matrix+import Data.Matrix.AsXYZ++spec :: Spec+spec = do++   describe "Data.Matrix.AsXYZ.fromXYZ" $ do++     it "read empty throws exception" $ do+       evaluate (fromXYZ "") `shouldThrow` anyException++     it "read x,y,z" $ do+       fromXYZ "x,y,z" `shouldBe` (identity 4)+++   describe "Data.Matrix.AsXYZ.prettyXYZ" $ do++     it "show 0 (3x3)" $ do+       prettyXYZ (zero 3 3) `shouldBe` "0,0,0"++     it "show 0 (3x4)" $ do+       prettyXYZ (zero 3 4) `shouldBe` "0,0,0"++     it "show 0 (4x4)" $ do+       prettyXYZ (zero 4 4) `shouldBe` "0,0,0"++     it "show 1 (4x4)" $ do+       prettyXYZ (identity 4) `shouldBe` "x,y,z"++     it "show 1 (3x4)" $ do+       prettyXYZ (submatrix 1 3 1 4 $ identity 4) `shouldBe` "x,y,z"++     it "show 1 (3x3)" $ do+       prettyXYZ (submatrix 1 3 1 3 $ identity 4) `shouldBe` "x,y,z"++     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"++     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"++   describe "Data.Matrix.AsXYZ.prettyABC" $ do++     it "show 0" $ do+       prettyABC (zero 4 4) `shouldBe` "0,0,0"++     it "show 1" $ do+       prettyABC (identity 4) `shouldBe` "a,b,c"
+ test/SlashSpec.hs view
@@ -0,0 +1,30 @@+module SlashSpec where++import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck hiding ((.&.))++import Control.Monad+import Data.Ratio+import Data.Ratio.Slash++spec :: Spec+spec = do+  describe "rational" $ do++    prop "read" $+      \n d -> d /= 0 && n /= 0 && d > 1 && denominator ( n % d ) > 1 ==>+        ( (read $ join [show n,"/",show d] ) :: Slash Integer ) `shouldBe` ( Slash $ n % d )++    prop "both ways" $+      \n d -> d /= 0 && n /= 0 && d > 1 && denominator ( n % d ) > 1 ==>+        ( read . show $ Slash ( n % d ) :: Slash Integer ) `shouldBe` ( Slash $ n % d )++  describe "integer" $ do+    prop "read" $+      \n -> True ==>+        ( (read $ show n ) :: Slash Integer ) `shouldBe` ( Slash $ n % 1 )++    prop "both ways" $+      \n -> True ==>+        ( read . show $ Slash ( n % 1 ) :: Slash Integer ) `shouldBe` ( Slash $ n % 1 )
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ test/doctests.hs view
@@ -0,0 +1,7 @@+import Test.DocTest++main = doctest [+  "src/Data/Ratio/Slash.hs",+  "src/Data/Ratio/ParseFloat.hs",+  "src/Data/Matrix/AsXYZ.hs"+  ]