packages feed

apply-merge-0.1.0.0: src/Data/DoublyLinkedList/STRef.hs

-- SPDX-FileCopyrightText: Copyright Preetham Gujjula
-- SPDX-License-Identifier: BSD-3-Clause
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedRecordDot #-}

module Data.DoublyLinkedList.STRef
  ( -- * Types
    DoublyLinkedList,
    DoublyLinkedNode,

    -- * Construction
    empty,
    fromList,

    -- * Traversal
    head,
    last,
    next,
    prev,

    -- * Query
    null,
    value,

    -- * Insertion
    cons,
    snoc,
    insertBefore,
    insertAfter,

    -- * Deletion
    delete,

    -- * List conversion
    toList,
  )
where

import Control.Arrow ((>>>))
import Control.Monad (forM_)
import Control.Monad.ST (ST)
import Data.STRef (STRef, newSTRef, readSTRef, writeSTRef)
import Prelude hiding (head, last, null)

data DoublyLinkedList s a = DoublyLinkedList
  { firstNode :: FirstNode s a,
    lastNode :: LastNode s a
  }

-- | The sentinel node at the beginning of the list.
newtype FirstNode s a = FirstNode {nextRef :: STRef s (NextNode s a)}

data DoublyLinkedNode s a = DoublyLinkedNode
  { _value :: a,
    nextRef :: STRef s (NextNode s a),
    prevRef :: STRef s (PrevNode s a)
  }

-- | The sentinel node at the end of the list.
newtype LastNode s a = LastNode
  { prevRef :: STRef s (PrevNode s a)
  }

-- | Type for nodes that are after other nodes.
newtype NextNode s a = NextNode
  { unNextNode :: Either (DoublyLinkedNode s a) (LastNode s a)
  }

nextNodeToDoublyLinkedNode :: NextNode s a -> Maybe (DoublyLinkedNode s a)
nextNodeToDoublyLinkedNode (NextNode (Left v)) = Just v
nextNodeToDoublyLinkedNode _ = Nothing

-- | Type for nodes that are before other nodes.
newtype PrevNode s a = PrevNode
  { unPrevNode :: Either (FirstNode s a) (DoublyLinkedNode s a)
  }

class HasNext node where
  next1 :: node s a -> ST s (NextNode s a)
  next1 = let x = x in x

  setNext1 :: (HasPrev next_node) => node s a -> next_node s a -> ST s ()
  setNext1 = let x = x in x

  toPrev1 :: node s a -> PrevNode s a
  toPrev1 = let x = x in x

  -- Abuse the MINIMAL pragma to not include hidden functions in Haddocks
  {-# MINIMAL #-}

instance HasNext FirstNode where
  next1 (FirstNode nextRef) = readSTRef nextRef
  {-# INLINE next1 #-}
  setNext1 (FirstNode nextRef) nextNode = writeSTRef nextRef (toNext1 nextNode)
  {-# INLINE setNext1 #-}
  toPrev1 = PrevNode . Left
  {-# INLINE toPrev1 #-}

instance HasNext DoublyLinkedNode where
  next1 valueNode = readSTRef valueNode.nextRef
  {-# INLINE next1 #-}
  setNext1 valueNode nextNode = writeSTRef valueNode.nextRef (toNext1 nextNode)
  {-# INLINE setNext1 #-}
  toPrev1 = PrevNode . Right
  {-# INLINE toPrev1 #-}

instance HasNext PrevNode where
  next1 = either next1 next1 . unPrevNode
  {-# INLINE next1 #-}
  setNext1 prevNode nextNode =
    either (`setNext1` nextNode) (`setNext1` nextNode) (unPrevNode prevNode)
  {-# INLINE setNext1 #-}
  toPrev1 = id
  {-# INLINE toPrev1 #-}

class HasPrev node where
  prev1 :: node s a -> ST s (PrevNode s a)
  prev1 = let x = x in x

  setPrev1 :: (HasNext prev_node) => node s a -> prev_node s a -> ST s ()
  setPrev1 = let x = x in x

  toNext1 :: node s a -> NextNode s a
  toNext1 = let x = x in x

  -- Abuse the MINIMAL pragma to not include hidden functions in Haddocks
  {-# MINIMAL #-}

instance HasPrev DoublyLinkedNode where
  prev1 valueNode = readSTRef valueNode.prevRef
  {-# INLINE prev1 #-}
  setPrev1 valueNode prevNode = writeSTRef valueNode.prevRef (toPrev1 prevNode)
  {-# INLINE setPrev1 #-}
  toNext1 = NextNode . Left
  {-# INLINE toNext1 #-}

instance HasPrev LastNode where
  prev1 (LastNode prevRef) = readSTRef prevRef
  {-# INLINE prev1 #-}
  setPrev1 (LastNode prevRef) prevNode = writeSTRef prevRef (toPrev1 prevNode)
  {-# INLINE setPrev1 #-}
  toNext1 = NextNode . Right
  {-# INLINE toNext1 #-}

instance HasPrev NextNode where
  prev1 = either prev1 prev1 . unNextNode
  {-# INLINE prev1 #-}
  setPrev1 nextNode prevNode =
    either (`setPrev1` prevNode) (`setPrev1` prevNode) (unNextNode nextNode)
  {-# INLINE setPrev1 #-}
  toNext1 = id
  {-# INLINE toNext1 #-}

null :: DoublyLinkedList s a -> ST s Bool
null list = do
  nextNode <- readSTRef list.firstNode.nextRef
  case nextNode of
    NextNode (Left _) -> pure False
    _ -> pure True
{-# INLINE null #-}

value :: DoublyLinkedNode s a -> a
value = _value
{-# INLINE value #-}

head :: DoublyLinkedList s a -> ST s (Maybe (DoublyLinkedNode s a))
head list =
  readSTRef list.firstNode.nextRef >>= \case
    NextNode (Left doublyLinkedNode) -> pure (Just doublyLinkedNode)
    _ -> pure Nothing

last :: DoublyLinkedList s a -> ST s (Maybe (DoublyLinkedNode s a))
last list =
  readSTRef list.lastNode.prevRef >>= \case
    PrevNode (Right doublyLinkedNode) -> pure (Just doublyLinkedNode)
    _ -> pure Nothing

next :: DoublyLinkedNode s a -> ST s (Maybe (DoublyLinkedNode s a))
next valueNode = do
  nextNode <- readSTRef valueNode.nextRef
  pure $ case nextNode of
    NextNode (Left valueNode') -> Just valueNode'
    _ -> Nothing

prev :: DoublyLinkedNode s a -> ST s (Maybe (DoublyLinkedNode s a))
prev valueNode = do
  prevNode <- readSTRef valueNode.prevRef
  pure $ case prevNode of
    PrevNode (Right valueNode') -> Just valueNode'
    _ -> Nothing

empty :: ST s (DoublyLinkedList s a)
empty = do
  firstNode <- FirstNode <$> newSTRef undefined
  lastNode <- LastNode <$> newSTRef undefined
  writeSTRef firstNode.nextRef (NextNode (Right lastNode))
  writeSTRef lastNode.prevRef (PrevNode (Left firstNode))
  pure (DoublyLinkedList firstNode lastNode)

insertAfter1 :: (HasNext node) => node s a -> a -> ST s (DoublyLinkedNode s a)
insertAfter1 nodeA x = do
  nodeC <- next1 nodeA
  nodeB <- DoublyLinkedNode x <$> newSTRef nodeC <*> newSTRef (toPrev1 nodeA)
  setNext1 nodeA nodeB
  setPrev1 nodeC nodeB
  pure nodeB

insertBefore1 :: (HasPrev node) => node s a -> a -> ST s (DoublyLinkedNode s a)
insertBefore1 nodeC x = do
  nodeA <- prev1 nodeC
  nodeB <- DoublyLinkedNode x <$> newSTRef (toNext1 nodeC) <*> newSTRef nodeA
  setNext1 nodeA nodeB
  setPrev1 nodeC nodeB
  pure nodeB

insertBefore :: DoublyLinkedNode s a -> a -> ST s (DoublyLinkedNode s a)
insertBefore = insertBefore1

insertAfter :: DoublyLinkedNode s a -> a -> ST s (DoublyLinkedNode s a)
insertAfter = insertAfter1

cons :: DoublyLinkedList s a -> a -> ST s (DoublyLinkedNode s a)
cons list = insertAfter1 list.firstNode
{-# INLINE cons #-}

snoc :: DoublyLinkedList s a -> a -> ST s (DoublyLinkedNode s a)
snoc list = insertBefore1 list.lastNode
{-# INLINE snoc #-}

delete :: DoublyLinkedNode s a -> ST s ()
delete nodeB = do
  nodeA <- prev1 nodeB
  nodeC <- next1 nodeB
  setNext1 nodeA nodeC
  setPrev1 nodeC nodeA

fromList :: [a] -> ST s (DoublyLinkedList s a)
fromList xs = do
  list <- empty
  forM_ xs $ \x ->
    snoc list x
  pure list

toList :: DoublyLinkedList s a -> ST s [a]
toList list = next1 list.firstNode >>= go
  where
    go :: NextNode s a -> ST s [a]
    go =
      nextNodeToDoublyLinkedNode >>> \case
        Nothing -> pure []
        Just valueNode -> (valueNode._value :) <$> (next1 valueNode >>= go)