Skip to content
This repository was archived by the owner on Mar 22, 2025. It is now read-only.

Commit 1609f23

Browse files
committed
Initial hacky implementation
1 parent 5a883d3 commit 1609f23

File tree

1 file changed

+96
-0
lines changed

1 file changed

+96
-0
lines changed

src/base.jl

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ Base.@propagate_inbounds function update_state!(
9595
) where {T,Op,Op!}
9696
# Our index into previous_cumsum is advanced by the number of values we drop from the
9797
# window.
98+
# TODO This could be dangerous, since if we get an error later the state will be
99+
# invalid.
98100
state.ri_previous_cumsum += num_dropped_from_window
99101

100102
# If this has taken us out-of-range of the _previous_cumsum values, we must recompute
@@ -167,6 +169,96 @@ Base.@propagate_inbounds function update_state!(
167169
return state
168170
end
169171

172+
"""
173+
drop_values!(
174+
state::WindowedAssociativeOp{T,Op,Op!}, n::Integer
175+
) where {T,Op,Op!}
176+
177+
Drop `n` values from the leading edge of the window.
178+
"""
179+
function drop_values!(state::WindowedAssociativeOp{T,Op,Op!}, n::Integer) where {T,Op,Op!}
180+
n > 0 || throw(ArgumentError("n must be positive, got n=$n"))
181+
182+
# FIXME At best this will be hacky
183+
# FIXME At best this will be hacky
184+
# FIXME At best this will be hacky
185+
# FIXME At best this will be hacky
186+
187+
# Our index into previous_cumsum is advanced by the number of values we drop from the
188+
# window.
189+
# TODO This could be dangerous, since if we get an error later the state will be
190+
# invalid.
191+
state.ri_previous_cumsum += n
192+
193+
# If this has taken us out-of-range of the _previous_cumsum values, we must recompute
194+
# them.
195+
elements_remaining = length(state.previous_cumsum) - state.ri_previous_cumsum
196+
197+
if elements_remaining < 0
198+
# We have overshot the end of previous_cumsum.
199+
200+
# We may also need to discard elements from values. This could happen if n > 1.
201+
num_values_to_remove = -elements_remaining
202+
@boundscheck if num_values_to_remove > length(state.values)
203+
throw(ArgumentError("n = $n is out of range"))
204+
end
205+
206+
# We now generate the partial sum, and set our index back to zero. values is also
207+
# emptied, since its information is now reflected in previous_cumsum.
208+
# NOTE: We need to take care here in the case of non-commutation. In accumulate, we
209+
# will be getting:
210+
#
211+
# (x0, op(x0, x1), op(op(x0, x1), x2), ...)
212+
#
213+
# but we actually want:
214+
#
215+
# (x0, op(x1, x0), op(x2, op(x1, x0)), ...)
216+
217+
# Conceptually the following code is equivalent to the following, however avoids
218+
# unnecessary allocations:
219+
# trimmed_reversed_values = state.values[end:-1:1 + num_values_to_remove]
220+
# state.previous_cumsum = accumulate(
221+
# (x, y) -> Op(y, x),
222+
# trimmed_reversed_values
223+
# )
224+
225+
empty!(state.previous_cumsum)
226+
upper = length(state.values)
227+
lower = 1 + num_values_to_remove
228+
if (upper - lower) >= 0 # i.e. we have a non-zero range
229+
i = upper
230+
accumulation = @inbounds(state.values[i])
231+
while true
232+
push!(state.previous_cumsum, accumulation)
233+
i -= 1
234+
i >= lower || break
235+
# If we were to use a mutating operation here, then we'd have to introduce
236+
# a copy above. So there is no drawback to using the non-mutating Op.
237+
accumulation = Op(@inbounds(state.values[i]), accumulation)
238+
end
239+
end
240+
241+
state.ri_previous_cumsum = 0
242+
empty!(state.values)
243+
# FIXME state.sum is now garbage.
244+
# This matters...
245+
end
246+
247+
248+
# # We want to make sure that we never actually mutate any value object that we pass to
249+
# # `update_state!`.
250+
# # Note that when initialising `state.sum`, we should take a copy if our mutating `Op!`
251+
# # is different to `Op`. This is because we know that `Op` is necessarily non-mutating.
252+
# _copy(x) = (Op == Op!) ? x : deepcopy(x)
253+
254+
# # Include the new value in sum and values.
255+
# state.sum = isempty(state.values) ? _copy(value) : Op!(state.sum, value)
256+
# state.sum =
257+
# push!(state.values, value)
258+
259+
return state
260+
end
261+
170262
"""
171263
window_value(state::WindowedAssociativeOp{T})::T where T
172264
@@ -188,6 +280,10 @@ function window_value(state::WindowedAssociativeOp{T,Op})::T where {T,Op}
188280
# We aren't using the A buffer, either because values is full or the A buffer has
189281
# not yet been populated.
190282
state.sum
283+
elseif length(state.values) == 0
284+
# We are using the A buffer, but not the B buffer.
285+
# `state.sum` has an uninitialised value.
286+
@inbounds(state.previous_cumsum[index])
191287
else
192288
Op(@inbounds(state.previous_cumsum[index]), state.sum)
193289
end

0 commit comments

Comments
 (0)