packages feed

moonlight-triangulation-0.1.0.0: src-dcel/Moonlight/Triangulation/Handles/Iterators/CircularIterator.hs

{-# LANGUAGE BangPatterns #-}

module Moonlight.Triangulation.Handles.Iterators.CircularIterator
  ( circularList
  , foldCircular'
  ) where

import GHC.Exts (build)

-- | The cycle reached from a start by repeated advance, in visit order.
--
-- Emitted forwards, for the reason 'Moonlight.Triangulation.Dcel.circularWalk'
-- is: accumulating in reverse and reversing at the end builds the ring twice
-- and hands back a list no consumer can fuse with.
circularList :: Eq a => Int -> (a -> a) -> a -> [a]
circularList limit advance start =
  build
    ( \link stop ->
        let go !remaining !current !visited
              | remaining <= 0 = stop
              | visited && current == start = stop
              | otherwise = link current (go (remaining - 1) (advance current) True)
         in go limit start False
    )
{-# INLINE circularList #-}

foldCircular' :: Eq a => Int -> (a -> a) -> a -> (b -> a -> b) -> b -> b
foldCircular' limit advance start step = go limit start False
 where
  go !remaining !current !visited !accumulator
    | remaining <= 0 = accumulator
    | visited && current == start = accumulator
    | otherwise = go (remaining - 1) (advance current) True (step accumulator current)