palindromes-1.0: src/Data/Algorithms/Palindromes/DNA.hs
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{- |
Module : Data.Algorithms.Palindromes.DNA
Copyright : (c) 2007 - 2025 Utrecht University (Department of Information and Computing Sciences) and Johan Jeuring
License : BSD3
Maintainer : johan@jeuring.net
Stability : provisional
Portability : portable
This program has been developed by students from the bachelor Computer Science at Utrecht
University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences) and Johan Jeuring
This module contains the DNA datatype and corresponding functions.
This type encodes a representation for DNA that can be used as input for finding
palindromes.
-}
module Data.Algorithms.Palindromes.DNA where
import Data.Char (toUpper)
import Data.Maybe (fromJust, isNothing)
import Data.Word (Word8)
import Data.Algorithms.Palindromes.PalEq (PalEq (..))
import qualified Data.Vector.Generic as G (Vector (..))
import qualified Data.Vector.Generic.Mutable as GM (MVector (..))
import qualified Data.Vector.Unboxed as U (MVector, Unbox, Vector, any, map)
{- | Datatype for the different DNA, note that (=)/Eq is not suitable for checking if DNA
has palindromes, instead PalEq should be used.
-}
data DNA = A | T | C | G | N deriving (Show, Eq, Enum)
newtype instance U.MVector s DNA = MV_DNA (U.MVector s Word8)
newtype instance U.Vector DNA = V_DNA (U.Vector Word8)
instance GM.MVector U.MVector DNA where
{-# INLINE basicLength #-}
basicLength (MV_DNA v) = GM.basicLength v
{-# INLINE basicUnsafeSlice #-}
basicUnsafeSlice a b (MV_DNA v) = MV_DNA $ GM.basicUnsafeSlice a b v
{-# INLINE basicOverlaps #-}
basicOverlaps (MV_DNA a) (MV_DNA b) = GM.basicOverlaps a b
{-# INLINE basicUnsafeNew #-}
basicUnsafeNew i = MV_DNA <$> GM.basicUnsafeNew i
{-# INLINE basicInitialize #-}
basicInitialize (MV_DNA v) = GM.basicInitialize v
{-# INLINE basicUnsafeRead #-}
basicUnsafeRead (MV_DNA v) i = toEnum . fromIntegral <$> GM.basicUnsafeRead v i
{-# INLINE basicUnsafeWrite #-}
basicUnsafeWrite (MV_DNA v) i = GM.basicUnsafeWrite v i . fromIntegral . fromEnum
instance G.Vector U.Vector DNA where
{-# INLINE basicUnsafeFreeze #-}
basicUnsafeFreeze (MV_DNA v) = V_DNA <$> G.basicUnsafeFreeze v
{-# INLINE basicUnsafeThaw #-}
basicUnsafeThaw (V_DNA v) = MV_DNA <$> G.basicUnsafeThaw v
{-# INLINE basicLength #-}
basicLength (V_DNA v) = G.basicLength v
{-# INLINE basicUnsafeSlice #-}
basicUnsafeSlice a b (V_DNA v) = V_DNA $ G.basicUnsafeSlice a b v
{-# INLINE basicUnsafeIndexM #-}
basicUnsafeIndexM (V_DNA v) i = toEnum . fromIntegral <$> G.basicUnsafeIndexM v i
instance U.Unbox DNA
-- | Declare instance PalEq for DNA. A and T form a couple, C and G form a couple.
instance {-# OVERLAPPING #-} PalEq DNA where
A =:= T = True
T =:= A = True
G =:= C = True
C =:= G = True
_ =:= _ = False
{- | Parsed a foldable with chars to a foldable with the DNA datatype. Returns Nothing if
the input cannot be fully parsed to DNA.
-}
toDNA :: U.Vector Char -> Maybe (U.Vector DNA)
toDNA x
| hasNothing = Nothing
| otherwise = Just $ U.map (fromJust . charToDNA) x
where
hasNothing = U.any (isNothing . charToDNA) x
-- | Parses a single Char to the DNA datatype. Returns Nothing if this is not possible.
charToDNA :: Char -> Maybe DNA
charToDNA 'A' = Just A
charToDNA 'T' = Just T
charToDNA 'C' = Just C
charToDNA 'G' = Just G
charToDNA 'U' = Just T
charToDNA 'a' = Just A
charToDNA 't' = Just T
charToDNA 'c' = Just C
charToDNA 'g' = Just G
charToDNA 'u' = Just T
charToDNA c
| toUpper c `elem` "RYKMSWBDHVN" = Just N
| otherwise = Nothing
-- | Converts the DNA datatype to the corresponding Char symbol
dnaToChar :: DNA -> Char
dnaToChar A = 'A'
dnaToChar T = 'T'
dnaToChar G = 'G'
dnaToChar C = 'C'
dnaToChar N = 'N'