-- From https://byorgey.wordpress.com/2016/10/25/adventures-in-enumerating-balanced-brackets/
import Data.Unfolder
import Data.MemoTrie (memo)
import Control.Applicative

enumBrackets :: Unfolder f => f String
enumBrackets = enumBracketsTail 0

enumBracketsTail :: Unfolder f => Int -> f String
enumBracketsTail = enumBracketsTail'
  where
    -- Ensure memoization happens for a specific `f`
    enumBracketsTail' = memo enumBracketsTail''
    enumBracketsTail'' 0 = pure "" <|> choose [('(':) <$> enumBracketsTail' 1]
    enumBracketsTail'' c =
      choose [('(':) <$> enumBracketsTail' (c+1)]
      <|>
      ((')':) <$> enumBracketsTail' (c-1))


{-

>>> enumBrackets 3 :: [String]
["((()))","(()())","(())()","()(())","()()()"]
>>> getNth (enumBrackets 40) 16221270422764920820
"((((((((()((())()(()()()())(()))((()()()()(()((()())))((()())))))))()))()())()))"
>>> size (enumBrackets 100)
896519947090131496687170070074100632420837521538745909320

-}