diff --git a/Biobase/AAseq.hs b/Biobase/AAseq.hs
new file mode 100644
--- /dev/null
+++ b/Biobase/AAseq.hs
@@ -0,0 +1,253 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE PatternGuards #-}
+{-# LANGUAGE ViewPatterns #-}
+
+-- | This module has the translation tables for the genetic code.
+
+module Biobase.AAseq where
+
+import           Control.Arrow ((***))
+import           Data.Ix (Ix(..))
+import           Data.Primitive.Types
+import           Data.Tuple (swap)
+import           GHC.Base (remInt,quotInt)
+import qualified Data.ByteString.Char8 as BS
+import qualified Data.ByteString.Lazy.Char8 as BSL
+import qualified Data.Map.Strict as M
+import qualified Data.Text as T
+import qualified Data.Vector.Generic as VG
+import qualified Data.Vector.Generic.Mutable as VGM
+import qualified Data.Vector.Unboxed as VU
+
+import Data.Array.Repa.ExtShape
+import Data.Array.Repa.Index
+import Data.Array.Repa.Shape
+
+import Biobase.Primary
+
+
+
+-- | The amino acid newtype.
+
+newtype AA = AA { unAA :: Int }
+  deriving (Eq,Ord,Ix)
+
+
+
+-- * Creating functions and aa data.
+
+(aStop:aA:aB:aC:aD:aE:aF:aG:aH:aI:aK:aL:aM:aN:aP:aQ:aR:aS:aT:aV:aW:aX:aY:aZ:aUndefined:_) = map AA [0..]
+
+aaRange = [aStop .. pred aUndefined]
+
+-- | Translate 'Char' amino acid representation into efficient 'AA' newtype.
+
+toAA :: Char -> AA
+toAA ((`lookup` charAAList) -> Just aa) = aa
+toAA c = error $ "unknown AA: " ++ show c
+
+-- | 'Char' representation of an 'AA'.
+
+fromAA :: AA -> Char
+fromAA ((`lookup` aACharList) -> Just c) = c
+fromAA (AA aa) = error $ "unknown AA: " ++ (show aa)
+
+-- | Create amino acid sequences from different data sources.
+
+class MkAAseq x where
+  mkAAseq :: x -> VU.Vector AA
+
+type AAseq = VU.Vector AA
+
+-- | Using the codon table, create an 'AAseq' from the 'Primary' sequence.
+
+primaryToAAseq :: Primary -> AAseq
+primaryToAAseq = mkAAseq . go where
+  go (VU.length -> 0) = []
+  go (VU.splitAt 3 -> (VU.toList -> hs,ts)) = case M.lookup hs nucCodonTable of
+    Just aa -> aa : go ts
+    _       -> error $ "primaryToAAseq: " ++ show (hs,ts)
+
+
+
+-- * lookup tables
+
+charAAList =
+  [ ('/',aStop)
+  , ('A',aA)
+  , ('B',aB)
+  , ('C',aC)
+  , ('D',aD)
+  , ('E',aE)
+  , ('F',aF)
+  , ('G',aG)
+  , ('H',aH)
+  , ('I',aI)
+  , ('K',aK)
+  , ('L',aL)
+  , ('M',aM)
+  , ('N',aN)
+  , ('P',aP)
+  , ('Q',aQ)
+  , ('R',aR)
+  , ('S',aS)
+  , ('T',aT)
+  , ('V',aV)
+  , ('W',aW)
+  , ('X',aX)
+  , ('Y',aY)
+  , ('Z',aZ)
+  ]
+
+aACharList = map swap charAAList
+
+codonTable = M.fromList
+  [ ("aaa",'K')
+  , ("aac",'N')
+  , ("aag",'K')
+  , ("aat",'N')
+  , ("aca",'T')
+  , ("acc",'T')
+  , ("acg",'T')
+  , ("act",'T')
+  , ("aga",'R')
+  , ("agc",'S')
+  , ("agg",'R')
+  , ("agt",'S')
+  , ("ata",'I')
+  , ("atc",'I')
+  , ("atg",'M')
+  , ("att",'I')
+  , ("caa",'Q')
+  , ("cac",'H')
+  , ("cag",'Q')
+  , ("cat",'H')
+  , ("cca",'P')
+  , ("ccc",'P')
+  , ("ccg",'P')
+  , ("cct",'P')
+  , ("cga",'R')
+  , ("cgc",'R')
+  , ("cgg",'R')
+  , ("cgt",'R')
+  , ("cta",'L')
+  , ("ctc",'L')
+  , ("ctg",'L')
+  , ("ctt",'L')
+  , ("gaa",'E')
+  , ("gac",'D')
+  , ("gag",'E')
+  , ("gat",'D')
+  , ("gca",'A')
+  , ("gcc",'A')
+  , ("gcg",'A')
+  , ("gct",'A')
+  , ("gga",'G')
+  , ("ggc",'G')
+  , ("ggg",'G')
+  , ("ggt",'G')
+  , ("gta",'V')
+  , ("gtc",'V')
+  , ("gtg",'V')
+  , ("gtt",'V')
+  , ("taa",'/')
+  , ("tac",'Y')
+  , ("tag",'/')
+  , ("tat",'Y')
+  , ("tca",'S')
+  , ("tcc",'S')
+  , ("tcg",'S')
+  , ("tct",'S')
+  , ("tga",'/')
+  , ("tgc",'C')
+  , ("tgg",'W')
+  , ("tgt",'C')
+  , ("tta",'L')
+  , ("ttc",'F')
+  , ("ttg",'L')
+  , ("ttt",'F')
+  ]
+
+nucCodonTable = M.fromList . map (map mkNuc *** toAA) . M.assocs $ codonTable
+
+
+
+-- * instances
+
+instance Show AA where
+  show n = [fromAA n]
+
+instance Read AA where
+  readsPrec p [] = []
+  readsPrec p (x:xs)
+    | x==' ' = readsPrec p xs
+    | Just aa <- x `lookup` charAAList = [(aa,xs)]
+    | otherwise = []
+
+deriving instance Prim AA
+deriving instance VGM.MVector VU.MVector AA
+deriving instance VG.Vector VU.Vector AA
+deriving instance VU.Unbox AA
+
+instance (Shape sh,Show sh) => Shape (sh :. AA) where
+  rank (sh:._) = rank sh + 1
+  zeroDim = zeroDim:.AA 0
+  unitDim = unitDim:.AA 1 -- TODO does this one make sense?
+  intersectDim (sh1:.n1) (sh2:.n2) = intersectDim sh1 sh2 :. min n1 n2
+  addDim (sh1:.AA n1) (sh2:.AA n2) = addDim sh1 sh2 :. AA (n1+n2) -- TODO will not necessarily yield a valid Nuc
+  size (sh1:.AA n) = size sh1 * n
+  sizeIsValid (sh1:.AA n) = sizeIsValid (sh1:.n)
+  toIndex (sh1:.AA sh2) (sh1':.AA sh2') = toIndex (sh1:.sh2) (sh1':.sh2')
+  fromIndex (ds:.AA d) n = fromIndex ds (n `quotInt` d) :. AA r where
+                              r | rank ds == 0 = n
+                                | otherwise    = n `remInt` d
+  inShapeRange (sh1:.n1) (sh2:.n2) (idx:.i) = i>=n1 && i<n2 && inShapeRange sh1 sh2 idx
+  listOfShape (sh:.AA n) = n : listOfShape sh
+  shapeOfList xx = case xx of
+    []   -> error "empty list in shapeOfList/Primary"
+    x:xs -> shapeOfList xs :. AA x
+  deepSeq (sh:.n) x = deepSeq sh (n `seq` x)
+  {-# INLINE rank #-}
+  {-# INLINE zeroDim #-}
+  {-# INLINE unitDim #-}
+  {-# INLINE intersectDim #-}
+  {-# INLINE addDim #-}
+  {-# INLINE size #-}
+  {-# INLINE sizeIsValid #-}
+  {-# INLINE toIndex #-}
+  {-# INLINE fromIndex #-}
+  {-# INLINE inShapeRange #-}
+  {-# INLINE listOfShape #-}
+  {-# INLINE shapeOfList #-}
+  {-# INLINE deepSeq #-}
+
+instance (Shape sh, Show sh, ExtShape sh) => ExtShape (sh :. AA) where
+  subDim (sh1:.AA n1) (sh2:.AA n2) = subDim sh1 sh2 :. AA (n1-n2)
+  rangeList (sh1:.AA n1) (sh2:.AA n2) = [ sh:.AA n | sh <- rangeList sh1 sh2, n <- [n1 .. (n1+n2)]]
+
+instance Enum AA where
+  toEnum = AA
+  fromEnum = unAA
+
+instance MkAAseq [Char] where
+  mkAAseq = VU.fromList . map toAA
+
+instance MkAAseq [AA] where
+  mkAAseq = VU.fromList
+
+instance MkAAseq (VU.Vector Char) where
+  mkAAseq = VU.map toAA
+
+instance MkAAseq BS.ByteString where
+  mkAAseq = VU.fromList . map toAA . BS.unpack
+
+instance MkAAseq BSL.ByteString where
+  mkAAseq = VU.fromList . map toAA . BSL.unpack
+
+instance MkAAseq T.Text where
+  mkAAseq = VU.fromList . map toAA . T.unpack
+
diff --git a/Biobase/Codon.hs b/Biobase/Codon.hs
deleted file mode 100644
--- a/Biobase/Codon.hs
+++ /dev/null
@@ -1,73 +0,0 @@
-
--- | This module has the translation tables for the genetic code.
-
-module Biobase.Codon where
-
-import qualified Data.Map.Strict as M
-
-codonTable = M.fromList
-  [ ("aaa",'K')
-  , ("aac",'N')
-  , ("aag",'K')
-  , ("aat",'N')
-  , ("aca",'T')
-  , ("acc",'T')
-  , ("acg",'T')
-  , ("act",'T')
-  , ("aga",'R')
-  , ("agc",'S')
-  , ("agg",'R')
-  , ("agt",'S')
-  , ("ata",'I')
-  , ("atc",'I')
-  , ("atg",'M')
-  , ("att",'I')
-  , ("caa",'Q')
-  , ("cac",'H')
-  , ("cag",'Q')
-  , ("cat",'H')
-  , ("cca",'P')
-  , ("ccc",'P')
-  , ("ccg",'P')
-  , ("cct",'P')
-  , ("cga",'R')
-  , ("cgc",'R')
-  , ("cgg",'R')
-  , ("cgt",'R')
-  , ("cta",'L')
-  , ("ctc",'L')
-  , ("ctg",'L')
-  , ("ctt",'L')
-  , ("gaa",'E')
-  , ("gac",'D')
-  , ("gag",'E')
-  , ("gat",'D')
-  , ("gca",'A')
-  , ("gcc",'A')
-  , ("gcg",'A')
-  , ("gct",'A')
-  , ("gga",'G')
-  , ("ggc",'G')
-  , ("ggg",'G')
-  , ("ggt",'G')
-  , ("gta",'V')
-  , ("gtc",'V')
-  , ("gtg",'V')
-  , ("gtt",'V')
-  , ("taa",'/')
-  , ("tac",'Y')
-  , ("tag",'/')
-  , ("tat",'Y')
-  , ("tca",'S')
-  , ("tcc",'S')
-  , ("tcg",'S')
-  , ("tct",'S')
-  , ("tga",'/')
-  , ("tgc",'C')
-  , ("tgg",'W')
-  , ("tgt",'C')
-  , ("tta",'L')
-  , ("ttc",'F')
-  , ("ttg",'L')
-  , ("ttt",'F')
-  ]
diff --git a/Biobase/Primary.hs b/Biobase/Primary.hs
--- a/Biobase/Primary.hs
+++ b/Biobase/Primary.hs
@@ -164,24 +164,6 @@
   subDim (sh1:.Nuc n1) (sh2:.Nuc n2) = subDim sh1 sh2 :. Nuc (n1-n2)
   rangeList (sh1:.Nuc n1) (sh2:.Nuc n2) = [ sh:.Nuc n | sh <- rangeList sh1 sh2, n <- [n1 .. (n1+n2)]]
 
-{-
--- | The bounded instance from GHC proper. Captures all defined symbols.
-
-instance Bounded Nuc where
-  minBound = nN
-  maxBound = nT
-
--- | Special bounds for energy / score arrays
-
-instance Bounds Nuc where
-  minNormal = nA
-  maxNormal = nT
-  minExtended = nN
-  maxExtended = nT
-
-
--}
-
 -- | Enum
 
 instance Enum Nuc where
diff --git a/Biobase/Primary/IUPAC.hs b/Biobase/Primary/IUPAC.hs
new file mode 100644
--- /dev/null
+++ b/Biobase/Primary/IUPAC.hs
@@ -0,0 +1,50 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+-- | Degenerate base symbol representation. We use the same conventions as in
+-- <<https://en.wikipedia.org/wiki/Nucleic_acid_notation>> which ignores
+-- @U@racil, except if it stands alone. Therefore, any RNA sequence should be
+-- converted to DNA (and back afterwards).
+--
+-- NOTE that the generic 'Char' instance is not optimized for speed.
+--
+-- TODO this should be easier once we have instances for RNA,DNA, etc
+
+module Biobase.Primary.IUPAC where
+
+import Data.ByteString.Char8 (ByteString,unpack)
+import Data.FileEmbed (embedFile)
+import Data.List (nub,sort)
+import Data.Tuple (swap)
+
+
+
+class Degenerate x where
+  fromSymbol :: x   -> [x]
+  toSymbol   :: [x] -> Maybe x
+
+instance Degenerate Char where
+  fromSymbol = maybe [] id . flip lookup iupacList
+  toSymbol   = flip lookup (map swap iupacList) . nub . sort
+
+-- instance Degenerate RNA where
+--
+-- instance Degenerate DNA where
+--
+-- instance Degenerate XNA where -- if we want a combined alphabet
+
+
+
+-- ** Raw embeddings
+
+-- | list of characters
+
+iupacList :: [(Char,String)]
+iupacList = map (go . words) . lines . unpack $ iupacNucleotides where
+  go [[c],cs] = (c,cs)
+{-# NOINLINE iupacList #-}
+
+-- | Raw iupac data, embedded into the library.
+
+iupacNucleotides :: ByteString
+iupacNucleotides = $(embedFile "sources/iupac-nucleotides")
+
diff --git a/BiobaseXNA.cabal b/BiobaseXNA.cabal
--- a/BiobaseXNA.cabal
+++ b/BiobaseXNA.cabal
@@ -1,5 +1,5 @@
 name:           BiobaseXNA
-version:        0.7.0.2
+version:        0.8.1.0
 author:         Christian Hoener zu Siederdissen
 maintainer:     choener@tbi.univie.ac.at
 homepage:       http://www.tbi.univie.ac.at/~choener/
@@ -13,21 +13,19 @@
 cabal-version:  >= 1.6.0
 description:
                 This is a base library for bioinformatics with emphasis on RNA
-                and DNA primary structure.
+                and DNA primary structure as well as amino acid sequences.
                 .
                 Provided are efficient encodings for short sequences, as
                 required by RNA folding tools. Extended RNA secondary
                 structures can be represented as well.
                 .
-                .
-                .
                 Contains data from:
                 .
+                @
                 Frequency and isostericity of RNA base pairs
-                .
                 Jesse Stombaugh, Craig L. Zirbel, Eric Westhof, and Neocles B. Leontis
-                .
                 Nucl. Acids Res. (2009)
+                @
                 .
                 <http://dx.crossref.org/10.1093%2Fnar%2Fgkp011>
 
@@ -36,6 +34,7 @@
 extra-source-files:
   sources/isostericity-matrices.csv
   sources/isostericity-detailed.csv
+  sources/iupac-nucleotides
   changelog
 
 library
@@ -46,17 +45,18 @@
     csv            >= 0.1.2         ,
     file-embed     >= 0.0.4.7       ,
     primitive      >= 0.5           ,
-    PrimitiveArray >= 0.5           ,
+    PrimitiveArray >= 0.5.3         ,
     repa           >= 3.2           ,
     text           >= 0.11          ,
     tuple          >= 0.2           ,
     vector         >= 0.10
 
   exposed-modules:
-    Biobase.Codon
+    Biobase.AAseq
     Biobase.Primary
     Biobase.Primary.Bounds
     Biobase.Primary.Hashed
+    Biobase.Primary.IUPAC
     Biobase.Secondary
     Biobase.Secondary.Constraint
     Biobase.Secondary.Diagrams
diff --git a/changelog b/changelog
--- a/changelog
+++ b/changelog
@@ -1,8 +1,21 @@
+0.8.1.0
+-------
+
+- Biobase.Primary.IUPAC for degenerate base symbol conversion
+
+0.8.0.0
+-------
+
+- Biobase.Codon -> Biobase.AAseq
+- and efficient encoding of AAseqs
+
 0.7
-    * updated to PrimitiveArray >= 0.5
+---
 
-    * added Codon table
+- updated to PrimitiveArray >= 0.5
+- added Codon table
 
 0.6.2.0
+-------
 
-    * Updated to PrimitiveArray >= 0.2.0.0
+- Updated to PrimitiveArray >= 0.2.0.0
diff --git a/sources/iupac-nucleotides b/sources/iupac-nucleotides
new file mode 100644
--- /dev/null
+++ b/sources/iupac-nucleotides
@@ -0,0 +1,16 @@
+A A
+C C
+G G
+T T
+U U
+W AT
+S CG
+M AC
+K GT
+R AG
+Y CT
+B CGT
+D AGT
+H ACT
+V ACG
+N ACGT
