-- |
-- Module      :  ELynx.Sequence.Translate
-- Description :  Translate sequences
-- Copyright   :  2021 Dominik Schrempf
-- License     :  GPL-3.0-or-later
--
-- Maintainer  :  dominik.schrempf@gmail.com
-- Stability   :  unstable
-- Portability :  portable
--
-- Creation date: Fri May 17 13:49:18 2019.
module ELynx.Sequence.Translate
  ( translateSeq,
  )
where

import qualified Data.Vector.Unboxed as V
import ELynx.Alphabet.Alphabet
import qualified ELynx.Alphabet.Character as C
import ELynx.Character.Codon
import ELynx.Sequence.Sequence

-- Chop list into chunks of given length. If the last chop is shorter than
-- length, it is dropped.
chopVec :: V.Unbox a => Int -> V.Vector a -> [V.Vector a]
chopVec :: forall a. Unbox a => Int -> Vector a -> [Vector a]
chopVec Int
n Vector a
xs
  | forall a. Unbox a => Vector a -> Int
V.length Vector a
xs forall a. Ord a => a -> a -> Bool
< Int
n = []
  | Bool
otherwise = forall a. Unbox a => Int -> Vector a -> Vector a
V.take Int
n Vector a
xs forall a. a -> [a] -> [a]
: forall a. Unbox a => Int -> Vector a -> [Vector a]
chopVec Int
n (forall a. Unbox a => Int -> Vector a -> Vector a
V.drop Int
n Vector a
xs)

-- | Translate a sequence from 'DNA' or 'DNAX' to 'ProteinS'.
translateSeq :: UniversalCode -> Int -> Sequence -> Sequence
translateSeq :: UniversalCode -> Int -> Sequence -> Sequence
translateSeq UniversalCode
uc Int
rf (Sequence Name
n Name
d Alphabet
a Characters
cs) = case Alphabet
a of
  Alphabet
DNA -> Name -> Name -> Alphabet -> Characters -> Sequence
Sequence Name
n Name
d Alphabet
ProteinS (forall {a} {a}.
(Character a, Character a) =>
(Codon a -> a) -> Characters
cs' forall a b. (a -> b) -> a -> b
$ UniversalCode -> Codon Nucleotide -> AminoAcidS
translate UniversalCode
uc)
  Alphabet
DNAX -> Name -> Name -> Alphabet -> Characters -> Sequence
Sequence Name
n Name
d Alphabet
ProteinS (forall {a} {a}.
(Character a, Character a) =>
(Codon a -> a) -> Characters
cs' forall a b. (a -> b) -> a -> b
$ UniversalCode -> Codon NucleotideX -> AminoAcidS
translateX UniversalCode
uc)
  Alphabet
DNAI -> Name -> Name -> Alphabet -> Characters -> Sequence
Sequence Name
n Name
d Alphabet
ProteinI (forall {a} {a}.
(Character a, Character a) =>
(Codon a -> a) -> Characters
cs' forall a b. (a -> b) -> a -> b
$ UniversalCode -> Codon NucleotideI -> AminoAcidI
translateI UniversalCode
uc)
  Alphabet
_ -> forall a. HasCallStack => [Char] -> a
error [Char]
"translate: can only translate DNA, DNAX, and DNAI."
  where
    cs' :: (Codon a -> a) -> Characters
cs' Codon a -> a
f = forall a. Character a => Vector a -> Characters
C.fromCVec forall a b. (a -> b) -> a -> b
$ forall a b.
(Unbox a, Ord a, Unbox b) =>
(Codon a -> b) -> Int -> Vector a -> Vector b
translateVecWith Codon a -> a
f Int
rf (forall a. Character a => Characters -> Vector a
C.toCVec Characters
cs)

-- Translate from DNA to Protein with given reading frame (0, 1, 2).
translateVecWith ::
  (V.Unbox a, Ord a, V.Unbox b) =>
  (Codon a -> b) ->
  Int ->
  V.Vector a ->
  V.Vector b
translateVecWith :: forall a b.
(Unbox a, Ord a, Unbox b) =>
(Codon a -> b) -> Int -> Vector a -> Vector b
translateVecWith Codon a -> b
f Int
rf Vector a
cs
  | Int
rf forall a. Ord a => a -> a -> Bool
> Int
2 = forall a. HasCallStack => [Char] -> a
error [Char]
"translateVecWith: reading frame is larger than 2."
  | Int
rf forall a. Ord a => a -> a -> Bool
< Int
0 = forall a. HasCallStack => [Char] -> a
error [Char]
"translateVecWith: reading frame is negative."
  | Bool
otherwise = Vector b
aas
  where
    codons :: [Codon a]
codons = forall a b. (a -> b) -> [a] -> [b]
map forall (v :: * -> *) a. Vector v a => v a -> Codon a
fromVecUnsafe forall a b. (a -> b) -> a -> b
$ forall a. Unbox a => Int -> Vector a -> [Vector a]
chopVec Int
3 forall a b. (a -> b) -> a -> b
$ forall a. Unbox a => Int -> Vector a -> Vector a
V.drop Int
rf Vector a
cs
    aas :: Vector b
aas = forall a. Unbox a => [a] -> Vector a
V.fromList forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map Codon a -> b
f [Codon a]
codons