diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -16,3 +16,5 @@
     mconcat (parMap rpar hash cs) == hash m
 
 All operations in this package are implemented in a very efficient manner using SSE instructions.
+
+![diagram](https://raw.githubusercontent.com/srijs/hwsl2-core/master/cat.png)
diff --git a/dist/build/testStub/testStub-tmp/testStub.hs b/dist/build/testStub/testStub-tmp/testStub.hs
--- a/dist/build/testStub/testStub-tmp/testStub.hs
+++ b/dist/build/testStub/testStub-tmp/testStub.hs
@@ -1,5 +1,5 @@
 module Main ( main ) where
-import Distribution.Simple.Test.LibV09 ( stubMain )
+import Distribution.Simple.Test ( stubMain )
 import Data.Hash.SL2.Test ( tests )
 main :: IO ()
 main = stubMain tests
diff --git a/hwsl2.cabal b/hwsl2.cabal
--- a/hwsl2.cabal
+++ b/hwsl2.cabal
@@ -4,13 +4,13 @@
 -- The name of the package.
 name:                hwsl2
 
--- The package version.  See the Haskell package versioning policy (PVP) 
+-- 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.3.0.1
+version:             0.3.1.0
 
 -- A short (one-line) description of the package.
 synopsis:            Hashing with SL2
@@ -47,53 +47,59 @@
 -- The package author(s).
 author:              Sam Rijs
 
--- An email address to which users can send suggestions, bug reports, and 
+-- An email address to which users can send suggestions, bug reports, and
 -- patches.
 maintainer:          srijs@airpost.net
 
 -- A copyright notice.
--- copyright:           
+-- copyright:
 
 category:            Data
 
 build-type:          Simple
 
--- Extra files to be distributed with the package, such as examples or a 
+-- Extra files to be distributed with the package, such as examples or a
 -- README.
 extra-source-files:  README.md
+                     src/core/sl2-inl.h src/core/gf2p127-inl.h
 
 -- Constraint on the version of Cabal needed to build this package.
 cabal-version:       >=1.10
 
+flag avx2
+  description:        Enable AVX 2 optimisations.
+  default:            True
 
 library
   -- Modules exported by the library.
   exposed-modules:     Data.Hash.SL2
                        Data.Hash.SL2.Mutable
                        Data.Hash.SL2.Unsafe
-  
+
   -- Modules included in this library but not exported.
   other-modules:       Data.Hash.SL2.Internal
-  
+
   -- LANGUAGE extensions used by modules in this package.
   other-extensions:    ForeignFunctionInterface CApiFFI
                        Safe Trustworthy Unsafe
-  
+
   -- Other library packages from which modules are imported.
   build-depends:       base >=4.7 && <4.8, bytestring >=0.10 && <0.11
-  
+
   -- Directories containing source files.
   hs-source-dirs:      src
 
   ghc-options:         -fwarn-unused-imports
 
   -- Options for foreign source files.
-  include-dirs:        src
-  includes:            src/sl2-inl.h src/gf2p127-inl.h
-  install-includes:    src/sl2-inl.h src/gf2p127-inl.h
-  cc-options:          -msse2 -msse4.1 -mpclmul
-  ghc-options:         -optc-msse2 -optc-msse4.1 -optc-mpclmul
-  
+  include-dirs:        src/core
+  if flag(avx2)
+    cc-options:        -Wall -O3 -mavx2 -msse4.1 -msse2 -mpclmul
+    ghc-options:       -O3 -optc-mavx2 -optc-msse4.1 -msse2 -optc-mpclmul
+  else
+    cc-options:        -Wall -O3 -msse4.1 -msse2 -mpclmul
+    ghc-options:       -O3 -optc-msse4.1 -msse2 -optc-mpclmul
+
   -- Base language which the package is written in.
   default-language:    Haskell2010
 
@@ -107,15 +113,18 @@
                        Data.Hash.SL2.Unsafe
                        Data.Hash.SL2.Internal
   build-depends:       base >=4.7 && <4.8,
-                       Cabal >=1.22.0.0,
+                       Cabal >=1.9.2,
                        bytestring >=0.10 && <0.11,
                        QuickCheck >=2.7 && <2.8,
                        quickcheck-properties>=0.1 && <0.2,
                        cabal-test-quickcheck>=0.1 && <0.2
-  include-dirs:        src
-  includes:            src/sl2-inl.h src/gf2p127-inl.h
-  install-includes:    src/sl2-inl.h src/gf2p127-inl.h
-  ghc-options:         -optc-msse2 -optc-msse4.1 -optc-mpclmul
+  include-dirs:        src/core
+  if flag(avx2)
+    cc-options:        -Wall -O3 -mavx2 -msse4.1 -msse2 -mpclmul
+    ghc-options:       -O3 -optc-mavx2 -optc-msse4.1 -msse2 -optc-mpclmul
+  else
+    cc-options:        -Wall -O3 -msse4.1 -msse2 -mpclmul
+    ghc-options:       -O3 -optc-msse4.1 -msse2 -optc-mpclmul
 
 benchmark bench
   type:                exitcode-stdio-1.0
@@ -126,12 +135,15 @@
                        Data.Hash.SL2.Unsafe
                        Data.Hash.SL2.Internal
   build-depends:       base >=4.7 && <4.8,
-                       Cabal >=1.22.0.0,
+                       Cabal >=1.9.2,
                        bytestring >=0.10 && <0.11,
                        criterion >=1.0 && <1.1,
                        cryptohash >=0.11 && <0.12,
                        parallel >=3.2 && <3.3
-  include-dirs:        src
-  includes:            src/sl2-inl.h src/gf2p127-inl.h
-  install-includes:    src/sl2-inl.h src/gf2p127-inl.h
-  ghc-options:         -rtsopts -threaded -optc-msse2 -optc-msse4.1 -optc-mpclmul
+  include-dirs:        src/core
+  if flag(avx2)
+    cc-options:        -Wall -O3 -mavx2 -msse4.1 -msse2 -mpclmul
+    ghc-options:       -rtsopts -threaded -O3 -optc-mavx2 -optc-msse4.1 -msse2 -optc-mpclmul
+  else
+    cc-options:        -Wall -O3 -msse4.1 -msse2 -mpclmul
+    ghc-options:       -rtsopts -threaded -O3 -optc-msse4.1 -msse2 -optc-mpclmul
diff --git a/src/Data/Hash/SL2.hs b/src/Data/Hash/SL2.hs
--- a/src/Data/Hash/SL2.hs
+++ b/src/Data/Hash/SL2.hs
@@ -43,7 +43,7 @@
   , unpack8, unpack16, unpack32, unpack64
   ) where
 
-import Prelude hiding (concat)
+import Prelude hiding (concat, foldl, foldr)
 
 import Data.Hash.SL2.Internal (Hash)
 import Data.Hash.SL2.Unsafe
@@ -56,7 +56,7 @@
 import Data.Word
 import Data.Monoid
 import Data.Functor
-import Data.Foldable (Foldable)
+import Data.Foldable (Foldable, foldl, foldl', foldr, foldr')
 
 instance Show Hash where
   show h = unsafePerformIO $ unsafeUseAsPtr h Mutable.serialize
@@ -64,6 +64,9 @@
 instance Eq Hash where
   a == b = unsafePerformIO $ unsafeUseAsPtr2 a b Mutable.eq
 
+instance Ord Hash where
+  compare a b = unsafePerformIO $ unsafeUseAsPtr2 a b Mutable.cmp
+
 instance Monoid Hash where
   mempty = unit
   mappend = concat
@@ -77,21 +80,27 @@
 -- A significantly faster equivalent of @((. 'hash') . 'concat')@.
 append :: Hash -> ByteString -> Hash
 append h s = fst $ unsafePerformIO $ Mutable.withCopy h $ Mutable.append s
+{-# RULES "hash/concat" forall h s . concat h (hash s) = append h s #-}
 
 -- | /O(n)/ Prepend the hash of the 'ByteString' to the existing 'Hash'.
 -- A significantly faster equivalent of @('concat' . 'hash')@.
 prepend :: ByteString -> Hash -> Hash
 prepend s h = fst $ unsafePerformIO $ Mutable.withCopy h $ Mutable.prepend s
+{-# RULES "concat/hash" forall s h . concat (hash s) h = prepend s h #-}
 
 -- | /O(n)/ Append the hash of every 'ByteString' to the existing 'Hash', from left to right.
 -- A significantly faster equivalent of @('foldl' 'append')@.
 foldAppend :: Foldable t => Hash -> t ByteString -> Hash
 foldAppend h ss = fst $ unsafePerformIO $ Mutable.withCopy h $ Mutable.foldAppend ss
+{-# RULES "foldl/append" forall h ss . foldl append h ss = foldAppend h ss #-}
+{-# RULES "foldl'/append" forall h ss . foldl' append h ss = foldAppend h ss #-}
 
 -- | /O(n)/ Prepend the hash of every 'ByteString' to the existing 'Hash', from right to left.
 -- A significantly faster equivalent of @('flip' ('foldr' 'prepend'))@.
 foldPrepend :: Foldable t => t ByteString -> Hash -> Hash
 foldPrepend ss h = fst $ unsafePerformIO $ Mutable.withCopy h $ Mutable.foldPrepend ss
+{-# RULES "foldr/prepend" forall ss h . foldr prepend h ss = foldPrepend ss h #-}
+{-# RULES "foldr'/prepend" forall ss h . foldr' prepend h ss = foldPrepend ss h #-}
 
 -- | /O(1)/ The unit element for concatenation. Alias for 'mempty'.
 unit :: Hash
@@ -100,6 +109,7 @@
 -- | /O(1)/ Concatenate two hashes. Alias for 'mappend'.
 concat :: Hash -> Hash -> Hash
 concat a b = fst $ unsafePerformIO $ unsafeWithNew (unsafeUseAsPtr2 a b . Mutable.concat)
+{-# INLINE[1] concat #-}
 
 -- | /O(n)/ Concatenate a list of hashes. Alias for 'mconcat'.
 concatAll :: [Hash] -> Hash
diff --git a/src/Data/Hash/SL2/Benchmark.hs b/src/Data/Hash/SL2/Benchmark.hs
--- a/src/Data/Hash/SL2/Benchmark.hs
+++ b/src/Data/Hash/SL2/Benchmark.hs
@@ -6,6 +6,7 @@
 
 import qualified Data.ByteString as B
 import qualified Crypto.Hash.SHA256 as SHA256
+import qualified Crypto.Hash.SHA512 as SHA512
 
 import Criterion.Main
 
@@ -14,8 +15,9 @@
 bs4M = B.pack $ take (4 * 1024 * 1024) $ cycle [0..255]
 
 main = defaultMain
-  [ bench "hwsl2 append" $ whnf (append union) bs4M
-  , bench "hwsl2 prepend" $ whnf (flip prepend union) bs4M
+  [ bench "hwsl2 append" $ whnf (append unit) bs4M
+  , bench "hwsl2 prepend" $ whnf (flip prepend unit) bs4M
   , bench "hwsl2 append parallel" $ whnf (concatAll . (parMap rpar hash)) [bs1M, bs1M, bs1M, bs1M]
   , bench "sha256" $ whnf SHA256.hash bs4M
+  , bench "sha512" $ whnf SHA512.hash bs4M
   ]
diff --git a/src/Data/Hash/SL2/Internal.hs b/src/Data/Hash/SL2/Internal.hs
--- a/src/Data/Hash/SL2/Internal.hs
+++ b/src/Data/Hash/SL2/Internal.hs
@@ -18,6 +18,9 @@
 foreign import capi "sl2-inl.h sl2_eq"
   eq :: Ptr Hash -> Ptr Hash -> IO CInt
 
+foreign import capi "sl2-inl.h sl2_cmp"
+  cmp :: Ptr Hash -> Ptr Hash -> IO CInt
+
 foreign import capi "sl2-inl.h sl2_unit"
   unit :: Ptr Hash -> IO ()
 
diff --git a/src/Data/Hash/SL2/Mutable.hs b/src/Data/Hash/SL2/Mutable.hs
--- a/src/Data/Hash/SL2/Mutable.hs
+++ b/src/Data/Hash/SL2/Mutable.hs
@@ -2,7 +2,7 @@
 
 module Data.Hash.SL2.Mutable
   ( valid
-  , eq
+  , eq, cmp
   , unit
   , concat
   , append, prepend
@@ -34,6 +34,11 @@
 eq :: Ptr Hash -> Ptr Hash -> IO Bool
 {-# INLINE eq #-}
 eq a b = fmap toBool $ Internal.eq a b
+
+-- | /O(1)/ Compare two hashes.
+cmp :: Ptr Hash -> Ptr Hash -> IO Ordering
+{-# INLINE cmp #-}
+cmp a b = fmap (compare 0) $ Internal.cmp a b
 
 -- | /O(1)/ Set the 'Hash' to the empty value.
 unit :: Ptr Hash -> IO ()
diff --git a/src/Data/Hash/SL2/Test.hs b/src/Data/Hash/SL2/Test.hs
--- a/src/Data/Hash/SL2/Test.hs
+++ b/src/Data/Hash/SL2/Test.hs
@@ -29,7 +29,34 @@
 tests :: IO [Test]
 tests = return
 
-  [ testGroup "packing"
+  [ testGroup "equality"
+
+    [ testProperty "true" $
+        \a -> hash a == hash a
+
+    , testProperty "false" $
+        \a b -> (a /= b) ==> hash a /= hash b
+    ]
+
+  , testGroup "compare"
+
+    [ testProperty "eq" $
+        \a -> compare (hash a) (hash a) == EQ
+
+    , testProperty "gt" $
+        \a b c -> (a > b && b > c) ==> (a :: Hash) > c
+
+    , testProperty "ge" $
+        \a b c -> (a >= b && b >= c) ==> (a :: Hash) >= c
+
+    , testProperty "lt" $
+        \a b c -> (a < b && b < c) ==> (a :: Hash) < c
+
+    , testProperty "le" $
+        \a b c -> (a <= b && b <= c) ==> (a :: Hash) <= c
+    ]
+
+  , testGroup "packing"
 
     [ testProperty "identity 8-bit" $
         \a -> Just a == pack8 (unpack8 a)
diff --git a/src/core/gf2p127-inl.h b/src/core/gf2p127-inl.h
new file mode 100644
--- /dev/null
+++ b/src/core/gf2p127-inl.h
@@ -0,0 +1,138 @@
+#pragma once
+
+#include <stdio.h>
+#include <stdint.h>
+#include <immintrin.h>
+#include <smmintrin.h>
+#include <wmmintrin.h>
+
+typedef __m128i gf2p127_t;
+#ifdef __AVX2__
+typedef __m256i gf2p127x2_t;
+#endif
+
+static const inline
+_Bool gf2p127_valid(const gf2p127_t a) {
+  return (_mm_extract_epi64(a, 1) & (UINT64_C(1) << 63)) == 0;
+}
+
+static const inline
+_Bool gf2p127_eq(const gf2p127_t a, const gf2p127_t b) {
+  _Bool lo = _mm_extract_epi64(a, 0) == _mm_extract_epi64(b, 0);
+  _Bool hi = _mm_extract_epi64(a, 1) == _mm_extract_epi64(b, 1);
+  return lo && hi;
+}
+
+static const inline
+gf2p127_t gf2p127_zero() {
+  return _mm_setzero_si128();
+}
+
+static const inline
+gf2p127_t gf2p127_from_int(int a) {
+  return _mm_cvtsi32_si128(a);
+}
+
+static const inline
+gf2p127_t gf2p127_mul_bit(const gf2p127_t a, const _Bool bit) {
+  return _mm_slli_epi64(a, !bit * 64);
+}
+
+static const inline
+gf2p127_t gf2p127_add(const gf2p127_t a, const gf2p127_t b) {
+  return _mm_xor_si128(a, b);
+}
+
+static const inline
+gf2p127_t gf2p127_mul_00(const gf2p127_t a) {
+  return _mm_setzero_si128();
+}
+
+static const inline
+gf2p127_t gf2p127_mul_01(const gf2p127_t a) {
+  return a;
+}
+
+static const gf2p127_t *minmax __attribute__((__aligned__(16))) =
+  (gf2p127_t *)(uint64_t [4]){0, 0, UINT64_MAX, UINT64_MAX};
+
+static const gf2p127_t *x127 __attribute__((__aligned__(16))) =
+  (gf2p127_t *)(uint32_t [4]){0, 0, 0, 1 << 31};
+
+static const inline
+gf2p127_t gf2p127_mul_10(const gf2p127_t a) {
+  // Shift lower and upper halves left by one bit,
+  // resembling a multiplication by two.
+  gf2p127_t sl = _mm_slli_epi64(a, 1);
+  // Check for a x^127 overflow, and add the polynom and carry bit.
+  gf2p127_t one = _mm_srli_epi64(_mm_alignr_epi8(a, sl, 8), 63);
+  gf2p127_t over = _mm_and_si128(sl, _mm_load_si128(x127));
+  gf2p127_t x127x63 = _mm_unpackhi_epi64(over, over);
+  return _mm_xor_si128(_mm_xor_si128(sl, one), x127x63);
+}
+
+#ifdef __AVX2__
+static const inline
+gf2p127x2_t gf2p127x2_mul_10(const gf2p127x2_t ab) {
+  // Shift lower and upper halves left by one bit,
+  // resembling a multiplication by two.
+  gf2p127x2_t sl = _mm256_slli_epi64(ab, 1);
+  // Check for the x^63 carry bit, and the x^127 overflow, and construct x^64 + 1 polynomial.
+  gf2p127x2_t carry = _mm256_srli_epi64(_mm256_alignr_epi8(ab, sl, 8), 63);
+  gf2p127x2_t carried = _mm256_xor_si256(sl, carry);
+  // Check for the x^127 overflow, and construct the x^127 + x^63 polymomial.
+  gf2p127x2_t over1 = _mm256_srli_epi64(sl, 63);
+  gf2p127x2_t over2 = _mm256_slli_epi64(over1, 63);
+  gf2p127x2_t overhi = _mm256_unpackhi_epi64(over2, over2);
+  return _mm256_xor_si256(overhi, carried);
+}
+#endif
+
+static const inline
+gf2p127_t gf2p127_mul_11(const gf2p127_t a) {
+  gf2p127_t mul01 = gf2p127_mul_01(a);
+  gf2p127_t mul10 = gf2p127_mul_10(a);
+  return _mm_xor_si128(mul01, mul10);
+};
+
+static const inline
+gf2p127_t gf2p127_mul(const gf2p127_t a, const gf2p127_t b) {
+  gf2p127_t tmp, lo, hi;
+
+  // Multiplication (Karatsuba):
+  //   tmp <- (a0 + a1) * (b0 + b1)
+  tmp = _mm_xor_si128(_mm_unpacklo_epi64(a, b), _mm_unpackhi_epi64(a, b));
+  tmp = _mm_clmulepi64_si128(tmp, tmp, 0x10);
+  //   lo <- a0 * b0
+  lo = _mm_clmulepi64_si128(a, b, 0x00);
+  //   hi <- a1 * b1
+  hi = _mm_clmulepi64_si128(a, b, 0x11);
+  //   tmp <- (a0 + a1) * (b0 + b1) + a0 * b0 + a1 * b1
+  tmp = _mm_xor_si128(tmp, _mm_xor_si128(lo, hi));
+  //   lo <- a0 * b0 + low64[(a0 + a1) * (b0 + b1) + a0 * b0 + a1 * b1] * x^64
+  lo = _mm_xor_si128(lo, _mm_slli_si128(tmp, 8));
+  //   hi <- a1 * b1 + high64[(a0 + a1) * (b0 + b1) + a0 * b0 + a1 * b1]
+  hi = _mm_xor_si128(hi, _mm_srli_si128(tmp, 8));
+
+  // Reduction (modulo f(x) = x^127 + x^63 + 1)
+  //   tmp <- low64[hi] * x^64 + high64[lo]
+  tmp = _mm_alignr_epi8(hi, lo, 8);
+  tmp = _mm_xor_si128(tmp, hi);
+  hi = _mm_slli_epi64(hi, 1);
+  lo = _mm_xor_si128(lo, hi);
+  hi = _mm_unpackhi_epi64(hi, tmp);
+  lo = _mm_xor_si128(lo, hi);
+  tmp = _mm_srli_epi64(tmp, 63);
+  lo = _mm_xor_si128(lo, tmp);
+  hi = _mm_unpacklo_epi64(tmp, tmp);
+  lo = _mm_xor_si128(lo, _mm_slli_epi64(hi, 63));
+
+  return lo;
+}
+
+static inline
+char *gf2p127_hex(char *str, const gf2p127_t m) {
+  sprintf(str, "%.16llx%.16llx", _mm_extract_epi64(m, 1),
+                                 _mm_extract_epi64(m, 0));
+  return str;
+}
diff --git a/src/core/sl2-inl.h b/src/core/sl2-inl.h
new file mode 100644
--- /dev/null
+++ b/src/core/sl2-inl.h
@@ -0,0 +1,330 @@
+#pragma once
+
+#include "gf2p127-inl.h"
+
+typedef gf2p127_t sl2_t[2][2] __attribute__((__aligned__(16)));
+
+static inline
+_Bool sl2_valid(sl2_t a) {
+  gf2p127_t det = gf2p127_add(gf2p127_mul(a[0][0], a[1][1]),
+                              gf2p127_mul(a[0][1], a[1][0]));
+  return _mm_extract_epi64(det, 0) == 1 &&
+         _mm_extract_epi64(det, 1) == 0 &&
+         gf2p127_valid(a[0][0]) &&
+         gf2p127_valid(a[0][1]) &&
+         gf2p127_valid(a[1][0]) &&
+         gf2p127_valid(a[1][1]);
+}
+
+static inline
+_Bool sl2_eq(sl2_t a, sl2_t b) {
+  return gf2p127_eq(a[0][0], b[0][0]) &&
+         gf2p127_eq(a[0][1], b[0][1]) &&
+         gf2p127_eq(a[1][0], b[1][0]) &&
+         gf2p127_eq(a[1][1], b[1][1]);
+}
+
+static inline
+int sl2_cmp(sl2_t a, sl2_t b) {
+  uint64_t *ua = (uint64_t *)a;
+  uint64_t *ub = (uint64_t *)b;
+  int i;
+  for (i = 0; i < sizeof(sl2_t) / sizeof(uint64_t); i++) {
+    if (ua[i] == ub[i]) {
+      continue;
+    }
+    if (ua[i] > ub[i]) {
+      return 1;
+    }
+    if (ua[i] < ub[i]) {
+      return -1;
+    }
+  }
+  return 0;
+}
+
+static inline
+void sl2_copy(sl2_t dst, sl2_t src) {
+  dst[0][0] = src[0][0];
+  dst[0][1] = src[0][1];
+  dst[1][0] = src[1][0];
+  dst[1][1] = src[1][1];
+}
+
+#ifdef __AVX2__
+static inline
+void sl2_mul_bit_left_x2(gf2p127x2_t *b00b01, gf2p127x2_t *b10b11, gf2p127_t bits) {
+  // A: {00 = 10, 01 = 01, 10 = 01, 11 = 00}
+  // B: {00 = 10, 01 = 11, 10 = 01, 11 = 01}
+  gf2p127x2_t bb = _mm256_broadcastsi128_si256(bits);
+  gf2p127x2_t masked = _mm256_and_si256(*b10b11, bb);
+  gf2p127x2_t maskedadd = _mm256_xor_si256(*b00b01, masked);
+  gf2p127x2_t mul = gf2p127x2_mul_10(maskedadd);
+  gf2p127x2_t muladd = _mm256_xor_si256(*b10b11, mul);
+  *b10b11 = maskedadd;
+  *b00b01 = muladd;
+}
+#endif
+
+static inline
+void sl2_mul_bit_left(gf2p127_t *b00, gf2p127_t *b01, gf2p127_t *b10, gf2p127_t *b11, gf2p127_t bits) {
+  // A: {00 = 10, 01 = 01, 10 = 01, 11 = 00}
+  // B: {00 = 10, 01 = 11, 10 = 01, 11 = 01}
+  gf2p127_t b10_ = *b10;
+  gf2p127_t b11_ = *b11;
+  *b10 = gf2p127_add(*b00, _mm_and_si128(*b10, bits));
+  *b11 = gf2p127_add(*b01, _mm_and_si128(*b11, bits));
+  *b00 = gf2p127_add(b10_, gf2p127_mul_10(*b10));
+  *b01 = gf2p127_add(b11_, gf2p127_mul_10(*b11));
+}
+
+#ifdef __AVX2__
+static inline
+void sl2_mul_bits_left_x2(gf2p127x2_t *b00b01, gf2p127x2_t *b10b11, unsigned char byte) {
+  sl2_mul_bit_left_x2(b00b01, b10b11, _mm_load_si128(&minmax[(byte >> 0) & 1]));
+  sl2_mul_bit_left_x2(b00b01, b10b11, _mm_load_si128(&minmax[(byte >> 1) & 1]));
+  sl2_mul_bit_left_x2(b00b01, b10b11, _mm_load_si128(&minmax[(byte >> 2) & 1]));
+  sl2_mul_bit_left_x2(b00b01, b10b11, _mm_load_si128(&minmax[(byte >> 3) & 1]));
+  sl2_mul_bit_left_x2(b00b01, b10b11, _mm_load_si128(&minmax[(byte >> 4) & 1]));
+  sl2_mul_bit_left_x2(b00b01, b10b11, _mm_load_si128(&minmax[(byte >> 5) & 1]));
+  sl2_mul_bit_left_x2(b00b01, b10b11, _mm_load_si128(&minmax[(byte >> 6) & 1]));
+  sl2_mul_bit_left_x2(b00b01, b10b11, _mm_load_si128(&minmax[(byte >> 7) & 1]));
+}
+#endif
+
+static inline
+void sl2_mul_bits_left(gf2p127_t *b00, gf2p127_t *b01, gf2p127_t *b10, gf2p127_t *b11, unsigned char byte) {
+  sl2_mul_bit_left(b00, b01, b10, b11, _mm_load_si128(&minmax[(byte >> 0) & 1]));
+  sl2_mul_bit_left(b00, b01, b10, b11, _mm_load_si128(&minmax[(byte >> 1) & 1]));
+  sl2_mul_bit_left(b00, b01, b10, b11, _mm_load_si128(&minmax[(byte >> 2) & 1]));
+  sl2_mul_bit_left(b00, b01, b10, b11, _mm_load_si128(&minmax[(byte >> 3) & 1]));
+  sl2_mul_bit_left(b00, b01, b10, b11, _mm_load_si128(&minmax[(byte >> 4) & 1]));
+  sl2_mul_bit_left(b00, b01, b10, b11, _mm_load_si128(&minmax[(byte >> 5) & 1]));
+  sl2_mul_bit_left(b00, b01, b10, b11, _mm_load_si128(&minmax[(byte >> 6) & 1]));
+  sl2_mul_bit_left(b00, b01, b10, b11, _mm_load_si128(&minmax[(byte >> 7) & 1]));
+}
+
+#ifdef __AVX2__
+static inline
+void sl2_mul_buf_left(sl2_t b, unsigned char *buf, size_t n) {
+  gf2p127x2_t b00b01 = _mm256_loadu2_m128i(&b[0][0], &b[0][1]);
+  gf2p127x2_t b10b11 = _mm256_loadu2_m128i(&b[1][0], &b[1][1]);
+  size_t i;
+  for (i = n; i > 0; i--) {
+    sl2_mul_bits_left_x2(&b00b01, &b10b11, buf[i - 1]);
+  }
+  _mm256_storeu2_m128i(&b[0][0], &b[0][1], b00b01);
+  _mm256_storeu2_m128i(&b[1][0], &b[1][1], b10b11);
+}
+#else
+static inline
+void sl2_mul_buf_left(sl2_t b, unsigned char *buf, size_t n) {
+  gf2p127_t b00 = b[0][0];
+  gf2p127_t b01 = b[0][1];
+  gf2p127_t b10 = b[1][0];
+  gf2p127_t b11 = b[1][1];
+  size_t i;
+  for (i = n; i > 0; i--) {
+    sl2_mul_bits_left(&b00, &b01, &b10, &b11, buf[i - 1]);
+  }
+  b[0][0] = b00;
+  b[0][1] = b01;
+  b[1][0] = b10;
+  b[1][1] = b11;
+}
+#endif
+
+#ifdef __AVX2__
+static inline
+void sl2_mul_bit_right_x2(gf2p127x2_t *a00a10, gf2p127x2_t *a01a11, gf2p127_t bits) {
+  // A: {00 = 10, 01 = 01, 10 = 01, 11 = 00}
+  // B: {00 = 10, 01 = 11, 10 = 01, 11 = 01}
+  gf2p127x2_t bb = _mm256_broadcastsi128_si256(bits);
+  gf2p127x2_t mul = gf2p127x2_mul_10(*a00a10);
+  gf2p127x2_t muladd = _mm256_xor_si256(mul, *a01a11);
+  gf2p127x2_t masked = _mm256_and_si256(muladd, bb);
+  gf2p127x2_t maskedadd = _mm256_xor_si256(masked, *a00a10);
+  *a01a11 = maskedadd;
+  *a00a10 = muladd;
+}
+#endif
+
+static inline
+void sl2_mul_bit_right(gf2p127_t *a00, gf2p127_t *a01, gf2p127_t *a10, gf2p127_t *a11, gf2p127_t bits) {
+  // A: {00 = 10, 01 = 01, 10 = 01, 11 = 00}
+  // B: {00 = 10, 01 = 11, 10 = 01, 11 = 01}
+  gf2p127_t a00_ = *a00;
+  gf2p127_t a10_ = *a10;
+  *a00 = gf2p127_add(gf2p127_mul_10(*a00), *a01);
+  *a10 = gf2p127_add(gf2p127_mul_10(*a10), *a11);
+  *a01 = gf2p127_add(a00_, _mm_and_si128(*a00, bits));
+  *a11 = gf2p127_add(a10_, _mm_and_si128(*a10, bits));
+}
+
+#ifdef __AVX2__
+static inline
+void sl2_mul_bits_right_x2(gf2p127x2_t *a00a01, gf2p127x2_t *a10a11, unsigned char byte) {
+  sl2_mul_bit_right_x2(a00a01, a10a11, _mm_load_si128(&minmax[(byte >> 7) & 1]));
+  sl2_mul_bit_right_x2(a00a01, a10a11, _mm_load_si128(&minmax[(byte >> 6) & 1]));
+  sl2_mul_bit_right_x2(a00a01, a10a11, _mm_load_si128(&minmax[(byte >> 5) & 1]));
+  sl2_mul_bit_right_x2(a00a01, a10a11, _mm_load_si128(&minmax[(byte >> 4) & 1]));
+  sl2_mul_bit_right_x2(a00a01, a10a11, _mm_load_si128(&minmax[(byte >> 3) & 1]));
+  sl2_mul_bit_right_x2(a00a01, a10a11, _mm_load_si128(&minmax[(byte >> 2) & 1]));
+  sl2_mul_bit_right_x2(a00a01, a10a11, _mm_load_si128(&minmax[(byte >> 1) & 1]));
+  sl2_mul_bit_right_x2(a00a01, a10a11, _mm_load_si128(&minmax[(byte >> 0) & 1]));
+}
+#endif
+
+static inline
+void sl2_mul_bits_right(gf2p127_t *a00, gf2p127_t *a01, gf2p127_t *a10, gf2p127_t *a11, unsigned char byte) {
+  sl2_mul_bit_right(a00, a01, a10, a11, _mm_load_si128(&minmax[(byte >> 7) & 1]));
+  sl2_mul_bit_right(a00, a01, a10, a11, _mm_load_si128(&minmax[(byte >> 6) & 1]));
+  sl2_mul_bit_right(a00, a01, a10, a11, _mm_load_si128(&minmax[(byte >> 5) & 1]));
+  sl2_mul_bit_right(a00, a01, a10, a11, _mm_load_si128(&minmax[(byte >> 4) & 1]));
+  sl2_mul_bit_right(a00, a01, a10, a11, _mm_load_si128(&minmax[(byte >> 3) & 1]));
+  sl2_mul_bit_right(a00, a01, a10, a11, _mm_load_si128(&minmax[(byte >> 2) & 1]));
+  sl2_mul_bit_right(a00, a01, a10, a11, _mm_load_si128(&minmax[(byte >> 1) & 1]));
+  sl2_mul_bit_right(a00, a01, a10, a11, _mm_load_si128(&minmax[(byte >> 0) & 1]));
+}
+
+#ifdef __AVX2__
+static inline
+void sl2_mul_buf_right(sl2_t a, unsigned char *buf, size_t n) {
+  size_t i;
+  gf2p127x2_t a00a10 = _mm256_loadu2_m128i(&a[0][0], &a[1][0]);
+  gf2p127x2_t a01a11 = _mm256_loadu2_m128i(&a[0][1], &a[1][1]);
+  for (i = 0; i < n; i++) {
+    sl2_mul_bits_right_x2(&a00a10, &a01a11, buf[i]);
+  }
+  _mm256_storeu2_m128i(&a[0][1], &a[1][1], a01a11);
+  _mm256_storeu2_m128i(&a[0][0], &a[1][0], a00a10);
+}
+#else
+static inline
+void sl2_mul_buf_right(sl2_t a, unsigned char *buf, size_t n) {
+  gf2p127_t a00 = _mm_load_si128(&a[0][0]);
+  gf2p127_t a01 = _mm_load_si128(&a[0][1]);
+  gf2p127_t a10 = _mm_load_si128(&a[1][0]);
+  gf2p127_t a11 = _mm_load_si128(&a[1][1]);
+  size_t i;
+  for (i = 0; i < n; i++) {
+    sl2_mul_bits_right(&a00, &a01, &a10, &a11, buf[i]);
+  }
+  a[0][0] = a00;
+  a[0][1] = a01;
+  a[1][0] = a10;
+  a[1][1] = a11;
+}
+#endif
+
+static inline
+void sl2_mul(sl2_t c, sl2_t a, sl2_t b) {
+  // Strassen algorithm
+  gf2p127_t m0, m1, m2, m3, m4, m5, m6;
+  m0 = gf2p127_mul(gf2p127_add(a[0][0], a[1][1]),
+                   gf2p127_add(b[0][0], b[1][1]));
+  m1 = gf2p127_mul(gf2p127_add(a[1][0], a[1][1]), b[0][0]);
+  m2 = gf2p127_mul(a[0][0], gf2p127_add(b[0][1], b[1][1]));
+  m3 = gf2p127_mul(a[1][1], gf2p127_add(b[1][0], b[0][0]));
+  m4 = gf2p127_mul(gf2p127_add(a[0][0], a[0][1]), b[1][1]);
+  m5 = gf2p127_mul(gf2p127_add(a[1][0], a[0][0]),
+                   gf2p127_add(b[0][0], b[0][1]));
+  m6 = gf2p127_mul(gf2p127_add(a[0][1], a[1][1]),
+                   gf2p127_add(b[1][0], b[1][1]));
+  c[0][0] = gf2p127_add(gf2p127_add(m0, m3), gf2p127_add(m4, m6));
+  c[0][1] = gf2p127_add(m2, m4);
+  c[1][0] = gf2p127_add(m1, m3);
+  c[1][1] = gf2p127_add(gf2p127_add(m0, m1), gf2p127_add(m2, m5));
+}
+
+static inline
+void sl2_mul_byte_left(sl2_t b, unsigned char byte, sl2_t m[256]) {
+  sl2_mul(m[byte], b, b);
+}
+
+static inline
+void sl2_mul_byte_right(sl2_t a, unsigned char byte, sl2_t m[256]) {
+  sl2_mul(a, m[byte], a);
+}
+
+static inline
+void sl2_init(sl2_t m[2]) {
+  m[0][0][0] = gf2p127_from_int(2);
+  m[0][0][1] = gf2p127_from_int(1);
+  m[0][1][0] = gf2p127_from_int(1);
+  m[0][1][1] = gf2p127_from_int(0);
+  m[1][0][0] = gf2p127_from_int(2);
+  m[1][0][1] = gf2p127_from_int(3);
+  m[1][1][0] = gf2p127_from_int(1);
+  m[1][1][1] = gf2p127_from_int(1);
+}
+
+static inline
+void sl2_unit(sl2_t a) {
+  a[0][0] = gf2p127_from_int(1);
+  a[0][1] = gf2p127_from_int(0);
+  a[1][0] = gf2p127_from_int(0);
+  a[1][1] = gf2p127_from_int(1);
+}
+
+static inline
+char *sl2_hex(char *buf, sl2_t a) {
+  gf2p127_hex(&buf[0],  a[0][0]);
+  gf2p127_hex(&buf[32], a[0][1]);
+  gf2p127_hex(&buf[64], a[1][0]);
+  gf2p127_hex(&buf[96], a[1][1]);
+  return buf;
+}
+
+static const unsigned char b64[64] =
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+
+static const unsigned char unb64[256] = {
+  ['A'] =  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,
+          13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+  ['a'] = 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+          39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+  ['0'] = 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+  ['-'] = 62, ['_'] = 63
+};
+
+static inline
+void sl2_serialize(sl2_t m, unsigned char buf[86]) {
+
+  int i, j;
+  unsigned char a, b, c, *data = (unsigned char *)m;
+
+  for (i = j = 0; i <= 64 - 3; i += 3, j += 4) {
+    a = data[i + 0];
+    b = data[i + 1];
+    c = data[i + 2];
+    buf[j + 0] = b64[a >> 2];
+    buf[j + 1] = b64[((0x03 & a) << 4) + (b >> 4)];
+    buf[j + 2] = b64[((0x0f & b) << 2) + (c >> 6)];
+    buf[j + 3] = b64[0x3f & c];
+  }
+
+  buf[84] = b64[data[i] >> 2];
+  buf[85] = b64[(0x3 & data[i]) << 4];
+
+}
+
+static inline
+void sl2_unserialize(sl2_t m, unsigned char buf[86]) {
+
+  int i, j;
+  unsigned char a, b, c, d, *data = (unsigned char *)m;
+
+  for (i = j = 0; i <= 86 - 4; i += 4, j += 3) {
+    a = unb64[buf[i + 0]];
+    b = unb64[buf[i + 1]];
+    c = unb64[buf[i + 2]];
+    d = unb64[buf[i + 3]];
+    data[j + 0] = (a << 2) | (b >> 4);
+    data[j + 1] = (b << 4) | (c >> 2);
+    data[j + 2] = (c << 6) | (d);
+  }
+
+  data[63] = (unb64[buf[i]] << 2) | (unb64[buf[i + 1]] >> 4);
+
+}
diff --git a/src/gf2p127-inl.h b/src/gf2p127-inl.h
deleted file mode 100644
--- a/src/gf2p127-inl.h
+++ /dev/null
@@ -1,117 +0,0 @@
-#pragma once
-
-#include <stdio.h>
-#include <stdint.h>
-#include <smmintrin.h>
-#include <wmmintrin.h>
-
-typedef __m128i gf2p127_t;
-
-static const inline
-_Bool gf2p127_valid(const gf2p127_t a) {
-  return (_mm_extract_epi64(a, 1) & (UINT64_C(1) << 63)) == 0;
-}
-
-static const inline
-_Bool gf2p127_eq(const gf2p127_t a, const gf2p127_t b) {
-  _Bool lo = _mm_extract_epi64(a, 0) == _mm_extract_epi64(b, 0);
-  _Bool hi = _mm_extract_epi64(a, 1) == _mm_extract_epi64(b, 1);
-  return lo && hi;
-}
-
-static const inline
-gf2p127_t gf2p127_zero() {
-  return _mm_setzero_si128();
-}
-
-static const inline
-gf2p127_t gf2p127_from_int(int a) {
-  return _mm_cvtsi32_si128(a);
-}
-
-static const inline
-gf2p127_t gf2p127_mul_bit(const gf2p127_t a, const _Bool bit) {
-  return _mm_slli_epi64(a, !bit * 64);
-}
-
-static const inline
-gf2p127_t gf2p127_add(const gf2p127_t a, const gf2p127_t b) {
-  return _mm_xor_si128(a, b);
-}
-
-static const inline
-gf2p127_t gf2p127_mul_00(const gf2p127_t a) {
-  return _mm_setzero_si128();
-}
-
-static const inline
-gf2p127_t gf2p127_mul_01(const gf2p127_t a) {
-  return a;
-}
-
-static const gf2p127_t *minmax __attribute__((__aligned__(16))) =
-  (gf2p127_t *)(uint64_t [4]){0, 0, UINT64_MAX, UINT64_MAX};
-
-static const gf2p127_t *x127 __attribute__((__aligned__(16))) =
-  (gf2p127_t *)(uint32_t [4]){0, 0, 0, 1 << 31};
-
-static const inline
-gf2p127_t gf2p127_mul_10(const gf2p127_t a) {
-  // Shift lower and upper halves left by one bit,
-  // resembling a multiplication by two.
-  gf2p127_t sl = _mm_slli_epi64(a, 1);
-  // Check for a x^127 overflow, and add the polynom and carry bit.
-  gf2p127_t one = _mm_srli_epi64(_mm_alignr_epi8(a, sl, 8), 63);
-  gf2p127_t over = _mm_and_si128(sl, _mm_load_si128(x127));
-  gf2p127_t x127x63 = _mm_unpackhi_epi64(over, over);
-  return _mm_xor_si128(_mm_xor_si128(sl, one), x127x63);
-}
-
-static const inline
-gf2p127_t gf2p127_mul_11(const gf2p127_t a) {
-  gf2p127_t mul01 = gf2p127_mul_01(a);
-  gf2p127_t mul10 = gf2p127_mul_10(a);
-  return _mm_xor_si128(mul01, mul10);
-};
-
-static const inline
-gf2p127_t gf2p127_mul(const gf2p127_t a, const gf2p127_t b) {
-  gf2p127_t tmp, lo, hi;
-
-  // Multiplication (Karatsuba):
-  //   tmp <- (a0 + a1) * (b0 + b1)
-  tmp = _mm_xor_si128(_mm_unpacklo_epi64(a, b), _mm_unpackhi_epi64(a, b));
-  tmp = _mm_clmulepi64_si128(tmp, tmp, 0x10);
-  //   lo <- a0 * b0
-  lo = _mm_clmulepi64_si128(a, b, 0x00);
-  //   hi <- a1 * b1
-  hi = _mm_clmulepi64_si128(a, b, 0x11);
-  //   tmp <- (a0 + a1) * (b0 + b1) + a0 * b0 + a1 * b1
-  tmp = _mm_xor_si128(tmp, _mm_xor_si128(lo, hi));
-  //   lo <- a0 * b0 + low64[(a0 + a1) * (b0 + b1) + a0 * b0 + a1 * b1] * x^64
-  lo = _mm_xor_si128(lo, _mm_slli_si128(tmp, 8));
-  //   hi <- a1 * b1 + high64[(a0 + a1) * (b0 + b1) + a0 * b0 + a1 * b1]
-  hi = _mm_xor_si128(hi, _mm_srli_si128(tmp, 8));
-
-  // Reduction (modulo f(x) = x^127 + x^63 + 1)
-  //   tmp <- low64[hi] * x^64 + high64[lo]
-  tmp = _mm_alignr_epi8(hi, lo, 8);
-  tmp = _mm_xor_si128(tmp, hi);
-  hi = _mm_slli_epi64(hi, 1);
-  lo = _mm_xor_si128(lo, hi);
-  hi = _mm_unpackhi_epi64(hi, tmp);
-  lo = _mm_xor_si128(lo, hi);
-  tmp = _mm_srli_epi64(tmp, 63);
-  lo = _mm_xor_si128(lo, tmp);
-  hi = _mm_unpacklo_epi64(tmp, tmp);
-  lo = _mm_xor_si128(lo, _mm_slli_epi64(hi, 63));
-
-  return lo;
-}
-
-static inline
-char *gf2p127_hex(char *str, const gf2p127_t m) {
-  sprintf(str, "%.16llx%.16llx", _mm_extract_epi64(m, 1),
-                                 _mm_extract_epi64(m, 0));
-  return str;
-}
diff --git a/src/sl2-inl.h b/src/sl2-inl.h
deleted file mode 100644
--- a/src/sl2-inl.h
+++ /dev/null
@@ -1,225 +0,0 @@
-#pragma once
-
-#include "gf2p127-inl.h"
-
-typedef gf2p127_t sl2_t[2][2] __attribute__((__aligned__(16)));
-
-static inline
-_Bool sl2_valid(sl2_t a) {
-  gf2p127_t det = gf2p127_add(gf2p127_mul(a[0][0], a[1][1]),
-                              gf2p127_mul(a[0][1], a[1][0]));
-  return _mm_extract_epi64(det, 0) == 1 &&
-         _mm_extract_epi64(det, 1) == 0 &&
-         gf2p127_valid(a[0][0]) &&
-         gf2p127_valid(a[0][1]) &&
-         gf2p127_valid(a[1][0]) &&
-         gf2p127_valid(a[1][1]);
-}
-
-static inline
-_Bool sl2_eq(sl2_t a, sl2_t b) {
-  return gf2p127_eq(a[0][0], b[0][0]) &&
-         gf2p127_eq(a[0][1], b[0][1]) &&
-         gf2p127_eq(a[1][0], b[1][0]) &&
-         gf2p127_eq(a[1][1], b[1][1]);
-}
-
-static inline
-void sl2_copy(sl2_t dst, sl2_t src) {
-  dst[0][0] = src[0][0];
-  dst[0][1] = src[0][1];
-  dst[1][0] = src[1][0];
-  dst[1][1] = src[1][1];
-}
-
-static inline
-void sl2_mul_bit_left(gf2p127_t *b00, gf2p127_t *b01, gf2p127_t *b10, gf2p127_t *b11, gf2p127_t bits) {
-  // A: {00 = 10, 01 = 01, 10 = 01, 11 = 00}
-  // B: {00 = 10, 01 = 11, 10 = 01, 11 = 01}
-  gf2p127_t b10_ = *b10;
-  gf2p127_t b11_ = *b11;
-  *b10 = gf2p127_add(*b00, _mm_and_si128(*b10, bits));
-  *b11 = gf2p127_add(*b01, _mm_and_si128(*b11, bits));
-  *b00 = gf2p127_add(b10_, gf2p127_mul_10(*b10));
-  *b01 = gf2p127_add(b11_, gf2p127_mul_10(*b11));
-}
-
-static inline
-void sl2_mul_bits_left(gf2p127_t *b00, gf2p127_t *b01, gf2p127_t *b10, gf2p127_t *b11, unsigned char byte) {
-  sl2_mul_bit_left(b00, b01, b10, b11, _mm_load_si128(&minmax[(byte >> 0) & 1]));
-  sl2_mul_bit_left(b00, b01, b10, b11, _mm_load_si128(&minmax[(byte >> 1) & 1]));
-  sl2_mul_bit_left(b00, b01, b10, b11, _mm_load_si128(&minmax[(byte >> 2) & 1]));
-  sl2_mul_bit_left(b00, b01, b10, b11, _mm_load_si128(&minmax[(byte >> 3) & 1]));
-  sl2_mul_bit_left(b00, b01, b10, b11, _mm_load_si128(&minmax[(byte >> 4) & 1]));
-  sl2_mul_bit_left(b00, b01, b10, b11, _mm_load_si128(&minmax[(byte >> 5) & 1]));
-  sl2_mul_bit_left(b00, b01, b10, b11, _mm_load_si128(&minmax[(byte >> 6) & 1]));
-  sl2_mul_bit_left(b00, b01, b10, b11, _mm_load_si128(&minmax[(byte >> 7) & 1]));
-}
-
-static inline
-void sl2_mul_buf_left(sl2_t b, unsigned char *buf, size_t n) {
-  gf2p127_t b00 = b[0][0];
-  gf2p127_t b01 = b[0][1];
-  gf2p127_t b10 = b[1][0];
-  gf2p127_t b11 = b[1][1];
-  size_t i;
-  for (i = n; i > 0; i--) {
-    sl2_mul_bits_left(&b00, &b01, &b10, &b11, buf[i - 1]);
-  }
-  b[0][0] = b00;
-  b[0][1] = b01;
-  b[1][0] = b10;
-  b[1][1] = b11;
-}
-
-static inline
-void sl2_mul_bit_right(gf2p127_t *a00, gf2p127_t *a01, gf2p127_t *a10, gf2p127_t *a11, gf2p127_t bits) {
-  // A: {00 = 10, 01 = 01, 10 = 01, 11 = 00}
-  // B: {00 = 10, 01 = 11, 10 = 01, 11 = 01}
-  gf2p127_t a00_ = *a00;
-  gf2p127_t a10_ = *a10;
-  *a00 = gf2p127_add(gf2p127_mul_10(*a00), *a01);
-  *a10 = gf2p127_add(gf2p127_mul_10(*a10), *a11);
-  *a01 = gf2p127_add(a00_, _mm_and_si128(*a00, bits));
-  *a11 = gf2p127_add(a10_, _mm_and_si128(*a10, bits));
-}
-
-static inline
-void sl2_mul_bits_right(gf2p127_t *a00, gf2p127_t *a01, gf2p127_t *a10, gf2p127_t *a11, unsigned char byte) {
-  sl2_mul_bit_right(a00, a01, a10, a11, _mm_load_si128(&minmax[(byte >> 7) & 1]));
-  sl2_mul_bit_right(a00, a01, a10, a11, _mm_load_si128(&minmax[(byte >> 6) & 1]));
-  sl2_mul_bit_right(a00, a01, a10, a11, _mm_load_si128(&minmax[(byte >> 5) & 1]));
-  sl2_mul_bit_right(a00, a01, a10, a11, _mm_load_si128(&minmax[(byte >> 4) & 1]));
-  sl2_mul_bit_right(a00, a01, a10, a11, _mm_load_si128(&minmax[(byte >> 3) & 1]));
-  sl2_mul_bit_right(a00, a01, a10, a11, _mm_load_si128(&minmax[(byte >> 2) & 1]));
-  sl2_mul_bit_right(a00, a01, a10, a11, _mm_load_si128(&minmax[(byte >> 1) & 1]));
-  sl2_mul_bit_right(a00, a01, a10, a11, _mm_load_si128(&minmax[(byte >> 0) & 1]));
-}
-
-static inline
-void sl2_mul_buf_right(sl2_t a, unsigned char *buf, size_t n) {
-  gf2p127_t a00 = _mm_load_si128(&a[0][0]);
-  gf2p127_t a01 = _mm_load_si128(&a[0][1]);
-  gf2p127_t a10 = _mm_load_si128(&a[1][0]);
-  gf2p127_t a11 = _mm_load_si128(&a[1][1]);
-  size_t i;
-  for (i = 0; i < n; i++) {
-    sl2_mul_bits_right(&a00, &a01, &a10, &a11, buf[i]);
-  }
-  a[0][0] = a00;
-  a[0][1] = a01;
-  a[1][0] = a10;
-  a[1][1] = a11;
-}
-
-static inline
-void sl2_mul(sl2_t c, sl2_t a, sl2_t b) {
-  // Strassen algorithm
-  gf2p127_t m0, m1, m2, m3, m4, m5, m6;
-  m0 = gf2p127_mul(gf2p127_add(a[0][0], a[1][1]),
-                   gf2p127_add(b[0][0], b[1][1]));
-  m1 = gf2p127_mul(gf2p127_add(a[1][0], a[1][1]), b[0][0]);
-  m2 = gf2p127_mul(a[0][0], gf2p127_add(b[0][1], b[1][1]));
-  m3 = gf2p127_mul(a[1][1], gf2p127_add(b[1][0], b[0][0]));
-  m4 = gf2p127_mul(gf2p127_add(a[0][0], a[0][1]), b[1][1]);
-  m5 = gf2p127_mul(gf2p127_add(a[1][0], a[0][0]),
-                   gf2p127_add(b[0][0], b[0][1]));
-  m6 = gf2p127_mul(gf2p127_add(a[0][1], a[1][1]),
-                   gf2p127_add(b[1][0], b[1][1]));
-  c[0][0] = gf2p127_add(gf2p127_add(m0, m3), gf2p127_add(m4, m6));
-  c[0][1] = gf2p127_add(m2, m4);
-  c[1][0] = gf2p127_add(m1, m3);
-  c[1][1] = gf2p127_add(gf2p127_add(m0, m1), gf2p127_add(m2, m5));
-}
-
-static inline
-void sl2_mul_byte_left(sl2_t b, unsigned char byte, sl2_t m[256]) {
-  sl2_mul(m[byte], b, b);
-}
-
-static inline
-void sl2_mul_byte_right(sl2_t a, unsigned char byte, sl2_t m[256]) {
-  sl2_mul(a, m[byte], a);
-}
-
-static inline
-void sl2_init(sl2_t m[2]) {
-  m[0][0][0] = gf2p127_from_int(2);
-  m[0][0][1] = gf2p127_from_int(1);
-  m[0][1][0] = gf2p127_from_int(1);
-  m[0][1][1] = gf2p127_from_int(0);
-  m[1][0][0] = gf2p127_from_int(2);
-  m[1][0][1] = gf2p127_from_int(3);
-  m[1][1][0] = gf2p127_from_int(1);
-  m[1][1][1] = gf2p127_from_int(1);
-}
-
-static inline
-void sl2_unit(sl2_t a) {
-  a[0][0] = gf2p127_from_int(1);
-  a[0][1] = gf2p127_from_int(0);
-  a[1][0] = gf2p127_from_int(0);
-  a[1][1] = gf2p127_from_int(1);
-}
-
-static inline
-char *sl2_hex(char *buf, sl2_t a) {
-  gf2p127_hex(&buf[0],  a[0][0]);
-  gf2p127_hex(&buf[32], a[0][1]);
-  gf2p127_hex(&buf[64], a[1][0]);
-  gf2p127_hex(&buf[96], a[1][1]);
-  return buf;
-}
-
-static const unsigned char b64[64] =
-  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
-
-static const unsigned char unb64[256] = {
-  ['A'] =  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,
-          13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
-  ['a'] = 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
-          39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
-  ['0'] = 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
-  ['-'] = 62, ['_'] = 63
-};
-
-static inline
-void sl2_serialize(sl2_t m, unsigned char buf[86]) {
-
-  int i, j;
-  unsigned char a, b, c, *data = (unsigned char *)m;
-
-  for (i = j = 0; i <= 64 - 3; i += 3, j += 4) {
-    a = data[i + 0];
-    b = data[i + 1];
-    c = data[i + 2];
-    buf[j + 0] = b64[a >> 2];
-    buf[j + 1] = b64[((0x03 & a) << 4) + (b >> 4)];
-    buf[j + 2] = b64[((0x0f & b) << 2) + (c >> 6)];
-    buf[j + 3] = b64[0x3f & c];
-  }
-
-  buf[84] = b64[data[i] >> 2];
-  buf[85] = b64[(0x3 & data[i]) << 4];
-
-}
-
-static inline
-void sl2_unserialize(sl2_t m, unsigned char buf[86]) {
-
-  int i, j;
-  unsigned char a, b, c, d, *data = (unsigned char *)m;
-
-  for (i = j = 0; i <= 86 - 4; i += 4, j += 3) {
-    a = unb64[buf[i + 0]];
-    b = unb64[buf[i + 1]];
-    c = unb64[buf[i + 2]];
-    d = unb64[buf[i + 3]];
-    data[j + 0] = (a << 2) | (b >> 4);
-    data[j + 1] = (b << 4) | (c >> 2);
-    data[j + 2] = (c << 6) | (d);
-  }
-
-  data[63] = (unb64[buf[i]] << 2) | (unb64[buf[i + 1]] >> 4);
-
-}
