packages feed

proarrow-0.1.0.0: test/Props/Cospan.hs

{-# LANGUAGE OverloadedLists #-}
{-# OPTIONS_GHC -Wno-orphans #-}

module Props.Cospan where

import Data.Foldable (toList)
import Data.Maybe (isJust)
import Data.Typeable ((:~:) (..))
import Test.Tasty (TestTree, testGroup)
import Prelude (Bool (..), Maybe (..), pure, zip, ($), (&&), (++), (<$>), (<*>), (||))

import Proarrow.Category.Instance.Cospan (COSPAN (..), Cospan (..))
import Proarrow.Category.Instance.FinSet (FINSET (..), findIso, unFinSet)
import Proarrow.Core (CAT, CategoryOf (..), UN, (//), (\\))

import Proarrow.Testing
  ( GenTotal (..)
  , Some (..)
  , Testable (..)
  , TestableProfunctor
  , TestableType (..)
  , TestingEqShow (..)
  , mapSome
  , pattern GenNonEmpty
  )
import Proarrow.Testing.Laws
import Props.FinSet (eqFinSet)

test :: TestTree
test =
  testGroup
    "Cospan(FinSet)"
    [ testCategory @(COSPAN FINSET)
    , testDagger @(COSPAN FINSET)
    , testMonoidal_ @(COSPAN FINSET)
    , testSymMonoidal_ @(COSPAN FINSET)
    , testClosed_ @(COSPAN FINSET)
    , testStarAutonomous_ @(COSPAN FINSET)
    , testCompactClosed_ @(COSPAN FINSET)
    , testCopyDiscard_ @(COSPAN FINSET)
    , testHypergraph_ @(COSPAN FINSET)
    ]

-- instance (Testable k, HasPushouts k, TestObIsOb k) => Testable (COSPAN k) where
instance Testable (COSPAN FINSET) where
  type TestOb a = Ob a
  showOb @a = showOb @_ @(UN CS a)
  genSome = mapSome CS <$> genSome
  genSomeSmall = mapSome CS <$> genSomeSmall

-- instance (Ob a, Ob b, Testable k, TestObIsOb k) => TestingEqShow (Cospan a (b :: COSPAN k)) where
instance (Ob a, Ob b) => TestingEqShow (Cospan a (b :: COSPAN FINSET)) where
  eqP (Cospan @c1 l1 r1) (Cospan @c2 l2 r2) =
    l1 //
      l2 //
        case eqFinSet @c1 @c2 of
          Just Refl -> do
            eql <- eqP l1 l2
            eqr <- eqP r1 r2
            -- Both legs map *into* the apex, so a relabelling has to satisfy every constraint the
            -- two leg pairs impose at once, which takes an actual search. Span's legs map out, so
            -- it can settle the question by comparing multisets (see "Props.Span").
            let hasIso =
                  isJust
                    ( findIso @(UN FS c1)
                        (zip (toList (unFinSet l1)) (toList (unFinSet l2)) ++ zip (toList (unFinSet r1)) (toList (unFinSet r2)))
                    )
            pure $ (eql && eqr) || hasIso
          Nothing -> pure False
  showP (Cospan @c l r) = "Cospan @(" ++ showOb @_ @c ++ ") (" ++ showP l ++ ") (" ++ showP r ++ ")" \\ l

-- instance (TestOb a, TestOb b, Testable k, TestObIsOb k) => TestableType (Cospan a (b :: COSPAN k)) where
instance (TestOb a, TestOb b) => TestableType (Cospan a (b :: COSPAN FINSET)) where
  gen = GenNonEmpty loop
    where
      loop = do
        Some @c <- genSome @_
        case (gen @(UN CS a ~> c), gen @(UN CS b ~> c)) of
          (GenEmpty _, _) -> loop
          (_, GenEmpty _) -> loop
          (GenNonEmpty l, GenNonEmpty r) -> Cospan <$> l <*> r

instance TestableProfunctor (Cospan :: CAT (COSPAN FINSET))