/// <summary> /// Adjusts the lifetime of incoming events to implement, when used in combination with aggregates, hopping windows. In this implementation each incoming /// event results in a single outgoing event, which means that subsequent aggregates only produce output when the input changes. For instance, if a single /// point event is received and the windowSize, period, and offset are 100, 2, and 0 respectively, a single aggregate output is produced with a lifetime of /// 100 ticks. /// </summary> /// <typeparam name="TKey">Type of (mapping) key in the stream</typeparam> /// <typeparam name="TPayload">Type of payload in the stream</typeparam> /// <param name="source">Input stream</param> /// <param name="windowSize">Window size</param> /// <param name="period">Period (or hop size)</param> /// <param name="offset">Offset from the start of time</param> /// <returns>Result (output) stream</returns> public static IStreamable <TKey, TPayload> HoppingWindowLifetime <TKey, TPayload>( this IStreamable <TKey, TPayload> source, long windowSize, long period, long offset = 0) { if (period > windowSize) { return(source .Select((vs, e) => new { StartTime = vs, Payload = e }) .Where(x => (((x.StartTime + period - offset) % period) > (period - windowSize)) || ((x.StartTime + period - offset) % period == 0)) .Select(x => x.Payload) .AlterEventLifetime(vs => AdjustStartTime(vs, offset, period), windowSize) .SetProperty().IsConstantHop(true, period, offset)); } if (windowSize % period == 0) { return(source.AlterEventLifetime(vs => AdjustStartTime(vs, offset, period), windowSize) .SetProperty().IsConstantHop(true, period, offset)); } else { return(source.AlterEventLifetime( vs => AdjustStartTime(vs, offset, period), vs => AdjustStartTime(vs + windowSize, offset, period) - AdjustStartTime(vs, offset, period)) .SetProperty().IsConstantHop(true, period, offset)); } }