diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Marco Zocca (c) 2018
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Marco Zocca nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,32 @@
+# record-encode
+
+[![Build Status](https://travis-ci.org/ocramz/record-encode.png)](https://travis-ci.org/ocramz/record-encode)
+
+This library provides generic machinery to encode values of some algebraic type as points in a vector space.
+
+Analyzing datasets that have one or more categorical variables (== values having a sum type) typically requires a series of boilerplate transformations, and the `encodeOneHot` function provided here does precisely that.
+
+# Usage example
+
+```
+    {-# language DeriveGeneric -#}
+
+    import qualified GHC.Generics as G
+    import qualified Generics.SOP as SOP
+    
+    import Data.Record.Encode
+
+    data X = A | B | C deriving (G.Generic)
+    instance SOP.Generic X
+```
+
+```
+    > encodeOneHot B
+    [0,1,0]
+```
+
+
+
+# Acknowledgements
+
+Gagandeep Bhatia (@gagandeepb) for his GSoC '18 work on `Frames-beam`, Mark Karpov (@mrkkrp) for his Template Haskell tutorial, Anthony Cowley (@acowley) for `vinyl` and `Frames`, @mniip on Freenode #haskell for helping me better understand what can be done with generic programming.
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/record-encode.cabal b/record-encode.cabal
new file mode 100644
--- /dev/null
+++ b/record-encode.cabal
@@ -0,0 +1,44 @@
+name:                record-encode
+version:             0.1.0.0
+synopsis:            Generic encoding of records
+description:         Generic encoding of records. Provides a single function and two typeclasses to encode values of sum types as one-hot vectors.
+homepage:            https://github.com/ocramz/record-encode
+license:             BSD3
+license-file:        LICENSE
+author:              Marco Zocca
+maintainer:          ocramz fripost org
+copyright:           2018 Marco Zocca
+category:            Data
+build-type:          Simple
+extra-source-files:  README.md
+cabal-version:       >=1.10
+tested-with:         GHC == 8.4.3
+
+library
+  default-language:    Haskell2010
+  ghc-options:         -Wall 
+  hs-source-dirs:      src
+  exposed-modules:     Data.Record.Encode
+  other-modules:       Data.Record.Encode.Generics
+  build-depends:       base >= 4.7 && < 5
+                     , generics-sop
+                     , vector
+
+
+test-suite spec
+  default-language:    Haskell2010
+  ghc-options:         -Wall
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Spec.hs
+  other-modules:     LibSpec
+  build-depends:       base
+                     , record-encode
+                     , generics-sop
+                     , hspec
+                     , QuickCheck
+                     , vector
+
+source-repository head
+  type:     git
+  location: https://github.com/ocramz/record-encode
diff --git a/src/Data/Record/Encode.hs b/src/Data/Record/Encode.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Record/Encode.hs
@@ -0,0 +1,96 @@
+{-# language FlexibleContexts #-}
+{-# language ScopedTypeVariables #-}
+{-# language DeriveGeneric #-}
+{-# language ConstraintKinds #-}
+
+{-|
+This library provides generic machinery (via GHC.Generics and `generics-sop`) to encode values of some algebraic type as points in a vector space.
+
+Processing datasets that have one or more categorical variables (which in other words are values of a sum type) typically requires a series of boilerplate transformations, and the 'encodeOneHot' function provided here does precisely that.
+
+
+== Internals
+
+This library makes use of generic programming to analyze both values and types (see the internal Data.Record.Encode.Generics module).
+
+Initially, it was relying on Template Haskell to analyze /types/, using the the instance generation machinery explained here: <https://markkarpov.com/tutorial/th.html#example-1-instance-generation>
+
+
+-}
+module Data.Record.Encode (
+  -- * One-hot encoding
+    encodeOneHot
+  , G
+  -- -- ** Typeclasses
+  -- , GVariants(..)
+  ) where
+
+import qualified GHC.Generics as G
+import Generics.SOP hiding (Proxy)
+import Data.Proxy
+
+import qualified Data.Vector as V
+import qualified Data.Vector.Mutable as VM
+
+import Data.Record.Encode.Generics
+
+
+
+data X a = A | B a | C | D | E | F deriving G.Generic
+instance Generic (X a)
+
+-- | Constraints necessary to 'encodeOneHot' a value.
+--
+-- NB: 'GVariants' is an internal typeclass, and this constraint is automatically satisfied if the type is an instance of 'G.Generic'
+type G a = (GVariants (G.Rep a), G.Generic a, Generic a)
+
+-- | Computes the one-hot encoding of a value of a sum type.
+--
+-- The type of the input value must be an instance of 'Generic' (from GHC.Generics) /and/ of 'Generic' (from the `generics-sop` library).
+--
+-- >>> :set -XDeriveGeneric
+--
+-- >>> import qualified GHC.Generics as G
+-- >>> import qualified Generics.SOP as SOP
+-- >>> import Data.Record.Encode
+--
+-- >>> data X = A | B | C deriving (G.Generic)
+-- >>> instance SOP.Generic X
+--
+-- >>> encodeOneHot B
+-- [0,1,0]
+encodeOneHot :: forall a . G a => a -> V.Vector Int
+encodeOneHot x = oneHot len i where
+  len = fromIntegral $ gnconstructors (Proxy :: Proxy a)
+  i = gindex $ from x
+
+-- | Create a one-hot vector
+oneHot :: Num a =>
+          Int  -- ^ Vector length
+       -> Int  -- ^ Index of "1" entry
+       -> V.Vector a
+oneHot n i = V.create $ do
+  vm <- VM.replicate n 0
+  VM.write vm i 1
+  return vm
+
+-- class Encode i d where
+--   -- type ETy d :: *
+--   encode :: d -> V.Vector i
+--   -- type EIx d :: *  
+--   -- encode :: d -> V.Vector (ETy d)
+--   -- encodeSparse :: d -> V.Vector (EIx d, EIx d, ETy d)
+
+-- -- | Some pointwise decision (e.g. maximum a posteriori) from a mixture of labels to a single value
+-- class Decode i d where
+--   decode :: V.Vector i -> d
+  
+  
+
+
+{- |
+
+from A
+  :: (C1 _ U1 :+: (C1 _ U1 +: C1 _ U1)) :+: (C1 _ U1 :+: (C1 _ U1 :+: C1 _ U1))
+
+-}
diff --git a/src/Data/Record/Encode/Generics.hs b/src/Data/Record/Encode/Generics.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Record/Encode/Generics.hs
@@ -0,0 +1,134 @@
+{-# language TypeOperators #-}
+{-# language DeriveGeneric #-}
+-- {-# language GADTs, DataKinds, PolyKinds, FlexibleInstances, FlexibleContexts #-}
+{-# language KindSignatures, FlexibleInstances, FlexibleContexts, ScopedTypeVariables #-}
+module Data.Record.Encode.Generics (
+    gindex
+  , GVariants(..)
+  , gnconstructors
+  ) where
+
+import qualified GHC.Generics as G
+import Data.Proxy
+
+import Generics.SOP hiding (Proxy)
+import Generics.SOP.NS
+
+
+data X a = A | B a | C | D | E | F deriving G.Generic
+instance Generic (X a)
+
+
+
+-- | Compute the structural index of a value of a sum type via its Generic representation
+-- e.g.:
+-- 
+-- >>> data S = Sa | Sb | Sc deriving (Eq, Show, G.Generic)
+-- >>> instance Generic S
+-- 
+-- >>> gindex $ from Sb
+-- 1
+gindex :: SOP f xs -> Int
+gindex = index_SOP
+
+
+-- | Counts the number of outermost constructors ("variants" of a type)
+class GVariants (f :: * -> *) where
+  vars :: proxy f -> Int
+
+instance GVariants (G.M1 G.C m f) where { vars _ = 1 }
+
+instance GVariants G.V1 where { vars _ = 0 }
+
+instance GVariants f => GVariants (G.M1 G.D m f) where { vars _ = vars (Proxy :: Proxy f) }
+
+instance (GVariants f, GVariants g) => GVariants (f G.:+: g) where { vars _ = vars (Proxy :: Proxy f) + vars (Proxy :: Proxy g) }
+
+-- | Counts the number of outermost constructors
+gnconstructors :: forall a . (G.Generic a, GVariants (G.Rep a)) => Proxy a -> Int
+gnconstructors _ = vars (Proxy :: Proxy (G.Rep a))
+
+
+-- [17:21] <mniip> @let class Variants f where { variants :: proxy f -> Int }; instance Variants (M1 C m f) where { variants _ = 1 }; instance Variants V1 where { variants _ = 0 }; instance Variants f => Variants (M1 D m f) where { variants _ = variants (Proxy :: Proxy f) }; instance (Variants f, Variants g) => Variants (f :+: g) where { variants _ = variants (Proxy :: Proxy f) + variants (Proxy :: Proxy g) }
+-- [17:21] <lambdabot>  Defined.
+-- [17:21] <mniip> @let variants' :: forall a. (Generic a, Variants (Rep a)) => Proxy a -> Int; variants' _ = variants (Proxy :: Proxy (Rep a))
+-- [17:21] <lambdabot>  Defined.
+-- [17:21] <mniip> > variants' (Proxy :: Proxy Bool)
+-- [17:21] <lambdabot>  2
+-- [17:22] <mniip> > variants' (Proxy :: Proxy (Maybe (Either Bool Bool)))
+-- [17:22] <lambdabot>  2
+-- [17:22] <mniip> as you can see this only focuses on the outer type constructor
+-- [17:22] <mniip> > variants (Proxy :: Proxy Proxy)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+-- -- | 
+
+-- class GEncode i o where
+--   gencode :: i x -> Maybe (o x)
+
+-- -- λ> :t fmap to . gencode . from
+-- -- 
+-- -- fmap to . gencode . from
+-- --   :: (GEncode (Rep a) (Rep b), Generic b, Generic a) =>
+-- --      a -> Maybe b
+
+
+-- -- instance GEncode (V1 p) where
+-- --   gencode _ = error "Cannot encode V1"
+
+-- -- instance GEncode (U1 p) where
+-- --   gencode U1 = error "Cannot encode U1"
+
+
+
+
+
+
+-- data OneHot = OH !Int !Int deriving (Eq, Show) 
+
+-- class Encode d where
+--   type ETy d :: *
+--   type ETy d = OneHot -- (Int, Int)
+--   -- encode :: d -> V.Vector (ETy d)
+--   encode :: d x -> ETy d
+
+-- -- instance Encode
+
+
+  
+
+
+
+-- instance Encode (V1 p) where
+--   encode _ = error "Cannot encode V1"
+
+-- instance Encode (U1 p) where
+--   encode U1 = error "Cannot encode U1"
+
+-- instance Encode (K1 i c p) where
+
+-- instance Encode (M1 i c f p) where
+
+-- instance Encode ((f :*: g) p) where
+
+-- instance Encode ((f :+: g) p) where  
+  
+
+
+
+
+-- class VG.Vector v x => GEncode' i v x where
+--   gencode' :: i x -> Maybe (v Int)
diff --git a/test/LibSpec.hs b/test/LibSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/LibSpec.hs
@@ -0,0 +1,79 @@
+{-# language DeriveGeneric #-}
+module LibSpec where
+
+import Test.Hspec
+-- import Test.Hspec.QuickCheck
+
+import qualified GHC.Generics as G
+import Generics.SOP 
+
+import qualified Data.Vector as V
+
+import Data.Record.Encode
+
+
+data X = Xa | Xb | Xc deriving (Eq, Show, G.Generic)
+instance Generic X
+
+
+
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec =
+  describe "Data.Record.Encode" $ do
+    it "creates a one-hot encoded vector from a sum type" $ do
+      V.length (encodeOneHot Xb) `shouldBe` 3
+      
+      
+    -- prop "ourAdd is commutative" $ \x y ->
+    --   ourAdd x y `shouldBe` ourAdd y x
+
+
+
+
+-- data P0 = P0 Bool Char deriving (Eq, Show, G.Generic)
+-- -- instance Generic P0
+
+-- deriveCountable ''Bool
+-- deriveCountable ''Char
+-- -- deriveCountable ''Integer
+-- deriveCountable ''P0
+
+
+-- -- λ> hcmap (Proxy :: Proxy Show) (mapIK (const ())) $ from $ P0 42 'z'
+-- -- SOP (Z (K () :* K () :* Nil))
+
+
+-- data Fx = Ax | Bx | Cx deriving (Eq, Show, Enum, G.Generic)
+-- -- instance Generic Fx
+
+-- data Fy a = Ay a | By | Cy deriving (Eq, Show, G.Generic)
+-- -- instance Generic (Fy a)
+
+-- data Gx = Ax' | Bx' | Cx' deriving (Eq, Show, Enum, G.Generic)
+-- -- instance Generic Gx
+
+-- -- | a Product-Of-Sums
+-- data P1 a = P1 Fx (Fy a) deriving (Eq, Show, G.Generic)
+-- -- instance Generic (P1 a)
+
+-- p10 :: P1 Integer
+-- p10 = P1 Ax (Ay 42)
+
+-- -- gp10 :: SOP I '[ '[Fx, Fy Integer] ]
+-- -- gp10 = from p10
+
+-- -- λ> from $ P1 Ax (Ay 42)
+-- -- SOP (Z (I Ax :* I (Ay 42) :* Nil))
+
+
+-- data P2 = P2 Fx Fx deriving (Eq, Show, G.Generic)
+-- -- instance Generic P2
+
+
+
+-- -- data Y = Y (X, X) deriving (Eq, Show, G.Generic)
+-- -- deriveCountable ''Y
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
