Infinitree: Infinitely deep trees for lazy stateless memoization

[ agpl, cache, caching, data-structures, library ] [ Propose Tags ] [ Report a vulnerability ]

Please see the example in the documentation at https://vegowotenks.de/projects/Infinitree/ or in the readme at https://git.jossco.de/VegOwOtenks/Infinitree#readme


[Skip to Readme]

Downloads

Note: This package has metadata revisions in the cabal description newer than included in the tarball. To unpack the package including the revisions, use 'cabal get'.

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.1.0.0
Change log CHANGELOG.md
Dependencies adjunctions (>=4.4.3 && <4.5), base (>=4.21.0 && <4.22), containers (>=0.7 && <0.8), distributive (>=0.6.2 && <0.7), infinite-list (>=0.1.2 && <0.2), random (>=1.3.1 && <1.4) [details]
License AGPL-3.0-or-later
Copyright 2025 VegOwOtenks
Author VegOwOtenks
Maintainer hackage@vegowotenks.de
Revised Revision 1 made by VegOwOtenks at 2025-10-15T13:11:54Z
Category Cache, Caching, Data Structures
Source repo head: git clone https://git.jossco.de/VegOwOtenks/Infinitree
Uploaded by VegOwOtenks at 2025-10-15T13:08:51Z
Distributions
Downloads 2 total (2 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs uploaded by user
Build status unknown [no reports yet]

Readme for Infinitree-0.1.0.0

[back to package description]

Infinitree

Memoization using Lazy Infinite trees indexed by natural numbers

Considerations

Using this data structure comes with trade-offs:

  • It is impossible to evict data from the cache
  • The cache is unbound in size
  • Indexing can be done only using Natural Numbers
  • Lookup is logarithmic in time and space

Usage

This is a rather constructed example.

fibonacci = Infinitree.build $ go
  where
    go 0 = 0
    go 1 = 1
    go n = Infinitree.index fibonacci (n - 1) + Infinitree.index fibonacci (n - 2)

It is also possible to use multiple levels of infinitrees, you can see an example of this in a solution to a puzzle from Advent Of Code 2024. The code below may be a spoiler if you're trying to do the puzzle linked above. It uses two layers of cache trees and may make a lot more sense after you've read the problem description.

{-# LANGUAGE MultiWayIf #-}
import Control.Arrow ( (>>>), Arrow((&&&)) )

import Data.Infinitree (Infinitree)
import Numeric.Natural (Natural)
import qualified Data.Infinitree as Infinitree

parse :: String -> [StoneNumber]
parse = words >>> map read

type StoneNumber = Natural
type StoneCount  = Natural
type BlinkCount  = Natural

lookupStoneCount :: BlinkCount -> StoneNumber -> StoneCount
lookupStoneCount i = Infinitree.index (Infinitree.index blinkTree i)

blinkTree :: Infinitree (Infinitree StoneCount)
blinkTree = Infinitree.build stoneTree

stoneTree :: Natural -> Infinitree StoneCount
stoneTree = Infinitree.build . countSplit

countSplit :: BlinkCount -> StoneNumber -> StoneCount
countSplit 0 _ = 1
countSplit i n = if
  | n == 0 ->
    lookupStoneCount (pred i) (succ n)
  | even nDigits ->
    lookupStoneCount (pred i) firstSplit + lookupStoneCount (pred i) secondSplit
  | otherwise ->
    lookupStoneCount (pred i) (n * 2024)
    where
      nDigits = digitCount n :: Int
      secondSplit    = n `mod` (10 ^ (nDigits `div` 2))
      firstSplit     = (n - secondSplit) `div` (10 ^ (nDigits `div` 2))

part1 :: [StoneNumber] -> StoneCount
part1 = map (lookupStoneCount 25)
  >>> sum
part2 :: [StoneNumber] -> StoneCount
part2 = map (lookupStoneCount 75)
  >>> sum

digitCount :: (Integral a, Integral b) => a -> b
digitCount = succ . floor . logBase 10 . fromIntegral

main :: IO ()
main = getContents
        >>= print
        . (part1 &&& part2)
        . parse