{-# OPTIONS_GHC -dsuppress-all #-}
{-# LANGUAGE
    BangPatterns,
    CPP,
    DeriveFunctor,
    DeriveFoldable,
    DeriveTraversable,
    DeriveGeneric,
    DerivingVia,
    EmptyCase,
    EmptyDataDeriving,
    TemplateHaskell
    #-}

{-# LANGUAGE TypeOperators, TypeFamilies #-}

import Control.Applicative (liftA2)
import GHC.Generics
import Data.Coerce (coerce)
import Data.Semigroup (Sum(..), All(..))

import Test.Inspection

import Generic.Data
import Generic.Data.Microsurgery
  ( ProductSurgery
  , CopyRep
  , Surgery'(..)
  )

import Inspection.Boilerplate

-- Test cases

data T = T Int Bool
  deriving Generic
  deriving (Semigroup, Monoid)
    via ProductSurgery (CopyRep (Sum Int, All)) T
  deriving (Eq, Ord)
    via Generically T

mappendT, mappendTG :: T -> T -> T
mappendT (T a1 b1) (T a2 b2) = T (a1 + a2) (b1 && b2)
mappendTG x y = x <> y

memptyT, memptyTG :: T
memptyT = T 0 True
memptyTG = mempty

eqT, eqTG :: T -> T -> Bool
eqT (T a1 b1) (T a2 b2) = a1 == a2 && b1 == b2
eqTG = (==)

compareT, compareTG :: T -> T -> Ordering
compareT (T a1 b1) (T a2 b2) = compare a1 a2 <> compare b1 b2
compareTG = compare

inspect $ 'mappendT ==- 'mappendTG
inspect $ 'memptyT ==- 'memptyTG
inspect $ 'eqT ==- 'eqTG
inspect $ 'compareT ==- 'compareTG

data Empty a
  deriving (Generic, Generic1, Eq, Ord, Functor, Foldable, Traversable)

-- Arity 0 (nullary)
data Ary0 a = Ary0
  deriving (Generic, Generic1, Eq, Ord, Functor, Foldable, Traversable)

-- Arity 1 (unary) (Lazy, Strict, Newtype)
data Ary1 a = Ary1 a
  deriving (Generic, Generic1, Eq, Ord, Functor, Foldable, Traversable)

data Ary1' a = Ary1' !a
  deriving (Generic, Generic1, Eq, Ord, Functor, Foldable, Traversable)

newtype Ary1NT a = Ary1NT a
  deriving (Generic, Generic1, Eq, Ord, Functor, Foldable, Traversable)

-- Arity 2 (binary)
data Ary2 a = Ary2 a a
  deriving (Generic, Generic1, Eq, Ord, Functor, Foldable, Traversable)

-- Arity 4 (quaternary)
data Ary4 a = Ary4 a a [Int] [a]
  deriving (Generic, Generic1, Eq, Ord, Functor, Foldable, Traversable)

-- A big sum of stuff
data Big a
  = Big0
  | Big1 a
  | Big2 a a
  | Big4 a a a a
  | Big8 Int a [a] [Int] [a] a a a
  deriving (Generic1, Eq, Ord, Functor, Foldable, Traversable)

-- Handwritten to add INLINE pragmas. TODO: get GHC to do this
instance Generic (Big a) where
  type Rep (Big a) =
        U1
    :+: K1 () a
    :+: (K1 () a :*: K1 () a)
    :+: (K1 () a :*: K1 () a :*: K1 () a :*: K1 () a)
    :+: (   K1 () Int :*: K1 () a :*: K1 () [a] :*: K1 () [Int]
        :*: K1 () ([a]) :*: K1 () a :*: K1 () a :*: K1 () a)
  from Big0 = L1 U1
  from (Big1 x) = R1 (L1 (K1 x))
  from (Big2 x1 x2) = R1 (R1 (L1 (K1 x1 :*: K1 x2)))
  from (Big4 x1 x2 x3 x4) = R1 (R1 (R1 (L1 (K1 x1 :*: K1 x2 :*: K1 x3 :*: K1 x4))))
  from (Big8 x1 x2 x3 x4 x5 x6 x7 x8) = R1 (R1 (R1 (R1 (K1 x1 :*: K1 x2 :*: K1 x3 :*: K1 x4 :*: K1 x5 :*: K1 x6 :*: K1 x7 :*: K1 x8))))
  {-# INLINE from #-}

  to (L1 _) = Big0
  to (R1 (L1 (K1 x))) = Big1 x
  to (R1 (R1 (L1 (K1 x1 :*: K1 x2)))) = Big2 x1 x2
  to (R1 (R1 (R1 (L1 (K1 x1 :*: K1 x2 :*: K1 x3 :*: K1 x4))))) = Big4 x1 x2 x3 x4
  to (R1 (R1 (R1 (R1 (K1 x1 :*: K1 x2 :*: K1 x3 :*: K1 x4 :*: K1 x5 :*: K1 x6 :*: K1 x7 :*: K1 x8))))) = Big8 x1 x2 x3 x4 x5 x6 x7 x8
  {-# INLINE to #-}

-- Empty

-- Stock deriving of fmap does not use an EmptyCase.
fmapEmptyRS :: (a -> b) -> Empty a -> Empty b
fmapEmptyRS _ = coerce

foldMapEmptyRS :: Monoid m => (a -> m) -> Empty a -> m
foldMapEmptyRS _ _ = mempty

--

mk_eq' ''Empty [| \ _ _ -> True |]
inspect $ 'eqEmptyR ==- 'eqEmptyS
inspect $ 'eqEmptyR ==- 'eqEmptyG

mk_compare' ''Empty [| \ _ _ -> EQ |]
inspect $ 'compareEmptyR ==- 'compareEmptyS
inspect $ 'compareEmptyR ==- 'compareEmptyG

mk_fmap ''Empty [| \ _ v -> case v of {} |]
inspect $ 'fmapEmptyRS ==- 'fmapEmptyS
inspect $ 'fmapEmptyR ==- 'fmapEmptyG

mk_foldMap ''Empty [| \ _ v -> case v of {} |]
inspect $ 'foldMapEmptyRS ==- 'foldMapEmptyS
inspect $ 'foldMapEmptyR ==- 'foldMapEmptyG

-- No EmptyCase!
mk_foldr ''Empty [| \_ b _ -> b |]
inspect $ 'foldrEmptyR ==- 'foldrEmptyS
inspect $ 'foldrEmptyR ==- 'foldrEmptyG

mk_traverse ''Empty [| \ _ v -> case v of {} |]
inspect $ 'traverseEmptyS ==- 'traverseEmptyS
inspect $ 'traverseEmptyR ==- 'traverseEmptyG

mk_sequenceA ''Empty [| \ v -> case v of {} |]
inspect $ 'sequenceAEmptyS ==- 'sequenceAEmptyS
inspect $ 'sequenceAEmptyR ==- 'sequenceAEmptyG

-- Ary0

eqAry0RS :: Ary0 a -> Ary0 a -> Bool
eqAry0RS Ary0 Ary0 = True

compareAry0RS :: Ary0 a -> Ary0 a -> Ordering
compareAry0RS Ary0 Ary0 = EQ

fmapAry0RS :: (a -> b) -> Ary0 a -> Ary0 b
fmapAry0RS _ = coerce

mk_eq' ''Ary0 [| \ _ _ -> True |]
inspect $ 'eqAry0RS ==- 'eqAry0S
inspect $ 'eqAry0R ==- 'eqAry0G

mk_compare' ''Ary0 [| \ _ _ -> EQ |]
inspect $ 'compareAry0RS ==- 'compareAry0S
inspect $ 'compareAry0R ==- 'compareAry0G

mk_fmap ''Ary0 [| \ _ _ -> Ary0 |]
inspect $ 'fmapAry0RS ==- 'fmapAry0S
inspect $ 'fmapAry0R ==- 'fmapAry0G

mk_foldMap ''Ary0 [| \ _ _ -> mempty |]
inspect $ 'foldMapAry0R ==- 'foldMapAry0S
inspect $ 'foldMapAry0R ==- 'foldMapAry0G

mk_foldr ''Ary0 [| \_ b _ -> b |]
inspect $ 'foldrAry0R ==- 'foldrAry0S
inspect $ 'foldrAry0R ==- 'foldrAry0G

mk_traverse ''Ary0 [| \ _ _ -> pure Ary0 |]
inspect $ 'traverseAry0S ==- 'traverseAry0S
inspect $ 'traverseAry0R ==- 'traverseAry0G

mk_sequenceA ''Ary0 [| \ _ -> pure Ary0 |]
inspect $ 'sequenceAAry0S ==- 'sequenceAAry0S
inspect $ 'sequenceAAry0R ==- 'sequenceAAry0G

-- Ary1

eqAry1RS :: Eq a => Ary1 a -> Ary1 a -> Bool
eqAry1RS (Ary1 x1) (Ary1 y1) = x1 == y1

compareAry1RS :: Ord a => Ary1 a -> Ary1 a -> Ordering
compareAry1RS (Ary1 x1) (Ary1 y1) = compare x1 y1

fmapAry1RS :: (a -> b) -> Ary1 a -> Ary1 b
fmapAry1RS f (Ary1 x) = Ary1 (f x)

foldMapAry1RS :: Monoid m => (a -> m) -> Ary1 a -> m
foldMapAry1RS f (Ary1 x) = f x

foldrAry1RS :: (a -> b -> b) -> b -> Ary1 a -> b
foldrAry1RS f b (Ary1 x) = f x b

traverseAry1RS :: Applicative f => (a -> f b) -> Ary1 a -> f (Ary1 b)
traverseAry1RS f (Ary1 x) = Ary1 <$> f x

sequenceAAry1RS :: Applicative f => Ary1 (f a) -> f (Ary1 a)
sequenceAAry1RS (Ary1 x) = Ary1 <$> x

mk_eq ''Ary1 [| \ ~(Ary1 x1) ~(Ary1 y1) -> x1 == y1 |]
inspect $ 'eqAry1RS ==- 'eqAry1S
inspect $ 'eqAry1R ==- 'eqAry1G

mk_compare ''Ary1 [| \ ~(Ary1 x1) ~(Ary1 y1) -> compare x1 y1 |]
inspect $ 'compareAry1RS ==- 'compareAry1S
inspect $ 'compareAry1R ==- 'compareAry1G

mk_fmap ''Ary1 [| \ f ~(Ary1 x) -> Ary1 (f x) |]
inspect $ 'fmapAry1RS ==- 'fmapAry1S
inspect $ 'fmapAry1R ==- 'fmapAry1G

mk_foldMap ''Ary1 [| \ f ~(Ary1 x) -> f x |]
inspect $ 'foldMapAry1RS ==- 'foldMapAry1S
inspect $ 'foldMapAry1R ==- 'foldMapAry1G

mk_foldr ''Ary1 [| \ f r ~(Ary1 x) -> f x r |]
inspect $ 'foldrAry1RS ==- 'foldrAry1S
inspect $ 'foldrAry1R ==- 'foldrAry1G

mk_traverse ''Ary1 [| \ f ~(Ary1 x) -> Ary1 <$> f x |]
inspect $ 'traverseAry1RS ==- 'traverseAry1S
inspect $ 'traverseAry1R ==- 'traverseAry1G

mk_sequenceA ''Ary1 [| \ ~(Ary1 x) -> Ary1 <$> x |]
inspect $ 'sequenceAAry1RS ==- 'sequenceAAry1S
inspect $ 'sequenceAAry1R ==- 'sequenceAAry1G

-- Generic @to@ seems to be lazy here
mk_ap ''Ary1 [| \ ~(Ary1 f1) ~(Ary1 x1) -> Ary1 (f1 x1) |]
inspect $ 'apAry1R ==- 'apAry1G

mk_liftA2 ''Ary1 [| \ f ~(Ary1 x1) ~(Ary1 x2) -> Ary1 (f x1 x2) |]
inspect $ 'liftA2Ary1R ==- 'liftA2Ary1G

-- Ary1' (strict, this is entirely the same as Ary1)

eqAry1'RS :: Eq a => Ary1' a -> Ary1' a -> Bool
eqAry1'RS (Ary1' x1) (Ary1' y1) = x1 == y1

compareAry1'RS :: Ord a => Ary1' a -> Ary1' a -> Ordering
compareAry1'RS (Ary1' x1) (Ary1' y1) = compare x1 y1

fmapAry1'RS :: (a -> b) -> Ary1' a -> Ary1' b
fmapAry1'RS f (Ary1' x) = Ary1' (f x)

foldMapAry1'RS :: Monoid m => (a -> m) -> Ary1' a -> m
foldMapAry1'RS f (Ary1' x) = f x

foldrAry1'RS :: (a -> b -> b) -> b -> Ary1' a -> b
foldrAry1'RS f b (Ary1' x) = f x b

traverseAry1'RS :: Applicative f => (a -> f b) -> Ary1' a -> f (Ary1' b)
traverseAry1'RS f (Ary1' x) = Ary1' <$> f x

sequenceAAry1'RS :: Applicative f => Ary1' (f a) -> f (Ary1' a)
sequenceAAry1'RS (Ary1' x) = Ary1' <$> x

mk_eq ''Ary1' [| \ ~(Ary1' x1) ~(Ary1' y1) -> x1 == y1 |]
inspect $ 'eqAry1'RS ==- 'eqAry1'S
inspect $ 'eqAry1'R ==- 'eqAry1'G

mk_compare ''Ary1' [| \ ~(Ary1' x1) ~(Ary1' y1) -> compare x1 y1 |]
inspect $ 'compareAry1'RS ==- 'compareAry1'S
inspect $ 'compareAry1'R ==- 'compareAry1'G

mk_fmap ''Ary1' [| \ f ~(Ary1' x) -> Ary1' (f x) |]
inspect $ 'fmapAry1'RS ==- 'fmapAry1'S
inspect $ 'fmapAry1'R ==- 'fmapAry1'G

mk_foldMap ''Ary1' [| \ f ~(Ary1' x) -> f x |]
inspect $ 'foldMapAry1'RS ==- 'foldMapAry1'S
inspect $ 'foldMapAry1'R ==- 'foldMapAry1'G

mk_foldr ''Ary1' [| \ f r ~(Ary1' x) -> f x r |]
inspect $ 'foldrAry1'RS ==- 'foldrAry1'S
inspect $ 'foldrAry1'R ==- 'foldrAry1'G

-- TODO: These tests fail because of a difference in how the Functor
-- dictionary is accessed via the Applicative dictionary.
-- The rest looks alright.
#if __GLASGOW_HASKELL__ >= 810
mk_traverse ''Ary1' [| \ f ~(Ary1' x) -> Ary1' <$> f x |]
inspect $ 'traverseAry1'RS ==- 'traverseAry1'S
inspect $ 'traverseAry1'R ==- 'traverseAry1'G

mk_sequenceA ''Ary1' [| \ ~(Ary1' x) -> Ary1' <$> x |]
inspect $ 'sequenceAAry1'RS ==- 'sequenceAAry1'S
inspect $ 'sequenceAAry1'R ==- 'sequenceAAry1'G
#endif

-- Generic @to@ seems to be lazy here
mk_ap ''Ary1' [| \ ~(Ary1' f1) ~(Ary1' x1) -> Ary1' (f1 x1) |]
inspect $ 'apAry1'R ==- 'apAry1'G

mk_liftA2 ''Ary1' [| \ f ~(Ary1' x1) ~(Ary1' x2) -> Ary1' (f x1 x2) |]
inspect $ 'liftA2Ary1'R ==- 'liftA2Ary1'G

-- Ary1NT

eqAry1NTRS :: Eq a => Ary1NT a -> Ary1NT a -> Bool
eqAry1NTRS = (coerce :: (a -> a -> Bool) -> Ary1NT a -> Ary1NT a -> Bool) (==)

compareAry1NTRS :: Ord a => Ary1NT a -> Ary1NT a -> Ordering
compareAry1NTRS = (coerce :: (a -> a -> Ordering) -> Ary1NT a -> Ary1NT a -> Ordering) compare

mk_eq ''Ary1NT [| \ (Ary1NT x1) (Ary1NT y1) -> x1 == y1 |]
inspect $ 'eqAry1NTRS ==- 'eqAry1NTS
inspect $ 'eqAry1NTR ==- 'eqAry1NTG

mk_compare ''Ary1NT [| \ (Ary1NT x1) (Ary1NT y1) -> compare x1 y1 |]
inspect $ 'compareAry1NTRS ==- 'compareAry1NTS
inspect $ 'compareAry1NTR ==- 'compareAry1NTG

mk_fmap ''Ary1NT [| \ f (Ary1NT x) -> Ary1NT (f x) |]
inspect $ 'fmapAry1NTR ==- 'fmapAry1NTS
inspect $ 'fmapAry1NTR ==- 'fmapAry1NTG

mk_foldMap ''Ary1NT [| \ f (Ary1NT x) -> f x |]
inspect $ 'foldMapAry1NTR ==- 'foldMapAry1NTS
inspect $ 'foldMapAry1NTR ==- 'foldMapAry1NTG

mk_foldr ''Ary1NT [| \ f r (Ary1NT x) -> f x r |]
inspect $ 'foldrAry1NTR ==- 'foldrAry1NTS
inspect $ 'foldrAry1NTR ==- 'foldrAry1NTG

mk_traverse ''Ary1NT [| \ f (Ary1NT x) -> fmap Ary1NT (f x) |]
inspect $ 'traverseAry1NTR ==- 'traverseAry1NTS
inspect $ 'traverseAry1NTR ==- 'traverseAry1NTG

mk_ap ''Ary1NT [| \ (Ary1NT f1) (Ary1NT x1) -> Ary1NT (f1 x1) |]
inspect $ 'apAry1NTR ==- 'apAry1NTG

mk_liftA2 ''Ary1NT [| \ f (Ary1NT x1) (Ary1NT x2) -> Ary1NT (f x1 x2) |]
inspect $ 'liftA2Ary1NTR ==- 'liftA2Ary1NTG

-- Ary2

mk_eq ''Ary2 [| \ (Ary2 x1 x2) (Ary2 y1 y2) -> x1 == y1 && x2 == y2 |]
inspect $ 'eqAry2R ==- 'eqAry2S
inspect $ 'eqAry2R ==- 'eqAry2G

mk_compare ''Ary2 [| \ (Ary2 x1 x2) (Ary2 y1 y2) -> compare x1 y1 <> compare x2 y2 |]
inspect $ 'compareAry2R ==- 'compareAry2S
inspect $ 'compareAry2R ==- 'compareAry2G

mk_fmap ''Ary2 [| \ f (Ary2 x y) -> Ary2 (f x) (f y) |]
inspect $ 'fmapAry2R ==- 'fmapAry2S
inspect $ 'fmapAry2R ==- 'fmapAry2G

mk_foldMap ''Ary2 [| \ f (Ary2 x y) -> f x `mappend` f y |]
inspect $ 'foldMapAry2R ==- 'foldMapAry2S
inspect $ 'foldMapAry2R ==- 'foldMapAry2G

mk_foldr ''Ary2 [| \ f r (Ary2 x y) -> f x (f y r) |]
inspect $ 'foldrAry2R ==- 'foldrAry2S
inspect $ 'foldrAry2R ==- 'foldrAry2G

mk_traverse ''Ary2 [| \ f (Ary2 x y) -> liftA2 Ary2 (f x) (f y) |]
inspect $ 'traverseAry2R ==- 'traverseAry2S
inspect $ 'traverseAry2R ==- 'traverseAry2G

mk_sequenceA ''Ary2 [| \ (Ary2 x y) -> liftA2 Ary2 x y |]
inspect $ 'sequenceAAry2R ==- 'sequenceAAry2S
inspect $ 'sequenceAAry2R ==- 'sequenceAAry2G

mk_ap ''Ary2 [| \ (Ary2 f1 f2) (Ary2 x1 x2) -> Ary2 (f1 x1) (f2 x2) |]
inspect $ 'apAry2R ==- 'apAry2G

mk_liftA2 ''Ary2 [| \ f (Ary2 x1 y1) (Ary2 x2 y2) -> Ary2 (f x1 x2) (f y1 y2) |]
inspect $ 'liftA2Ary2R ==- 'liftA2Ary2G

-- Ary4

sequenceAAry4RS :: Applicative f => Ary4 (f a) -> f (Ary4 a)
sequenceAAry4RS = traverse id

-- The simplifier is good enough to reassociate (&&)
mk_eq ''Ary4
  [| \ (Ary4 x1 x2 x3 x4) (Ary4 y1 y2 y3 y4) ->
       x1 == y1 && x2 == y2 && x3 == y3 && x4 == y4 |]
inspect $ 'eqAry4R ==- 'eqAry4S
inspect $ 'eqAry4R ==- 'eqAry4G

-- The simplifier is good enough to reassociate (<>)
mk_compare ''Ary4
  [| \ (Ary4 x1 x2 x3 x4) (Ary4 y1 y2 y3 y4) ->
       compare x1 y1 <> compare x2 y2 <> compare x3 y3 <> compare x4 y4 |]
inspect $ 'compareAry4R ==- 'compareAry4S
inspect $ 'compareAry4R ==- 'compareAry4G

mk_fmap ''Ary4
  [| \ f (Ary4 x y z t) -> Ary4 (f x) (f y) z (fmap f t) |]
inspect $ 'fmapAry4R ==- 'fmapAry4S
inspect $ 'fmapAry4R ==- 'fmapAry4G

mk_foldMap ''Ary4
  [| \ f (Ary4 x y _ z) -> f x `mappend` (f y `mappend` foldMap f z) |]
inspect $ 'foldMapAry4R ==- 'foldMapAry4S
inspect $ 'foldMapAry4R ==- 'foldMapAry4G

mk_foldr ''Ary4
  [| \ f r (Ary4 x y _ t) -> f x (f y (foldr f r t)) |]
inspect $ 'foldrAry4R ==- 'foldrAry4S
inspect $ 'foldrAry4R ==- 'foldrAry4G

mk_traverse ''Ary4
  [| \ f (Ary4 x y z t) ->
       liftA2 (\x' y' -> Ary4 x' y' z) (f x) (f y) <*> traverse f t |]
inspect $ 'traverseAry4R ==- 'traverseAry4S
inspect $ 'traverseAry4R ==- 'traverseAry4G

mk_sequenceA ''Ary4 [| \ (Ary4 x y z t) -> liftA2 (\x' y' -> Ary4 x' y' z) x y <*> sequenceA t |]
inspect $ 'sequenceAAry4RS ==- 'sequenceAAry4S
inspect $ 'sequenceAAry4R ==- 'sequenceAAry4G

mk_ap ''Ary4
  [| \ (Ary4 f1 f2 fz f3) (Ary4 x1 x2 xz x3) ->
       Ary4 (f1 x1) (f2 x2) (fz <> xz) (f3 <*> x3) |]
inspect $ 'apAry4R ==- 'apAry4G

mk_liftA2 ''Ary4
  [| \ f (Ary4 x1 y1 fz z1) (Ary4 x2 y2 xz z2) ->
       Ary4 (f x1 x2) (f y1 y2) (fz <> xz) (liftA2 f z1 z2) |]
inspect $ 'liftA2Ary4R ==- 'liftA2Ary4G

-- Big

-- The simplifier is good enough to reassociate (&&)
mk_eq ''Big
  [| \ x y -> case (x, y) of
       (Big0, Big0) -> True
       (Big1 x1, Big1 y1) ->
         x1 == y1
       (Big2 x1 x2, Big2 y1 y2) ->
         x1 == y1 && x2 == y2
       (Big4 x1 x2 x3 x4, Big4 y1 y2 y3 y4) ->
         x1 == y1 && x2 == y2 && x3 == y3 && x4 == y4
       (Big8 x1 x2 x3 x4 x5 x6 x7 x8, Big8 y1 y2 y3 y4 y5 y6 y7 y8) ->
         x1 == y1 && x2 == y2 && x3 == y3 && x4 == y4 &&
         x5 == y5 && x6 == y6 && x7 == y7 && x8 == y8
       (_, _) -> False |]
inspect $ 'eqBigR === 'eqBigS
inspect $ 'eqBigR =/= 'eqBigG -- TODO make this test pass

{- TODO Update the rest, after figuring out the above test case
-- The simplifier is good enough to reassociate (<>)
mk_compare ''Big
  [| \ (Big4 x1 x2 x3 x4) (Big4 y1 y2 y3 y4) ->
       compare x1 y1 <> compare x2 y2 <> compare x3 y3 <> compare x4 y4 |]
inspect $ 'compareBigR === 'compareBigS
inspect $ 'compareBigR === 'compareBigG

mk_fmap ''Big
  [| \ f (Big4 x y z t) -> Big (f x) (f y) z (fmap f t) |]
inspect $ 'fmapBigR ==- 'fmapBigS
inspect $ 'fmapBigR ==- 'fmapBigG

mk_foldMap ''Big
  [| \ f (Big4 x y _ z) -> f x `mappend` (f y `mappend` foldMap f z) |]
inspect $ 'foldMapBigR ==- 'foldMapBigS
inspect $ 'foldMapBigR ==- 'foldMapBigG

mk_foldr ''Big
  [| \ f r (Big4 x y _ t) -> f x (f y (foldr f r t)) |]
inspect $ 'foldrBigR ==- 'foldrBigS
inspect $ 'foldrBigR ==- 'foldrBigG

mk_traverse ''Big
  [| \ f (Big4 x y z t) ->
       liftA2 (\x' y' -> Big x' y' z) (f x) (f y) <*> (traverse f t) |]
inspect $ 'traverseBigR ==- 'traverseBigS
inspect $ 'traverseBigR ==- 'traverseBigG

mk_sequenceA ''Big [| \ (Big4 x y z t) -> liftA2 (\x' y' -> Big x' y' z) x y <*> sequenceA t |]
inspect $ 'sequenceABigRS ==- 'sequenceABigS
inspect $ 'sequenceABigR ==- 'sequenceABigG

mk_ap ''Big
  [| \ (Big4 f1 f2 fz f3) (Big4 x1 x2 xz x3) ->
       Big (f1 x1) (f2 x2) (fz <> xz) (f3 <*> x3) |]
inspect $ 'apBigR ==- 'apBigG

mk_liftA2 ''Big
  [| \ f (Big4 x1 y1 fz z1) (Big4 x2 y2 xz z2) ->
       Big (f x1 x2) (f y1 y2) (fz <> xz) (liftA2 f z1 z2) |]
inspect $ 'liftA2BigR ==- 'liftA2BigG
-}

-- dummy
main :: IO ()
main = pure ()
