packages feed

yaya-containers-1.0.0.0: src/Yaya/Containers/Pattern/Set.hs

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

module Yaya.Containers.Pattern.Set
  ( Set (Bin, Tip),
  )
where

import "base" Control.Applicative ((*>), (<*>), (<|>))
import "base" Control.Category ((.))
import "base" Data.Bool (Bool (False, True), (&&))
import "base" Data.Eq (Eq, (==))
import "base" Data.Foldable (Foldable)
import "base" Data.Function (($))
import "base" Data.Functor (Functor, (<$), (<$>))
import "base" Data.Functor.Classes
  ( Eq1,
    Eq2,
    Ord1,
    Ord2,
    Read1,
    Read2,
    Show1,
    Show2,
    liftCompare,
    liftCompare2,
    liftEq,
    liftEq2,
    liftReadPrec,
    liftReadPrec2,
    liftShowsPrec,
    liftShowsPrec2,
  )
import "base" Data.Ord (Ord, Ordering (EQ, GT, LT), compare, (<=))
import "base" Data.Semigroup ((<>))
import "base" Data.Traversable (Traversable)
import qualified "base" Data.Tuple as Tuple
import "base" GHC.Generics (Generic, Generic1)
import "base" GHC.Read (Read, expectP, parens, readListPrec, readPrec)
import "base" Text.ParserCombinators.ReadPrec (prec, step)
import qualified "base" Text.Read.Lex as Lex
import "base" Text.Show (Show, showList, showParen, showString, showsPrec)
import qualified "containers" Data.Set.Internal as Set
import "yaya" Yaya.Fold
  ( Projectable,
    Recursive,
    Steppable,
    cata,
    embed,
    project,
  )
import qualified "yaya-unsafe" Yaya.Unsafe.Fold as Unsafe
import "base" Prelude ((+))

data Set a r = Tip | Bin Set.Size a r r
  deriving stock
    ( Eq,
      Ord,
      Generic,
      -- | @since 0.1.2.0
      Read,
      Show,
      Foldable,
      Functor,
      Generic1,
      Traversable
    )

instance Projectable (->) (Set.Set a) (Set a) where
  project Set.Tip = Tip
  project (Set.Bin size a l r) = Bin size a l r

instance Recursive (->) (Set.Set a) (Set a) where
  cata = Unsafe.unsafeCata

instance Steppable (->) (Set.Set a) (Set a) where
  embed Tip = Set.Tip
  embed (Bin size a l r) = Set.Bin size a l r

instance (Eq a) => Eq1 (Set a) where
  liftEq = liftEq2 (==)

instance Eq2 Set where
  liftEq2 f g = Tuple.curry $ \case
    (Tip, Tip) -> True
    (Bin size a l r, Bin size' a' l' r') ->
      size == size' && f a a' && g l l' && g r r'
    (_, _) -> False

instance (Ord a) => Ord1 (Set a) where
  liftCompare = liftCompare2 compare

instance Ord2 Set where
  liftCompare2 f g = Tuple.curry $ \case
    (Tip, Tip) -> EQ
    (Tip, Bin {}) -> LT
    (Bin {}, Tip) -> GT
    (Bin size a l r, Bin size' a' l' r') ->
      compare size size' <> f a a' <> g l l' <> g r r'

-- | @since 0.1.2.0
instance (Read a) => Read1 (Set a) where
  liftReadPrec = liftReadPrec2 readPrec readListPrec

-- | @since 0.1.2.0
instance Read2 Set where
  liftReadPrec2 readPrecA _ readPrecR _ =
    let appPrec = 10
     in parens . prec appPrec $
          Tip
            <$ expectP (Lex.Ident "Tip")
            <|> expectP (Lex.Ident "Bin")
              *> ( Bin
                     <$> step readPrec
                     <*> step readPrecA
                     <*> step readPrecR
                     <*> step readPrecR
                 )

instance (Show a) => Show1 (Set a) where
  liftShowsPrec = liftShowsPrec2 showsPrec showList

instance Show2 Set where
  liftShowsPrec2 showsPrecA _ showsPrecR _ p =
    let appPrec = 10
        nextPrec = appPrec + 1
     in \case
          Tip -> showString "Tip"
          Bin size a l r ->
            showParen (nextPrec <= p) $
              showString "Bin "
                . showsPrec nextPrec size
                . showString " "
                . showsPrecA nextPrec a
                . showString " "
                . showsPrecR nextPrec l
                . showString " "
                . showsPrecR nextPrec r