packages feed

linearmap-category-0.6.0.0: Math/LinearMap/Category/Instances/Deriving.hs

-- |
-- Module      : Math.LinearMap.Instances.Deriving
-- Copyright   : (c) Justus Sagemüller 2021
-- License     : GPL v3
-- 
-- Maintainer  : (@) jsag $ hvl.no
-- Stability   : experimental
-- Portability : portable
-- 
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE UndecidableInstances       #-}
{-# LANGUAGE TypeOperators              #-}
{-# LANGUAGE NoStarIsType               #-}
{-# LANGUAGE TypeFamilies               #-}
{-# LANGUAGE FunctionalDependencies     #-}
{-# LANGUAGE AllowAmbiguousTypes        #-}
{-# LANGUAGE TypeApplications           #-}
{-# LANGUAGE DataKinds                  #-}
{-# LANGUAGE Rank2Types                 #-}
{-# LANGUAGE ScopedTypeVariables        #-}
{-# LANGUAGE InstanceSigs               #-}
{-# LANGUAGE UnicodeSyntax              #-}
{-# LANGUAGE StandaloneDeriving         #-}
{-# LANGUAGE DeriveGeneric              #-}
{-# LANGUAGE GADTs                      #-}
{-# LANGUAGE DerivingStrategies         #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TemplateHaskell            #-}
{-# LANGUAGE CPP                        #-}
{-# LANGUAGE TupleSections              #-}
{-# LANGUAGE LambdaCase                 #-}
{-# LANGUAGE PatternSynonyms            #-}

module Math.LinearMap.Category.Instances.Deriving
   ( makeLinearSpaceFromBasis, makeFiniteDimensionalFromBasis
   , copyNewtypeInstances, pattern AbstractDualVector
   -- * The instantiated classes
   , AffineSpace(..), Semimanifold(..), PseudoAffine(..)
   , TensorSpace(..), LinearSpace(..), FiniteDimensional(..), SemiInner(..)
   -- * Internals
   , BasisGeneratedSpace(..), LinearSpaceFromBasisDerivationConfig, def
   ) where

import Math.LinearMap.Category.Class
import Math.LinearMap.Category.Instances
import Math.VectorSpace.DimensionAware
import Math.VectorSpace.Docile

import Data.VectorSpace
import Data.AffineSpace
import Data.Basis
import qualified Data.Map as Map
import Data.Tree (Forest)
import Data.MemoTrie
import Data.Hashable
import Data.Void

import Prelude ()
import qualified Prelude as Hask

import Control.Category.Constrained.Prelude hiding (type (+))
import Control.Arrow.Constrained
import Control.Monad.ST (ST)

import Data.Coerce
import Data.Type.Coercion
import Data.Tagged
import Data.Proxy
import qualified Data.Kind as Kind
import Data.Traversable (traverse)
import Data.Default.Class

import qualified Data.Vector.Generic as GArr
import qualified Data.Vector.Generic.Mutable as GMArr

import Math.Manifold.Core.PseudoAffine
import Math.LinearMap.Asserted
import Math.VectorSpace.ZeroDimensional
import Data.VectorSpace.Free

import GHC.Generics (Generic)
import GHC.TypeLits (Nat, KnownNat, natVal, type (+), type (*))

#if MIN_VERSION_singletons(3,0,0)
import GHC.TypeLits.Singletons (withKnownNat)
import Data.Maybe.Singletons (FromJust)
import Prelude.Singletons
#else
import Data.Singletons.TypeLits (withKnownNat)
import Data.Singletons.Prelude
#endif
    (SingI, sing, withSingI, SMaybe(..), SNum(..))
import qualified Math.VectorSpace.DimensionAware.Theorems.MaybeNat as Maybe

import Language.Haskell.TH
import Language.Haskell.TH.Syntax (Name(..), OccName(..)
#if MIN_VERSION_template_haskell(2,17,0)
           , Specificity(..)
#endif
           )
import qualified Language.Haskell.TH.Datatype as D

-- | Given a type @V@ that is already a 'VectorSpace' and 'HasBasis', generate
--   the other class instances that are needed to use the type with this
--   library.
--
--   Prerequisites: (these can often be derived automatically,
--   using either the @newtype@ \/ @via@ strategy or generics \/ anyclass)
--
-- @
-- instance 'AdditiveGroup' V
--
-- instance 'VectorSpace' V where
--   type Scalar V = -- a simple number type, usually 'Double'
--
-- instance 'HasBasis' V where
--   type Basis V = -- a type with an instance of 'HasTrie'
-- @
--
--   Note that the 'Basis' does /not/ need to be orthonormal – in fact it
--   is not necessary to have a scalar product (i.e. an 'InnerSpace' instance)
--   at all.
--
--   The macro, invoked like
-- @
-- makeLinearSpaceFromBasis [t| V |]
-- @
--
--   will then generate @V@-instances for the classes 'Semimanifold',
--   'PseudoAffine', 'AffineSpace', 'TensorSpace' and 'LinearSpace'.
--
--   It also works on parameterised types, in that case you need to use
--   universal-quantification syntax, e.g.
--
-- @
-- makeLinearSpaceFromBasis [t| ∀ n . (KnownNat n) => V n |]
-- @
makeLinearSpaceFromBasis :: Q Type -> DecsQ
makeLinearSpaceFromBasis v
   = makeLinearSpaceFromBasis' def $ deQuantifyType v

data LinearSpaceFromBasisDerivationConfig
  = LinearSpaceFromBasisDerivationConfig
      { _treatBasisAsFinite :: Bool
      }
instance Default LinearSpaceFromBasisDerivationConfig where
  def = LinearSpaceFromBasisDerivationConfig
      { _treatBasisAsFinite = False }


requireExtensions :: [Extension] -> Q ()
requireExtensions reqExts = do
  exts <- extsEnabled
  forM_ reqExts $ \re -> do
    if re`elem`exts
     then return ()
     else reportError $ "This macro requires -X"++show re++"."

-- | More general version of 'makeLinearSpaceFromBasis', that can be used with
--   parameterised types.
makeLinearSpaceFromBasis' :: LinearSpaceFromBasisDerivationConfig
                -> Q ([TyVarBndr
#if MIN_VERSION_template_haskell(2,17,0)
                        Specificity
#endif
                          ], Cxt, Type) -> DecsQ
makeLinearSpaceFromBasis' config cxtv = do
 (cxt,v) <- do
   (_, cxt', v') <- cxtv
   return (pure cxt', pure v')
 
 requireExtensions [ TypeFamilies, MultiParamTypeClasses
                   , ScopedTypeVariables, TypeApplications ]
 
 sequence (
  [ InstanceD Nothing <$> cxt <*> [t|Semimanifold $v|] <*> [d|
         type instance Needle $v = $v
#if !MIN_VERSION_manifolds_core(0,6,0)
         type instance Interior $v = $v
         $(varP 'toInterior) = pure
         $(varP 'fromInterior) = id
         $(varP 'translateP) = Tagged (^+^)
         $(varP 'semimanifoldWitness) = SemimanifoldWitness BoundarylessWitness
#endif
         $(varP '(.+~^)) = (^+^)
      |]
  , InstanceD Nothing <$> cxt <*> [t|PseudoAffine $v|] <*> do
      [d|
         $(varP '(.-~!)) = (^-^)
         $(varP '(.-~.)) = \p q -> pure (p^-^q)
       |]
  , InstanceD Nothing <$> cxt <*> [t|AffineSpace $v|] <*> [d|
         type instance Diff $v = $v
         $(varP '(.+^)) = (^+^)
         $(varP '(.-.)) = (^-^)
       |]
  , if _treatBasisAsFinite config
     then InstanceD Nothing <$> cxt <*> [t|DimensionAware $v|] <*> [d|
         type instance StaticDimension $v = Cardinality (Basis $v)
         $(varP 'dimensionalityWitness) = IsStaticDimensional
       |]
     else InstanceD Nothing <$> cxt <*> [t|DimensionAware $v|] <*> [d|
         type instance StaticDimension $v = 'Nothing
         $(varP 'dimensionalityWitness) = IsFlexibleDimensional
       |]
  , InstanceD Nothing <$> cxt <*> [t|TensorSpace $v|] <*> [d|
         type instance TensorProduct $v w = Basis $v :->: w
         $(varP 'wellDefinedVector) = \v
            -> if v==v then Just v else Nothing
         $(varP 'wellDefinedTensor) = \(Tensor v)
            -> fmap (const $ Tensor v) . traverse (wellDefinedVector . snd) $ enumerate v
         $(varP 'zeroTensor) = Tensor . trie $ const zeroV
         $(varP 'toFlatTensor) = LinearFunction $ Tensor . trie . decompose'
         $(varP 'fromFlatTensor) = LinearFunction $ \(Tensor t)
                 -> recompose $ enumerate t
         $(varP 'scalarSpaceWitness) = ScalarSpaceWitness
         $(varP 'linearManifoldWitness) = LinearManifoldWitness
#if !MIN_VERSION_manifolds_core(0,6,0)
                                 BoundarylessWitness
#endif
         $(varP 'addTensors) = \(Tensor v) (Tensor w)
             -> Tensor $ (^+^) <$> v <*> w
         $(varP 'subtractTensors) = \(Tensor v) (Tensor w)
             -> Tensor $ (^-^) <$> v <*> w
         $(varP 'tensorProduct) = bilinearFunction
           $ \v w -> Tensor . trie $ \bv -> decompose' v bv *^ w
         $(varP 'transposeTensor) = LinearFunction $ \(Tensor t)
              -> sumV [ (tensorProduct-+$>w)-+$>basisValue b
                      | (b,w) <- enumerate t ]
         $(varP 'fmapTensor) = bilinearFunction
           $ \(LinearFunction f) (Tensor t)
                -> Tensor $ fmap f t
         $(varP 'fzipTensorWith) = bilinearFunction
           $ \(LinearFunction f) (Tensor tv, Tensor tw)
                -> Tensor $ liftA2 (curry f) tv tw
         $(varP 'tensorUnsafeFromArrayWithOffset)
             = \i ar -> Tensor . trie
                 $ \bv -> let w = unsafeFromArrayWithOffset
                                   (i + dimensionOf w * lookupBasisIndex bv)
                                   ar
                          in w
         $(varP 'tensorUnsafeWriteArrayWithOffset)
             = \ar i (Tensor t) -> forM_ (zip [0..] enumBasis) $ \(j,bv) -> do
                  let w = untrie t bv
                  unsafeWriteArrayWithOffset ar (i + dimensionOf w * j) w
         $(varP 'coerceFmapTensorProduct) = \_ VSCCoercion
           -> error "Cannot yet coerce tensors defined from a `HasBasis` instance. This would require `RoleAnnotations` on `:->:`. Cf. https://gitlab.haskell.org/ghc/ghc/-/issues/8177"
       |]
  , InstanceD Nothing <$> cxt <*> [t|BasisGeneratedSpace $v|] <*> do
      [d|
         $(varP 'proveTensorProductIsTrie) = \φ -> φ
       |]
  , InstanceD Nothing <$> cxt <*> [t|LinearSpace $v|] <*> [d|
         type instance DualVector $v = DualVectorFromBasis $v
         $(varP 'dualSpaceWitness) = case closedScalarWitness @(Scalar $v) of
              ClosedScalarWitness -> DualSpaceWitness
         $(varP 'linearId) = LinearMap . trie $ basisValue
         $(varP 'tensorId) = tid
             where tid :: ∀ w . (LinearSpace w, Scalar w ~ Scalar $v)
                     => ($v⊗w) +> ($v⊗w)
                   tid = case dualSpaceWitness @w of
                    DualSpaceWitness -> LinearMap . trie $ Tensor . \i
                     -> getTensorProduct $
                       (fmapTensor @(DualVector w)
                           -+$>(LinearFunction $ \w -> Tensor . trie
                                        $ (\j -> if i==j then w else zeroV)
                                         :: $v⊗w))
                        -+$> case linearId @w of
                              LinearMap lw -> Tensor lw :: DualVector w⊗w
         $(varP 'applyDualVector) = bilinearFunction
              $ \(DualVectorFromBasis f) v
                    -> sum [decompose' f i * vi | (i,vi) <- decompose v]
         $(varP 'applyLinear) = bilinearFunction
              $ \(LinearMap f) v
                    -> sumV [vi *^ untrie f i | (i,vi) <- decompose v]
         $(varP 'applyTensorFunctional) = atf
             where atf :: ∀ u . (LinearSpace u, Scalar u ~ Scalar $v)
                    => Bilinear (DualVector ($v ⊗ u))
                                   ($v ⊗ u) (Scalar $v)
                   atf = case dualSpaceWitness @u of
                    DualSpaceWitness -> bilinearFunction
                     $ \(LinearMap f) (Tensor t)
                       -> sum [ (applyDualVector-+$>fi)-+$>untrie t i
                              | (i, fi) <- enumerate f ]
         $(varP 'applyTensorLinMap) = atlm
             where atlm :: ∀ u w . ( LinearSpace u, TensorSpace w
                                   , Scalar u ~ Scalar $v, Scalar w ~ Scalar $v )
                            => Bilinear (($v ⊗ u) +> w) ($v ⊗ u) w
                   atlm = case dualSpaceWitness @u of
                     DualSpaceWitness -> bilinearFunction
                       $ \(LinearMap f) (Tensor t)
                        -> sumV [ (applyLinear-+$>(LinearMap fi :: u+>w))
                                   -+$> untrie t i
                                | (i, Tensor fi) <- enumerate f ]
         $(varP 'useTupleLinearSpaceComponents) = \_ -> usingNonTupleTypeAsTupleError
 
       |]
  ] ++ if _treatBasisAsFinite config then [do
     dim <- pure . VarT <$> newName "n"
     InstanceD Nothing <$> ((:)<$>[t|($dim)#(Basis $v)|]<*>cxt)
                 <*> [t|Dimensional $dim $v|] <*> [d|
         $(varP 'unsafeFromArrayWithOffset)
             = \i ar -> recompose
                         [ (b, ar GArr.! (i+j))
                         | (j, b) <- zip [0..] enumBasis ]
         $(varP 'unsafeWriteArrayWithOffset)
             = \ar i v -> forM_ (zip [0..] enumBasis) $ \(j,b) ->
                  GMArr.unsafeWrite ar (i+j) $ decompose' v b
       |]]
    else []
   )

data FiniteDimensionalFromBasisDerivationConfig
         = FiniteDimensionalFromBasisDerivationConfig
instance Default FiniteDimensionalFromBasisDerivationConfig where
  def = FiniteDimensionalFromBasisDerivationConfig

-- | Like 'makeLinearSpaceFromBasis', but additionally generate instances for
--   'FiniteDimensional' and 'SemiInner'.
makeFiniteDimensionalFromBasis :: Q Type -> DecsQ
makeFiniteDimensionalFromBasis v
   = makeFiniteDimensionalFromBasis' def $ deQuantifyType v

makeFiniteDimensionalFromBasis' :: FiniteDimensionalFromBasisDerivationConfig
              -> Q ([TyVarBndr
#if MIN_VERSION_template_haskell(2,17,0)
                        Specificity
#endif
                       ], Cxt, Type) -> DecsQ
makeFiniteDimensionalFromBasis' _ cxtv = do
 generalInsts <- makeLinearSpaceFromBasis' def{_treatBasisAsFinite=True} cxtv
 (cxt,v) <- do
   (_, cxt', v') <- cxtv
   return (pure cxt', pure v')
 vtnameHash <- abs . hash . show <$> v
 
 fdInsts <- sequence
  [ InstanceD Nothing <$> cxt <*> [t|FiniteDimensional $v|] <*> do
    
    -- This is a hack. Ideally, @newName@ should generate globally unique names,
    -- but it doesn't, so we append a hash of the vector space type.
    -- Cf. https://gitlab.haskell.org/ghc/ghc/-/issues/13054
    subBasisCstr <- newName $ "CompleteBasis"++show vtnameHash

    tySyns <- sequence [
#if MIN_VERSION_template_haskell(2,15,0)
       DataInstD [] Nothing
          <$> (AppT (ConT ''SubBasis) <$> v)
          <*> pure Nothing
          <*> pure [NormalC subBasisCstr []]
          <*> pure []
#else
       DataInstD [] ''SubBasis
          <$> ((:[]) <$> v)
          <*> pure Nothing
          <*> pure [NormalC subBasisCstr []]
          <*> pure []
#endif
     ]
    methods <- [d|
        $(varP 'entireBasis) = $(conE subBasisCstr)
        $(varP 'enumerateSubBasis) =
            \ $(conP subBasisCstr []) -> basisValue . fst <$> enumerate (trie $ const ())
        $(varP 'tensorEquality)
          = \(Tensor t) (Tensor t')  -> and [ti == untrie t' i | (i,ti) <- enumerate t]
        $(varP 'decomposeLinMap) = dlm
           where dlm :: ∀ w . ($v+>w)
                       -> (SubBasis $v, [w]->[w])
                 dlm (LinearMap f) = 
                         ( $(conE subBasisCstr)
                         , (map snd (enumerate f) ++) )
        $(varP 'decomposeLinMapWithin) = dlm
           where dlm :: ∀ w . SubBasis $v
                        -> ($v+>w)
                        -> Either (SubBasis $v, [w]->[w])
                                  ([w]->[w])
                 dlm $(conP subBasisCstr []) (LinearMap f) = 
                         (Right (map snd (enumerate f) ++) )
        $(varP 'recomposeSB) = rsb
           where rsb :: SubBasis $v
                        -> [Scalar $v]
                        -> ($v, [Scalar $v])
                 rsb $(conP subBasisCstr []) cs = first recompose
                           $ zipWith' (,) (fst <$> enumerate (trie $ const ())) cs
        $(varP 'recomposeSBTensor) = rsbt
           where rsbt :: ∀ w . (FiniteDimensional w, Scalar w ~ Scalar $v)
                     => SubBasis $v -> SubBasis w
                        -> [Scalar $v]
                        -> ($v⊗w, [Scalar $v])
                 rsbt $(conP subBasisCstr []) sbw ws = 
                         (first (\iws -> Tensor $ trie (Map.fromList iws Map.!))
                           $ zipConsumeWith' (\i cs' -> first (\c->(i,c))
                                                       $ recomposeSB sbw cs')
                                 (fst <$> enumerate (trie $ const ())) ws)
        $(varP 'recomposeLinMap) = rlm
           where rlm :: ∀ w . SubBasis $v
                        -> [w]
                        -> ($v+>w, [w])
                 rlm $(conP subBasisCstr []) ws = 
                    (first (\iws -> LinearMap $ trie (Map.fromList iws Map.!))
                      $ zipWith' (,) (fst <$> enumerate (trie $ const ())) ws)
        $(varP 'recomposeContraLinMap) = rclm
           where rclm :: ∀ w f . (LinearSpace w, Scalar w ~ Scalar $v, Hask.Functor f)
                      => (f (Scalar w) -> w) -> f (DualVectorFromBasis $v)
                        -> ($v+>w)
                 rclm f vs = 
                       (LinearMap $ trie (\i -> f $ fmap (`decompose'`i) vs))
        $(varP 'recomposeContraLinMapTensor) = rclm
           where rclm :: ∀ u w f
                   . ( FiniteDimensional u, LinearSpace w
                     , Scalar u ~ Scalar $v, Scalar w ~ Scalar $v, Hask.Functor f
                     )
                      => (f (Scalar w) -> w) -> f ($v+>DualVector u)
                        -> (($v⊗u)+>w)
                 rclm f vus = case dualSpaceWitness @u of
                   DualSpaceWitness -> 
                              (
                       (LinearMap $ trie
                           (\i -> case recomposeContraLinMap @u @w @f f
                                      $ fmap (\(LinearMap vu) -> untrie vu (i :: Basis $v)) vus of
                              LinearMap wuff -> Tensor wuff :: DualVector u⊗w )))
        $(varP 'uncanonicallyFromDual) = LinearFunction getDualVectorFromBasis
        $(varP 'uncanonicallyToDual) = LinearFunction DualVectorFromBasis

      |]
    return $ tySyns ++ methods
  , InstanceD Nothing <$> cxt <*> [t|SemiInner $v|] <*> do
     [d|
        $(varP 'dualBasisCandidates)
           = cartesianDualBasisCandidates
               (enumerateSubBasis CompleteDualVBasis)
               (\v -> map (abs . realToFrac . decompose' v . fst)
                       $ enumerate (trie $ const ()) )
      |]
  ]
 return $ generalInsts ++ fdInsts


deQuantifyType :: Q Type -> Q ([TyVarBndr
#if MIN_VERSION_template_haskell(2,17,0)
                                 Specificity
#endif
                                ], Cxt, Type)
deQuantifyType t = do
   t' <- t
   return $ case t' of
     ForallT tvbs cxt instT -> (tvbs, cxt, instT)
     _ -> ([], [], t')


newtype DualVectorFromBasis v = DualVectorFromBasis { getDualVectorFromBasis :: v }
  deriving newtype (Eq, AdditiveGroup, VectorSpace, HasBasis)

instance AdditiveGroup v => Semimanifold (DualVectorFromBasis v) where
  type Needle (DualVectorFromBasis v) = DualVectorFromBasis v
#if !MIN_VERSION_manifolds_core(0,6,0)
  type Interior (DualVectorFromBasis v) = DualVectorFromBasis v
  toInterior = pure
  fromInterior = id
  translateP = Tagged (^+^)
  semimanifoldWitness = SemimanifoldWitness BoundarylessWitness
#endif
  (.+~^) = (^+^)

instance AdditiveGroup v => AffineSpace (DualVectorFromBasis v) where
  type Diff (DualVectorFromBasis v) = DualVectorFromBasis v
  (.+^) = (^+^)
  (.-.) = (^-^)

instance AdditiveGroup v => PseudoAffine (DualVectorFromBasis v) where
  (.-~!) = (^-^)
  p.-~.q = pure (p^-^q)

type family Cardinality b :: Maybe Nat

type instance Cardinality Void = 'Just 0
type instance Cardinality () = 'Just 1
type instance Cardinality (Either a b)
         = Maybe.ZipWithPlus (Cardinality a) (Cardinality b)
type instance Cardinality (a,b)
         = Maybe.ZipWithTimes (Cardinality a) (Cardinality b)

class (KnownNat n, KnownCardinality b, Cardinality b ~ 'Just n) => n#b where
  enumBasis :: [b]
  lookupBasisIndex :: b -> Int

instance 0#Void where
  enumBasis = []
  lookupBasisIndex = absurd
instance 1#() where
  enumBasis = [()]
  lookupBasisIndex () = 0
instance (n#a, m#b, KnownNat nm, nm~(n+m)) => nm # Either a b where
  enumBasis = (Left<$>enumBasis)++(Right<$>enumBasis)
  lookupBasisIndex (Left bl) = lookupBasisIndex bl
  lookupBasisIndex (Right br) = fromIntegral (natVal @n Proxy) + lookupBasisIndex br
instance (n#a, m#b, KnownNat nm, nm~(n*m)) => nm # (a,b) where
  enumBasis = (,)<$>enumBasis<*>enumBasis
  lookupBasisIndex (ba,bb) = fromIntegral (natVal @m Proxy) * lookupBasisIndex ba
                               + lookupBasisIndex bb

type FiniteCardinality b = FromJust (Cardinality b)

data CardinalityWitness b where
  FiniteCardinality :: n#b => CardinalityWitness b
  NonfiniteCardinality :: Cardinality b ~ 'Nothing => CardinalityWitness b

class KnownCardinality b where
  cardinalityWitness :: CardinalityWitness b

instance KnownCardinality Void where cardinalityWitness = FiniteCardinality
instance KnownCardinality () where cardinalityWitness = FiniteCardinality
instance ∀ a b . (KnownCardinality a, KnownCardinality b)
           => KnownCardinality (Either a b) where 
  cardinalityWitness = case (cardinalityWitness @a, cardinalityWitness @b) of
    (FiniteCardinality, FiniteCardinality)
       -> withKnownNat (sing @(FiniteCardinality a)%+sing @(FiniteCardinality b))
                       FiniteCardinality
    (NonfiniteCardinality, _) -> NonfiniteCardinality
    (_, NonfiniteCardinality) -> NonfiniteCardinality
instance ∀ a b . (KnownCardinality a, KnownCardinality b)
           => KnownCardinality (a,b) where 
  cardinalityWitness = case (cardinalityWitness @a, cardinalityWitness @b) of
    (FiniteCardinality, FiniteCardinality)
       -> withKnownNat (sing @(FiniteCardinality a)%*sing @(FiniteCardinality b))
                       FiniteCardinality
    (NonfiniteCardinality, _) -> NonfiniteCardinality
    (_, NonfiniteCardinality) -> NonfiniteCardinality

instance (HasBasis v, KnownCardinality (Basis v))
              => DimensionAware (DualVectorFromBasis v) where
  type StaticDimension (DualVectorFromBasis v) = Cardinality (Basis v)
  dimensionalityWitness = case cardinalityWitness @(Basis v) of
     NonfiniteCardinality -> IsFlexibleDimensional
     FiniteCardinality -> IsStaticDimensional
instance ( HasBasis v, n#Basis v )
              => n`Dimensional`DualVectorFromBasis v where
  unsafeFromArrayWithOffset i ar
     = recompose [ (b, ar GArr.! (i+j))
                 | (j, b) <- zip [0..] enumBasis ]
  unsafeWriteArrayWithOffset ar i v
     = forM_ (zip [0..] enumBasis) $ \(j,b) -> do
         GMArr.unsafeWrite ar (i+j) $ decompose' v b
  

instance ∀ v . ( HasBasis v, Num' (Scalar v)
               , KnownCardinality (Basis v)
               , Scalar (Scalar v) ~ Scalar v
               , HasTrie (Basis v)
               , Eq v )
     => TensorSpace (DualVectorFromBasis v) where
  type TensorProduct (DualVectorFromBasis v) w = Basis v :->: w
  wellDefinedVector v
   | v==v       = Just v
   | otherwise  = Nothing
  wellDefinedTensor (Tensor v)
     = fmap (const $ Tensor v) . traverse (wellDefinedVector . snd) $ enumerate v
  zeroTensor = Tensor . trie $ const zeroV
  toFlatTensor = LinearFunction $ Tensor . trie . decompose'
  fromFlatTensor = LinearFunction $ \(Tensor t)
          -> recompose $ enumerate t
  scalarSpaceWitness = ScalarSpaceWitness
  linearManifoldWitness = LinearManifoldWitness
#if !MIN_VERSION_manifolds_core(0,6,0)
        BoundarylessWitness
#endif
  addTensors (Tensor v) (Tensor w) = Tensor $ (^+^) <$> v <*> w
  subtractTensors (Tensor v) (Tensor w) = Tensor $ (^-^) <$> v <*> w
  tensorProduct = bilinearFunction
    $ \v w -> Tensor . trie $ \bv -> decompose' v bv *^ w
  transposeTensor = LinearFunction $ \(Tensor t)
       -> sumV [ (tensorProduct-+$>w)-+$>basisValue b
               | (b,w) <- enumerate t ]
  fmapTensor = bilinearFunction
    $ \(LinearFunction f) (Tensor t)
         -> Tensor $ fmap f t
  fzipTensorWith = bilinearFunction
    $ \(LinearFunction f) (Tensor tv, Tensor tw)
         -> Tensor $ liftA2 (curry f) tv tw
  tensorUnsafeFromArrayWithOffset :: ∀ w m α
        . (m`Dimensional`w, Scalar w ~ Scalar v, GArr.Vector α (Scalar v))
       => Int -> α (Scalar v)
            -> Tensor (Scalar v) (DualVectorFromBasis v) w
  tensorUnsafeFromArrayWithOffset i ar
      = case cardinalityWitness @(Basis v) of
         FiniteCardinality -> Tensor . trie
           $ lookupBasisIndex >>> \j
               -> unsafeFromArrayWithOffset (i + j * dimension @w) ar
  tensorUnsafeWriteArrayWithOffset :: ∀ w m α σ
        . (m`Dimensional`w, Scalar w ~ Scalar v, GArr.Vector α (Scalar v))
       => GArr.Mutable α σ (Scalar v) -> Int
            -> Tensor (Scalar v) (DualVectorFromBasis v) w -> ST σ ()
  tensorUnsafeWriteArrayWithOffset ar i
      = case cardinalityWitness @(Basis v) of
         FiniteCardinality -> \(Tensor t)
          -> forM_ (zip [0..] enumBasis) $ \(j, bv) ->
              unsafeWriteArrayWithOffset ar (i + j * dimension @w) $ untrie t bv
  coerceFmapTensorProduct _ VSCCoercion
    = error "Cannot yet coerce tensors defined from a `HasBasis` instance. This would require `RoleAnnotations` on `:->:`. Cf. https://gitlab.haskell.org/ghc/ghc/-/issues/8177"


-- | Do not manually instantiate this class. It is used internally
--   by 'makeLinearSpaceFromBasis'.
class ( HasBasis v, Num' (Scalar v)
      , KnownCardinality (Basis v)
      , StaticDimension v ~ (Cardinality (Basis v))
      , SingI (StaticDimension v)
      , LinearSpace v, DualVector v ~ DualVectorFromBasis v)
    => BasisGeneratedSpace v where
  proveTensorProductIsTrie
    :: ∀ w φ . (TensorProduct v w ~ (Basis v :->: w) => φ) -> φ

instance ∀ v . ( BasisGeneratedSpace v
               , Scalar (Scalar v) ~ Scalar v
               , HasTrie (Basis v)
               , Eq v, Eq (Basis v) )
     => LinearSpace (DualVectorFromBasis v) where
  type DualVector (DualVectorFromBasis v) = v
  dualSpaceWitness = case closedScalarWitness @(Scalar v) of
    ClosedScalarWitness -> DualSpaceWitness
  linearId = proveTensorProductIsTrie @v @(DualVectorFromBasis v)
     (LinearMap . trie $ DualVectorFromBasis . basisValue)
  tensorId = tid
   where tid :: ∀ w . (LinearSpace w, Scalar w ~ Scalar v)
           => (DualVectorFromBasis v⊗w) +> (DualVectorFromBasis v⊗w)
         tid = proveTensorProductIsTrie @v @(DualVector w⊗(DualVectorFromBasis v⊗w))
                    ( case dualSpaceWitness @w of
          DualSpaceWitness -> LinearMap . trie $ Tensor . \i
           -> getTensorProduct $
             (fmapTensor @(DualVector w)
                 -+$>(LinearFunction $ \w -> Tensor . trie
                              $ (\j -> if i==j then w else zeroV)
                               :: DualVectorFromBasis v⊗w))
              -+$> case linearId @w of
                    LinearMap lw -> Tensor lw :: DualVector w⊗w )
  applyDualVector = proveTensorProductIsTrie @v @(DualVectorFromBasis v)
     ( bilinearFunction $ \f (DualVectorFromBasis v)
          -> sum [decompose' f i * vi | (i,vi) <- decompose v] )
  applyLinear = ali
   where ali :: ∀ w . (TensorSpace w, Scalar w~Scalar v)
           => Bilinear (DualVectorFromBasis v +> w) (DualVectorFromBasis v) w
         ali = proveTensorProductIsTrie @v @w ( bilinearFunction
            $ \(LinearMap f) (DualVectorFromBasis v)
                -> sumV [vi *^ untrie f i | (i,vi) <- decompose v] )
  applyTensorFunctional = atf
   where atf :: ∀ u . (LinearSpace u, Scalar u ~ Scalar v)
          => Bilinear (DualVector (DualVectorFromBasis v ⊗ u))
                         (DualVectorFromBasis v ⊗ u) (Scalar v)
         atf = proveTensorProductIsTrie @v @(DualVector u) (case dualSpaceWitness @u of
          DualSpaceWitness -> bilinearFunction
           $ \(LinearMap f) (Tensor t)
             -> sum [ (applyDualVector-+$>fi)-+$>untrie t i
                    | (i, fi) <- enumerate f ]
               )
  applyTensorLinMap = atlm
   where atlm :: ∀ u w . ( LinearSpace u, TensorSpace w
                         , Scalar u ~ Scalar v, Scalar w ~ Scalar v )
                  => Bilinear ((DualVectorFromBasis v ⊗ u) +> w)
                               (DualVectorFromBasis v ⊗ u) w
         atlm = proveTensorProductIsTrie @v @(DualVector u⊗w) (
          case dualSpaceWitness @u of
           DualSpaceWitness -> bilinearFunction
             $ \(LinearMap f) (Tensor t)
              -> sumV [ (applyLinear-+$>(LinearMap fi :: u+>w))
                         -+$> untrie t i
                      | (i, Tensor fi) <- enumerate f ]
          )
  useTupleLinearSpaceComponents _ = usingNonTupleTypeAsTupleError
  coerceDoubleDual = VSCCoercion


zipWith' :: (a -> b -> c) -> [a] -> [b] -> ([c], [b])
zipWith' _ _ [] = ([], [])
zipWith' _ [] ys = ([], ys)
zipWith' f (x:xs) (y:ys) = first (f x y :) $ zipWith' f xs ys

zipConsumeWith' :: (a -> [b] -> (c,[b])) -> [a] -> [b] -> ([c], [b])
zipConsumeWith' _ _ [] = ([], [])
zipConsumeWith' _ [] ys = ([], ys)
zipConsumeWith' f (x:xs) ys
    = case f x ys of
       (z, ys') -> first (z :) $ zipConsumeWith' f xs ys'

instance ∀ v . ( BasisGeneratedSpace v, FiniteDimensional v
               , Scalar (Scalar v) ~ Scalar v
               , HasTrie (Basis v), Ord (Basis v)
               , Eq v, Eq (Basis v) )
     => FiniteDimensional (DualVectorFromBasis v) where
  data SubBasis (DualVectorFromBasis v) = CompleteDualVBasis
  entireBasis = CompleteDualVBasis
  enumerateSubBasis CompleteDualVBasis
      = basisValue . fst <$> enumerate (trie $ const ())
  tensorEquality (Tensor t) (Tensor t')
      = and [ti == untrie t' i | (i,ti) <- enumerate t]
  decomposeLinMap = dlm
   where dlm :: ∀ w . (DualVectorFromBasis v+>w)
               -> (SubBasis (DualVectorFromBasis v), [w]->[w])
         dlm (LinearMap f) = proveTensorProductIsTrie @v @w
                 ( CompleteDualVBasis
                 , (map snd (enumerate f) ++) )
  decomposeLinMapWithin = dlm
   where dlm :: ∀ w . SubBasis (DualVectorFromBasis v)
                -> (DualVectorFromBasis v+>w)
                -> Either (SubBasis (DualVectorFromBasis v), [w]->[w])
                          ([w]->[w])
         dlm CompleteDualVBasis (LinearMap f) = proveTensorProductIsTrie @v @w
                 (Right (map snd (enumerate f) ++) )
  recomposeSB = rsb
   where rsb :: SubBasis (DualVectorFromBasis v)
                -> [Scalar v]
                -> (DualVectorFromBasis v, [Scalar v])
         rsb CompleteDualVBasis cs = first recompose
                   $ zipWith' (,) (fst <$> enumerate (trie $ const ())) cs
  recomposeSBTensor = rsbt
   where rsbt :: ∀ w . (FiniteDimensional w, Scalar w ~ Scalar v)
             => SubBasis (DualVectorFromBasis v) -> SubBasis w
                -> [Scalar v]
                -> (DualVectorFromBasis v⊗w, [Scalar v])
         rsbt CompleteDualVBasis sbw ws = proveTensorProductIsTrie @v @w
                 (first (\iws -> Tensor $ trie (Map.fromList iws Map.!))
                   $ zipConsumeWith' (\i cs' -> first (i,) $ recomposeSB sbw cs')
                         (fst <$> enumerate (trie $ const ())) ws)
  recomposeLinMap = rlm
   where rlm :: ∀ w . SubBasis (DualVectorFromBasis v)
                -> [w]
                -> (DualVectorFromBasis v+>w, [w])
         rlm CompleteDualVBasis ws = proveTensorProductIsTrie @v @w
                 (first (\iws -> LinearMap $ trie (Map.fromList iws Map.!))
                   $ zipWith' (,) (fst <$> enumerate (trie $ const ())) ws)
  recomposeContraLinMap = rclm
   where rclm :: ∀ w f . (LinearSpace w, Scalar w ~ Scalar v, Hask.Functor f)
              => (f (Scalar w) -> w) -> f v
                -> (DualVectorFromBasis v+>w)
         rclm f vs = proveTensorProductIsTrie @v @w
               (LinearMap $ trie (\i -> f $ fmap (`decompose'`i) vs))
  recomposeContraLinMapTensor = rclm
   where rclm :: ∀ u w f
           . ( FiniteDimensional u, LinearSpace w
             , Scalar u ~ Scalar v, Scalar w ~ Scalar v, Hask.Functor f
             )
              => (f (Scalar w) -> w) -> f (DualVectorFromBasis v+>DualVector u)
                -> ((DualVectorFromBasis v⊗u)+>w)
         rclm f vus = case dualSpaceWitness @u of
           DualSpaceWitness -> proveTensorProductIsTrie @v @(DualVector u)
                      (proveTensorProductIsTrie @v @(DualVector u⊗w)
               (LinearMap $ trie
                   (\i -> case recomposeContraLinMap @u @w @f f
                              $ fmap (\(LinearMap vu) -> untrie vu (i :: Basis v)) vus of
                      LinearMap wuff -> Tensor wuff :: DualVector u⊗w )))
  uncanonicallyFromDual = LinearFunction DualVectorFromBasis
  uncanonicallyToDual = LinearFunction getDualVectorFromBasis


instance ∀ v . ( BasisGeneratedSpace v, FiniteDimensional v
               , Real (Scalar v), Scalar (Scalar v) ~ Scalar v
               , HasTrie (Basis v), Ord (Basis v)
               , Eq v, Eq (Basis v) )
     => SemiInner (DualVectorFromBasis v) where
  dualBasisCandidates = cartesianDualBasisCandidates
          (enumerateSubBasis entireBasis)
          (\v -> map (abs . realToFrac . decompose' v . fst)
                  $ enumerate (trie $ const ()) )


newtype AbstractDualVector a c
           = AbstractDualVector_ { getConcreteDualVector :: DualVector c }
deriving newtype instance (Eq (DualVector c)) => Eq (AbstractDualVector a c)

class ( Coercible v (VectorSpaceImplementation v)
      , AdditiveGroup (VectorSpaceImplementation v) )
        => AbstractAdditiveGroup v where
  type VectorSpaceImplementation v :: Kind.Type

class (AbstractAdditiveGroup v, VectorSpace (VectorSpaceImplementation v))
        => AbstractVectorSpace v where
  scalarsSameInAbstraction
    :: ( Scalar (VectorSpaceImplementation v) ~ Scalar v
         => ρ ) -> ρ

class ( AbstractVectorSpace v, TensorSpace (VectorSpaceImplementation v)
#if !MIN_VERSION_manifolds_core(0,6,0)
      , Semimanifold v, Interior v ~ v
#endif
      , StaticDimension v ~ StaticDimension (VectorSpaceImplementation v)
      ) => AbstractTensorSpace v where
  sameDimensionalInAbstraction
    :: n`Dimensional`VectorSpaceImplementation v
            => (n`Dimensional`v => ρ) -> ρ
  abstractTensorProductsCoercion
    :: Coercion (TensorProduct v w)
                (TensorProduct (VectorSpaceImplementation v) w)

class ( AbstractTensorSpace v, LinearSpace (VectorSpaceImplementation v)
      , DualVector v
          ~ AbstractDualVector v (VectorSpaceImplementation v) )
    => AbstractLinearSpace v

scalarsSameInAbstractionAndDuals :: ∀ v ρ . AbstractLinearSpace v
     => ( ( Scalar (VectorSpaceImplementation v) ~ Scalar v
          , Scalar (DualVector v) ~ Scalar v
          , Scalar (DualVector (VectorSpaceImplementation v)) ~ Scalar v )
         => ρ ) -> ρ
scalarsSameInAbstractionAndDuals φ
     = case dualSpaceWitness @(VectorSpaceImplementation v) of
        DualSpaceWitness -> scalarsSameInAbstraction @v φ

abstractDualVectorCoercion :: ∀ a s
   . ( LinearSpace (VectorSpaceImplementation a)
     , Scalar (VectorSpaceImplementation a) ~ s )
    => VSCCoercion s (AbstractDualVector a (VectorSpaceImplementation a))
                     (DualVector (VectorSpaceImplementation a))
abstractDualVectorCoercion = case dualSpaceWitness @(VectorSpaceImplementation a) of
   DualSpaceWitness -> VSCCoercion

abstractDualTensorsCoercion :: ∀ a s c w
  . ( AbstractVectorSpace a, LinearSpace c
    , c ~ VectorSpaceImplementation a, TensorSpace w )
      => VSCCoercion s (AbstractDualVector a c⊗w) (DualVector c⊗w)
abstractDualTensorsCoercion = case dualSpaceWitness @c of
  DualSpaceWitness -> VSCCoercion

abstractLinmapCoercion :: ∀ a s c w
  . ( AbstractLinearSpace a, LinearSpace c
    , c ~ VectorSpaceImplementation a, TensorSpace w )
      => VSCCoercion s (AbstractDualVector a c+>w) (DualVector c+>w)
abstractLinmapCoercion = case ( dualSpaceWitness @c
                              , abstractTensorProductsCoercion @a @w ) of
   (DualSpaceWitness, Coercion) -> VSCCoercion

coerceLinearMapCodomain :: ∀ v w x
   . ( LinearSpace v
     , TensorSpace w, TensorSpace x, Coercible w x
     , Scalar w ~ Scalar v, Scalar x ~ Scalar w
     , StaticDimension w ~ StaticDimension x )
         => (v+>w) -> (v+>x)
coerceLinearMapCodomain = case dualSpaceWitness @v of
 DualSpaceWitness -> \(LinearMap m)
     -> LinearMap $ (coerceFmapTensorProduct ([]::[DualVector v])
                            (VSCCoercion :: VSCCoercion (Scalar v) w x) $ m)

instance (Show (DualVector c)) => Show (AbstractDualVector a c) where
  showsPrec p (AbstractDualVector_ φ) = showParen (p>10)
       $ ("AbstractDualVector "++) . showsPrec 11 φ

instance ∀ a c . ( AbstractLinearSpace a, VectorSpaceImplementation a ~ c
                 , AdditiveGroup (DualVector c) )
     => AdditiveGroup (AbstractDualVector a c) where
  zeroV = AbstractDualVector zeroV
  (^+^) = coerce ((^+^) @(DualVector c))
  negateV = coerce (negateV @(DualVector c))

instance ∀ a c . (AbstractLinearSpace a, VectorSpaceImplementation a ~ c
                 , AdditiveGroup (DualVector c))
     => AffineSpace (AbstractDualVector a c) where
  type Diff (AbstractDualVector a c) = AbstractDualVector a c
  (.+^) = coerce ((^+^) @(DualVector c))
  (.-.) = coerce ((^-^) @(DualVector c))

instance ∀ a c . ( AbstractLinearSpace a, VectorSpaceImplementation a ~ c
                 , AdditiveGroup (DualVector c) )
     => Semimanifold (AbstractDualVector a c) where
  type Needle (AbstractDualVector a c) = AbstractDualVector a c
  (.+~^) = (^+^)
#if !MIN_VERSION_manifolds_core(0,6,0)
  type instance Interior (AbstractDualVector a c) = AbstractDualVector a c
  toInterior = pure
  fromInterior = id
  translateP = Tagged (^+^)
  semimanifoldWitness = SemimanifoldWitness BoundarylessWitness
#endif

instance ∀ a c . ( AbstractLinearSpace a, VectorSpaceImplementation a ~ c
                 , AdditiveGroup (DualVector c) )
     => PseudoAffine (AbstractDualVector a c) where
  v.-~.w = pure (v^-^w)
  (.-~!) = (^-^)

instance ∀ a c . ( AbstractLinearSpace a, VectorSpaceImplementation a ~ c
                 , VectorSpace (DualVector c) )
     => VectorSpace (AbstractDualVector a c) where
  type Scalar (AbstractDualVector a c) = Scalar a
  (*^) = scalarsSameInAbstractionAndDuals @a (coerce ((*^) @(DualVector c)))

instance ∀ a c . ( AbstractLinearSpace a, VectorSpaceImplementation a ~ c
                 , TensorSpace (DualVector c) )
     => DimensionAware (AbstractDualVector a c) where
  type StaticDimension (AbstractDualVector a c) = StaticDimension c
  dimensionalityWitness = case dimensionalityWitness @c of
     IsStaticDimensional -> IsStaticDimensional
     IsFlexibleDimensional -> IsFlexibleDimensional
instance ∀ n a c . ( AbstractLinearSpace a
                   , VectorSpaceImplementation a ~ c
                   , n`Dimensional`c
                   , TensorSpace (DualVector c) )
     => n`Dimensional`AbstractDualVector a c where
  knownDimensionalitySing = dimensionalitySing @c
  unsafeFromArrayWithOffset i
     = scalarsSameInAbstraction @a (
           case (dualSpaceWitness @c, dimensionalityWitness @(DualVector c)) of
         (DualSpaceWitness, IsStaticDimensional)
            -> AbstractDualVector_ . unsafeFromArrayWithOffset i )
  unsafeWriteArrayWithOffset ar i
     = scalarsSameInAbstraction @a (
           case (dualSpaceWitness @c, dimensionalityWitness @(DualVector c)) of
         (DualSpaceWitness, IsStaticDimensional)
            -> \(AbstractDualVector_ v) -> unsafeWriteArrayWithOffset ar i v )

instance ∀ a c . ( AbstractLinearSpace a, VectorSpaceImplementation a ~ c
                 , TensorSpace (DualVector c) )
     => TensorSpace (AbstractDualVector a c) where
  type TensorProduct (AbstractDualVector a c) w
          = TensorProduct (DualVector c) w
  scalarSpaceWitness = scalarsSameInAbstractionAndDuals @a
     (case scalarSpaceWitness @(DualVector c) of ScalarSpaceWitness -> ScalarSpaceWitness)
  linearManifoldWitness = scalarsSameInAbstractionAndDuals @a
     (case linearManifoldWitness @(DualVector c) of
#if MIN_VERSION_manifolds_core(0,6,0)
       LinearManifoldWitness -> LinearManifoldWitness
#else
       LinearManifoldWitness BoundarylessWitness
          -> LinearManifoldWitness BoundarylessWitness
#endif
         )
  zeroTensor = zt
   where zt :: ∀ w . (TensorSpace w, Scalar w ~ Scalar a)
            => (AbstractDualVector a c ⊗ w)
         zt = scalarsSameInAbstractionAndDuals @a
                (coerce (zeroTensor @(DualVector c) @w))
  addTensors = at
   where at :: ∀ w . (TensorSpace w, Scalar w ~ Scalar a)
            => (AbstractDualVector a c ⊗ w) -> (AbstractDualVector a c ⊗ w)
                                            -> (AbstractDualVector a c ⊗ w)
         at = scalarsSameInAbstractionAndDuals @a
                (coerce (addTensors @(DualVector c) @w))
  subtractTensors = st
   where st :: ∀ w . (TensorSpace w, Scalar w ~ Scalar a)
            => (AbstractDualVector a c ⊗ w) -> (AbstractDualVector a c ⊗ w)
                                            -> (AbstractDualVector a c ⊗ w)
         st = scalarsSameInAbstractionAndDuals @a
                (coerce (subtractTensors @(DualVector c) @w))
  negateTensor = nt
   where nt :: ∀ w . (TensorSpace w, Scalar w ~ Scalar a)
            => (AbstractDualVector a c ⊗ w) -+> (AbstractDualVector a c ⊗ w)
         nt = scalarsSameInAbstractionAndDuals @a
                (coerce (negateTensor @(DualVector c) @w))
  scaleTensor = st
   where st :: ∀ w . (TensorSpace w, Scalar w ~ Scalar a)
            => Bilinear (Scalar a) (AbstractDualVector a c ⊗ w)
                                   (AbstractDualVector a c ⊗ w)
         st = scalarsSameInAbstractionAndDuals @a
                (coerce (scaleTensor @(DualVector c) @w))
  toFlatTensor = scalarsSameInAbstractionAndDuals @a
                    ( coerce (toFlatTensor @(DualVector c)) )
  fromFlatTensor = scalarsSameInAbstractionAndDuals @a
                    ( coerce (fromFlatTensor @(DualVector c)) )
  tensorProduct = tp
   where tp :: ∀ w . (TensorSpace w, Scalar w ~ Scalar a)
            => Bilinear (AbstractDualVector a c) w
                                   (AbstractDualVector a c ⊗ w)
         tp = scalarsSameInAbstractionAndDuals @a
                (coerce (tensorProduct @(DualVector c) @w))
  wellDefinedVector (AbstractDualVector v) = AbstractDualVector <$> wellDefinedVector v
  wellDefinedTensor = wdt
   where wdt :: ∀ w . (TensorSpace w, Scalar w ~ Scalar a)
            => (AbstractDualVector a c ⊗ w) -> Maybe (AbstractDualVector a c ⊗ w)
         wdt = scalarsSameInAbstractionAndDuals @a
                (coerce (wellDefinedTensor @(DualVector c) @w))
  transposeTensor = scalarsSameInAbstractionAndDuals @a tt
   where tt :: ∀ w . ( TensorSpace w, Scalar w ~ Scalar a
                     , Scalar (DualVector c) ~ Scalar a )
            => (AbstractDualVector a c ⊗ w) -+> (w ⊗ AbstractDualVector a c)
         tt = case dualSpaceWitness @c of
           DualSpaceWitness -> case coerceFmapTensorProduct @w []
                       (VSCCoercion @(DualVector c) @(AbstractDualVector a c)) of
             Coercion -> coerce (transposeTensor @(DualVector c) @w)
  fmapTensor = ft
   where ft :: ∀ w x . ( TensorSpace w, Scalar w ~ Scalar a
                       , TensorSpace x, Scalar x ~ Scalar a )
           => Bilinear (w-+>x) (AbstractDualVector a c ⊗ w) (AbstractDualVector a c ⊗ x) 
         ft = scalarsSameInAbstractionAndDuals @a
                 (coerce $ fmapTensor @(DualVector c) @w @x)
  fzipTensorWith = ft
   where ft :: ∀ u w x . ( TensorSpace w, Scalar w ~ Scalar a
                         , TensorSpace u, Scalar u ~ Scalar a
                         , TensorSpace x, Scalar x ~ Scalar a )
           => Bilinear ((w,x)-+>u)
                       (AbstractDualVector a c ⊗ w, AbstractDualVector a c ⊗ x)
                       (AbstractDualVector a c ⊗ u) 
         ft = scalarsSameInAbstractionAndDuals @a
                 (coerce $ fzipTensorWith @(DualVector c) @u @w @x)
  tensorUnsafeFromArrayWithOffset :: ∀ w m α
          . ( m`Dimensional`w, TensorSpace w, Scalar w ~ Scalar a
            , GArr.Vector α (Scalar a) )
          => Int -> α (Scalar a) -> Tensor (Scalar a) (AbstractDualVector a c) w
  tensorUnsafeFromArrayWithOffset i
      = case dimensionalityWitness @(DualVector c) of
         IsStaticDimensional -> scalarsSameInAbstractionAndDuals @a
          (coerce . tensorUnsafeFromArrayWithOffset @(DualVector c) @w i)
  tensorUnsafeWriteArrayWithOffset :: ∀ w m α σ
          . ( m`Dimensional`w, TensorSpace w, Scalar w ~ Scalar a
            , GArr.Vector α (Scalar a) )
          => GArr.Mutable α σ (Scalar a) -> Int
            -> Tensor (Scalar a) (AbstractDualVector a c) w -> ST σ ()
  tensorUnsafeWriteArrayWithOffset ar
      = case dimensionalityWitness @(DualVector c) of
         IsStaticDimensional -> scalarsSameInAbstractionAndDuals @a
          (coerce (tensorUnsafeWriteArrayWithOffset @(DualVector c) @w ar))
  coerceFmapTensorProduct _ = scalarsSameInAbstractionAndDuals @a
        (coerceFmapTensorProduct @(DualVector c) [])

witnessAbstractDualVectorTensorSpacyness
  :: ∀ a c r . ( AbstractLinearSpace a, VectorSpaceImplementation a ~ c
               , LinearSpace a, LinearSpace c
               , TensorSpace (DualVector c), Num (Scalar a) )
    => (( TensorSpace (AbstractDualVector a c)
        , LinearSpace (DualVector c)
        , Scalar (DualVector c) ~ Scalar a )
            => r) -> r
witnessAbstractDualVectorTensorSpacyness φ = case dualSpaceWitness @c of
   DualSpaceWitness -> scalarsSameInAbstraction @a φ

instance ∀ a c . ( AbstractLinearSpace a, VectorSpaceImplementation a ~ c
                 , LinearSpace a, LinearSpace c
                 , TensorSpace (DualVector c), Num (Scalar a) )
     => LinearSpace (AbstractDualVector a c) where
  type DualVector (AbstractDualVector a c) = a
  dualSpaceWitness = case (dualSpaceWitness @c, scalarSpaceWitness @c) of
    (DualSpaceWitness, ScalarSpaceWitness)
        -> scalarsSameInAbstraction @a DualSpaceWitness
  linearId = witnessAbstractDualVectorTensorSpacyness @a @c
       (symVSC (abstractLinmapCoercion @a)
           $ sampleLinearFunction @(DualVector c)
           -+$> linearFunction AbstractDualVector)
  tensorId = tid
   where tid :: ∀ w . (LinearSpace w, Scalar w ~ Scalar a)
            => (AbstractDualVector a c ⊗ w) +> (AbstractDualVector a c ⊗ w) 
         tid = case ( dualSpaceWitness @w, dualSpaceWitness @c ) of
          (DualSpaceWitness, DualSpaceWitness)
            -> witnessAbstractDualVectorTensorSpacyness @a (
                let LinearMap ida = linearId :: (DualVector c ⊗ w) +> (DualVector c ⊗ w)
                in LinearMap $ 
                    sym (abstractTensorProductsCoercion @a
                            @(DualVector w ⊗ (AbstractDualVector a c⊗w)) )
                    . coerceFmapTensorProduct ([]::[c ⊗ DualVector w])
                       (VSCCoercion @(DualVector c ⊗ w) @(AbstractDualVector a c ⊗ w))
                    $ ida )
  applyDualVector = scalarsSameInAbstraction @a ( bilinearFunction
     $ \v (AbstractDualVector d) -> (applyDualVector -+$> d)-+$>(coerce v::c) )
  applyLinear = witnessAbstractDualVectorTensorSpacyness @a ( LinearFunction
     $ \f -> (applyLinear -+$> abstractLinmapCoercion $ f) . LinearFunction coerce
      )
  applyTensorFunctional = atf
   where atf :: ∀ u . ( LinearSpace u, Scalar u ~ Scalar a )
                  => Bilinear (DualVector (AbstractDualVector a c⊗u))
                                       (AbstractDualVector a c⊗u) (Scalar a)
         atf = case (scalarSpaceWitness @a, dualSpaceWitness @u) of
          (ScalarSpaceWitness, DualSpaceWitness)
            -> witnessAbstractDualVectorTensorSpacyness @a (
                LinearFunction $ \f
                 -> (applyTensorFunctional @(DualVector c)
                         -+$> abstractLinmapCoercion @a $ f)
                      . LinearFunction (abstractDualTensorsCoercion @a $)
              )
  applyTensorLinMap = atlm
   where atlm :: ∀ u w . ( LinearSpace u, Scalar u ~ Scalar a
                         , TensorSpace w, Scalar w ~ Scalar a )
                  => Bilinear ((AbstractDualVector a c⊗u)+>w)
                                       (AbstractDualVector a c⊗u) w
         atlm = case (dualSpaceWitness @c, dualSpaceWitness @u) of
          (DualSpaceWitness, DualSpaceWitness)
                      -> witnessAbstractDualVectorTensorSpacyness @a (
             LinearFunction $ \(LinearMap f) ->
                     let f' = LinearMap (abstractTensorProductsCoercion
                                           @a @((Tensor (Scalar a) (DualVector u) w))
                                          $ coerce f) :: (DualVector c⊗u)+>w
                     in (applyTensorLinMap @(DualVector c)-+$>f')
                              . LinearFunction (abstractDualTensorsCoercion @a $)
           )
  useTupleLinearSpaceComponents = \_ -> usingNonTupleTypeAsTupleError
  coerceDoubleDual = VSCCoercion

instance ∀ a c . ( AbstractLinearSpace a, VectorSpaceImplementation a ~ c
                 , FiniteDimensional a, FiniteDimensional c
                 , TensorSpace (DualVector c), Eq (DualVector c), Num (Scalar a) )
     => FiniteDimensional (AbstractDualVector a c) where
  newtype SubBasis (AbstractDualVector a c) = AbstractDualVectorSubBasis {
                        getAbstractDualVectorSubBasis :: SubBasis (DualVector c) }
  dualFinitenessWitness = scalarsSameInAbstraction @a
       ( case (scalarSpaceWitness @a, dualSpaceWitness @a) of
        (ScalarSpaceWitness, DualSpaceWitness)
            -> DualFinitenessWitness DualSpaceWitness )
  entireBasis = case dualFinitenessWitness @c of
    DualFinitenessWitness _ -> coerce (entireBasis @(DualVector c))
  enumerateSubBasis = case dualFinitenessWitness @c of
    DualFinitenessWitness _ 
          -> coerce (enumerateSubBasis @(DualVector c))
  decomposeLinMap = scalarsSameInAbstraction @a dclm
   where dclm :: ∀ w . (LSpace w, Scalar w ~ Scalar c)
            => (AbstractDualVector a c +> w)
                  -> (SubBasis (AbstractDualVector a c), DList w)
         dclm = case (dualFinitenessWitness @c, abstractTensorProductsCoercion @a @w) of
          (DualFinitenessWitness DualSpaceWitness, Coercion)
              -> coerce (decomposeLinMap @(DualVector c) @w)
  decomposeLinMapWithin = scalarsSameInAbstraction @a dclm
   where dclm :: ∀ w . (LSpace w, Scalar w ~ Scalar c)
            => SubBasis (AbstractDualVector a c) -> (AbstractDualVector a c +> w)
                   -> Either (SubBasis (AbstractDualVector a c), DList w) (DList w)
         dclm = case (dualFinitenessWitness @c, abstractTensorProductsCoercion @a @w) of
          (DualFinitenessWitness DualSpaceWitness, Coercion)
              -> coerce (decomposeLinMapWithin @(DualVector c) @w)
  recomposeSB = case dualFinitenessWitness @c of
          DualFinitenessWitness DualSpaceWitness -> scalarsSameInAbstraction @a
                                (coerce $ recomposeSB @(DualVector c))
  recomposeSBTensor = scalarsSameInAbstraction @a rst
   where rst :: ∀ w . (FiniteDimensional w, Scalar w ~ Scalar c)
           => SubBasis (AbstractDualVector a c) -> SubBasis w -> [Scalar c]
                  -> (AbstractDualVector a c ⊗ w, [Scalar c])
         rst = case dualFinitenessWitness @c of
          DualFinitenessWitness DualSpaceWitness
                  -> coerce (recomposeSBTensor @(DualVector c) @w)
  recomposeLinMap = scalarsSameInAbstraction @a rlm
   where rlm :: ∀ w . (LSpace w, Scalar w ~ Scalar c)
           => SubBasis (AbstractDualVector a c)
                 -> [w] -> (AbstractDualVector a c +> w, [w])
         rlm = case (dualFinitenessWitness @c, abstractTensorProductsCoercion @a @w) of
          (DualFinitenessWitness DualSpaceWitness, Coercion)
              -> coerce (recomposeLinMap @(DualVector c) @w)
  recomposeContraLinMap = scalarsSameInAbstraction @a rclm
   where rclm :: ∀ f w . (LinearSpace w, Scalar w ~ Scalar c, Hask.Functor f)
           => (f (Scalar w) -> w) -> f a -> AbstractDualVector a c +> w
         rclm = case (dualFinitenessWitness @c, abstractTensorProductsCoercion @a @w) of
          (DualFinitenessWitness DualSpaceWitness, Coercion) -> \f ->
             (coerce $ recomposeContraLinMap @(DualVector c) @w @f) f
               . fmap (coerce :: a -> c)
  recomposeContraLinMapTensor = scalarsSameInAbstraction @a rclmt
   where rclmt :: ∀ f w u . ( LinearSpace w, Scalar w ~ Scalar c
                            , FiniteDimensional u, Scalar u ~ Scalar c
                            , Hask.Functor f )
           => (f (Scalar w) -> w) -> f (AbstractDualVector a c+>DualVector u)
                   -> (AbstractDualVector a c⊗u) +> w
         rclmt = scalarsSameInAbstraction @a (case dualSpaceWitness @u of
           DualSpaceWitness ->
                 case ( dualFinitenessWitness @c
                      , abstractTensorProductsCoercion @a @(DualVector u)
                      , abstractTensorProductsCoercion @a
                          @(Tensor (Scalar a) (DualVector u) w) ) of
            (DualFinitenessWitness DualSpaceWitness, Coercion, Coercion) -> \f ->
              (coerce $ recomposeContraLinMapTensor @(DualVector c) @u @w @f) f
                . fmap (coerce :: (AbstractDualVector a c+>DualVector u)
                                    -> (DualVector c+>DualVector u))
          )
  uncanonicallyFromDual = case dualFinitenessWitness @c of
    DualFinitenessWitness DualSpaceWitness
        -> coerce (uncanonicallyFromDual @(DualVector c))
  uncanonicallyToDual = case dualFinitenessWitness @c of
    DualFinitenessWitness DualSpaceWitness
        -> coerce (uncanonicallyToDual @(DualVector c))
  tensorEquality = te
   where te :: ∀ w . (TensorSpace w, Eq w, Scalar w ~ Scalar a)
                => (AbstractDualVector a c ⊗ w) -> (AbstractDualVector a c ⊗ w) -> Bool
         te = case dualFinitenessWitness @c of
           DualFinitenessWitness _ -> scalarsSameInAbstractionAndDuals @a
                (coerce (tensorEquality @(DualVector c) @w))

instance ∀ a c . ( AbstractLinearSpace a, VectorSpaceImplementation a ~ c
                 , SemiInner a, LinearSpace c, SemiInner (DualVector c)
                 , TensorSpace (DualVector c), Eq (DualVector c), Num (Scalar a) )
     => SemiInner (AbstractDualVector a c) where
  dualBasisCandidates = case dualSpaceWitness @c of
    DualSpaceWitness -> coerce (dualBasisCandidates @(DualVector c))
  tensorDualBasisCandidates = scalarsSameInAbstraction @a tdbc
   where tdbc :: ∀ w . (SemiInner w, Scalar w ~ Scalar c)
            => [(Int, AbstractDualVector a c ⊗ w)]
             -> Forest (Int, AbstractDualVector a c +> DualVector w)
         tdbc = case (dualSpaceWitness @c, dualSpaceWitness @w) of
           (DualSpaceWitness, DualSpaceWitness)
               -> case abstractTensorProductsCoercion @a @(DualVector w) of
             Coercion -> coerce (tensorDualBasisCandidates @(DualVector c) @w)
  symTensorDualBasisCandidates = case dualSpaceWitness @c of
     DualSpaceWitness -> scalarsSameInAbstraction @a
          ( case ( coerceFmapTensorProduct @c [] (VSCCoercion @a @c)
                          . abstractTensorProductsCoercion @a @a
                 , coerceFmapTensorProduct @(DualVector c) []
                      (VSCCoercion @(AbstractDualVector a c) @(DualVector c)) ) of
             (Coercion, Coercion)
               -> coerce (symTensorDualBasisCandidates @(DualVector c))
          )

 

pattern AbstractDualVector
    :: AbstractLinearSpace v => DualVector (VectorSpaceImplementation v) -> DualVector v
pattern AbstractDualVector φ = AbstractDualVector_ φ



abstractVS_zeroV :: ∀ v .
    (AbstractAdditiveGroup v)
       => v
abstractVS_zeroV = coerce (zeroV @(VectorSpaceImplementation v))

abstractVS_addvs :: ∀ v .
    (AbstractAdditiveGroup v)
       => v -> v -> v
abstractVS_addvs = coerce ((^+^) @(VectorSpaceImplementation v))

abstractVS_subvs :: ∀ v .
    (AbstractAdditiveGroup v)
       => v -> v -> v
abstractVS_subvs = coerce ((^-^) @(VectorSpaceImplementation v))

abstractVS_negateV :: ∀ v .
    (AbstractAdditiveGroup v)
       => v -> v
abstractVS_negateV = coerce (negateV @(VectorSpaceImplementation v))

abstractVS_scalev :: ∀ v .
    (AbstractVectorSpace v)
       => Scalar v -> v -> v
abstractVS_scalev = scalarsSameInAbstraction @v
  ( coerce ((*^) @(VectorSpaceImplementation v)) )

abstractVS_innerProd :: ∀ v .
    (AbstractVectorSpace v, InnerSpace (VectorSpaceImplementation v))
       => v -> v -> Scalar v
abstractVS_innerProd = scalarsSameInAbstraction @v
  ( coerce ((<.>) @(VectorSpaceImplementation v)) )

abstractVS_dimensionalityWitness
    :: ∀ v . ( AbstractTensorSpace v
             , DimensionAware (VectorSpaceImplementation v) )
      => DimensionalityWitness v
abstractVS_dimensionalityWitness
   = case dimensionalityWitness @(VectorSpaceImplementation v) of
           IsStaticDimensional
             -> sameDimensionalInAbstraction @v IsStaticDimensional
           IsFlexibleDimensional -> IsFlexibleDimensional

abstractVS_scalarsSameInAbstraction :: ∀ v ρ .
    ( AbstractVectorSpace v
    , Scalar (VectorSpaceImplementation v) ~ Scalar v
    )
   => ( Scalar (VectorSpaceImplementation v) ~ Scalar v
         => ρ ) -> ρ
abstractVS_scalarsSameInAbstraction φ
   = φ

abstractVS_scalarSpaceWitness :: ∀ v .
    (AbstractTensorSpace v)
       => ScalarSpaceWitness v
abstractVS_scalarSpaceWitness
   = case scalarSpaceWitness @(VectorSpaceImplementation v) of
      ScalarSpaceWitness -> scalarsSameInAbstraction @v ScalarSpaceWitness 

abstractVS_linearManifoldWitness :: ∀ v .
    ( AbstractTensorSpace v, AffineSpace v, Needle v ~ v, Diff v ~ v
    )
       => LinearManifoldWitness v
abstractVS_linearManifoldWitness
   = case linearManifoldWitness @(VectorSpaceImplementation v) of
#if MIN_VERSION_manifolds_core(0,6,0)
           LinearManifoldWitness -> LinearManifoldWitness
#else
           LinearManifoldWitness BoundarylessWitness
                -> LinearManifoldWitness BoundarylessWitness
#endif

abstractVS_toFlatTensor :: ∀ v .
    ( AbstractTensorSpace v
    , Coercible (TensorProduct v (Scalar v))
                (TensorProduct (VectorSpaceImplementation v)
                               (Scalar (VectorSpaceImplementation v))) )
       => v -+> (v ⊗ Scalar v)
abstractVS_toFlatTensor = coerce (toFlatTensor @(VectorSpaceImplementation v))

abstractVS_fromFlatTensor :: ∀ v .
    ( AbstractTensorSpace v
    , Coercible (TensorProduct v (Scalar v))
                (TensorProduct (VectorSpaceImplementation v)
                               (Scalar (VectorSpaceImplementation v))) )
       => (v ⊗ Scalar v) -+> v
abstractVS_fromFlatTensor = coerce (fromFlatTensor @(VectorSpaceImplementation v))

abstractVS_zeroTensor :: ∀ v w
       . ( AbstractTensorSpace v
         , TensorSpace w, Scalar w ~ Scalar v
         , Coercible (TensorProduct v w)
                     (TensorProduct (VectorSpaceImplementation v) w) )
           => (v ⊗ w)
abstractVS_zeroTensor = scalarsSameInAbstraction @v
  (coerce (zeroTensor @(VectorSpaceImplementation v) @w))

abstractVS_addTensors :: ∀ v w
       . ( AbstractTensorSpace v
         , TensorSpace w, Scalar w ~ Scalar v
         , Coercible (TensorProduct v w)
                     (TensorProduct (VectorSpaceImplementation v) w) )
           => (v ⊗ w) -> (v ⊗ w) -> (v ⊗ w)
abstractVS_addTensors = scalarsSameInAbstraction @v
  (coerce (addTensors @(VectorSpaceImplementation v) @w))

abstractVS_subtractTensors :: ∀ v w
       . ( AbstractTensorSpace v
         , TensorSpace w, Scalar w ~ Scalar v
         , Coercible (TensorProduct v w)
                     (TensorProduct (VectorSpaceImplementation v) w) )
           => (v ⊗ w) -> (v ⊗ w) -> (v ⊗ w)
abstractVS_subtractTensors = scalarsSameInAbstraction @v
  (coerce (subtractTensors @(VectorSpaceImplementation v) @w))

abstractVS_scaleTensor :: ∀ v w
       . ( AbstractTensorSpace v
         , TensorSpace w, Scalar w ~ Scalar v
         , Coercible (TensorProduct v w)
                     (TensorProduct (VectorSpaceImplementation v) w) )
           => Bilinear (Scalar v) (v ⊗ w) (v ⊗ w)
abstractVS_scaleTensor = scalarsSameInAbstraction @v
  (coerce (scaleTensor @(VectorSpaceImplementation v) @w))

abstractVS_negateTensor :: ∀ v w
       . ( AbstractTensorSpace v
         , TensorSpace w, Scalar w ~ Scalar v
         , Coercible (TensorProduct v w)
                     (TensorProduct (VectorSpaceImplementation v) w) )
           => (v ⊗ w) -+> (v ⊗ w)
abstractVS_negateTensor = scalarsSameInAbstraction @v
  (coerce (negateTensor @(VectorSpaceImplementation v) @w))

abstractVS_wellDefinedVector :: ∀ v
         . ( AbstractTensorSpace v
           ) => v -> Maybe v
abstractVS_wellDefinedVector = coerce (wellDefinedVector @(VectorSpaceImplementation v))

abstractVS_wellDefinedTensor :: ∀ v w
         . ( AbstractTensorSpace v
           , TensorSpace w, Scalar w ~ Scalar v
           ) => (v ⊗ w) -> Maybe (v ⊗ w)
abstractVS_wellDefinedTensor
    = scalarsSameInAbstraction @v
        (case abstractTensorProductsCoercion @v @w of
           Coercion -> coerce (wellDefinedTensor @(VectorSpaceImplementation v) @w))

abstractVS_tensorProduct :: ∀ v w . ( AbstractTensorSpace v
           , TensorSpace w, Scalar w ~ Scalar v
           ) => Bilinear v w (v ⊗ w)
abstractVS_tensorProduct = scalarsSameInAbstraction @v
    ( case ( abstractTensorProductsCoercion @v @w ) of
       Coercion -> coerce (tensorProduct @(VectorSpaceImplementation v) @w) )

abstractVS_transposeTensor :: ∀ v w . ( AbstractTensorSpace v
           , TensorSpace v
           , TensorSpace w, Scalar w ~ Scalar v
           ) => (v ⊗ w) -+> (w ⊗ v)
abstractVS_transposeTensor
    = scalarsSameInAbstraction @v ( case
           ( abstractTensorProductsCoercion @v @w
           , coerceFmapTensorProduct @w []
                (VSCCoercion @(VectorSpaceImplementation v) @(v)) ) of
   (Coercion, Coercion) -> scalarsSameInAbstraction @v
      (coerce (transposeTensor @(VectorSpaceImplementation v) @w))
  )

abstractVS_fmapTensor :: ∀ v u w . ( AbstractTensorSpace v
           , TensorSpace u, Scalar u ~ Scalar v
           , TensorSpace w, Scalar w ~ Scalar v )
                   => Bilinear (u -+> w) (v ⊗ u) (v ⊗ w)
abstractVS_fmapTensor
   = scalarsSameInAbstraction @v
       ( case ( abstractTensorProductsCoercion @v @u
              , abstractTensorProductsCoercion @v @w ) of
           (Coercion, Coercion)
              -> coerce (fmapTensor @(VectorSpaceImplementation v) @u @w) )

abstractVS_fzipTensorsWith :: ∀ v u w x . ( AbstractTensorSpace v
           , TensorSpace u, Scalar u ~ Scalar v
           , TensorSpace w, Scalar w ~ Scalar v
           , TensorSpace x, Scalar x ~ Scalar v )
                   => Bilinear ((w, x) -+> u) (v ⊗ w, v ⊗ x) (v ⊗ u)
abstractVS_fzipTensorsWith = scalarsSameInAbstraction @v
       ( case ( abstractTensorProductsCoercion @v @u
              , abstractTensorProductsCoercion @v @w
              , abstractTensorProductsCoercion @v @x ) of
           (Coercion, Coercion, Coercion)
              -> coerce (fzipTensorWith @(VectorSpaceImplementation v) @u @w @x)
        )

abstractVS_unsafeFromArrayWithOffset :: ∀ v n α
         . ( AbstractTensorSpace v, n`Dimensional`VectorSpaceImplementation v
           , GArr.Vector α (Scalar v) )
                   => Int -> α (Scalar v) -> v
abstractVS_unsafeFromArrayWithOffset = scalarsSameInAbstraction @v
    (\i -> coerce
       . unsafeFromArrayWithOffset @n @(VectorSpaceImplementation v) i )

abstractVS_unsafeWriteArrayWithOffset :: ∀ v n α σ
         . ( AbstractTensorSpace v, n`Dimensional`VectorSpaceImplementation v
           , GArr.Vector α (Scalar v) )
                   => GArr.Mutable α σ (Scalar v) -> Int -> v -> ST σ ()
abstractVS_unsafeWriteArrayWithOffset = scalarsSameInAbstraction @v
    (\ar -> coerce (unsafeWriteArrayWithOffset @n @(VectorSpaceImplementation v) ar))

abstractVS_tensorUnsafeFromArrayWithOffset :: ∀ v w n m α
         . ( AbstractTensorSpace v, TensorSpace v
           , n`Dimensional`VectorSpaceImplementation v
           , TensorSpace w, m`Dimensional`w, Scalar w ~ Scalar v
           , GArr.Vector α (Scalar v) )
                   => Int -> α (Scalar v) -> (v⊗w)
abstractVS_tensorUnsafeFromArrayWithOffset = scalarsSameInAbstraction @v
    (\i -> arr (symVSC abstractTensorsCoercion)
       . tensorUnsafeFromArrayWithOffset @(VectorSpaceImplementation v) i )

abstractVS_tensorUnsafeWriteArrayWithOffset :: ∀ v w n m α σ
         . ( AbstractTensorSpace v, TensorSpace v
           , n`Dimensional`VectorSpaceImplementation v
           , TensorSpace w, m`Dimensional`w, Scalar w ~ Scalar v
           , GArr.Vector α (Scalar v) )
                   => GArr.Mutable α σ (Scalar v) -> Int -> (v⊗w) -> ST σ ()
abstractVS_tensorUnsafeWriteArrayWithOffset = scalarsSameInAbstraction @v
    (\ar i -> 
       tensorUnsafeWriteArrayWithOffset @(VectorSpaceImplementation v) ar i
        . arr abstractTensorsCoercion )

abstractVS_coerceFmapTensorProduct :: ∀ v u w s p .
         ( AbstractTensorSpace v, TensorSpace u, TensorSpace w
         , Scalar (VectorSpaceImplementation v) ~ s
         , Scalar u ~ s
         , Scalar w ~ s
         ) => p v -> VSCCoercion s u w
                  -> Coercion (TensorProduct v u) (TensorProduct v w)
abstractVS_coerceFmapTensorProduct _ crc
      = case ( abstractTensorProductsCoercion @v @u
             , abstractTensorProductsCoercion @v @w
             , coerceFmapTensorProduct @(VectorSpaceImplementation v) [] crc ) of
          (Coercion, Coercion, Coercion) -> Coercion

abstractVS_dualSpaceWitness :: ∀ v . (AbstractLinearSpace v
        , LinearSpace v
        , LinearSpace (VectorSpaceImplementation v))
     => DualSpaceWitness v
abstractVS_dualSpaceWitness
      = scalarsSameInAbstraction @v
  ( case dualSpaceWitness @(VectorSpaceImplementation v) of
      DualSpaceWitness -> DualSpaceWitness
   )

abstractVS_linearId :: ∀ v . ( AbstractLinearSpace v, TensorSpace v
           , LinearSpace (VectorSpaceImplementation v) )
                   => v +> v
abstractVS_linearId = case dualSpaceWitness @(VectorSpaceImplementation v) of
 DualSpaceWitness -> scalarsSameInAbstraction @v ( case coerceFmapTensorProduct
                             @(DualVector (VectorSpaceImplementation v)) []
                             (VSCCoercion @v @(VectorSpaceImplementation v)) of
   Coercion -> coerce (linearId @(VectorSpaceImplementation v))
  )

abstractTensorsCoercion :: ∀ v w s . AbstractTensorSpace v
    => VSCCoercion s (v⊗w) (VectorSpaceImplementation v⊗w)
abstractTensorsCoercion = case abstractTensorProductsCoercion @v @w of
   Coercion -> VSCCoercion

abstractVS_tensorId :: ∀ v w . ( AbstractLinearSpace v
           , TensorSpace v
           , LinearSpace (VectorSpaceImplementation v)
           , LinearSpace w, Scalar w ~ Scalar v )
                   => (v ⊗ w) +> (v ⊗ w) 
abstractVS_tensorId = scalarsSameInAbstraction @v
  (case (dualSpaceWitness @w, dualSpaceWitness @(VectorSpaceImplementation v)) of
     (DualSpaceWitness, DualSpaceWitness)
       -> case coerceFmapTensorProduct @(DualVector w) []
                 $ abstractTensorsCoercion @v @w of
         Coercion
           -> case ( coerceFmapTensorProduct 
                      @(DualVector (VectorSpaceImplementation v)) []
                      (VSCCoercion :: VSCCoercion (Scalar v)
                          (Tensor (Scalar v) (DualVector w) (Tensor (Scalar v) v w))
                          (Tensor (Scalar v)
                                  (DualVector w)
                                  (Tensor (Scalar v)
                                          (VectorSpaceImplementation v) w)))
                   ) of
            Coercion
               -> coerce (tensorId @(VectorSpaceImplementation v) @w)
       )

abstractVS_applyDualVector :: ∀ v . ( AbstractLinearSpace v
           , LinearSpace (VectorSpaceImplementation v) )
                   => Bilinear (DualVector v) v (Scalar v)
abstractVS_applyDualVector = scalarsSameInAbstraction @v
 ( case dualSpaceWitness @(VectorSpaceImplementation v) of
    DualSpaceWitness -> coerce (applyDualVector @(VectorSpaceImplementation v)) )

abstractVS_applyLinear :: ∀ v w . ( AbstractLinearSpace v
           , LinearSpace (VectorSpaceImplementation v)
           , TensorSpace w, Scalar w ~ Scalar v )
                   => Bilinear (v +> w) v w
abstractVS_applyLinear = scalarsSameInAbstraction @v
 ( coerce (applyLinear @(VectorSpaceImplementation v) @w)
 )

abstractVS_applyTensorFunctional :: ∀ v u .
       ( AbstractLinearSpace v
       , LinearSpace (VectorSpaceImplementation v)
       , LinearSpace u, Scalar u ~ Scalar v )
           => Bilinear (DualVector (v⊗u)) (v⊗u) (Scalar v)
abstractVS_applyTensorFunctional = scalarsSameInAbstraction @v
 (case abstractTensorProductsCoercion @v @u of
   Coercion -> coerce (applyTensorFunctional @(VectorSpaceImplementation v) @u))

abstractVS_applyTensorLinMap :: ∀ v u w .
       ( AbstractLinearSpace v
       , LinearSpace (VectorSpaceImplementation v)
       , LinearSpace u, Scalar u ~ Scalar v
       , TensorSpace w, Scalar w ~ Scalar v )
                         => Bilinear ((v⊗u)+>w) (v⊗u) w
abstractVS_applyTensorLinMap = scalarsSameInAbstraction @v
 ( case abstractTensorProductsCoercion @v @u of
   Coercion -> coerce (applyTensorLinMap @(VectorSpaceImplementation v) @u @w) )

abstractSubbasisCoercion :: ∀ v .
       Coercible (SubBasis v) (SubBasis (VectorSpaceImplementation v))
     => Coercion (SubBasis v) (SubBasis (VectorSpaceImplementation v))
abstractSubbasisCoercion = Coercion

precomposeCoercion :: Coercion a b -> Coercion (b -> c) (a -> c)
precomposeCoercion Coercion = Coercion

postcomposeCoercion :: Coercion b c -> Coercion (a -> b) (a -> c)
postcomposeCoercion Coercion = Coercion

firstCoercion :: Coercion a b -> Coercion (a,c) (b,c)
firstCoercion Coercion = Coercion

leftCoercion :: Coercion a b -> Coercion (Either a c) (Either b c)
leftCoercion Coercion = Coercion

abstractVS_dualFinitenessWitness :: ∀ v .
       ( AbstractLinearSpace v, FiniteDimensional v
       , FiniteDimensional (VectorSpaceImplementation v) )
     => DualFinitenessWitness v
abstractVS_dualFinitenessWitness = scalarsSameInAbstraction @v
  (case dualFinitenessWitness @(VectorSpaceImplementation v) of
     DualFinitenessWitness DualSpaceWitness
        -> DualFinitenessWitness DualSpaceWitness
    )

abstractVS_entireBasis :: ∀ v .
       ( AbstractLinearSpace v, FiniteDimensional (VectorSpaceImplementation v)
       , Coercible (SubBasis v) (SubBasis (VectorSpaceImplementation v)) )
          => SubBasis v
abstractVS_entireBasis = sym (abstractSubbasisCoercion @v)
            $ entireBasis @(VectorSpaceImplementation v)

abstractVS_enumerateSubBasis :: ∀ v .
       ( AbstractLinearSpace v, FiniteDimensional (VectorSpaceImplementation v)
       , Coercible (SubBasis v) (SubBasis (VectorSpaceImplementation v)) )
          => SubBasis v -> [v]
abstractVS_enumerateSubBasis = precomposeCoercion
               (abstractSubbasisCoercion @v)
    $ coerce (enumerateSubBasis @(VectorSpaceImplementation v))

abstractVS_decomposeLinMap :: ∀ v w .
       ( AbstractLinearSpace v
       , FiniteDimensional (VectorSpaceImplementation v)
       , Coercible (SubBasis v) (SubBasis (VectorSpaceImplementation v))
       , LSpace w, Scalar w ~ Scalar v )
                   => (v +> w) -> (SubBasis v, DList w)
abstractVS_decomposeLinMap = scalarsSameInAbstraction @v
   ( postcomposeCoercion (firstCoercion $ sym
            (abstractSubbasisCoercion @v))
      $ case abstractTensorProductsCoercion @v @w of
         Coercion -> ( coerce (decomposeLinMap @(VectorSpaceImplementation v) @w)
                         :: (v +> w) -> ( SubBasis (VectorSpaceImplementation v)
                                        , DList w ) )
     )

abstractVS_decomposeLinMapWithin :: ∀ v w . ( AbstractLinearSpace v
       , FiniteDimensional (VectorSpaceImplementation v)
       , Coercible (SubBasis v) (SubBasis (VectorSpaceImplementation v))
       , LSpace w, Scalar w ~ Scalar v )
   => SubBasis v -> (v +> w) -> Either (SubBasis v, DList w) (DList w)
abstractVS_decomposeLinMapWithin = scalarsSameInAbstraction @v
 ( precomposeCoercion (abstractSubbasisCoercion @v)
    . postcomposeCoercion (postcomposeCoercion
        . leftCoercion . firstCoercion $ sym
              (abstractSubbasisCoercion @v))
      $ coerce (decomposeLinMapWithin @(VectorSpaceImplementation v) @w)
  )

abstractVS_recomposeSB :: ∀ v . ( AbstractLinearSpace v
       , FiniteDimensional (VectorSpaceImplementation v)
       , Coercible (SubBasis v) (SubBasis (VectorSpaceImplementation v)) )
   => SubBasis v -> [Scalar v] -> (v, [Scalar v])
abstractVS_recomposeSB = scalarsSameInAbstraction @v
 ( precomposeCoercion (abstractSubbasisCoercion @v)
  $ coerce (recomposeSB @(VectorSpaceImplementation v))
  )

abstractVS_recomposeSBTensor :: ∀ v w . ( AbstractLinearSpace v
       , FiniteDimensional (VectorSpaceImplementation v)
       , Coercible (SubBasis v) (SubBasis (VectorSpaceImplementation v))
       , FiniteDimensional w, Scalar w ~ Scalar v )
   => SubBasis v -> SubBasis w -> [Scalar v] -> (v ⊗ w, [Scalar v])
abstractVS_recomposeSBTensor = scalarsSameInAbstraction @v
 ( precomposeCoercion (abstractSubbasisCoercion @v)
  $ case abstractTensorProductsCoercion @v @w of
     Coercion -> coerce (recomposeSBTensor @(VectorSpaceImplementation v) @w)
  )

abstractVS_recomposeLinMap :: ∀ v w . ( AbstractLinearSpace v
       , FiniteDimensional (VectorSpaceImplementation v)
       , Coercible (SubBasis v) (SubBasis (VectorSpaceImplementation v))
       , LSpace w, Scalar w ~ Scalar v )
   => SubBasis v -> [w] -> (v +> w, [w])
abstractVS_recomposeLinMap = scalarsSameInAbstraction @v
 ( precomposeCoercion (abstractSubbasisCoercion @v)
  $ coerce (recomposeLinMap @(VectorSpaceImplementation v) @w)
  )

abstractVS_recomposeContraLinMap :: ∀ v f w . ( AbstractLinearSpace v
       , FiniteDimensional (VectorSpaceImplementation v)
       , Coercible (SubBasis v) (SubBasis (VectorSpaceImplementation v))
       , LinearSpace w, Scalar w ~ Scalar v
       , Hask.Functor f )
                  => (f (Scalar w) -> w) -> f (DualVector v) -> v +> w
abstractVS_recomposeContraLinMap f = scalarsSameInAbstraction @v
 ( coerce (recomposeContraLinMap @(VectorSpaceImplementation v) @w @f f)
                . fmap getConcreteDualVector
  )

abstractVS_recomposeLinMapTensor :: ∀ v f w u . ( AbstractLinearSpace v
       , FiniteDimensional (VectorSpaceImplementation v)
       , Coercible (SubBasis v) (SubBasis (VectorSpaceImplementation v))
       , LinearSpace w, Scalar w ~ Scalar v
       , FiniteDimensional u, Scalar u ~ Scalar v
       , Hask.Functor f )
   => (f (Scalar w) -> w) -> f (v+>DualVector u) -> (v⊗u) +> w
abstractVS_recomposeLinMapTensor f = scalarsSameInAbstraction @v
 ( coerce (recomposeContraLinMapTensor @(VectorSpaceImplementation v) @u @w @f f)
              . fmap (coerce :: (v+>DualVector u)
                          -> (VectorSpaceImplementation v+>DualVector u))
  )

abstractVS_uncanonicallyFromDual :: ∀ v . ( AbstractLinearSpace v
        , FiniteDimensional (VectorSpaceImplementation v) )
   => DualVector v -+> v
abstractVS_uncanonicallyFromDual = scalarsSameInAbstraction @v
 ( case abstractDualVectorCoercion @v of
            VSCCoercion -> coerce (uncanonicallyFromDual @(VectorSpaceImplementation v))
  )

abstractVS_uncanonicallyToDual :: ∀ v . ( AbstractLinearSpace v
        , FiniteDimensional (VectorSpaceImplementation v) )
   => v -+> DualVector v
abstractVS_uncanonicallyToDual = scalarsSameInAbstraction @v
 ( case abstractDualVectorCoercion @v of
            VSCCoercion -> coerce (uncanonicallyToDual @(VectorSpaceImplementation v))
  )

abstractVS_tensorEquality :: ∀ v w . ( AbstractLinearSpace v
        , FiniteDimensional (VectorSpaceImplementation v)
        , TensorSpace w, Eq w, Scalar w ~ Scalar v )
                       => (v ⊗ w) -> (v ⊗ w) -> Bool
abstractVS_tensorEquality = scalarsSameInAbstraction @v
 ( case abstractTensorProductsCoercion @v @w of
    Coercion -> coerce (tensorEquality @(VectorSpaceImplementation v) @w)
  )

abstractVS_dualBasisCandidates :: ∀ v . ( AbstractLinearSpace v
        , SemiInner (VectorSpaceImplementation v) )
      => [(Int, v)] -> Forest (Int, DualVector v)
abstractVS_dualBasisCandidates = scalarsSameInAbstraction @v
 ( case abstractDualVectorCoercion @v of
            VSCCoercion -> coerce (dualBasisCandidates @(VectorSpaceImplementation v))
  )

abstractVS_tensorDualBasisCandidates :: ∀ v w . ( AbstractLinearSpace v
        , LinearSpace v
        , SemiInner (VectorSpaceImplementation v)
        , SemiInner w, Scalar w ~ Scalar v)
                    => [(Int, v ⊗ w)]
                     -> Forest (Int, v +> DualVector w)
abstractVS_tensorDualBasisCandidates = scalarsSameInAbstraction @v
 ( case (dualSpaceWitness @v, dualSpaceWitness @w) of
    (DualSpaceWitness, DualSpaceWitness)
         -> case ( abstractDualVectorCoercion @v
                 , abstractTensorProductsCoercion @v @(DualVector w)
                 , abstractTensorProductsCoercion @v @w
                 ) of
       (VSCCoercion, Coercion, Coercion)
          -> coerce (tensorDualBasisCandidates @(VectorSpaceImplementation v) @w)
  )

abstractVS_symTensorDualBasisCandidates :: ∀ v . ( AbstractLinearSpace v
         , LinearSpace v
         , SemiInner (VectorSpaceImplementation v) )
        => [(Int, SymmetricTensor (Scalar v) v)]
              -> Forest (Int, SymmetricTensor (Scalar v) (DualVector v))
abstractVS_symTensorDualBasisCandidates = scalarsSameInAbstraction @v
 ( case ( dualSpaceWitness @v
        , dualSpaceWitness @(VectorSpaceImplementation v)
        , abstractDualVectorCoercion @v ) of
    (DualSpaceWitness, DualSpaceWitness, crdv)
       -> case ( abstractTensorProductsCoercion @v @v
               , coerceFmapTensorProduct @(DualVector (VectorSpaceImplementation v)) []
                   crdv
               , coerceFmapTensorProduct @(VectorSpaceImplementation v) []
                   crdv
               , coerceFmapTensorProduct @(VectorSpaceImplementation v) []
                   (VSCCoercion @v @(VectorSpaceImplementation v))
               ) of
     (Coercion, Coercion, Coercion, Coercion)
        -> coerce (symTensorDualBasisCandidates @(VectorSpaceImplementation v))
  )

-- | More powerful version of @deriving newtype@, specialised to the classes from
--   this package (and of @manifolds-core@). The 'DualVector' space will be a separate
--   type, even if the type you abstract over is self-dual.
copyNewtypeInstances :: Q Type -> [Name] -> DecsQ
copyNewtypeInstances cxtv classes = do

 requireExtensions [ TypeFamilies, MultiParamTypeClasses
                   , ScopedTypeVariables, TypeApplications
                   , DataKinds ]
 
 (tvbs, cxt, (a,c)) <- do
   (tvbs', cxt', a') <- deQuantifyType cxtv
   let extractImplementationType (AppT tc (VarT tvb)) atvbs
#if MIN_VERSION_template_haskell(2,17,0)
              = extractImplementationType tc $ atvbs++[PlainTV tvb SpecifiedSpec]
#else
              = extractImplementationType tc $ atvbs++[PlainTV tvb]
#endif
       extractImplementationType (ConT aName) atvbs = do
         D.reifyDatatype aName >>= \case
          D.DatatypeInfo{ D.datatypeVariant = D.Newtype
                        , D.datatypeVars = dttvbs
                        , D.datatypeCons = [
                           D.ConstructorInfo
                              { D.constructorFields = [c''] } ]
                        }
#if MIN_VERSION_template_haskell(2,17,0)
             -> let replaceTVs :: [TyVarBndr ()] -> [TyVarBndr Specificity] -> Type -> Type
                    replaceTVs (PlainTV infoTV _:infoTVs)
                               (PlainTV instTV _:instTVs)
                        = replaceTVs infoTVs instTVs . replaceTV infoTV instTV
                    replaceTVs (KindedTV infoTV flag _:infoTVs) instTVs
                        = replaceTVs (PlainTV infoTV flag:infoTVs) instTVs
                    replaceTVs infoTVs (KindedTV instTV flag _:instTVs)
                        = replaceTVs infoTVs (PlainTV instTV flag:instTVs)
#else
             -> let replaceTVs :: [TyVarBndr] -> [TyVarBndr] -> Type -> Type
                    replaceTVs (PlainTV infoTV:infoTVs) (PlainTV instTV:instTVs)
                        = replaceTVs infoTVs instTVs . replaceTV infoTV instTV
                    replaceTVs (KindedTV infoTV _:infoTVs) instTVs
                        = replaceTVs (PlainTV infoTV:infoTVs) instTVs
                    replaceTVs infoTVs (KindedTV instTV _:instTVs)
                        = replaceTVs infoTVs (PlainTV instTV:instTVs)
#endif
                    replaceTVs [] [] = id
                    replaceTVs infoTVs instTVs
                        = error $ "infoTVs = "++show infoTVs++", instTVs = "++show instTVs
                    replaceTV :: Name -> Name -> Type -> Type
                    replaceTV infoTV instTV (AppT tc (VarT tv))
                     | tv==infoTV  = AppT (replaceTV infoTV instTV tc) (VarT instTV)
                    replaceTV infoTV instTV (AppT tc ta)
                           = AppT (replaceTV infoTV instTV tc)
                                  (replaceTV infoTV instTV ta)
                    replaceTV _ _ t@(TupleT _) = t
                    replaceTV _ _ t@(ConT _) = t
                    replaceTV _ _ t
                        = error $ "Don't know how to substitute type variables in "
                                    ++ show t
                in return $ replaceTVs dttvbs atvbs c''
          _ -> error $ show aName ++ " is not a newtype."
       extractImplementationType a'' _
           = error $ "Don't know how to handle type "++show a''
                            ++" (specified: "++show a'++")"
   c' <- extractImplementationType a' []
   return (tvbs', pure cxt', (pure a', pure c'))
 
 let allClasses =
       [ ''AbstractAdditiveGroup | _<-[()], ''AdditiveGroup `elem` classes ]
      ++ [ ''AbstractVectorSpace | _<-[()], ''VectorSpace `elem` classes ]
      ++ [ ''AbstractTensorSpace | _<-[()], ''TensorSpace `elem` classes ]
      ++ [ ''AbstractLinearSpace | _<-[()], ''LinearSpace `elem` classes ]
      ++ classes

 vtnameHash <- abs . hash . show <$> a

 sequence [case dClass of
     "AbstractAdditiveGroup" -> InstanceD Nothing <$> cxt <*>
                          [t|AbstractAdditiveGroup $a|] <*> [d|
         type instance VectorSpaceImplementation $a = $c
      |]
     "AdditiveGroup" -> InstanceD Nothing <$> cxt <*>
                          [t|AdditiveGroup $a|] <*> [d|
         $(varP 'zeroV) = abstractVS_zeroV
         $(varP '(^+^)) = abstractVS_addvs
         $(varP '(^-^)) = abstractVS_subvs
         $(varP 'negateV) = abstractVS_negateV
      |]
     "AffineSpace" -> InstanceD Nothing <$> cxt <*>
                          [t|AffineSpace $a|] <*> [d|
         type instance Diff $a = $a
         $(varP '(.-.)) = abstractVS_subvs
         $(varP '(.+^)) = abstractVS_addvs
      |]
     "VectorSpace" -> InstanceD Nothing <$> cxt <*>
                          [t|VectorSpace $a|] <*> [d|
         type instance Scalar $a = Scalar ($c)
         $(varP '(*^)) = abstractVS_scalev
      |]
     "AbstractVectorSpace" -> InstanceD Nothing <$> cxt <*>
                          [t|AbstractVectorSpace $a|] <*> [d|
         $(varP 'scalarsSameInAbstraction)
            = abstractVS_scalarsSameInAbstraction @($a)
      |]
     "InnerSpace" -> InstanceD Nothing <$> cxt <*>
                          [t|InnerSpace $a|] <*> [d|
         $(varP '(<.>)) = abstractVS_innerProd
      |]
     "Semimanifold" -> InstanceD Nothing <$> cxt <*>
                          [t|Semimanifold $a|] <*> [d|
         type instance Needle $a = $a
         $(varP '(.+~^)) = abstractVS_addvs
#if !MIN_VERSION_manifolds_core(0,6,0)
         type instance Interior $a = $a
         $(varP 'toInterior) = pure
         $(varP 'fromInterior) = id
         $(varP 'translateP) = Tagged (^+^)
         $(varP 'semimanifoldWitness) = SemimanifoldWitness BoundarylessWitness
#endif
      |]
     "PseudoAffine" -> InstanceD Nothing <$> cxt <*>
                          [t|PseudoAffine $a|] <*> [d|
         $(varP '(.-~.)) = \p q -> Just (abstractVS_subvs p q)
         $(varP '(.-~!)) = abstractVS_subvs
      |]
     "DimensionAware" -> InstanceD Nothing <$> cxt <*>
                          [t|DimensionAware $a|] <*> [d|
         type instance StaticDimension $a = StaticDimension $c
         $(varP 'dimensionalityWitness) = abstractVS_dimensionalityWitness
      |]
     "Dimensional" -> do
       dim <- pure . VarT <$> newName "n"
       InstanceD Nothing <$> ((:)<$>[t|StaticDimension $c ~ 'Just $dim|]
                          <*>cxt) <*>
                          [t|Dimensional $dim $a|] <*> [d|
         $(varP 'unsafeFromArrayWithOffset) = abstractVS_unsafeFromArrayWithOffset
         $(varP 'unsafeWriteArrayWithOffset) = abstractVS_unsafeWriteArrayWithOffset
        |]
     "TensorSpace" -> InstanceD Nothing <$> cxt <*>
                          [t|TensorSpace $a|] <*> [d|
         type instance TensorProduct $a w = TensorProduct $c w
         $(varP 'scalarSpaceWitness) = abstractVS_scalarSpaceWitness
         $(varP 'linearManifoldWitness) = abstractVS_linearManifoldWitness
         $(varP 'toFlatTensor) = abstractVS_toFlatTensor
         $(varP 'fromFlatTensor) = abstractVS_fromFlatTensor
         $(varP 'zeroTensor) = abstractVS_zeroTensor
         $(varP 'addTensors) = abstractVS_addTensors
         $(varP 'subtractTensors) = abstractVS_subtractTensors
         $(varP 'scaleTensor) = abstractVS_scaleTensor
         $(varP 'negateTensor) = abstractVS_negateTensor
         $(varP 'wellDefinedVector) = abstractVS_wellDefinedVector
         $(varP 'wellDefinedTensor) = abstractVS_wellDefinedTensor
         $(varP 'tensorProduct) = abstractVS_tensorProduct
         $(varP 'transposeTensor) = abstractVS_transposeTensor
         $(varP 'fmapTensor) = abstractVS_fmapTensor
         $(varP 'fzipTensorWith) = abstractVS_fzipTensorsWith
         $(varP 'tensorUnsafeFromArrayWithOffset)
                       = abstractVS_tensorUnsafeFromArrayWithOffset
         $(varP 'tensorUnsafeWriteArrayWithOffset)
                       = abstractVS_tensorUnsafeWriteArrayWithOffset
         $(varP 'coerceFmapTensorProduct) = abstractVS_coerceFmapTensorProduct
      |]
     "AbstractTensorSpace" -> InstanceD Nothing <$> cxt <*>
                          [t|AbstractTensorSpace $a|] <*> [d|
         $(varP 'sameDimensionalInAbstraction)
                  = \φ -> φ
         $(varP 'abstractTensorProductsCoercion)
                  = Coercion
      |]
     "LinearSpace" -> InstanceD Nothing <$> cxt <*>
                          [t|LinearSpace $a|] <*> [d|
         type instance DualVector $a = AbstractDualVector $a $c
         $(varP 'dualSpaceWitness) = abstractVS_dualSpaceWitness
         $(varP 'linearId) = abstractVS_linearId
         $(varP 'tensorId) = abstractVS_tensorId
         $(varP 'applyDualVector) = abstractVS_applyDualVector
         $(varP 'applyLinear) = abstractVS_applyLinear
         $(varP 'applyTensorFunctional) = abstractVS_applyTensorFunctional
         $(varP 'applyTensorLinMap) = abstractVS_applyTensorLinMap
         $(varP 'useTupleLinearSpaceComponents) = \_ -> usingNonTupleTypeAsTupleError
      |]
     "AbstractLinearSpace" -> InstanceD Nothing <$> cxt <*>
                          [t|AbstractLinearSpace $a|] <*> [d|
      |]
     "FiniteDimensional" -> InstanceD Nothing <$> cxt <*>
                          [t|FiniteDimensional $a|] <*> do
        subBasisCstr <- newName $ "SubBasis"++show vtnameHash
        
        tySyns <- sequence [
#if MIN_VERSION_template_haskell(2,15,0)
           NewtypeInstD [] (Just (
#if MIN_VERSION_template_haskell(2,17,0)
                                  map (fmap $ const ()) tvbs
#else
                                  tvbs
#endif
                                      ))
              <$> (AppT (ConT ''SubBasis) <$> a)
              <*> pure Nothing
              <*> (NormalC subBasisCstr . pure .
                          (Bang NoSourceUnpackedness NoSourceStrictness,)
                     <$> [t| SubBasis $c |])
              <*> pure []
#else
           NewtypeInstD [] ''SubBasis
              <$> ((:[]) <$> a)
              <*> pure Nothing
              <*> (NormalC subBasisCstr . pure . 
                          (Bang NoSourceUnpackedness NoSourceStrictness,)
                     <$> [t| SubBasis $c |])
              <*> pure []
#endif
         ]
        methods <- [d|
         $(varP 'dualFinitenessWitness) = abstractVS_dualFinitenessWitness
         $(varP 'entireBasis) = abstractVS_entireBasis
         $(varP 'enumerateSubBasis) = abstractVS_enumerateSubBasis
         $(varP 'decomposeLinMap) = abstractVS_decomposeLinMap
         $(varP 'decomposeLinMapWithin) = abstractVS_decomposeLinMapWithin
         $(varP 'recomposeSB) = abstractVS_recomposeSB
         $(varP 'recomposeSBTensor) = abstractVS_recomposeSBTensor
         $(varP 'recomposeLinMap) = abstractVS_recomposeLinMap
         $(varP 'recomposeContraLinMap) = abstractVS_recomposeContraLinMap
         $(varP 'recomposeContraLinMapTensor) = abstractVS_recomposeLinMapTensor
         $(varP 'uncanonicallyFromDual) = abstractVS_uncanonicallyFromDual
         $(varP 'uncanonicallyToDual) = abstractVS_uncanonicallyToDual
         $(varP 'tensorEquality) = abstractVS_tensorEquality
          |]
        return $ tySyns ++ methods
     "SemiInner" -> InstanceD Nothing <$> cxt <*>
                          [t|SemiInner $a|] <*> [d|
          $(varP 'dualBasisCandidates) = abstractVS_dualBasisCandidates
          $(varP 'tensorDualBasisCandidates) = abstractVS_tensorDualBasisCandidates
          $(varP 'symTensorDualBasisCandidates) = abstractVS_symTensorDualBasisCandidates
       |]
     _ -> error $ "Unsupported class to derive newtype instance for: ‘"++dClass++"’"
   | Name (OccName dClass) _ <- allClasses
   ]