diff --git a/CHANGES.md b/CHANGES.md
new file mode 100644
--- /dev/null
+++ b/CHANGES.md
@@ -0,0 +1,6 @@
+Changelog for lol-repa project
+================================
+
+0.0.0.1
+----
+ * Initial split from lol package.
diff --git a/Crypto/Lol/Cyclotomic/Tensor/Repa.hs b/Crypto/Lol/Cyclotomic/Tensor/Repa.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Lol/Cyclotomic/Tensor/Repa.hs
@@ -0,0 +1,276 @@
+{-|
+Module      : Crypto.Lol.Cyclotomic.Tensor.Repa
+Description : A pure, repa-based implementation of the 'Tensor' interface.
+Copyright   : (c) Eric Crockett, 2011-2017
+                  Chris Peikert, 2011-2017
+License     : GPL-2
+Maintainer  : ecrockett0@email.com
+Stability   : experimental
+Portability : POSIX
+
+A pure, repa-based implementation of the 'Tensor' interface.
+-}
+
+{-# LANGUAGE ConstraintKinds       #-}
+{-# LANGUAGE DataKinds             #-}
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE GADTs                 #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE PolyKinds             #-}
+{-# LANGUAGE RebindableSyntax      #-}
+{-# LANGUAGE RoleAnnotations       #-}
+{-# LANGUAGE ScopedTypeVariables   #-}
+{-# LANGUAGE StandaloneDeriving    #-}
+{-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE TypeOperators         #-}
+{-# LANGUAGE UndecidableInstances  #-}
+
+module Crypto.Lol.Cyclotomic.Tensor.Repa
+( RT ) where
+
+import Crypto.Lol.Cyclotomic.Tensor                      as T
+import Crypto.Lol.Cyclotomic.Tensor.Repa.CRT
+import Crypto.Lol.Cyclotomic.Tensor.Repa.Dec
+import Crypto.Lol.Cyclotomic.Tensor.Repa.Extension
+import Crypto.Lol.Cyclotomic.Tensor.Repa.GL
+import Crypto.Lol.Cyclotomic.Tensor.Repa.Instances ()
+import Crypto.Lol.Cyclotomic.Tensor.Repa.RTCommon  as RT hiding
+                                                                ((++))
+import Crypto.Lol.Prelude                                as LP
+import Crypto.Lol.Types.FiniteField                      as FF
+import Crypto.Lol.Types.IZipVector
+import Crypto.Lol.Types.Proto
+import Crypto.Lol.Utils.ShowType
+
+import Algebra.Additive     as Additive (C)
+import Algebra.Module       as Module (C)
+import Algebra.ZeroTestable as ZeroTestable (C)
+
+import Control.Applicative  hiding ((*>))
+import Control.Arrow        hiding (arr)
+import Control.DeepSeq      (NFData (rnf))
+import Control.Monad.Random
+import Data.Coerce
+import Data.Constraint      hiding ((***))
+import Data.Foldable        as F
+import Data.Maybe
+import Data.Traversable     as T
+import Data.Vector          as V hiding (force, (++))
+import Data.Vector.Unboxed  as U hiding (force, (++))
+
+-- | An implementation of 'Tensor' backed by repa.
+data RT (m :: Factored) r where
+  RT :: Unbox r => !(Arr m r) -> RT m r
+  ZV :: IZipVector m r -> RT m r
+
+deriving instance Show r => Show (RT m r)
+
+instance Show (ArgType RT) where
+  show _ = "RT"
+
+instance (Protoable (IZipVector m r), Fact m, Unbox r) => Protoable (RT m r) where
+  type ProtoType (RT m r) = ProtoType (IZipVector m r)
+
+  toProto x@(RT _) = toProto $ toZV x
+  toProto (ZV x) = toProto x
+
+  fromProto x = toRT <$> ZV <$> fromProto x
+
+
+instance Eq r => Eq (RT m r) where
+  (ZV a) == (ZV b) = a == b
+  (RT a) == (RT b) = a == b
+  a@(RT _) == b = a == toRT b
+  a == b@(RT _) = toRT a == b
+  {-# INLINABLE (==) #-}
+
+zvToArr :: Unbox r => IZipVector m r -> Arr m r
+zvToArr v = let vec = convert $ unIZipVector v
+            in Arr $ fromUnboxed (Z :. U.length vec) vec
+
+-- converts to RT constructor
+toRT :: Unbox r => RT m r -> RT m r
+toRT v@(RT _) = v
+toRT (ZV v) = RT $ zvToArr v
+
+toZV :: Fact m => RT m r -> RT m r
+toZV (RT (Arr v)) = ZV $ fromMaybe (error "toZV: internal error") $
+                    iZipVector $ convert $ toUnboxed v
+toZV v@(ZV _) = v
+
+{-# INLINABLE wrap #-}
+wrap :: Unbox r => (Arr l r -> Arr m r) -> RT l r -> RT m r
+wrap f (RT v) = RT $ f v
+wrap f (ZV v) = RT $ f $ zvToArr v
+
+{-# INLINABLE wrapM #-}
+wrapM :: (Unbox r, Monad mon) => (Arr l r -> mon (Arr m r))
+         -> RT l r -> mon (RT m r)
+wrapM f (RT v) = RT <$> f v
+wrapM f (ZV v) = RT <$> f (zvToArr v)
+
+instance Tensor RT where
+
+  type TElt RT r = (Unbox r, Elt r)
+
+  entailIndexT  = tag $ Sub Dict
+  entailEqT     = tag $ Sub Dict
+  entailZTT     = tag $ Sub Dict
+  entailNFDataT = tag $ Sub Dict
+  entailRandomT = tag $ Sub Dict
+  entailShowT   = tag $ Sub Dict
+  entailModuleT = tag $ Sub Dict
+
+  scalarPow = RT . scalarPow'
+
+  l = wrap fL
+  lInv = wrap fLInv
+
+  mulGPow = wrap fGPow
+  mulGDec = wrap fGDec
+
+  divGPow = wrapM fGInvPow
+  divGDec = wrapM fGInvDec
+
+  crtFuncs = (,,,,) <$>
+             ((RT .) <$> scalarCRT') <*>
+             (wrap <$> mulGCRT') <*>
+             (wrap <$> divGCRT') <*>
+             (wrap <$> fCRT) <*>
+             (wrap <$> fCRTInv)
+
+  tGaussianDec = fmap RT . tGaussianDec'
+
+  gSqNormDec (RT e) = gSqNormDec' e
+  gSqNormDec e = gSqNormDec $ toRT e
+
+  twacePowDec = wrap twacePowDec'
+
+  embedPow = wrap embedPow'
+  embedDec = wrap embedDec'
+
+  crtExtFuncs = (,) <$> (wrap <$> twaceCRT') <*> (wrap <$> embedCRT')
+
+  coeffs = wrapM coeffs'
+
+  powBasisPow = (RT <$>) <$> powBasisPow'
+
+  crtSetDec = (RT <$>) <$> crtSetDec'
+
+  fmapT f (RT v) = RT $ (coerce $ force . RT.map f) v
+  fmapT f v@(ZV _) = fmapT f $ toRT v
+
+  zipWithT f (RT (Arr a1)) (RT (Arr a2)) = RT $ Arr $ force $ RT.zipWith f a1 a2
+  zipWithT f v1 v2 = zipWithT f (toRT v1) (toRT v2)
+
+  unzipT v@(RT _) = unzipT $ toZV v
+  unzipT (ZV v) = ZV *** ZV $ unzipIZV v
+
+  {-# INLINABLE entailIndexT #-}
+  {-# INLINABLE entailEqT #-}
+  {-# INLINABLE entailZTT #-}
+  {-# INLINABLE entailNFDataT #-}
+  {-# INLINABLE entailRandomT #-}
+  {-# INLINABLE entailShowT #-}
+  {-# INLINABLE scalarPow #-}
+  {-# INLINABLE l #-}
+  {-# INLINABLE lInv #-}
+  {-# INLINABLE mulGPow #-}
+  {-# INLINABLE mulGDec #-}
+  {-# INLINABLE divGPow #-}
+  {-# INLINABLE divGDec #-}
+  {-# INLINABLE crtFuncs #-}
+  {-# INLINABLE twacePowDec #-}
+  {-# INLINABLE embedPow #-}
+  {-# INLINABLE embedDec #-}
+  {-# INLINABLE tGaussianDec #-}
+  {-# INLINABLE gSqNormDec #-}
+  {-# INLINABLE crtExtFuncs #-}
+  {-# INLINABLE coeffs #-}
+  {-# INLINABLE powBasisPow #-}
+  {-# INLINABLE crtSetDec #-}
+  {-# INLINABLE fmapT #-}
+  {-# INLINABLE zipWithT #-}
+  {-# INLINABLE unzipT #-}
+
+
+---------- Category-theoretic instances ----------
+
+instance Fact m => Functor (RT m) where
+  -- Functor instance is implied by Applicative
+  fmap f x = pure f <*> x
+
+instance Fact m => Applicative (RT m) where
+  pure = ZV . pure
+
+  -- RT can never hold an a -> b
+  (ZV f) <*> (ZV a) = ZV (f <*> a)
+  f@(ZV _) <*> v@(RT _) = f <*> toZV v
+
+instance Fact m => Foldable (RT m) where
+  -- Foldable instance is implied by Traversable
+  foldMap = foldMapDefault
+
+instance Fact m => Traversable (RT m) where
+  traverse f r@(RT _) = T.traverse f $ toZV r
+  traverse f (ZV v) = ZV <$> T.traverse f v
+
+
+---------- Numeric Prelude instances ----------
+
+--CJP: Additive, Ring are not necessary when we use zipWithT
+--EAC: But we need an Additive instance for the Module instance
+
+instance (Unbox r, Additive (Arr m r)) => Additive.C (RT m r) where
+  zero = RT zero
+
+  (RT a) + (RT b) = RT $ a + b
+  a + b = toRT a + toRT b
+
+  (RT a) - (RT b) = RT $ a - b
+  a - b = toRT a - toRT b
+
+  negate (RT a) = RT $ negate a
+  negate a = negate $ toRT a
+
+  {-# INLINABLE (+) #-}
+  {-# INLINABLE (-) #-}
+  {-# INLINABLE zero #-}
+  {-# INLINABLE negate #-}
+
+{-
+instance (Unbox r, Ring (Arr m r)) => Ring.C (RT m r) where
+  (RT a) * (RT b) = RT $ a * b
+  a * b = toRT a * toRT b
+
+  fromInteger = RT . fromInteger
+  {-# INLINABLE (*) #-}
+  {-# INLINABLE fromInteger #-}
+-}
+
+instance (ZeroTestable (Arr m r), ZeroTestable (IZipVector m r))
+    => ZeroTestable.C (RT m r) where
+  isZero (RT a) = isZero a
+  isZero (ZV v) = isZero v
+  {-# INLINABLE isZero #-}
+
+instance (GFCtx fp d, Fact m, Additive (RT m fp))
+    => Module.C (GF fp d) (RT m fp) where
+
+  r *> v = case v of
+    RT (Arr arr) -> RT $ Arr $ RT.fromList (extent arr)
+                    $ unCoeffs $ r *> Coeffs $ RT.toList arr
+    ZV zv -> ZV $ fromJust $ iZipVector $ V.fromList
+             $ unCoeffs $ r *> Coeffs $ V.toList $ unIZipVector zv
+
+---------- Miscellaneous instances ----------
+
+instance (Unbox r, Random (Arr m r)) => Random (RT m r) where
+  random = runRand $ RT <$> liftRand random
+
+  randomR = error "randomR nonsensical for RT"
+
+instance (NFData r) => NFData (RT m r) where
+  rnf (RT v) = rnf v
+  rnf (ZV v) = rnf v
diff --git a/Crypto/Lol/Cyclotomic/Tensor/Repa/CRT.hs b/Crypto/Lol/Cyclotomic/Tensor/Repa/CRT.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Lol/Cyclotomic/Tensor/Repa/CRT.hs
@@ -0,0 +1,233 @@
+{-|
+Module      : Crypto.Lol.Cyclotomic.Tensor.Repa.CRT
+Description : Functions to support the chinese remainder transform on Repa arrays.
+Copyright   : (c) Eric Crockett, 2011-2017
+                  Chris Peikert, 2011-2017
+License     : GPL-2
+Maintainer  : ecrockett0@email.com
+Stability   : experimental
+Portability : POSIX
+
+Functions to support the chinese remainder transform on Repa arrays.
+-}
+
+{-# LANGUAGE ConstraintKinds       #-}
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE GADTs                 #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE NoImplicitPrelude     #-}
+{-# LANGUAGE ScopedTypeVariables   #-}
+
+module Crypto.Lol.Cyclotomic.Tensor.Repa.CRT
+( scalarCRT'
+, fCRT, fCRTInv
+, mulGCRT', divGCRT'
+, gCRT, gInvCRT
+) where
+
+import Crypto.Lol.CRTrans
+import Crypto.Lol.Cyclotomic.Tensor
+import Crypto.Lol.Cyclotomic.Tensor.Repa.RTCommon as RT
+import Crypto.Lol.Prelude                               as LP
+
+import Control.Applicative
+import Data.Coerce
+import Data.Singletons.Prelude
+
+-- | Embeds a scalar into the CRT basis (when it exists).
+scalarCRT' :: forall mon m r . (Fact m, CRTrans mon r, Unbox r)
+              => mon (r -> Arr m r)
+{-# INLINABLE scalarCRT' #-}
+scalarCRT'
+  = let n = proxy totientFact (Proxy::Proxy m)
+        sz = Z :. n
+    in pure $ Arr . force . fromFunction sz . const
+
+-- | Multiply by @g_m@ in the CRT basis (when it exists).
+mulGCRT' :: (Fact m, CRTrans mon r, Unbox r)
+            => mon (Arr m r -> Arr  m r)
+{-# INLINABLE mulGCRT' #-}
+mulGCRT' = (coerce (\x -> force . RT.zipWith (*) x) `asTypeOf` asTypeOf) <$> gCRT
+
+-- | Divide by @g@ in the CRT basis (when it exists).
+divGCRT' :: (Fact m, CRTrans mon r, Unbox r) => mon (Arr m r -> Arr m r)
+{-# INLINABLE divGCRT' #-}
+divGCRT' = (coerce (\x -> force . RT.zipWith (*) x) `asTypeOf` asTypeOf) <$> gInvCRT
+
+wrapVector :: forall mon m r . (Monad mon, Fact m, Ring r, Unbox r)
+              => TaggedT m mon (Kron r) -> mon (Arr m r)
+wrapVector v = do
+  vmat <- proxyT v (Proxy::Proxy m)
+  let n = proxy totientFact (Proxy::Proxy m)
+  return $ coerce $ force $ RT.fromFunction (Z:.n)
+    (\(Z:.i) -> indexK vmat i 0)
+
+gCRT, gInvCRT :: (Fact m, CRTrans mon r, Unbox r) => mon (Arr m r)
+{-# INLINABLE gCRT #-}
+{-# INLINABLE gInvCRT #-}
+
+-- | The coefficient vector of @g@ in the CRT basis (when it exists).
+gCRT = wrapVector gCRTK
+-- | The coefficient vector of @g^{ -1 }@ in the CRT basis (when it exists).
+gInvCRT = wrapVector gInvCRTK
+
+fCRT, fCRTInv ::
+  forall mon m r . (Fact m, CRTrans mon r, Unbox r, Elt r)
+  => mon (Arr m r -> Arr m r)
+
+{-# INLINABLE fCRT #-}
+{-# INLINABLE fCRTInv #-}
+
+-- | The Chinese Remainder Transform.
+-- Exists if and only if CRT exists for all prime powers.
+fCRT = evalM $ fTensor ppCRT
+
+-- divide by mhat after doing crtInv'
+-- | The inverse Chinese Remainder Transform.
+-- Exists if and only if CRT exists for all prime powers.
+fCRTInv = do
+  (_, mhatInv) :: (CRTInfo r) <- proxyT crtInfo (Proxy :: Proxy m)
+  let totm = proxy totientFact (Proxy :: Proxy m)
+      divMhat = trans totm $ RT.map (*mhatInv)
+  evalM $ (divMhat .*) <$> fTensor ppCRTInv'
+
+ppDFT, ppDFTInv', ppCRT, ppCRTInv' ::
+  forall mon pp r . (PPow pp, CRTrans mon r, Unbox r, Elt r)
+  => TaggedT pp mon (Trans r)
+
+{-# INLINABLE ppDFT #-}
+{-# INLINABLE ppDFTInv' #-}
+{-# INLINABLE ppCRT #-}
+{-# INLINABLE ppCRTInv' #-}
+
+ppDFT = case (sing :: SPrimePower pp) of
+  (SPP (STuple2 sp SO)) -> tagT $ withWitnessT pDFT sp
+  spp@(SPP (STuple2 sp (SS se'))) ->
+    tagT $ do
+      let spp' = SPP (STuple2 sp se')
+      pp'dft <- withWitnessT ppDFT spp'
+      pptwid <- withWitnessT (ppTwid False) spp
+      pdft <- withWitnessT pDFT sp
+      return $ (pp'dft @* Id (dim pdft)) .* pptwid .* (Id (dim pp'dft) @* pdft)
+
+ppDFTInv' = case (sing :: SPrimePower pp) of
+  (SPP (STuple2 sp SO)) -> tagT $ withWitnessT pDFTInv' sp
+  spp@(SPP (STuple2 sp (SS se'))) ->
+    tagT $ do
+      let spp' = SPP (STuple2 sp se')
+      pp'dftInv' <- withWitnessT ppDFTInv' spp'
+      pptwidInv <- withWitnessT (ppTwid True) spp
+      pdftInv' <- withWitnessT pDFTInv' sp
+      return $ (Id (dim pp'dftInv') @* pdftInv') .* pptwidInv .*
+                 (pp'dftInv' @* Id (dim pdftInv'))
+
+ppCRT = case (sing :: SPrimePower pp) of
+  (SPP (STuple2 sp SO)) -> tagT $ withWitnessT pCRT sp
+  spp@(SPP (STuple2 sp (SS se'))) ->
+    tagT $ do
+      let spp' = SPP (STuple2 sp se')
+      pp'dft <- withWitnessT ppDFT spp'
+      pptwid <- withWitnessT (ppTwidHat False) spp
+      pcrt <- withWitnessT pCRT sp
+      return $
+        (pp'dft @* Id (dim pcrt)) .* pptwid .*
+        -- save some work when p=2
+        (if dim pcrt > 1 then Id (dim pp'dft) @* pcrt else Id (dim pp'dft))
+
+ppCRTInv' = case (sing :: SPrimePower pp) of
+  (SPP (STuple2 sp SO)) -> tagT $ withWitnessT pCRTInv' sp
+  spp@(SPP (STuple2 sp (SS se'))) ->
+    tagT $ do
+      let spp' = SPP (STuple2 sp se')
+      pp'dftInv' <- withWitnessT ppDFTInv' spp'
+      pptwidInv <- withWitnessT (ppTwidHat True) spp
+      pcrtInv' <- withWitnessT pCRTInv' sp
+      return $
+        (Id (dim pp'dftInv') @* pcrtInv') .* pptwidInv .*
+        (pp'dftInv' @* Id (dim pcrtInv'))
+
+butterfly :: (Additive r) => Trans r
+butterfly = trans 2 $ \arr ->
+            fromFunction (extent arr) $
+                             \(sh:.j) -> case j of
+                                           0 -> arr ! (sh:.0) +
+                                                arr ! (sh:.1)
+                                           1 -> arr ! (sh:.0) -
+                                                arr ! (sh:.1)
+
+-- DFT_p, CRT_p, scaled DFT_p^{ -1 } and CRT_p^{ -1 }
+pDFT, pDFTInv', pCRT, pCRTInv' ::
+  forall mon p r . (Prime p, CRTrans mon r, Unbox r, Elt r)
+  => TaggedT p mon (Trans r)
+
+{-# INLINABLE pDFT #-}
+{-# INLINABLE pDFTInv' #-}
+{-# INLINABLE pCRT #-}
+{-# INLINABLE pCRTInv' #-}
+
+pDFT = let pval = proxy valuePrime (Proxy::Proxy p)
+       in if pval == 2
+          then return butterfly
+          else do (omegaPPow, _) <- crtInfo
+                  return $ trans pval $ mulMat $ force $
+                         fromFunction (Z :. pval :. pval)
+                                          (\(Z:.i:.j) -> omegaPPow (i*j))
+
+pDFTInv' = let pval = proxy valuePrime (Proxy::Proxy p)
+           in if pval == 2
+              then return butterfly
+              else do (omegaPPow, _) <- crtInfo
+                      return $ trans pval $ mulMat $ force $
+                             fromFunction (Z :. pval :. pval)
+                                              (\(Z:.i:.j) -> omegaPPow (-i*j))
+
+pCRT = let pval = proxy valuePrime (Proxy::Proxy p)
+       in if pval == 2
+          then return $ Id 1
+          else do (omegaPPow, _) <- crtInfo
+                  return $ trans (pval-1) $ mulMat $ force $
+                         fromFunction (Z :. pval-1 :. pval-1)
+                                          (\(Z:.i:.j) -> omegaPPow ((i+1)*j))
+
+-- crt_p * this = \hat{p}*I, for all prime p.
+pCRTInv' =
+  let pval = proxy valuePrime (Proxy::Proxy p)
+  in if pval == 2 then return $ Id 1
+     else do
+       (omegaPPow, _) <- crtInfo
+       return $ trans (pval-1) $  mulMat $ force $
+              fromFunction (Z :. pval-1 :. pval-1)
+                               (\(Z:.i:.j) -> omegaPPow (negate i*(j+1)) -
+                                              omegaPPow (j+1))
+
+-- twiddle factors for DFT_pp and CRT_pp decompositions
+ppTwid, ppTwidHat ::
+  forall mon pp r . (PPow pp, CRTrans mon r, Unbox r)
+  => Bool -> TaggedT pp mon (Trans r)
+
+{-# INLINABLE ppTwid #-}
+{-# INLINABLE ppTwidHat #-}
+
+ppTwid inv =
+  let pp@(p,e) = proxy ppPPow (Proxy :: Proxy pp)
+      ppval = valuePP pp
+  in do
+    (omegaPPPow, _) <- crtInfo
+    return $ trans ppval $ mulDiag $ force $
+                           fromFunction (Z :. ppval)
+                           (\(Z:.i) -> let (iq,ir) = i `divMod` p
+                                           pow = (if inv then negate else id)
+                                                 ir * digitRev (p,e-1) iq
+                                       in omegaPPPow pow)
+
+ppTwidHat inv =
+  let pp@(p,e) = proxy ppPPow (Proxy :: Proxy pp)
+      pptot = totientPP pp
+  in do
+    (omegaPPPow, _) <- crtInfo
+    return $ trans pptot $ mulDiag $ force $
+                           fromFunction (Z :. pptot)
+                           (\(Z:.i) -> let (iq,ir) = i `divMod` (p-1)
+                                           pow = (if inv then negate else id)
+                                                 (ir+1) * digitRev (p,e-1) iq
+                                       in omegaPPPow pow)
diff --git a/Crypto/Lol/Cyclotomic/Tensor/Repa/Dec.hs b/Crypto/Lol/Cyclotomic/Tensor/Repa/Dec.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Lol/Cyclotomic/Tensor/Repa/Dec.hs
@@ -0,0 +1,87 @@
+{-|
+Module      : Crypto.Lol.Cyclotomic.Tensor.Repa.Dec
+Description : Linear transforms and operations related to the decoding basis.
+Copyright   : (c) Eric Crockett, 2011-2017
+                  Chris Peikert, 2011-2017
+License     : GPL-2
+Maintainer  : ecrockett0@email.com
+Stability   : experimental
+Portability : POSIX
+
+Linear transforms and operations related to the decoding basis.
+-}
+
+{-# LANGUAGE ConstraintKinds     #-}
+{-# LANGUAGE FlexibleContexts    #-}
+{-# LANGUAGE RebindableSyntax    #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Crypto.Lol.Cyclotomic.Tensor.Repa.Dec
+( tGaussianDec', gSqNormDec' ) where
+
+import Crypto.Lol.Cyclotomic.Tensor.Repa.RTCommon as R
+import Crypto.Lol.GaussRandom
+import Crypto.Lol.Prelude
+
+import Control.Monad.Random
+
+-- | Given @v=r^2@, yields the decoding-basis coefficients of a sample
+-- from the tweaked Gaussian @t_m \cdot D_r@.
+tGaussianDec' :: forall m v r rnd .
+                 (Fact m, OrdFloat r, Random r, Unbox r, Elt r,
+                  ToRational v, MonadRandom rnd)
+                 => v -> rnd (Arr m r)
+tGaussianDec' =
+  let pm = Proxy::Proxy m
+      m = proxy valueFact pm
+      n = proxy totientFact pm
+      rad = proxy radicalFact pm
+  in \v -> do             -- rnd monad
+    x <- realGaussians (v * fromIntegral (m `div` rad)) n
+    let arr = Arr $ fromUnboxed (Z:.n) x
+    return $ fE arr
+
+-- | The @E_m@ transformation for an arbitrary @m@.
+fE :: (Fact m, Transcendental r, Unbox r, Elt r) => Arr m r -> Arr m r
+fE = eval $ fTensor $ ppTensor pE
+
+-- | The @E_p@ transformation for a prime @p@.
+pE :: forall p r . (Prime p, Transcendental r, Unbox r, Elt r)
+      => Tagged p (Trans r)
+pE = let pval = proxy valuePrime (Proxy::Proxy p)
+     in tag $ if pval==2 then Id 1
+              else trans (pval-1) $ mulMat $ force $
+                   fromFunction (Z :. pval-1 :. pval-1)
+              (\(Z:.i:.j) ->
+               -- sqrt(2)*[ cos(2pi*i*(j+1)/p) | sin(same) ]
+               -- (signs of columns doesn't matter for our purposes.)
+               let theta = 2 * pi * fromIntegral (i*(j+1)) /
+                           fromIntegral pval
+               in sqrt 2 * if j < pval `div` 2
+                           then cos theta else sin theta)
+
+-- | Given coefficient tensor @e@ with respect to the decoding basis
+-- of @R@, yield the (scaled) squared norm of @g_m \cdot e@ under
+-- the canonical embedding, namely,
+--  @\hat{m}^{ -1 } \cdot || \sigma(g_m -- \cdot e) ||^2@ .
+gSqNormDec' :: (Fact m, Ring r, Unbox r, Elt r) => Arr m r -> r
+gSqNormDec' e@(Arr ae) = let (Arr ae') = fGramDec' e
+                         -- use sumAllP (define it in RTCommon)?
+                         in sumAllS $ force $ R.zipWith (*) ae ae'
+
+-- | Multiply by @\hat{m}@ times the Gram matrix of decoding basis of
+-- @R^vee@.
+fGramDec' :: (Fact m, Ring r, Unbox r, Elt r) => Arr m r -> Arr m r
+fGramDec' = eval $ fTensor $ ppTensor pGramDec
+
+-- | Multiply by (scaled) Gram matrix of decoding basis: (I_{p-1} + all-1s).
+pGramDec :: forall p r . (Prime p, Ring r, Unbox r, Elt r) => Tagged p (Trans r)
+pGramDec =
+  let pval = proxy valuePrime (Proxy::Proxy p)
+  in tag $ if pval==2 then Id 1
+           else trans (pval-1) $
+                    \arr -> let sums = sumS arr
+                            in fromFunction (extent arr)
+                                   (\sh@(sh' :. _) -> arr ! sh + sums ! sh')
+
+
diff --git a/Crypto/Lol/Cyclotomic/Tensor/Repa/Extension.hs b/Crypto/Lol/Cyclotomic/Tensor/Repa/Extension.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Lol/Cyclotomic/Tensor/Repa/Extension.hs
@@ -0,0 +1,211 @@
+{-|
+Module      : Crypto.Lol.Cyclotomic.Tensor.Repa.Extension
+Description : RT-specific functions for embedding/twacing in various bases.
+Copyright   : (c) Eric Crockett, 2011-2017
+                  Chris Peikert, 2011-2017
+License     : GPL-2
+Maintainer  : ecrockett0@email.com
+Stability   : experimental
+Portability : POSIX
+
+RT-specific functions for embedding/twacing in various bases.
+-}
+
+{-# LANGUAGE BangPatterns          #-}
+{-# LANGUAGE ConstraintKinds       #-}
+{-# LANGUAGE DataKinds             #-}
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE NoImplicitPrelude     #-}
+{-# LANGUAGE PolyKinds             #-}
+{-# LANGUAGE ScopedTypeVariables   #-}
+{-# LANGUAGE TemplateHaskell       #-}
+{-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE TypeOperators         #-}
+
+module Crypto.Lol.Cyclotomic.Tensor.Repa.Extension
+( twacePowDec', twaceCRT', embedPow', embedDec', embedCRT'
+, coeffs', powBasisPow', crtSetDec'
+) where
+
+import           Crypto.Lol.CRTrans
+import qualified Crypto.Lol.Cyclotomic.Tensor                     as T
+import           Crypto.Lol.Cyclotomic.Tensor.Repa.CRT
+import           Crypto.Lol.Cyclotomic.Tensor.Repa.RTCommon as RT
+import           Crypto.Lol.Prelude                               as LP
+
+import Crypto.Lol.Types.FiniteField
+import Crypto.Lol.Types.ZmStar
+
+import Control.Applicative
+import Control.Arrow       (first, second)
+
+import           Data.Coerce
+import           Data.Default
+import           Data.Maybe
+import           Data.Reflection              (reify)
+import qualified Data.Vector                  as V
+import qualified Data.Vector.Unboxed          as U
+import           Data.Vector.Unboxed.Deriving
+
+-- Default instances
+instance Default Z where def = Z
+instance (Default a, Default b) => Default (a:.b) where def = def:.def
+
+-- derived Unbox instances
+derivingUnbox "DIM1"
+  [t| (Z:.Int) -> Int |]
+  [| \(Z:.i) -> i |]
+  [| (Z :.) |]
+
+-- | The "tweaked trace" function in either the powerful or decoding
+-- basis of the m'th cyclotomic ring to the mth cyclotomic ring when
+-- @m | m'@.
+twacePowDec' :: forall m m' r . (m `Divides` m', Unbox r)
+                 => Arr m' r -> Arr m r
+twacePowDec'
+  = let indices = proxy extIndicesPowDec (Proxy::Proxy '(m, m'))
+    in coerce $ \ !arr -> force $ backpermute (extent indices) (indices !) arr
+
+-- | The "tweaked trace" function in the CRT
+-- basis of the m'th cyclotomic ring to the mth cyclotomic ring when
+-- @m | m'@.
+twaceCRT' :: forall mon m m' r .
+             (m `Divides` m', CRTrans mon r, Unbox r, Elt r)
+             => mon (Arr m' r -> Arr m r)
+twaceCRT' = do
+  g' :: Arr m' r <- gCRT
+  gInv <- gInvCRT
+  embed :: Arr m r -> Arr m' r <- embedCRT'
+  (_, m'hatinv) <- proxyT crtInfo (Proxy::Proxy m')
+  let hatRatioInv = m'hatinv * fromIntegral (proxy valueHatFact (Proxy::Proxy m))
+      -- tweak = mhat * g' / (m'hat * g)
+      tweak = (coerce $ \x -> force . RT.map (* hatRatioInv) . RT.zipWith (*) x) (embed gInv) g' :: Arr m' r
+      indices = proxy extIndicesCRT (Proxy::Proxy '(m, m'))
+  return $
+    -- take true trace after mul-by-tweak
+    coerce (\ !arr -> sumS . backpermute (extent indices) (indices !) . RT.zipWith (*) arr) tweak
+
+embedPow', embedDec' :: forall m m' r .
+             (m `Divides` m', Unbox r, Additive r)
+             => Arr m r -> Arr m' r
+-- | Embeds an array in the powerful basis of the the mth cyclotomic ring
+-- to an array in the powerful basis of the m'th cyclotomic ring when @m | m'@
+embedPow'
+  = let indices = proxy baseIndicesPow (Proxy::Proxy '(m, m'))
+    in coerce $ \ !arr -> force $ fromFunction (extent indices)
+                       (\idx -> let (j0,j1) = (indices ! idx)
+                                in if j0 == 0 then arr ! j1 else zero)
+-- | Embeds an array in the decoding basis of the the mth cyclotomic ring
+-- to an array in the decoding basis of the m'th cyclotomic ring when @m | m'@
+embedDec'
+  = let indices = proxy baseIndicesDec (Proxy::Proxy '(m, m'))
+    in coerce $ \ !arr -> force $
+                       fromFunction (extent indices)
+                         (\idx -> maybe zero
+                                  (\(sh,b) -> if b then negate (arr ! sh)
+                                              else arr ! sh)
+                                  (indices ! idx))
+
+-- | Embeds an array in the CRT basis of the the mth cyclotomic ring
+-- to an array in the CRT basis of the m'th cyclotomic ring when @m | m'@
+embedCRT' :: forall mon m m' r . (m `Divides` m', CRTrans mon r, Unbox r)
+             => mon (Arr m r -> Arr m' r)
+embedCRT' = do
+  -- first check existence of CRT transform of index m'
+  _ <- proxyT crtInfo (Proxy::Proxy m') :: mon (CRTInfo r)
+  let idxs = proxy baseIndicesCRT (Proxy::Proxy '(m,m'))
+  return $ coerce $ \ !arr -> (force $ backpermute (extent idxs) (idxs !) arr)
+
+-- | maps an array in the powerful/decoding basis, representing an
+-- O_m' element, to an array of arrays representing O_m elements in
+-- the same type of basis
+coeffs' :: forall m m' r . (m `Divides` m', Unbox r)
+             => Arr m' r -> [Arr m r]
+coeffs' =
+  let indices = proxy extIndicesCoeffs (Proxy::Proxy '(m, m'))
+  in coerce $ \ !arr -> V.toList $
+  V.map (\idxs -> force $ backpermute (extent idxs) (idxs !) arr) indices
+
+-- | The powerful extension basis, wrt the powerful basis.
+-- Outputs a list of arrays in O_m' that are an O_m basis for O_m'
+powBasisPow' :: forall m m' r . (m `Divides` m', Ring r, Unbox r)
+                => Tagged m [Arr m' r]
+powBasisPow' = return $
+  let (_, phi, phi', _) = proxy T.indexInfo (Proxy::Proxy '(m,m'))
+      idxs = proxy T.baseIndicesPow (Proxy::Proxy '(m,m'))
+  in LP.map (\k -> Arr $ force $ fromFunction (Z :. phi')
+                         (\(Z:.j) -> let (j0,j1) = idxs U.! j
+                                     in if j0==k && j1==0 then one else zero))
+      [0..phi' `div` phi - 1]
+
+-- | A list of arrays representing the mod-p CRT set of the
+-- extension O_m'/O_m
+crtSetDec' :: forall m m' fp .
+              (m `Divides` m', PrimeField fp, Coprime (PToF (CharOf fp)) m',
+               Unbox fp)
+              => Tagged m [Arr m' fp]
+crtSetDec' = return $
+  let m'p = Proxy :: Proxy m'
+      p = proxy valuePrime (Proxy::Proxy (CharOf fp))
+      phi = proxy totientFact m'p
+
+      d = proxy (order p) m'p
+      h :: Int = proxy valueHatFact m'p
+      hinv = recip $ fromIntegral h
+  in reify d $ \(_::Proxy d) ->
+       let twCRTs' :: T.Kron (GF fp d)
+             = fromMaybe (error "internal error: crtSetDec': twCRTs") $ proxyT T.twCRTs m'p
+           zmsToIdx = proxy T.zmsToIndexFact m'p
+           elt j i = T.indexK twCRTs' j (zmsToIdx i)
+           trace' = trace :: GF fp d -> fp
+           cosets = proxy (partitionCosets p) (Proxy::Proxy '(m,m'))
+       in LP.map (\is -> Arr $ force $ fromFunction (Z :. phi)
+                          (\(Z:.j) -> hinv * trace'
+                                      (sum $ LP.map (elt j) is))) cosets
+
+
+-- convert memoized reindexing Vectors to Arrays, for convenience and speed
+
+extIndicesPowDec :: forall m m' . (m `Divides` m')
+                    => Tagged '(m, m') (Array U DIM1 DIM1)
+extIndicesPowDec = do
+  idxs <- T.extIndicesPowDec
+  return $ fromUnboxed (Z :. U.length idxs) $ U.map (Z:.) idxs
+
+extIndicesCRT :: forall m m' . (m `Divides` m')
+                 => Tagged '(m, m') (Array U DIM2 DIM1)
+extIndicesCRT =
+  let phi = proxy totientFact (Proxy::Proxy m)
+      phi' = proxy totientFact (Proxy::Proxy m')
+  in do
+    idxs <- T.extIndicesCRT
+    return $ fromUnboxed (Z :. phi :. phi' `div` phi) $ U.map (Z:.) idxs
+
+baseIndicesPow :: forall m m' . (m `Divides` m')
+                  => Tagged '(m, m') (Array U DIM1 (Int,DIM1))
+
+baseIndicesDec :: forall m m' . (m `Divides` m')
+                  => Tagged '(m, m') (Array U DIM1 (Maybe (DIM1, Bool)))
+
+baseIndicesCRT :: forall m m' . (m `Divides` m')
+                  => Tagged '(m, m') (Array U DIM1 DIM1)
+
+baseIndicesPow = do
+  idxs <- T.baseIndicesPow
+  return $ fromUnboxed (Z :. U.length idxs) $ U.map (second (Z:.)) idxs
+
+baseIndicesDec = do
+  idxs <- T.baseIndicesDec
+  return $ fromUnboxed (Z :. U.length idxs) $ U.map (liftA (first (Z:.))) idxs
+
+baseIndicesCRT = do
+  idxs <- T.baseIndicesCRT
+  return $ fromUnboxed (Z :. U.length idxs) $ U.map (Z:.) idxs
+
+extIndicesCoeffs :: forall m m' . (m `Divides` m')
+                    => Tagged '(m, m') (V.Vector (Array U DIM1 DIM1))
+extIndicesCoeffs =
+  V.map (\arr -> fromUnboxed (Z :. U.length arr) $
+                 U.map (Z:.) arr) <$> T.extIndicesCoeffs
diff --git a/Crypto/Lol/Cyclotomic/Tensor/Repa/GL.hs b/Crypto/Lol/Cyclotomic/Tensor/Repa/GL.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Lol/Cyclotomic/Tensor/Repa/GL.hs
@@ -0,0 +1,151 @@
+{-|
+Module      : Crypto.Lol.Cyclotomic.Tensor.Repa.GL
+Description : The @G@ and @L@ transforms for Repa arrays.
+Copyright   : (c) Eric Crockett, 2011-2017
+                  Chris Peikert, 2011-2017
+License     : GPL-2
+Maintainer  : ecrockett0@email.com
+Stability   : experimental
+Portability : POSIX
+
+The @G@ and @L@ transforms for Repa arrays.
+-}
+
+{-# LANGUAGE BangPatterns          #-}
+{-# LANGUAGE ConstraintKinds       #-}
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE GADTs                 #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE RankNTypes            #-}
+{-# LANGUAGE RebindableSyntax      #-}
+{-# LANGUAGE ScopedTypeVariables   #-}
+
+module Crypto.Lol.Cyclotomic.Tensor.Repa.GL
+( fL, fLInv, fGPow, fGDec, fGInvPow, fGInvDec
+) where
+
+import Crypto.Lol.Cyclotomic.Tensor.Repa.RTCommon as RT
+import Crypto.Lol.Prelude                               as LP
+import Data.Coerce
+
+fLInv, fGPow :: (Fact m, Additive r, Unbox r)
+  => Arr m r -> Arr m r
+fL, fGDec :: (Fact m, Additive r, Unbox r, Elt r)
+  => Arr m r -> Arr m r
+{-# INLINABLE fL #-}
+{-# INLINABLE fLInv #-}
+{-# INLINABLE fGPow #-}
+{-# INLINABLE fGDec #-}
+
+fGInvPow, fGInvDec ::
+ (Fact m, IntegralDomain r, ZeroTestable r, Unbox r, Elt r)
+  => Arr m r -> Maybe (Arr m r)
+{-# INLINABLE fGInvPow #-}
+{-# INLINABLE fGInvDec #-}
+
+-- | Arbitrary-index @L@ transform, which converts from decoding-basis
+-- to powerful-basis representation.
+fL = eval $ fTensor $ ppTensor pL
+-- | Arbitrary-index @L^{ -1 }@ transform, which converts from
+-- powerful-basis to decoding-basis representation.
+fLInv = eval $ fTensor $ ppTensor pLInv
+-- | Arbitrary-index multiplication by @g_m@ in the powerful basis.
+fGPow = eval $ fTensor $ ppTensor pGPow
+-- | Arbitrary-index multiplication by @g_m@ in the decoding basis.
+fGDec = eval $ fTensor $ ppTensor pGDec
+-- | Arbitrary-index division by @g_m@ in the powerful
+-- basis. Outputs 'Nothing' if the input is not evenly divisible by
+-- @g_m@.  Warning: not constant time!
+fGInvPow = wrapGInv' pGInvPow'
+-- | Arbitrary-index division by @g_m@ in the decoding
+-- basis. Outputs 'Nothing' if the input is no evenly divisible by
+-- @g_m@.  Warning: not constant time!
+fGInvDec = wrapGInv' pGInvDec'
+
+wrapGInv' :: forall m r .
+  (Fact m, IntegralDomain r, ZeroTestable r, Unbox r)
+  => (forall p . Prime p => Tagged p (Trans r))
+  -> Arr m r -> Maybe (Arr m r)
+wrapGInv' ginv =
+  let fGInv = eval $ fTensor $ ppTensor ginv
+      oddrad = fromIntegral $ proxy oddRadicalFact (Proxy::Proxy m)
+  in (`divCheck` oddrad) . fGInv
+{-# INLINABLE wrapGInv' #-}
+
+-- | This is not a constant-time algorithm!  Depending on its usage,
+-- it might provide a timing side-channel.
+divCheck :: (IntegralDomain r, ZeroTestable r, Unbox r)
+            => Arr m r -> r -> Maybe (Arr m r)
+divCheck = coerce $  \ !arr den ->
+  let qrs = force $ RT.map (`divMod` den) arr
+      pass = foldAllS (&&) True $ RT.map (isZero . snd) qrs
+      out = force $ RT.map fst qrs
+  in if pass then Just out else Nothing
+{-# INLINABLE divCheck #-}
+
+pWrap :: forall p r . Prime p
+         => (forall rep . Source rep r => Int -> Array rep DIM2 r -> Array D DIM2 r)
+         -> Tagged p (Trans r)
+pWrap f = let pval = proxy valuePrime (Proxy::Proxy p)
+              -- special case: return identity function for p=2
+          in return $ if pval > 2
+                      then trans  (pval-1) $ f pval
+                      else Id 1
+{-# INLINABLE pWrap #-}
+
+
+pLInv, pGPow :: (Prime p, Additive r) => Tagged p (Trans r)
+pL, pGDec :: (Prime p, Additive r, Elt r, Unbox r) => Tagged p (Trans r)
+pGInvPow', pGInvDec' :: (Prime p, Ring r, Unbox r, Elt r)
+  => Tagged p (Trans r)
+{-# INLINABLE pL #-}
+{-# INLINABLE pLInv #-}
+{-# INLINABLE pGPow #-}
+{-# INLINABLE pGDec #-}
+{-# INLINABLE pGInvPow' #-}
+{-# INLINABLE pGInvDec' #-}
+
+pL = pWrap (\_ !arr ->
+             fromFunction (extent arr) $
+             \ (i':.i) -> sumAllS $ extract (Z:.0) (Z:.(i+1)) $ slice arr (i':.All))
+
+pLInv = pWrap (\_ !arr ->
+                let f (i' :. 0) = arr! (i' :. 0)
+                    f (i' :. i) = arr! (i' :. i) - arr! (i' :. i-1)
+                in fromFunction (extent arr) f)
+
+
+-- multiplicaton by g_p=1-zeta_p in power basis.
+-- this is "wrong" for p=2 but we never use that case thanks to pWrap.
+pGPow = pWrap (\p !arr ->
+                let f (i':.0) = arr! (i':.p-2) + arr! (i':.0)
+                    f (i':.i) = arr! (i':.p-2) + arr! (i':.i) - arr! (i':.i-1)
+                in fromFunction (extent arr) f)
+
+-- multiplication by g_p=1-zeta_p in decoding basis
+pGDec = pWrap (\_ !arr ->
+                let f (i':.0) = arr! (i':.0) + sumAllS (slice arr (i':.All))
+                    f (i':.i) = arr! (i':.i) - arr! (i':.i-1)
+                in fromFunction (extent arr) f)
+
+-- CJP: profiling suggests that this does two read passes through the
+-- array; see if we can rewrite to make it one
+
+-- doesn't do division by (odd) p
+pGInvPow' =
+  pWrap (\p !arr ->
+          let f (i':.i) =
+                let col = slice arr (i':.All)
+                in fromIntegral (p-i-1) * sumAllS (extract (Z:.0) (Z:.i+1) col) +
+                   fromIntegral (-i-1) * sumAllS (extract (Z:.i+1) (Z:.p-i-2) col)
+          in fromFunction (extent arr) f)
+
+-- doesn't do division by (odd) p
+pGInvDec' =
+  pWrap (\p !arr ->
+          let f (i':.i) =
+                let col = slice arr (i':.All)
+                    nats = fromFunction (Z:.p-1) (\(Z:.j) -> fromIntegral j+1)
+                in (sumAllS $ RT.zipWith (*) col nats) -
+                   fromIntegral p * sumAllS (extract (Z:.i+1) (Z:.p-i-2) col)
+          in fromFunction (extent arr) f)
diff --git a/Crypto/Lol/Cyclotomic/Tensor/Repa/Instances.hs b/Crypto/Lol/Cyclotomic/Tensor/Repa/Instances.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Lol/Cyclotomic/Tensor/Repa/Instances.hs
@@ -0,0 +1,134 @@
+{-|
+Module      : Crypto.Lol.Cyclotomic.Tensor.Repa.Instances
+Description : RT-specific instances.
+Copyright   : (c) Eric Crockett, 2011-2017
+                  Chris Peikert, 2011-2017
+License     : GPL-2
+Maintainer  : ecrockett0@email.com
+Stability   : experimental
+Portability : POSIX
+
+RT-specific instances.
+-}
+
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE PolyKinds                  #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
+{-# LANGUAGE StandaloneDeriving         #-}
+{-# LANGUAGE TemplateHaskell            #-}
+{-# LANGUAGE TypeFamilies               #-}
+
+module Crypto.Lol.Cyclotomic.Tensor.Repa.Instances where
+
+-- EAC: Do not import Crypto.Lol.Types, because it exports an IrreduciblePoly
+-- instance which screw with GHC. Probably #10338.
+import Crypto.Lol.Types.Unsafe.Complex
+import Crypto.Lol.Types.Unsafe.RRq
+import Crypto.Lol.Types.Unsafe.ZqBasic
+
+import Data.Array.Repa.Eval     as R
+import qualified Number.Complex as C hiding (exp, signum)
+
+import qualified Data.Vector.Generic         as G
+import qualified Data.Vector.Generic.Mutable as M
+import qualified Data.Vector.Unboxed         as U
+import Data.Vector.Unboxed.Deriving
+
+
+
+
+instance (R.Elt a) => R.Elt (Complex a) where
+    touch (Complex c) = do
+        touch $ C.real c
+        touch $ C.imag c
+    zero = Complex $ R.zero C.+: R.zero
+    one = Complex $ R.one C.+: R.zero
+
+derivingUnbox "Complex"
+  [t| forall a . (U.Unbox a) => Complex a -> (a, a) |]
+  [| \ (Complex x) -> (C.real x, C.imag x) |]
+  [| \ (r, i) -> Complex $ r C.+: i |]
+
+
+
+
+
+
+deriving instance (Elt r) => Elt (RRq q r)
+
+-- CJP: restored manual Unbox instances, until we have a better way
+-- (NewtypeDeriving or TH)
+
+newtype instance U.MVector s (RRq q r) = MV_RRq (U.MVector s r)
+newtype instance U.Vector (RRq q r) = V_RRq (U.Vector r)
+
+-- Unbox, when underlying representation is
+instance U.Unbox r => U.Unbox (RRq q r)
+
+{- purloined and tweaked from code in `vector` package that defines
+types as unboxed -}
+instance U.Unbox r => M.MVector U.MVector (RRq q r) where
+  basicLength (MV_RRq v) = M.basicLength v
+  basicUnsafeSlice z n (MV_RRq v) = MV_RRq $ M.basicUnsafeSlice z n v
+  basicOverlaps (MV_RRq v1) (MV_RRq v2) = M.basicOverlaps v1 v2
+  basicInitialize (MV_RRq v) = M.basicInitialize v
+  basicUnsafeNew n = MV_RRq <$> M.basicUnsafeNew n
+  basicUnsafeReplicate n (RRq' x) = MV_RRq <$> M.basicUnsafeReplicate n x
+  basicUnsafeRead (MV_RRq v) z = RRq' <$> M.basicUnsafeRead v z
+  basicUnsafeWrite (MV_RRq v) z (RRq' x) = M.basicUnsafeWrite v z x
+  basicClear (MV_RRq v) = M.basicClear v
+  basicSet (MV_RRq v) (RRq' x) = M.basicSet v x
+  basicUnsafeCopy (MV_RRq v1) (MV_RRq v2) = M.basicUnsafeCopy v1 v2
+  basicUnsafeMove (MV_RRq v1) (MV_RRq v2) = M.basicUnsafeMove v1 v2
+  basicUnsafeGrow (MV_RRq v) n = MV_RRq <$> M.basicUnsafeGrow v n
+
+instance U.Unbox r => G.Vector U.Vector (RRq q r) where
+  basicUnsafeFreeze (MV_RRq v) = V_RRq <$> G.basicUnsafeFreeze v
+  basicUnsafeThaw (V_RRq v) = MV_RRq <$> G.basicUnsafeThaw v
+  basicLength (V_RRq v) = G.basicLength v
+  basicUnsafeSlice z n (V_RRq v) = V_RRq $ G.basicUnsafeSlice z n v
+  basicUnsafeIndexM (V_RRq v) z = RRq' <$> G.basicUnsafeIndexM v z
+  basicUnsafeCopy (MV_RRq mv) (V_RRq v) = G.basicUnsafeCopy mv v
+  elemseq _ = seq
+
+
+
+
+
+deriving instance (Elt i) => Elt (ZqBasic q i)
+
+-- CJP: restored manual Unbox instances, until we have a better way
+-- (NewtypeDeriving or TH)
+
+newtype instance U.MVector s (ZqBasic q z) = MV_ZqBasic (U.MVector s z)
+newtype instance U.Vector (ZqBasic q z) = V_ZqBasic (U.Vector z)
+
+-- Unbox, when underlying representation is
+instance U.Unbox z => U.Unbox (ZqBasic q z)
+
+{- purloined and tweaked from code in `vector` package that defines
+types as unboxed -}
+instance U.Unbox z => M.MVector U.MVector (ZqBasic q z) where
+  basicLength (MV_ZqBasic v) = M.basicLength v
+  basicUnsafeSlice z n (MV_ZqBasic v) = MV_ZqBasic $ M.basicUnsafeSlice z n v
+  basicOverlaps (MV_ZqBasic v1) (MV_ZqBasic v2) = M.basicOverlaps v1 v2
+  basicInitialize (MV_ZqBasic v) = M.basicInitialize v
+  basicUnsafeNew n = MV_ZqBasic <$> M.basicUnsafeNew n
+  basicUnsafeReplicate n (ZqB x) = MV_ZqBasic <$> M.basicUnsafeReplicate n x
+  basicUnsafeRead (MV_ZqBasic v) z = ZqB <$> M.basicUnsafeRead v z
+  basicUnsafeWrite (MV_ZqBasic v) z (ZqB x) = M.basicUnsafeWrite v z x
+  basicClear (MV_ZqBasic v) = M.basicClear v
+  basicSet (MV_ZqBasic v) (ZqB x) = M.basicSet v x
+  basicUnsafeCopy (MV_ZqBasic v1) (MV_ZqBasic v2) = M.basicUnsafeCopy v1 v2
+  basicUnsafeMove (MV_ZqBasic v1) (MV_ZqBasic v2) = M.basicUnsafeMove v1 v2
+  basicUnsafeGrow (MV_ZqBasic v) n = MV_ZqBasic <$> M.basicUnsafeGrow v n
+
+instance U.Unbox z => G.Vector U.Vector (ZqBasic q z) where
+  basicUnsafeFreeze (MV_ZqBasic v) = V_ZqBasic <$> G.basicUnsafeFreeze v
+  basicUnsafeThaw (V_ZqBasic v) = MV_ZqBasic <$> G.basicUnsafeThaw v
+  basicLength (V_ZqBasic v) = G.basicLength v
+  basicUnsafeSlice z n (V_ZqBasic v) = V_ZqBasic $ G.basicUnsafeSlice z n v
+  basicUnsafeIndexM (V_ZqBasic v) z = ZqB <$> G.basicUnsafeIndexM v z
+  basicUnsafeCopy (MV_ZqBasic mv) (V_ZqBasic v) = G.basicUnsafeCopy mv v
+  elemseq _ = seq
diff --git a/Crypto/Lol/Cyclotomic/Tensor/Repa/RTCommon.hs b/Crypto/Lol/Cyclotomic/Tensor/Repa/RTCommon.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Lol/Cyclotomic/Tensor/Repa/RTCommon.hs
@@ -0,0 +1,289 @@
+{-|
+Module      : Crypto.Lol.Cyclotomic.Tensor.Repa.Common
+Description : A simple DSL for tensoring Repa arrays.
+Copyright   : (c) Eric Crockett, 2011-2017
+                  Chris Peikert, 2011-2017
+License     : GPL-2
+Maintainer  : ecrockett0@email.com
+Stability   : experimental
+Portability : POSIX
+
+A simple DSL for tensoring Repa arrays and other common functionality
+on Repa arrays.
+-}
+
+{-# LANGUAGE BangPatterns               #-}
+{-# LANGUAGE ConstraintKinds            #-}
+{-# LANGUAGE DataKinds                  #-}
+{-# LANGUAGE FlexibleContexts           #-}
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE GADTs                      #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE KindSignatures             #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE RankNTypes                 #-}
+{-# LANGUAGE RebindableSyntax           #-}
+{-# LANGUAGE RoleAnnotations            #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
+{-# LANGUAGE TypeOperators              #-}
+
+module Crypto.Lol.Cyclotomic.Tensor.Repa.RTCommon
+( module R
+, module Data.Array.Repa.Eval
+, module Data.Array.Repa.Repr.Unboxed
+, Arr(..), repl, replM, eval, evalM, fTensor, ppTensor
+, Trans(Id), trans, dim, (.*), (@*), force
+, mulMat, mulDiag
+, scalarPow'
+, sumS, sumAllS
+) where
+
+import Crypto.Lol.Prelude as LP hiding ((!!))
+
+import Algebra.Additive     as Additive (C)
+import Algebra.Ring         as Ring (C)
+import Algebra.ZeroTestable as ZeroTestable (C)
+
+import Control.DeepSeq              (NFData (..))
+import Control.Monad.Identity       ()
+import Control.Monad.Random
+import Data.Array.Repa              as R hiding (sumAllP, sumAllS, sumP,
+                                          sumS, (*^), (+^), (-^), (/^))
+import Data.Array.Repa.Eval         hiding (one, zero)
+import Data.Array.Repa.Repr.Unboxed
+import Data.Coerce
+import Data.Singletons
+import Data.Singletons.Prelude      hiding ((:.))
+import Data.Vector.Unboxed          as U (replicate, replicateM)
+
+-- always unboxed (manifest); intermediate calculations can use
+-- delayed arrays
+
+-- | Indexed newtype for 1-dimensional Unbox repa arrays
+newtype Arr (m :: Factored) r = Arr (Array U DIM1 r)
+                              deriving (Eq, Show, NFData)
+
+-- the first argument, though phantom, affects representation
+-- CJP: why must the second arg be nominal?
+-- EAC: From https://ghc.haskell.org/trac/ghc/wiki/Roles#Thesolution:
+--   "The exception to the above algorithm is for classes: all parameters for a class default to a nominal role."
+-- Arr is a synonym for Array, which is an associated data type to the class Source. The parameter `r` above
+-- corresponds to the parameter `e` in the definition of class Source, so it's role must be nominal.
+type role Arr nominal nominal
+
+-- | An 'Arr' filled with the argument.
+repl :: forall m r . (Fact m, Unbox r) => r -> Arr m r
+repl = let n = proxy totientFact (Proxy::Proxy m)
+       in Arr . fromUnboxed (Z:.n) . U.replicate n
+{-# INLINABLE repl #-}
+
+-- | Monadic version of 'repl'.
+replM :: forall m r mon . (Fact m, Unbox r, Monad mon)
+         => mon r -> mon (Arr m r)
+replM = let n = proxy totientFact (Proxy::Proxy m)
+        in fmap (Arr . fromUnboxed (Z:.n)) . U.replicateM n
+{-# INLINABLE replM #-}
+
+instance (Fact m, Additive r, Unbox r) => Additive.C (Arr m r) where
+  zero = repl zero
+  (Arr a) + (Arr b) = Arr $ force $ R.zipWith (+) a b
+  negate (Arr a) = Arr $ force $ R.map negate a
+  {-# INLINABLE zero #-}
+  {-# INLINABLE (+) #-}
+  {-# INLINABLE negate #-}
+
+instance (Fact m, Ring r, Unbox r) => Ring.C (Arr m r) where
+  one = repl one
+  (Arr a) * (Arr b) = Arr $ force $ R.zipWith (*) a b
+  fromInteger = repl . fromInteger
+  {-# INLINABLE one #-}
+  {-# INLINABLE (*) #-}
+  {-# INLINABLE fromInteger #-}
+
+instance (ZeroTestable r, Unbox r, Elt r) => ZeroTestable.C (Arr m r) where
+  -- not using 'zero' to avoid Additive r constraint
+  isZero (Arr a)
+      = isZero $ foldAllS (\ x y -> if isZero x then y else x) (a R.! (Z:.0)) a
+  {-# INLINABLE isZero #-}
+
+
+instance (Unbox r) => NFData (Array U DIM1 r) where
+  -- EAC: Repa doesn't define any NFData instances,
+  -- I'm hoping deepSeqArray is a reasonable approx
+  rnf x = deepSeqArray x ()
+
+instance (Unbox r, Random r, Fact m) => Random (Arr m r) where
+  random = runRand $ replM (liftRand random)
+
+  randomR = error "randomR nonsensical for Arr"
+
+-- | For a factored index, tensors up any function defined for (and
+-- tagged by) any prime power
+fTensor :: forall m r mon . (Fact m, Monad mon)
+  => (forall pp . (PPow pp) => TaggedT pp mon (Trans r))
+  -> TaggedT m mon (Trans r)
+
+fTensor func = tagT $ go $ sUnF (sing :: SFactored m)
+  where
+    go :: Sing (pplist :: [PrimePower]) -> mon (Trans r)
+    go spps = case spps of
+          SNil -> return $ Id 1
+          (SCons spp rest) -> do
+            rest' <- go rest
+            func' <- withWitnessT func spp
+            return $ rest' @* func'
+{-# INLINABLE fTensor #-}
+
+-- | For a prime power p^e, tensors up any function f defined for
+-- (and tagged by) a prime to @I_(p^{e-1}) \otimes f@
+ppTensor :: forall pp r mon . (PPow pp, Monad mon)
+            => (forall p . (Prime p) => TaggedT p mon (Trans r))
+            -> TaggedT pp mon (Trans r)
+
+ppTensor func = tagT $ case (sing :: SPrimePower pp) of
+  pp@(SPP (STuple2 sp _)) -> do
+    func' <- withWitnessT func sp
+    let lts = withWitness valuePPow pp `div` withWitness valuePrime sp
+    return $ Id lts @* func'
+{-# INLINABLE ppTensor #-}
+
+
+-- deeply embedded DSL for transformations and their various
+-- compositions
+
+-- (dim(f), f) where f operates on innermost dimension of array
+data Tensorable r = Tensorable
+  !Int !(forall rep . Source rep r => Array rep DIM2 r -> Array D DIM2 r)
+
+-- transform component: a Tensorable with particular I_l, I_r
+type TransC r = (Tensorable r, Int, Int)
+
+-- full transform: sequence of zero or more components
+-- | a DSL for tensor transforms on Repa arrays
+data Trans r = Id !Int                      -- ^| identity sentinel
+             | TSnoc !(Trans r) !(TransC r) -- ^| (function) composition of transforms
+
+dimC :: TransC r -> Int
+dimC (Tensorable d _, l, r) = l*d*r
+{-# INLINABLE dimC #-}
+
+-- | Returns the (linear) dimension of a transform
+dim :: Trans r -> Int
+dim (Id n) = n
+dim (TSnoc _ f) = dimC f        -- just use dimension of head
+{-# INLINABLE dim #-}
+
+-- | smart constructor from a Tensorable
+trans :: Int -> (forall rep . Source rep r => Array rep DIM2 r -> Array D DIM2 r) -> Trans r
+trans d f = TSnoc (Id d) (Tensorable d f, 1, 1)
+{-# INLINABLE trans #-}
+
+-- | compose transforms
+(.*) :: Trans r -> Trans r -> Trans r
+f .* g | dim f == dim g = f ..* g
+       | otherwise = error $ "(.*): transform dimensions don't match "
+                     LP.++ show (dim f) LP.++ ", " LP.++ show (dim g)
+  where
+    f' ..* (Id _) = f'          -- drop sentinel
+    f' ..* (TSnoc rest g') = TSnoc (f' ..* rest) g'
+{-# INLINABLE (.*) #-}
+
+-- | tensor/Kronecker product (otimes)
+(@*) :: Trans r -> Trans r -> Trans r
+-- merge identity transforms
+(Id n) @* (Id m) = Id (n*m)
+-- Id on left or right
+i@(Id n) @* (TSnoc g' (g, l, r)) = TSnoc (i @* g') (g, n*l, r)
+(TSnoc f' (f, l, r)) @* i@(Id n) = TSnoc (f' @* i) (f, l, r*n)
+-- no Ids: compose
+f @* g = (f @* Id (dim g)) .* (Id (dim f) @* g)
+{-# INLINABLE (@*) #-}
+
+evalC :: (Unbox r) => TransC r -> Array U DIM1 r -> Array U DIM1 r
+evalC (Tensorable d f, _, r) = force . unexpose r . f . expose d r
+{-# INLINABLE evalC #-}
+
+-- | Creates an evaluatable Haskell function from a tensored transform
+eval :: (Unbox r) => Tagged m (Trans r) -> Arr m r -> Arr m r
+eval x = coerce $ eval' $ untag x
+  where eval' (Id _) = id
+        eval' (TSnoc rest f) = eval' rest . evalC f
+{-# INLINABLE eval #-}
+
+-- | Monadic version of 'eval'
+evalM :: (Unbox r, Monad mon) => TaggedT m mon (Trans r) -> mon (Arr m r -> Arr m r)
+evalM = fmap (eval . return) . untagT
+{-# INLINE evalM #-}
+
+-- | maps the innermost dimension to a 2-dim array with innermost dim d,
+-- for performing a I_l \otimes f_d \otimes I_r transformation
+expose :: (Source r1 r)
+          => Int -> Int -> Array r1 DIM1 r -> Array D DIM2 r
+expose !d !r !arr =
+  let (Z :. sz) = extent arr
+      f (Z :. i :. j) = let imodr = i `mod` r
+                        in (Z :. (i-imodr)*d + j*r + imodr)
+  in backpermute (Z :. sz `div` d :. d) f arr
+{-# INLINABLE expose #-}
+
+-- | inverse of expose
+unexpose :: (Source r1 r) => Int -> Array r1 DIM2 r -> Array D DIM1 r
+unexpose !r !arr =
+  let (Z :. sz :. d) = extent arr
+      f (Z :. i) = let (idivr,imodr) = i `divMod` r
+                       (idivrd,j) = idivr `divMod` d
+                   in (Z :. r*idivrd + imodr :. j)
+  in backpermute (Z :. sz*d) f arr
+{-# INLINABLE unexpose #-}
+
+-- | general matrix multiplication along innermost dim of v
+mulMat :: (Source r1 r, Source r2 r, Ring r, Unbox r, Elt r)
+          => Array r1 DIM2 r -> Array r2 DIM2 r -> Array D DIM2 r
+mulMat !m !v
+  = let (Z :. mrows :. mcols) = extent m
+        (sh :. vrows) = extent v
+        f (sh' :. i) = sumAllS $ R.zipWith (*) (slice m (Z:.i:.All)) $ slice v (sh':.All)
+    in if mcols == vrows then fromFunction (sh :. mrows) f
+       else error "mulMatVec: mcols != vdim"
+{-# INLINABLE mulMat #-}
+
+-- | multiplication by a diagonal matrix along innermost dim
+mulDiag :: (Source r1 r, Source r2 r, Ring r)
+           => Array r1 DIM1 r -> Array r2 DIM2 r -> Array D DIM2 r
+mulDiag !diag !arr = fromFunction (extent arr) f
+  where f idx@(_ :. i) = (arr ! idx) * (diag ! (Z:.i))
+{-# INLINABLE mulDiag #-}
+
+-- misc Tensor functions
+
+-- | Embeds a scalar into a powerful-basis representation of a Repa array,
+-- tagged by the cyclotomic index
+scalarPow' :: forall m r . (Fact m, Additive r, Unbox r) => r -> Arr m r
+scalarPow' = coerce . go (proxy totientFact (Proxy::Proxy m))
+  where go n !r = let fct (Z:.0) = r
+                      fct _ = LP.zero
+                  in force $ fromFunction (Z:.n) fct
+{-# INLINABLE scalarPow' #-}
+
+-- | Forces a delayed array to a manifest array.
+force :: (Shape sh, Unbox r) => Array D sh r -> Array U sh r
+force = computeS
+--force = runIdentity . computeP
+{-# INLINABLE force #-}
+
+-- copied implementations of functions we need that normally require
+-- Num
+
+-- | Sum the inner-most dimension of an array sequentially
+sumS :: (Source r a, Elt a, Unbox a, Additive a, Shape sh)
+  => Array r (sh :. Int) a
+  -> Array U sh a
+sumS = foldS (+) LP.zero
+{-# INLINABLE sumS #-}
+
+-- | Sum all array indices to a scalar sequentially
+sumAllS :: (Shape sh, Source r a, Elt a, Unbox a, Additive a)
+  => Array r sh a
+  -> a
+sumAllS = foldAllS (+) LP.zero
+{-# INLINABLE sumAllS #-}
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,339 @@
+             GNU GENERAL PUBLIC LICENSE
+                Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                     Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+             GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                     NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+              END OF TERMS AND CONDITIONS
+
+     How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/README b/README
new file mode 100644
--- /dev/null
+++ b/README
@@ -0,0 +1,6 @@
+
+This package contains a pure Haskell implementation of the 'Tensor' interface for
+Lol, using the highly optimized and parallelizable array library Repa.
+
+You can test this package by running `stack test lol-repa`, and benchmark by
+running `stack bench lol-repa`.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/benchmarks/BenchRepaMain.hs b/benchmarks/BenchRepaMain.hs
new file mode 100644
--- /dev/null
+++ b/benchmarks/BenchRepaMain.hs
@@ -0,0 +1,83 @@
+{-|
+Module      : BenchRepaMain
+Description : Main driver for RT benchmarks.
+Copyright   : (c) Eric Crockett, 2011-2017
+                  Chris Peikert, 2011-2017
+License     : GPL-2
+Maintainer  : ecrockett0@email.com
+Stability   : experimental
+Portability : POSIX
+
+Main driver for RT benchmarks.
+-}
+
+{-# LANGUAGE DataKinds      #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE TypeOperators  #-}
+
+module BenchRepaMain where
+
+import Crypto.Lol.Benchmarks
+import Crypto.Lol.Benchmarks.Standard
+import Crypto.Lol.Cyclotomic.Tensor.Repa
+import Crypto.Lol.Factored
+import qualified Crypto.Lol.Utils.PrettyPrint.Diagnostic as D
+--import qualified Crypto.Lol.Utils.PrettyPrint.Table as T
+import Crypto.Random.DRBG
+
+import Data.Proxy
+
+-- choose which layers of Lol to benchmark
+ls :: [String]
+ls = [
+  "STensor",
+  "Tensor",
+  "SUCyc",
+  "UCyc",
+  "Cyc"
+  ]
+
+-- choose which operations to benchmark
+bs :: [String]
+bs = [
+  {-"unzipPow",
+  "unzipDec",
+  "unzipCRT",
+  "zipWith (*)",
+  "crt",
+  "crtInv",
+  "l",
+  "lInv",
+  "*g Pow",
+  "*g Dec",
+  "*g CRT",
+  "divg Pow",
+  "divg Dec",
+  "divg CRT",
+  "lift",
+  "error",
+  "twacePow",
+  "twaceDec",
+  "twaceCRT",
+  "embedPow",
+  "embedDec",
+  "embedCRT"-}
+  ]
+
+main :: IO ()
+main = diagnosticMain
+{-
+tableMain :: IO ()
+tableMain = do
+  let opts = (T.defaultOpts "UCyc"){T.benches=bs}
+  g1 <- defaultBenches (Proxy::Proxy RT)
+  mapM_ (T.prettyBenches opts) g1
+-}
+diagnosticMain :: IO ()
+diagnosticMain = do
+  let opts = D.defaultOpts{D.levels=ls, D.benches=bs}
+  b1 <- benchGroup "Single Index"
+          [oneIdxBenches (Proxy::Proxy '(F64*F9*F25, Zq 14401)) (Proxy::Proxy RT) (Proxy::Proxy HashDRBG)]
+  b2 <- benchGroup "Twace-Embed"
+          [twoIdxBenches (Proxy::Proxy '(F64*F9*F25, F64*F9*F25, Zq 14401)) (Proxy::Proxy RT)]
+  mapM_ (D.prettyBenches opts) [b1,b2]
diff --git a/lol-repa.cabal b/lol-repa.cabal
new file mode 100644
--- /dev/null
+++ b/lol-repa.cabal
@@ -0,0 +1,122 @@
+name:                lol-repa
+-- The package version.  See the Haskell package versioning policy (PVP)
+-- for standards guiding when and how versions should be incremented.
+-- http://www.haskell.org/haskellwiki/Package_versioning_policy
+-- PVP summary:      +-+------- breaking API changes
+--                   | | +----- non-breaking API additions
+--                   | | | +--- code changes with no API change
+version:             0.0.0.1
+synopsis:            A repa backend for <https://hackage.haskell.org/package/lol Λ ∘ λ>.
+homepage:            https://github.com/cpeikert/Lol
+Bug-Reports:         https://github.com/cpeikert/Lol/issues
+license:             GPL-2
+license-file:        LICENSE
+author:              Eric Crockett <ecrockett0@gmail.com>, Chris Peikert <cpeikert@alum.mit.edu>
+maintainer:          Eric Crockett <ecrockett0@gmail.com>
+copyright:           Eric Crockett, Chris Peikert
+category:            Crypto
+stability:           experimental
+build-type:          Simple
+extra-source-files:  README, CHANGES.md
+cabal-version:       >= 1.10
+description:
+    Λ ∘ λ (Lol) is a general-purpose library for ring-based lattice cryptography.
+    This package provides a pure Haskell implementation of Lol's Tensor interface
+    using the repa library for parallel arrays.
+source-repository head
+  type: git
+  location: https://github.com/cpeikert/Lol
+
+-- For information on compiling C with cabal: http://blog.ezyang.com/2010/06/setting-up-cabal-the-ffi-and-c2hs/
+
+Flag llvm
+  Description:  Compile via LLVM. This produces much better object code,
+                but you need to have the LLVM compiler installed.
+  -- If you enable this and get errors like "Error: can't resolve `.rodata' {.rodata section}"
+  -- then GHC doesn't like your version of LLVM!
+  Default:      False
+
+Flag opt
+  Description: Turn on library optimizations
+  Default:     True
+
+library
+  default-language:   Haskell2010
+  ghc-options: -fwarn-dodgy-imports
+
+  if flag(llvm)
+    ghc-options: -fllvm -optlo-O3
+
+  -- ghc optimizations
+  if flag(opt)
+    -- makes lift much faster!
+    ghc-options: -funfolding-use-threshold1000
+  exposed-modules:
+    Crypto.Lol.Cyclotomic.Tensor.Repa
+
+  other-modules:
+    Crypto.Lol.Cyclotomic.Tensor.Repa.CRT
+    Crypto.Lol.Cyclotomic.Tensor.Repa.Extension
+    Crypto.Lol.Cyclotomic.Tensor.Repa.Dec
+    Crypto.Lol.Cyclotomic.Tensor.Repa.GL
+    Crypto.Lol.Cyclotomic.Tensor.Repa.Instances
+    Crypto.Lol.Cyclotomic.Tensor.Repa.RTCommon
+
+  build-depends:
+    arithmoi >= 0.4.1.3,
+    base >= 4.9 && < 5,
+    bytestring,
+    constraints,
+    containers >= 0.5.6.2,
+    crypto-api,
+    data-default >= 0.3.0,
+    deepseq >= 1.4.1.1,
+    lol >= 0.6.0.0,
+    monadcryptorandom,
+    MonadRandom >= 0.2,
+    mtl >= 2.2.1,
+    numeric-prelude >= 0.4.2,
+    protocol-buffers,
+    protocol-buffers-descriptor,
+    random >= 1.1,
+    reflection >= 1.5.1,
+    repa>=3.4,
+    singletons >= 1.1.2.1,
+    th-desugar >= 1.5.4,
+    tagged-transformer >= 0.7,
+    template-haskell  >=  2.2.0.0,
+    transformers >= 0.4.2.0,
+    vector>=0.11,
+    vector-th-unbox >= 0.2.1.0
+
+  other-extensions: TemplateHaskell
+
+Benchmark bench-lol-repa
+  type:             exitcode-stdio-1.0
+  default-language: Haskell2010
+  main-is:          BenchRepaMain.hs
+  ghc-options:      -main-is BenchRepaMain
+  hs-source-dirs:   benchmarks
+
+  ghc-options: -O2 -funfolding-creation-threshold=15000 -funfolding-use-threshold=1000
+  ghc-options: -fsimpl-tick-factor=110
+
+  build-depends:
+    base >= 4.9 && < 5,
+    DRBG,
+    lol >= 0.6.0.0,
+    lol-benches,
+    lol-repa
+
+test-suite test-lol-repa
+  type:             exitcode-stdio-1.0
+  default-language: Haskell2010
+  main-is:          TestRepaMain.hs
+  ghc-options:      -main-is TestRepaMain
+  hs-source-dirs:   tests
+  ghc-options:      -threaded -O2
+
+  build-depends:
+    base >= 4.9 && < 5,
+    lol-repa,
+    lol-tests
diff --git a/tests/TestRepaMain.hs b/tests/TestRepaMain.hs
new file mode 100644
--- /dev/null
+++ b/tests/TestRepaMain.hs
@@ -0,0 +1,21 @@
+{-|
+Module      : TestRepaMain
+Description : Main driver for RT tests.
+Copyright   : (c) Eric Crockett, 2011-2017
+                  Chris Peikert, 2011-2017
+License     : GPL-2
+Maintainer  : ecrockett0@email.com
+Stability   : experimental
+Portability : POSIX
+
+Main driver for RT tests.
+-}
+
+module TestRepaMain where
+
+import Crypto.Lol.Cyclotomic.Tensor.Repa
+import Crypto.Lol.Tests.Standard
+import Data.Proxy
+
+main :: IO ()
+main = defaultTestMain (Proxy::Proxy RT)
