From 185b857a9795d35bc1c5628d3721b949c5328d9f Mon Sep 17 00:00:00 2001
From: Michiel Cottaar <michiel.cottaar@ndcn.ox.ac.uk>
Date: Thu, 15 Feb 2024 15:16:48 +0000
Subject: [PATCH] Adjust iteration to go across multiple repeats

---
 src/all_sequences/base_sequences.jl | 47 ++++++++++++++++++++++++-----
 src/all_sequences/sequences.jl      |  6 ++--
 2 files changed, 43 insertions(+), 10 deletions(-)

diff --git a/src/all_sequences/base_sequences.jl b/src/all_sequences/base_sequences.jl
index 24ceedf..e610e7b 100644
--- a/src/all_sequences/base_sequences.jl
+++ b/src/all_sequences/base_sequences.jl
@@ -1,7 +1,7 @@
 module BaseSequences
 import ...ContainerBlocks: ContainerBlock
 import ...AllBuildingBlocks: BaseBuildingBlock
-import ...Variables: TR, duration
+import ...Variables: TR, duration, start_time
 
 """
 Super-type of any sequence of non-overlapping building blocks that should be played after each other.
@@ -11,27 +11,58 @@ It contains `N` [`ContainerBlock`](@ref) objects (e.g., building blocks or other
 Main interface:
 - Acts as an iterable containing the blocks and sequences.
     - Indiviual blocks/sequences can be obtained using indexing.
+    - If there is a finite number of repeats, the iteration will continue over all repeats.
 
 Sub-types need to implement:
-- `Base.getindex`: returns the actual component for each integer index in 1:N.
+- `get_index_single_TR`: return the index assuming it is between 1 and N
+- [`nrepeat`](@ref): how often the sequence should repeat (if not implemented, this will be 1).
+- [`TR`](@ref): time scale on which to repeat (if not implemented, this will be the sum of the individual block durations).
 """
 abstract type BaseSequence{N} <: ContainerBlock end
 
-
+function Base.getindex(bs::BaseSequence{N}, index::Integer) where {N}
+    if index < 1 || index > length(bs)
+        throw(BoundsError(bs, index))
+    end
+    base_index = ((index - 1) % N) + 1
+    return get_index_single_TR(bs, base_index)
+end
 Base.iterate(bs::BaseSequence) = Base.iterate(bs, 1)
-Base.iterate(bs::BaseSequence{N}, index::Integer) where {N} = index > N ? nothing : (bs[index], index + 1)
-Base.length(::BaseSequence{N}) where {N} = N
+Base.iterate(bs::BaseSequence{N}, index::Integer) where {N} = index > length(bs) ? nothing : (bs[index], index + 1)
+Base.length(bs::BaseSequence{N}) where {N} = nrepeat(bs) * N
 Base.eltype(::Type{<:BaseSequence}) = ContainerBlock
 
+function start_time(bs::BaseSequence{N}, index::Integer) where {N}
+    nTR = div(index-1, N, RoundDown)
+    if 0 < nrepeat(bs) <= nTR
+        throw(BoundsError(bs, index))
+    end
+    base_index = ((index - 1) % N) + 1
+    base_time = sum(i -> duration(bs[i]), 1:base_index)
+    if iszero(nTR)
+        return base_time
+    else
+        return nTR * TR(bs) + base_time
+    end
+end
+
+
+"""
+    get_index_single_TR(sequence, index)
+
+Used internally by any [`BaseSequence`](@ref) to get a specific block.
+The `index` should be between 1 and N.
+It should be implemented for any sub-classes of [`BaseSequence`](@ref).
+"""
+function get_index_single_TR end
+
 
 """
     nrepeat(sequence)
 
 How often sequence should be repeated. 
-
-Set to 0 to repeat indefinetely (default value if not overriden for a particular sequence type).
 """
-nrepeat(bs::BaseSequence) = 0
+nrepeat(bs::BaseSequence) = 1
 
 """
     TR(sequence)
diff --git a/src/all_sequences/sequences.jl b/src/all_sequences/sequences.jl
index 81062be..af67159 100644
--- a/src/all_sequences/sequences.jl
+++ b/src/all_sequences/sequences.jl
@@ -5,7 +5,7 @@ import ...Variables: get_free_variable, TR, VariableType
 import ...AllBuildingBlocks: Wait, BuildingBlock
 import ...BuildSequences: global_model
 import ...Components: EventComponent
-import ..BaseSequences: ContainerBlock, BaseSequence, nrepeat
+import ..BaseSequences: ContainerBlock, BaseSequence, nrepeat, get_index_single_TR
 
 """
     Sequence(blocks; TR=:min, nrepeat=0)
@@ -35,7 +35,9 @@ end
 
 Sequence(blocks...; kwargs...) = Sequence([blocks...]; kwargs...)
 
-Base.getindex(s::Sequence, i::Integer) = s.blocks[i]
+get_index_single_TR(s::Sequence, i::Integer) = s.blocks[i]
+Base.IteratorSize(::Type{<:Sequence}) = Base.IsInfinite()
+nrepeat(::Sequence) = Inf
 
 """
     to_block(block_like)
-- 
GitLab