packages feed

singular-factory-0.1: src/Math/Singular/Factory/Variables.hs

-- | Variables.
--
-- Singular-factory supports only a single linear sequence of variables,
-- indexed starting from 1, optionally having single-character names.
--
-- So what we do is to use (phantom) types to encode naming conventions.
--

{-# LANGUAGE BangPatterns, DataKinds, ScopedTypeVariables, KindSignatures #-}
module Math.Singular.Factory.Variables where

--------------------------------------------------------------------------------

import Data.Char
import Data.List ( findIndex )
import Text.Read

import Data.Proxy
import GHC.TypeLits

import System.IO.Unsafe as Unsafe

import Math.Singular.Factory.Internal.CanonicalForm -- Factory

--------------------------------------------------------------------------------
-- * Raw factory variables

-- | A variable index. 
--
-- In factory, there is a single linear sequence of variables.
-- Variables are indexed starting from 1.
-- 
type VarIdx = Int

-- | In factory, there is a single linear sequence of variables.
-- We \"precalculate\" these (lazily).
theFactoryVars :: [Var]
theFactoryVars = map mk [1..] where
  mk i = Unsafe.unsafePerformIO $ newVarL i

theNthVar :: VarIdx -> Var
theNthVar idx = theFactoryVars !! (idx-1)

--------------------------------------------------------------------------------
-- * Variable sets    

-- | The class of variable sets. Since Factory only supports a single linear
-- variable set, these differ only by naming conventions.
--
class VariableSet v where
  varIdxName   :: Proxy v -> VarIdx -> String
  recogVarName :: Proxy v -> String -> Maybe VarIdx

-- | The variable set @x1, x2, x3, x4...@ (where \"x\" can be any string)
data VarN  (s :: Symbol)   

-- | The variable set @x_1, x_2, x_3, x_4...@ (where \"x\" can be any string)
data Var_N (s :: Symbol)           

-- | The variable set @x[1], x[2], x[3], x[4]...@ (where \"x\" can be any string)
data VarBracketN (s :: Symbol)     

-- | The variable set @a, b, c, d...@
data VarAbc    

-- | The variable set @A, B, C, D...@
data VarABC    

-- | The variable set @x, y, z, u, v, w, a, b, c...@
data VarXyz    

-- | The variable set @X, Y, Z, U, V, W, A, B, C...@
data VarXYZ    

instance forall s. KnownSymbol s => VariableSet (VarN s) where
  varIdxName   _ = indexedVars  (symbolVal (Proxy :: Proxy s))
  recogVarName _ = recogIndexed (symbolVal (Proxy :: Proxy s))

instance forall s. KnownSymbol s => VariableSet (Var_N s) where
  varIdxName   _ = indexedVarsUnderscore  (symbolVal (Proxy :: Proxy s))
  recogVarName _ = recogIndexedUnderscore (symbolVal (Proxy :: Proxy s))

instance forall s. KnownSymbol s => VariableSet (VarBracketN s) where
  varIdxName   _ = indexedVarsBracket  (symbolVal (Proxy :: Proxy s))
  recogVarName _ = recogIndexedBracket (symbolVal (Proxy :: Proxy s))

instance VariableSet VarAbc where
  varIdxName   _ = abcVars
  recogVarName _ = recogAbc  

instance VariableSet VarABC where
  varIdxName   _ = capitalAbcVars
  recogVarName _ = recogABC

instance VariableSet VarXyz where
  varIdxName   _ = xyzVars
  recogVarName _ = recogXyz
  
instance VariableSet VarXYZ where
  varIdxName   _ = capitalXyzVars
  recogVarName _ = recogXYZ
  
--------------------------------------------------------------------------------
-- * Standard naming conventions of variables

-- | Eg. @x1, x2, x3...@
indexedVars :: String -> VarIdx -> String
indexedVars prefix = \i -> prefix ++ show i

-- | Eg. @x_1, x_2, x_3...@
indexedVarsUnderscore :: String -> VarIdx -> String
indexedVarsUnderscore prefix = \i -> prefix ++ "_" ++ show i

-- | Eg. @x[1], x[2], x[3]...@
indexedVarsBracket :: String -> VarIdx -> String
indexedVarsBracket prefix = \i -> prefix ++ "[" ++ show i ++ "]"

-- | That is, @a, b, c...@
abcVars :: VarIdx -> String
abcVars idx = lowerVarList !! (idx-1)

-- | That is, @A, B, C...@
capitalAbcVars :: VarIdx -> String
capitalAbcVars idx = lowerVarList !! (idx-1)

-- | @x, y, z, u, v, w, a, b, c ... , t@
xyzVars :: VarIdx -> String
xyzVars idx = [ varListXyz !! (idx-1) ]

capitalXyzVars :: VarIdx -> String
capitalXyzVars idx = [ varListCapitalXYZ !! (idx-1) ]

--------------------------------------------------------------------------------
-- * Variable lists

varListXyz :: [Char]
varListXyz = "xyzuvwabcdefghijklmnopqrst" 

varListCapitalXYZ :: [Char]
varListCapitalXYZ = "XYZUVWABCDEFGHIJKLMNOPQRST"

-- | The infinite list of variables @a, b ..., z, aa, ab, ac, ...@
lowerVarList :: [String]
lowerVarList = map (:[]) abc ++ [ ys ++ [y] | ys<-lowerVarList , y<-abc ] where
  abc = ['a'..'z']

upperVarList :: [String]
upperVarList = map (:[]) abc ++ [ ys ++ [y] | ys<-lowerVarList , y<-abc ] where
  abc = ['A'..'Z']

--------------------------------------------------------------------------------
-- * Parsing standard variable names

readPosIdxMaybe :: String -> Maybe VarIdx
readPosIdxMaybe s = case readMaybe s :: Maybe Word of
  Nothing -> Nothing
  Just w  -> let j = (fromIntegral w :: Int) 
             in  if j >= 1 then Just j else Nothing
              
recogIndexed :: String -> String -> Maybe VarIdx
recogIndexed !prefix = recog where
  !n = length prefix
  recog s = case splitAt n s of 
    (p,q) -> if p /= prefix
      then Nothing
      else readPosIdxMaybe q 
      
recogIndexedUnderscore :: String -> String -> Maybe VarIdx
recogIndexedUnderscore !prefix = recog where
  !n = length prefix
  recog s = case splitAt n s of 
    (p,q) -> if p /= prefix
      then Nothing
      else case q of
        ('_':r ) -> readPosIdxMaybe r
        _        -> Nothing
        
recogIndexedBracket :: String -> String -> Maybe VarIdx
recogIndexedBracket !prefix = recog where
  !n = length prefix
  recog s = case splitAt n s of 
    (p,q) -> if p /= prefix
      then Nothing
      else if length q >= 3 && head q == '[' && last q == ']'
        then readPosIdxMaybe (init $ tail q) 
        else Nothing
     
recogAbc :: String -> Maybe VarIdx
recogAbc [c] = if c >= 'a' && c <= 'z' then Just (ord c - 96) else Nothing
recogAbc []  = Nothing
recogAbc _   = Nothing 

recogABC :: String -> Maybe VarIdx
recogABC [c] = if c >= 'A' && c <= 'Z' then Just (ord c - 64) else Nothing
recogABC []  = Nothing
recogABC _   = Nothing 

recogXyz :: String -> Maybe VarIdx
recogXyz [c] = if c >= 'a' && c <= 'z' then (+1) <$> findIndex (==c) varListXyz else Nothing
recogXyz _   = Nothing 

recogXYZ :: String -> Maybe VarIdx
recogXYZ [c] = if c >= 'A' && c <= 'Z' then (+1) <$> findIndex (==c) varListCapitalXYZ else Nothing
recogXYZ _   = Nothing 

--------------------------------------------------------------------------------