-- |
-- Module      : Data.Rhythm.Christoffel
-- Copyright   : (c) Eric Bailey, 2025
--
-- License     : MIT
-- Maintainer  : eric@ericb.me
-- Stability   : experimental
-- Portability : POSIX
--
-- Christoffel words.
module Data.Rhythm.Christoffel
  ( christoffelWord,
  )
where

import Data.Bool (bool)
import Data.List.NonEmpty (NonEmpty (..))
import Data.List.NonEmpty qualified as NE
import Data.Maybe (fromMaybe)

-- | Generate the upper or lower Christoffel word for a given slope with a given
-- number of terms.
--
-- >>> christoffelWord False 3 7 Nothing
-- [0,0,0,1,0,0,1,0,0,1]
--
-- >>> christoffelWord True 3 7 Nothing
-- [1,0,0,1,0,0,1,0,0,0]
--
-- >>> christoffelWord True 3 7 (Just 20)
-- [1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,0,0]
christoffelWord :: Bool -> Integer -> Integer -> Maybe Int -> [Integer]
christoffelWord :: Bool -> Integer -> Integer -> Maybe Int -> [Integer]
christoffelWord Bool
isUpperWord Integer
numerator Integer
denominator Maybe Int
nTerms =
  Int -> NonEmpty Integer -> [Integer]
forall a. Int -> NonEmpty a -> [a]
NE.take Int
k (NonEmpty Integer -> [Integer]) -> NonEmpty Integer -> [Integer]
forall a b. (a -> b) -> a -> b
$
    NonEmpty Integer -> NonEmpty Integer
forall a. NonEmpty a -> NonEmpty a
NE.cycle (NonEmpty Integer -> NonEmpty Integer)
-> NonEmpty Integer -> NonEmpty Integer
forall a b. (a -> b) -> a -> b
$
      Integer -> Integer -> Bool -> Integer
forall a. a -> a -> Bool -> a
bool Integer
0 Integer
1 Bool
isUpperWord Integer -> [Integer] -> NonEmpty Integer
forall a. a -> [a] -> NonEmpty a
:| Int -> Integer -> Integer -> [Integer]
forall {a}. Num a => Int -> Integer -> Integer -> [a]
go Int
1 Integer
numerator Integer
denominator
  where
    go :: Int -> Integer -> Integer -> [a]
go !Int
i !Integer
a !Integer
b
      | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
n = []
      | Bool
otherwise =
          case Integer -> Integer -> Ordering
forall a. Ord a => a -> a -> Ordering
compare Integer
a Integer
b of
            Ordering
GT -> a
1 a -> [a] -> [a]
forall a. a -> [a] -> [a]
: Int -> Integer -> Integer -> [a]
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Integer
a (Integer
b Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
denominator)
            Ordering
EQ -> a -> a -> Bool -> a
forall a. a -> a -> Bool -> a
bool a
1 a
0 Bool
isUpperWord a -> [a] -> [a]
forall a. a -> [a] -> [a]
: Int -> Integer -> Integer -> [a]
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Integer
numerator Integer
denominator
            Ordering
LT -> a
0 a -> [a] -> [a]
forall a. a -> [a] -> [a]
: Int -> Integer -> Integer -> [a]
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (Integer
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
numerator) Integer
b
    n :: Int
n = Integer -> Int
forall a. Num a => Integer -> a
fromInteger (Integer
numerator Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
denominator)
    k :: Int
k = Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
n Maybe Int
nTerms