packages feed

zwirn-core-0.1.1.0: src/Zwirn/Core/Tree.hs

{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}

module Zwirn.Core.Tree where

{-
    Tree.hs - a structure for parallel signals
    Copyright (C) 2025, Martin Gius

    This library is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this library.  If not, see <http://www.gnu.org/licenses/>.
-}

import Data.Bifunctor
import Data.Fixed (mod')
import Zwirn.Core.Types

nth :: (RealFrac r) => r -> [a] -> a
nth = wrapAt

wrapAt :: (RealFrac r) => r -> [a] -> a
wrapAt t ls = ls !! phase
  where
    l = fromIntegral $ length ls
    phase = mod (floor t) (length ls)

frac :: (Real r) => r -> r
frac d = mod' d 1

data Tree a
  = Leaf a
  | Branch [Tree a]
  deriving (Show, Eq, Functor)

instance ToList Tree where
  toList (Leaf a) = [a]
  toList (Branch as) = concatMap toList as

(!!!) :: (Num b, RealFrac b) => [a] -> b -> a
(!!!) as r = nth r as

empty :: Tree a
empty = Branch []

isEmpty :: Tree a -> Bool
isEmpty (Leaf _) = False
isEmpty (Branch ts) = all isEmpty ts

singleton :: a -> Tree a
singleton = Leaf

fromList :: [a] -> Tree a
fromList as = Branch $ map singleton as

look :: Int -> Tree a -> Tree a
look _ (Leaf x) = Leaf x
look i (Branch xs) = xs !!! fromIntegral i

look' :: Int -> Tree a -> Tree a
look' 0 (Leaf x) = Leaf x
look' _ (Leaf x) = empty
look' i (Branch xs) = if length xs > i && i >= 0 then xs !! i else empty

lookup :: [Int] -> Tree a -> Tree a
lookup is x = foldl (flip look) x is

concatMapTree :: (a -> Tree b) -> Tree a -> Tree b
concatMapTree f x = squeezeJoin $ fmap f x

topLength :: Tree a -> Int
topLength (Leaf _) = 1
topLength (Branch xs) = length xs

push :: Tree a -> Tree a -> Tree a
push x l@(Leaf _) = Branch [x, l]
push x (Branch xs) = Branch (x : xs)

pop :: Tree a -> Tree a
pop (Leaf _) = empty
pop (Branch []) = empty
pop (Branch (x : xs)) = Branch xs

insertT :: Int -> Tree a -> Tree a -> Tree a
insertT 0 x (Leaf y) = Branch [x, Leaf y]
insertT _ x (Leaf y) = Branch [Leaf y, x]
insertT i x (Branch ys) = Branch (ys1 ++ [x] ++ ys2)
  where
    (ys1, ys2) = splitAt i ys

removeT :: Int -> Tree a -> Tree a
removeT _ (Leaf x) = empty
removeT i (Branch xs) = case splitAt i xs of
  (xs1, []) -> Branch xs1
  (xs1, _ : xs2) -> Branch $ xs1 ++ xs2

-------------------------------------------------------
------------------- APPLICATIVE STUFF -----------------
-------------------------------------------------------

instance MultiApplicative [] where
  liftA2Left f [] _ = []
  liftA2Left f _ [] = []
  liftA2Left f as bs = map (\i -> f (as !! i) (bs !! floor ((fromIntegral i / fromIntegral n) * fromIntegral m))) [0 .. n - 1]
    where
      n = length as
      m = length bs
  liftA2Right f as bs = liftA2Left (flip f) bs as

instance Applicative Tree where
  pure = Leaf
  liftA2 f (Leaf x) (Leaf y) = Leaf $ f x y
  liftA2 f l@(Leaf _) (Branch ys) = Branch $ map (liftA2 f l) ys
  liftA2 f (Branch xs) l@(Leaf _) = Branch $ map (\x -> liftA2 f x l) xs
  liftA2 f (Branch xs) (Branch ys) = Branch $ lift2Both (liftA2 f) xs ys

instance MultiApplicative Tree where
  liftA2Left f (Leaf x) (Leaf y) = Leaf $ f x y
  liftA2Left f l@(Leaf _) (Branch ys) = Branch $ map (liftA2Left f l) ys
  liftA2Left f (Branch xs) l@(Leaf _) = Branch $ map (\x -> liftA2Left f x l) xs
  liftA2Left f (Branch xs) (Branch ys) = Branch $ liftA2Left (liftA2Left f) xs ys
  liftA2Right f (Leaf x) (Leaf y) = Leaf $ f x y
  liftA2Right f l@(Leaf _) (Branch ys) = Branch $ map (liftA2Right f l) ys
  liftA2Right f (Branch xs) l@(Leaf _) = Branch $ map (\x -> liftA2Right f x l) xs
  liftA2Right f (Branch xs) (Branch ys) = Branch $ liftA2Right (liftA2Right f) xs ys

lift2Both :: (a -> b -> c) -> [a] -> [b] -> [c]
lift2Both f as bs =
  if n < m
    then liftA2Right f as bs
    else liftA2Left f as bs
  where
    n = length as
    m = length bs

--------------------------------------------------
------------------- MONAD STUFF ------------------
--------------------------------------------------

instance MultiMonad [] where
  innerJoin = concat
  outerJoin = concat
  squeezeJoin = concat

instance Monad Tree where
  (>>=) x f = innerJoin $ f <$> x

instance MultiMonad Tree where
  innerJoin t = squeezeJoin $ fmap select indx
    where
      indx = indexTree t
      select (is, x) = reduceNested is x
  outerJoin = innerJoin
  squeezeJoin (Leaf x) = x
  squeezeJoin (Branch xs) = Branch $ map squeezeJoin xs

indexTree :: Tree a -> Tree ([(Int, Int)], a)
indexTree (Leaf x) = Leaf ([], x)
indexTree (Branch bs) = Branch $ map (\i -> first ((i, length bs) :) <$> indexTree (bs !! i)) [0 .. length bs - 1]

reduce :: (Int, Int) -> Tree a -> [Tree a]
reduce (i, _) (Leaf x) = [Leaf x]
reduce (i, n) (Branch xs) = map (\l -> nth (fromIntegral l) xs) ind
  where
    m = length xs
    ind = [j | j <- [0 .. m - 1], fromIntegral i / fromIntegral n <= fromIntegral j / fromIntegral m, fromIntegral j / fromIntegral m < fromIntegral (i + 1) / fromIntegral n]

reduceNested :: [(Int, Int)] -> Tree a -> Tree a
reduceNested [] x = x
reduceNested (i : is) x = Branch $ map (reduceNested is) (reduce i x)