# LANGUAGE DataKinds #
{-# LANGUAGE RankNTypes #-}
# LANGUAGE TemplateHaskell #
{-# OPTIONS_GHC -O2 -fno-hpc #-}
# OPTIONS_GHC -dsuppress - idinfo -dsuppress - coercions 
       -dsuppress - type - applications 
       -dsuppress - module - prefixes -dsuppress - type - signatures 
       -dsuppress - uniques #
      -dsuppress-type-applications
      -dsuppress-module-prefixes -dsuppress-type-signatures
      -dsuppress-uniques #-}

module Main where

import Control.Subcategory
import Data.Function ((&))
import qualified Data.Sequence as Seq
import Data.Sized (Sized, zipWithSame)
import qualified Data.Sized as SV
import Data.Type.Natural
import qualified Data.Vector as V
import qualified Data.Vector.Generic as G
import Data.Vector.Storable (Storable)
import qualified Data.Vector.Storable as S
import Data.Vector.Unboxed (Unbox)
import qualified Data.Vector.Unboxed as U
import Numeric.Natural (Natural)
import Shared
import Test.Tasty
import Test.Tasty.ExpectedFailure (expectFailBecause)
import Test.Tasty.Inspection

type LSized = Sized []

type VSized = Sized V.Vector

type USized = Sized U.Vector

type SSized = Sized S.Vector

type SeqSized = Sized Seq.Seq

{-# ANN module "HLINT: ignore Use camelCase" #-}

zipWith_subcat_List ::
  (Int -> Int -> Int) -> [Int] -> [Int] -> [Int]
zipWith_subcat_List = czipWith

zipWith_List ::
  (Int -> Int -> Int) -> LSized n Int -> LSized m Int -> LSized (Min n m) Int
zipWith_List = SV.zipWith

zipWithSame_List ::
  (Int -> Int -> Int) -> LSized n Int -> LSized n Int -> LSized n Int
zipWithSame_List = zipWithSame

zipWith_List_Prel :: (Int -> Int -> Int) -> [Int] -> [Int] -> [Int]
zipWith_List_Prel = zipWith

zipWithSame_Boxed :: (a -> b -> c) -> VSized n a -> VSized n b -> VSized n c
zipWithSame_Boxed = zipWithSame

zipWithSame_Boxed_mono ::
  (Int -> (Integer -> Bool) -> [Int]) ->
  VSized n Int ->
  VSized n (Integer -> Bool) ->
  VSized n [Int]
zipWithSame_Boxed_mono = zipWithSame

zipWithSame_Unboxed ::
  (Unbox a, Unbox b, Unbox c) =>
  (a -> b -> c) ->
  USized n a ->
  USized n b ->
  USized n c
zipWithSame_Unboxed = zipWithSame

zipWithSame_Unboxed_monomorphic ::
  (Int -> Char -> Bool) -> USized n Int -> USized n Char -> USized n Bool
zipWithSame_Unboxed_monomorphic = zipWithSame

zipWith_Unboxed ::
  (Unbox a, Unbox b, Unbox c) =>
  (a -> b -> c) ->
  U.Vector a ->
  U.Vector b ->
  U.Vector c
zipWith_Unboxed = U.zipWith

zipWith_Unboxed_monomorphic ::
  (Int -> Char -> Bool) -> U.Vector Int -> U.Vector Char -> U.Vector Bool
zipWith_Unboxed_monomorphic = U.zipWith

zipWithSame_Storable ::
  (Storable a, Storable b, Storable c) =>
  (a -> b -> c) ->
  SSized n a ->
  SSized n b ->
  SSized n c
zipWithSame_Storable = zipWithSame

zipWithSame_Seq ::
  (a -> b -> c) -> SeqSized n a -> SeqSized n b -> SeqSized n c
zipWithSame_Seq = zipWithSame

zipWith_Boxed :: (a -> b -> c) -> V.Vector a -> V.Vector b -> V.Vector c
zipWith_Boxed = V.zipWith

length_two :: Dom f a => Sized f 2 a -> Int
length_two = SV.length

const_two_dom :: Dom f a => Sized f 2 a -> Int
const_two_dom = const 2

main :: IO ()
main =
  defaultMain $
    testGroup
      "Optimisation test"
      [ testGroup
          "czipWith"
          [ $( inspecting "doesn't contain type classes" $
                hasNoTypeClasses 'zipWith_subcat_List
             )
          ]
      , testGroup
          "zipWith"
          [ $( inspecting "doesn't contain type classes" $
                hasNoTypeClasses 'zipWith_List
             )
          ]
      , testGroup
          "zipWithSame"
          [ testGroup
              "list"
              [ $( inspecting "doesn't contain type classes" $
                    hasNoTypeClasses 'zipWithSame_List
                 )
              , $( inspecting "is almost the same as the original zipWith (list)" $
                    'zipWithSame_List ==- 'zipWith_List_Prel
                 )
              ]
          , testGroup
              "Boxed Vector"
              [ $( inspecting "doesn't contain type classes, except for G.Vector" $
                    'zipWithSame_Boxed
                      `hasNoTypeClassesExcept` [''G.Vector]
                 )
              , $( inspecting "is almost the same as the original zipWith (Boxed)" $
                    'zipWithSame_Boxed ==- 'zipWith_Boxed
                 )
              ]
          , testGroup
              "Unboxed Vector"
              [ $( inspecting "doesn't contain type classes except for Unbox" $
                    'zipWithSame_Unboxed
                      `hasNoTypeClassesExcept` [''Unbox]
                 )
                  & if ghcVer >= GHC9_0
                    then expectFailBecause "This suffers from Simplified Subsumption"
                    else id
              , $( inspecting "doesn't contain type classes if fully instnatiated" $
                    hasNoTypeClasses 'zipWithSame_Unboxed_monomorphic
                 )
              , $( inspecting "is almost the same as the original zipWith, if fully instantiated" $
                    'zipWithSame_Unboxed_monomorphic
                      ==- 'zipWith_Unboxed_monomorphic
                 )
              ]
          ]
      , testGroup
          "length"
          [ $( inspecting "is a constant function when length is concrete (with Dom dictionary)" $
                'length_two ==- 'const_two_dom
             )
          , $( inspecting "doesn't contain Integer when the length is concrete" $ hasNoType 'length_two ''Integer
             )
          , $( inspecting "doesn't contain Natural when the length is concrete" $ hasNoType 'length_two ''Natural
             )
          ]
      ]

# LANGUAGE RankNTypes #
# OPTIONS_GHC -O2 -fno-hpc #
# ANN module "HLINT: ignore Use camelCase" #
# LANGUAGE DataKinds #
# LANGUAGE TemplateHaskell #
# OPTIONS_GHC -dsuppress - idinfo -dsuppress - coercions 
       -dsuppress - type - applications 
       -dsuppress - module - prefixes -dsuppress - type - signatures 
       -dsuppress - uniques #
      -dsuppress-type-applications
      -dsuppress-module-prefixes -dsuppress-type-signatures
      -dsuppress-uniques #-}

module Main where

import Control.Subcategory
import Data.Function ((&))
import qualified Data.Sequence as Seq
import Data.Sized (Sized, zipWithSame)
import qualified Data.Sized as SV
import Data.Type.Natural
import qualified Data.Vector as V
import qualified Data.Vector.Generic as G
import Data.Vector.Storable (Storable)
import qualified Data.Vector.Storable as S
import Data.Vector.Unboxed (Unbox)
import qualified Data.Vector.Unboxed as U
import Numeric.Natural (Natural)
import Shared
import Test.Tasty
import Test.Tasty.ExpectedFailure (expectFailBecause)
import Test.Tasty.Inspection

type LSized = Sized []

type VSized = Sized V.Vector

type USized = Sized U.Vector

type SSized = Sized S.Vector

type SeqSized = Sized Seq.Seq


zipWith_subcat_List ::
  (Int -> Int -> Int) -> [Int] -> [Int] -> [Int]
zipWith_subcat_List = czipWith

zipWith_List ::
  (Int -> Int -> Int) -> LSized n Int -> LSized m Int -> LSized (Min n m) Int
zipWith_List = SV.zipWith

zipWithSame_List ::
  (Int -> Int -> Int) -> LSized n Int -> LSized n Int -> LSized n Int
zipWithSame_List = zipWithSame

zipWith_List_Prel :: (Int -> Int -> Int) -> [Int] -> [Int] -> [Int]
zipWith_List_Prel = zipWith

zipWithSame_Boxed :: (a -> b -> c) -> VSized n a -> VSized n b -> VSized n c
zipWithSame_Boxed = zipWithSame

zipWithSame_Boxed_mono ::
  (Int -> (Integer -> Bool) -> [Int]) ->
  VSized n Int ->
  VSized n (Integer -> Bool) ->
  VSized n [Int]
zipWithSame_Boxed_mono = zipWithSame

zipWithSame_Unboxed ::
  (Unbox a, Unbox b, Unbox c) =>
  (a -> b -> c) ->
  USized n a ->
  USized n b ->
  USized n c
zipWithSame_Unboxed = zipWithSame

zipWithSame_Unboxed_monomorphic ::
  (Int -> Char -> Bool) -> USized n Int -> USized n Char -> USized n Bool
zipWithSame_Unboxed_monomorphic = zipWithSame

zipWith_Unboxed ::
  (Unbox a, Unbox b, Unbox c) =>
  (a -> b -> c) ->
  U.Vector a ->
  U.Vector b ->
  U.Vector c
zipWith_Unboxed = U.zipWith

zipWith_Unboxed_monomorphic ::
  (Int -> Char -> Bool) -> U.Vector Int -> U.Vector Char -> U.Vector Bool
zipWith_Unboxed_monomorphic = U.zipWith

zipWithSame_Storable ::
  (Storable a, Storable b, Storable c) =>
  (a -> b -> c) ->
  SSized n a ->
  SSized n b ->
  SSized n c
zipWithSame_Storable = zipWithSame

zipWithSame_Seq ::
  (a -> b -> c) -> SeqSized n a -> SeqSized n b -> SeqSized n c
zipWithSame_Seq = zipWithSame

zipWith_Boxed :: (a -> b -> c) -> V.Vector a -> V.Vector b -> V.Vector c
zipWith_Boxed = V.zipWith

length_two :: Dom f a => Sized f 2 a -> Int
length_two = SV.length

const_two_dom :: Dom f a => Sized f 2 a -> Int
const_two_dom = const 2

main :: IO ()
main =
  defaultMain $
    testGroup
      "Optimisation test"
      [ testGroup
          "czipWith"
          [ $( inspecting "doesn't contain type classes" $
                hasNoTypeClasses 'zipWith_subcat_List
             )
          ]
      , testGroup
          "zipWith"
          [ $( inspecting "doesn't contain type classes" $
                hasNoTypeClasses 'zipWith_List
             )
          ]
      , testGroup
          "zipWithSame"
          [ testGroup
              "list"
              [ $( inspecting "doesn't contain type classes" $
                    hasNoTypeClasses 'zipWithSame_List
                 )
              , $( inspecting "is almost the same as the original zipWith (list)" $
                    'zipWithSame_List ==- 'zipWith_List_Prel
                 )
              ]
          , testGroup
              "Boxed Vector"
              [ $( inspecting "doesn't contain type classes, except for G.Vector" $
                    'zipWithSame_Boxed
                      `hasNoTypeClassesExcept` [''G.Vector]
                 )
              , $( inspecting "is almost the same as the original zipWith (Boxed)" $
                    'zipWithSame_Boxed ==- 'zipWith_Boxed
                 )
              ]
          , testGroup
              "Unboxed Vector"
              [ $( inspecting "doesn't contain type classes except for Unbox" $
                    'zipWithSame_Unboxed
                      `hasNoTypeClassesExcept` [''Unbox]
                 )
                  & if ghcVer >= GHC9_0
                    then expectFailBecause "This suffers from Simplified Subsumption"
                    else id
              , $( inspecting "doesn't contain type classes if fully instnatiated" $
                    hasNoTypeClasses 'zipWithSame_Unboxed_monomorphic
                 )
              , $( inspecting "is almost the same as the original zipWith, if fully instantiated" $
                    'zipWithSame_Unboxed_monomorphic
                      ==- 'zipWith_Unboxed_monomorphic
                 )
              ]
          ]
      , testGroup
          "length"
          [ $( inspecting "is a constant function when length is concrete (with Dom dictionary)" $
                'length_two ==- 'const_two_dom
             )
          , $( inspecting "doesn't contain Integer when the length is concrete" $ hasNoType 'length_two ''Integer
             )
          , $( inspecting "doesn't contain Natural when the length is concrete" $ hasNoType 'length_two ''Natural
             )
          ]
      ]
