{-# LANGUAGE FlexibleInstances, CPP #-}
{-|
Module      : Data.Matrix.SymmetryOperationsSymbols
Description : Read and Display Geometric representation of symmetry operation
Copyright   : (c) Jun Narumi, 2018-2020
License     : MIT
Maintainer  : narumij@gmail.com
Stability   : experimental

Haskell Derivation of symbols and coordinate triplets Library

[References]

1. W. Fischer. and E. Koch. (2006), Derivation of symbols and coordinate triplets International Tables for Crystallography (2006). Vol. A, Chapter 11.2, pp. 812–816.

2. Wondratschek, H. & Neubu ̈ser, J. (1967). Determination of the symmetry elements of a space group from the ‘general positions’ listed in International Tables for X-ray Crystallography, Vol. I. Acta Cryst. 23, 349–352.

-}
module Data.Matrix.SymmetryOperationsSymbols (
  fromMatrix,
  fromMatrix',
  readMatrix,
  readMatrix',
  toMatrix,
  toMatrixHex,
  notHexagonal,
  hexagonal,
  liftError,
  ) where

import Data.Ratio (Ratio)
import Data.Matrix (Matrix,detLU,trace,identity)
import Text.ParserCombinators.Parsec (ParseError,parse)

import Data.Matrix.SymmetryOperationsSymbols.Common

import Data.Matrix.SymmetryOperationsSymbols.UnitMatrixCase
import Data.Matrix.SymmetryOperationsSymbols.GlideOrReflectionCase
import Data.Matrix.SymmetryOperationsSymbols.RotationCase
import Data.Matrix.SymmetryOperationsSymbols.RotInversionCase

import Data.Matrix.SymmetryOperationsSymbols.Parser
import Data.Matrix.SymmetryOperationsSymbols.SymopGeom
import Data.Matrix.SymmetryOperationsSymbols.Calc

import Data.Matrix.SymmetryOperationsSymbols.PlainText

-- for doctest
import Data.Matrix.AsXYZ (fromXYZ,prettyXYZ)

#if MIN_VERSION_base(4,13,0)
import Control.Monad.Fail (MonadFail(..))
instance MonadFail (Either String) where
  fail = Left
#endif

-- | Derivation of geometric representation of symmetry operations from given matrix of symmetry operations
--
-- jpn) 与えられた対称操作の行列から、対称操作の幾何的表現を導出
--
-- >>> fromMatrix . fromXYZ $ "x,y,z"
-- Right " 1 "
--
-- >>> fromMatrix . fromXYZ $ "-y,x,-z"
-- Right "-4- 0,0,z; 0,0,0"
--
fromMatrix :: Integral a =>
              Matrix (Ratio a) -- ^ 3x4 or 4x4 Matrix
           -> Either String String
fromMatrix = fromMatrix'

-- | Derivation of geometric representation of symmetry operations from given matrix of symmetry operations
--
-- jpn) 与えられた対称操作の行列から、対称操作の幾何的表現を導出
--
#if MIN_VERSION_base(4,13,0)
fromMatrix' :: (Integral a, MonadFail f) => Matrix (Ratio a) -> f String
#else
fromMatrix' :: (Monad m, Integral a) => Matrix (Ratio a) -> m String
#endif
fromMatrix' m = showAsPlainText <$> readMatrix' m

readMatrix :: Integral a =>
              Matrix (Ratio a) -- ^ 3x4 or 4x4 Matrix
           -> Maybe (SymopGeom a)
readMatrix = readMatrix'

-- | Derivation of geometric representation of symmetry operations from given matrix of symmetry operations
--
-- jpn) 与えられた対称操作の行列から、対称操作の幾何的表現を導出
--
#if MIN_VERSION_base(4,13,0)
readMatrix' :: (MonadFail m, Integral a) => Matrix (Ratio a) -> m (SymopGeom a)
#else
readMatrix' :: (Monad m, Integral a) => Matrix (Ratio a) -> m (SymopGeom a)
#endif
readMatrix' m
  -- (i)
  | rotPart m == identity 3             = unitMatrixCase m
  -- (ii) (a)
  | correpondToRotInversion tr det      = rotInversionCase m
  -- -- (ii) (b)
  | correpondToNFoldRotation tr det     = nFoldRotationCase m
  -- -- (ii) (c)
  | correpondToGlideOrReflection tr det = glideOrReflectionCase m
  -- --
  | otherwise = fail $ "coordinate triplet '" ++ prettyXYZ m ++ "' is not symmetry operation."
  where
  tr  = trace (rotPart m)
  det = detLU (rotPart m)

-- | Derivation of matrix representation from a string of geometric representations of symmetric operations
-- for cubic, tetragonal, orthorhombic, monoclinic, triclinic or rhombohedral.
--
-- jpn) 対称操作の幾何的表現の文字列から行列表現の導出
--
-- >>> prettyXYZ <$> toMatrixHex "-4- 0,0,z; 0,0,0"
-- Right "-y,x,-z"
--
toMatrix :: Integral a =>
            String -- ^ like " -1 0,0,0"
         -> Either ParseError (Matrix (Ratio a)) -- ^ 3x4 Matrix
toMatrix st = parse notHexagonal st st

-- | Derivation of matrix representation from a string of geometric representations of symmetric operations
-- for hexagonal.
--
-- jpn) 対称操作の幾何的表現の文字列から行列表現の導出(六方晶用)
--
-- >>> prettyXYZ <$> toMatrixHex "-3+ 0,0,z; 0,0,0"
-- Right "y,y-x,-z"
--
toMatrixHex :: Integral a =>
               String -- ^ like " -1 0,0,0"
            -> Either ParseError (Matrix (Ratio a)) -- ^ 3x4 Matrix
toMatrixHex st = parse hexagonal st st

#if MIN_VERSION_base(4,13,0)
liftError :: MonadFail m => Either ParseError a -> m a
#else
liftError :: Monad m => Either ParseError a -> m a
#endif
liftError (Left s) = fail ""
liftError (Right m) = return m