MyNixOS website logo
Description

Infinitely deep trees for lazy stateless memoization.

Please see the example in the documentation at https://vegowotenks.de/projects/Infinitree/

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
Metadata

Version

0.1.0.0

Platforms (76)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    WASI
    Windows
Show all
  • aarch64-darwin
  • aarch64-freebsd
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64-windows
  • aarch64_be-none
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-linux
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • vc4-none
  • wasm32-wasi
  • wasm64-wasi
  • x86_64-cygwin
  • x86_64-darwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-windows