/// <inheritdoc/> public override InterpolationResult <T> Interpolate(DateTime interpolationTime, IEnumerable <Message <T> > messages, DateTime?closedOriginatingTime) { // If no messages available, if (messages.Count() == 0) { // Then depending on orDefault, either create a default value or return does not exist. return(this.orDefault ? InterpolationResult <T> .Create(this.defaultValue, DateTime.MinValue) : InterpolationResult <T> .DoesNotExist(DateTime.MinValue)); } Message <T> nearestMatch = default; TimeSpan minDistance = TimeSpan.MaxValue; DateTime upperBound = (this.relativeTimeInterval.Right < TimeSpan.Zero) ? interpolationTime.BoundedAdd(this.relativeTimeInterval.Right) : interpolationTime; foreach (var message in messages) { var delta = message.OriginatingTime - interpolationTime; // Determine if the message is on the right side of the window start var messageIsAfterWindowStart = this.relativeTimeInterval.LeftEndpoint.Inclusive ? delta >= this.relativeTimeInterval.Left : delta > this.relativeTimeInterval.Left; // Only consider messages that occur within the lookback window. if (messageIsAfterWindowStart) { // Determine if the message is outside the window end var messageIsOutsideWindowEnd = this.relativeTimeInterval.RightEndpoint.Inclusive ? delta > this.relativeTimeInterval.Right : delta >= this.relativeTimeInterval.Right; // We stop searching either when we reach a message that is beyond the lookahead // window or when the distance (absolute delta) exceeds the best distance. var distance = delta.Duration(); if (messageIsOutsideWindowEnd || (distance > minDistance)) { break; } // keep track of the nearest match so far and its delta nearestMatch = message; minDistance = distance; } } // If minDistance is anything other than MaxValue, we found a nearest matching message. if (minDistance < TimeSpan.MaxValue) { // Return the matching message value as the matched result. // All messages before the matching message are obsolete. return(InterpolationResult <T> .Create(nearestMatch.Data, nearestMatch.OriginatingTime)); } else { // o/w, that means no match was found. In that case, either return // DoesNotExist or the default value (according to the parameter) return(this.orDefault ? InterpolationResult <T> .Create(this.defaultValue, upperBound) : InterpolationResult <T> .DoesNotExist(upperBound)); } }
/// <inheritdoc/> public override InterpolationResult <T> Interpolate(DateTime interpolationTime, IEnumerable <Message <T> > messages, DateTime?closedOriginatingTime) { // If no messages available, if (messages.Count() == 0) { // Then depending on orDefault, either create a default value or return does not exist. return(this.orDefault ? InterpolationResult <T> .Create(this.defaultValue, DateTime.MinValue) : InterpolationResult <T> .DoesNotExist(DateTime.MinValue)); } Message <T> firstMessage = default; bool found = false; foreach (var message in messages) { var delta = message.OriginatingTime - interpolationTime; // Determine if the message is on the right side of the window start var messageIsAfterWindowStart = this.relativeTimeInterval.LeftEndpoint.Inclusive ? delta >= this.relativeTimeInterval.Left : delta > this.relativeTimeInterval.Left; // Only consider messages that occur within the lookback window. if (messageIsAfterWindowStart) { // Determine if the message is outside the window end var messageIsOutsideWindowEnd = this.relativeTimeInterval.RightEndpoint.Inclusive ? delta > this.relativeTimeInterval.Right : delta >= this.relativeTimeInterval.Right; // We stop searching either when we reach a message that is beyond the lookahead window if (messageIsOutsideWindowEnd) { break; } // keep track of message and exit the loop as we found the first one firstMessage = message; found = true; break; } } // If we found a first message in the window if (found) { // Return the matching message value as the matched result. // All messages before the matching message are obsolete. return(InterpolationResult <T> .Create(firstMessage.Data, firstMessage.OriginatingTime)); } else { // o/w, that means no match was found, which implies there was no message in the // window. In that case, either return DoesNotExist or the default value. var windowLeft = interpolationTime.BoundedAdd(this.relativeTimeInterval.Left); return(this.orDefault ? InterpolationResult <T> .Create(this.defaultValue, windowLeft) : InterpolationResult <T> .DoesNotExist(windowLeft)); } }
/// <inheritdoc/> public override InterpolationResult <TOut> Interpolate(DateTime interpolationTime, IEnumerable <Message <TIn> > messages, DateTime?closedOriginatingTime) { // If no messages available, if (messages.Count() == 0) { // If stream is closed, if (closedOriginatingTime.HasValue) { // Then no other value or better match will appear, so depending on orDefault, // either create a default value or return does not exist. return(this.orDefault ? InterpolationResult <TOut> .Create(this.defaultValue, DateTime.MinValue) : InterpolationResult <TOut> .DoesNotExist(DateTime.MinValue)); } else { // otherwise if the stream is not closed yet, insufficient data return(InterpolationResult <TOut> .InsufficientData()); } } Message <TIn> lastMessage = default; foreach (var message in messages) { // If we have a message past or at the interpolation time if (message.OriginatingTime >= interpolationTime) { // Then if we have a previous message if (lastMessage != default) { // Then interpolate and return the result var ratio = (interpolationTime - lastMessage.OriginatingTime).Ticks / (double)(message.OriginatingTime - lastMessage.OriginatingTime).Ticks; return(InterpolationResult <TOut> .Create( this.interpolator(lastMessage.Data, message.Data, ratio), message.OriginatingTime == interpolationTime?message.OriginatingTime : lastMessage.OriginatingTime)); } else if (message.OriginatingTime == interpolationTime) { // O/w if the message is right at the interpolation time, we don't need the previous // message return(InterpolationResult <TOut> .Create(this.interpolator(default, message.Data, 1), message.OriginatingTime));
/// <inheritdoc/> public override InterpolationResult <T> Interpolate(DateTime interpolationTime, IEnumerable <Message <T> > messages, DateTime?closedOriginatingTime) { // If no messages available, if (messages.Count() == 0) { // If stream is closed, if (closedOriginatingTime.HasValue) { // Then no other value or better match will appear, so depending on orDefault, // either create a default value or return does not exist. return(this.orDefault ? InterpolationResult <T> .Create(this.defaultValue, DateTime.MinValue) : InterpolationResult <T> .DoesNotExist(DateTime.MinValue)); } else { // otherwise if the stream is not closed yet, insufficient data return(InterpolationResult <T> .InsufficientData()); } } // Look for the last match that's stil within the window Message <T> lastMatchingMessage = default; bool found = false; foreach (var message in messages) { var delta = message.OriginatingTime - interpolationTime; // Determine if the message is on the right side of the window start var messageIsAfterWindowStart = this.relativeTimeInterval.LeftEndpoint.Inclusive ? delta >= this.relativeTimeInterval.Left : delta > this.relativeTimeInterval.Left; // Only consider messages that occur within the lookback window. if (messageIsAfterWindowStart) { // Determine if the message is outside the window end var messageIsOutsideWindowEnd = this.relativeTimeInterval.RightEndpoint.Inclusive ? delta > this.relativeTimeInterval.Right : delta >= this.relativeTimeInterval.Right; // We stop searching when we reach a message that is beyond the window end if (messageIsOutsideWindowEnd) { break; } // keep track of the best match so far and its delta lastMatchingMessage = message; found = true; } } // Compute whether the last available message is beyond the lookup window var lastMessageDelta = messages.Last().OriginatingTime - interpolationTime; var lastMessageIsOutsideWindowEnd = this.relativeTimeInterval.RightEndpoint.Inclusive ? lastMessageDelta > this.relativeTimeInterval.Right : lastMessageDelta >= this.relativeTimeInterval.Right; // If we found a last message in the lookup window if (found) { // Then we need to make sure it is indeed provably the last message we will see in // that window. For this to be the case, either the stream has to be closed, or the // last matching message has to be on the right end of an inclusive window, or the // last message we have available must be beyond the right end of the lookup window. var provablyLast = closedOriginatingTime.HasValue || (this.relativeTimeInterval.RightEndpoint.Inclusive && (lastMatchingMessage.OriginatingTime - interpolationTime == this.relativeTimeInterval.Right)) || lastMessageIsOutsideWindowEnd; // If we can prove this is indeed the last message if (provablyLast) { // Return the matching message value as the matched result. // All messages strictly before the matching message are obsolete. return(InterpolationResult <T> .Create(lastMatchingMessage.Data, lastMatchingMessage.OriginatingTime)); } else { // O/w return insufficient data. return(InterpolationResult <T> .InsufficientData()); } } // If we arrive here, it means we did not find a last message in the lookup window, // which also means there is no message in the loopkup window. // If the stream was closed or last message is at or past the upper search bound, if (closedOriginatingTime.HasValue || lastMessageIsOutsideWindowEnd) { // Then no future messages will match better, so return DoesNotExist or the default value // (according to the parameter) var windowLeftDateTime = interpolationTime.BoundedAdd(this.relativeTimeInterval.Left); return(this.orDefault ? InterpolationResult <T> .Create(this.defaultValue, windowLeftDateTime) : InterpolationResult <T> .DoesNotExist(windowLeftDateTime)); } else { // Otherwise signal insufficient data. return(InterpolationResult <T> .InsufficientData()); } }
/// <inheritdoc/> public override InterpolationResult <T> Interpolate(DateTime interpolationTime, IEnumerable <Message <T> > messages, DateTime?closedOriginatingTime) { var count = messages.Count(); // If no messages available if (count == 0) { // If stream is closed, if (closedOriginatingTime.HasValue) { // Then no other value or better match will appear, so depending on orDefault, // either create a default value or return does not exist. return(this.orDefault ? InterpolationResult <T> .Create(this.defaultValue, DateTime.MinValue) : InterpolationResult <T> .DoesNotExist(DateTime.MinValue)); } else { // otherwise if the stream is not closed yet, insufficient data return(InterpolationResult <T> .InsufficientData()); } } // Look for the nearest match Message <T> nearestMatch = default; TimeSpan minDistance = TimeSpan.MaxValue; DateTime upperBound = (this.relativeTimeInterval.Right < TimeSpan.Zero) ? interpolationTime.BoundedAdd(this.relativeTimeInterval.Right) : interpolationTime; int i = 0; foreach (var message in messages) { var delta = message.OriginatingTime - interpolationTime; // Determine if the message is on the right side of the window start var messageIsAfterWindowStart = this.relativeTimeInterval.LeftEndpoint.Inclusive ? delta >= this.relativeTimeInterval.Left : delta > this.relativeTimeInterval.Left; // Only consider messages that occur within the lookback window. if (messageIsAfterWindowStart) { // Determine if the message is outside the window end var messageIsOutsideWindowEnd = this.relativeTimeInterval.RightEndpoint.Inclusive ? delta > this.relativeTimeInterval.Right : delta >= this.relativeTimeInterval.Right; var distance = delta.Duration(); // We stop searching either when we reach a message that is beyond the window end // when the distance (absolute delta) exceeds the minimum distance. if (messageIsOutsideWindowEnd || (distance > minDistance)) { break; } // keep track of the nearest match so far and its delta nearestMatch = message; minDistance = distance; } i++; } // If minDistance is anything other than MaxValue, we found a nearest matching message. if (minDistance < TimeSpan.MaxValue) { // Check if we need to satisfy additional conditions // If the nearest match is the last available message and the stream has not closed if ((i == count) && !closedOriginatingTime.HasValue) { // Then other messages might arrive that might constitute an even better match. // We need to guarantee that nearestMatch is indeed provably the nearest match. If it has an // originating time that occurs at or after the interpolation time (or the // upper boundary of the window, whichever occurs earlier in time), then this // must be true as we will never see a closer match in any of the messages // that may arrive in the future (if the stream was closed then we know that no // messages may arrive in the future). However if it is before the interpolation time, // then we will need to see a message beyond the match/window time to // be sure that there is no closer match (i.e. as long as we haven't seen a // message at or past the match/window time, it is always possible that // a future message will show up with a distance that is closer to the // interpolation time.) if (nearestMatch.OriginatingTime < upperBound) { // Signal insufficient data to continue waiting for more messages. return(InterpolationResult <T> .InsufficientData()); } } // Return the matching message value as the matched result. // All messages strictly before the matching message are obsolete. return(InterpolationResult <T> .Create(nearestMatch.Data, nearestMatch.OriginatingTime)); } // If we arrive here, it means no suitable match was found among the messages. // If the stream is closed, or if the last message is at or past the upper search bound // then it is the case that no future messages will be closer. if (closedOriginatingTime.HasValue || messages.Last().OriginatingTime >= upperBound) { // Then, no matched value exists at that time, and we either return DoesNotExist or // the default value (according to the parameter) return(this.orDefault ? InterpolationResult <T> .Create(this.defaultValue, upperBound) : InterpolationResult <T> .DoesNotExist(upperBound)); } else { // Otherwise a better future message might arrive, therefore we signal insufficient data. return(InterpolationResult <T> .InsufficientData()); } }
/// <inheritdoc/> public override InterpolationResult <T> Interpolate(DateTime interpolationTime, IEnumerable <Message <T> > messages, DateTime?closedOriginatingTime) { // If no messages available, if (messages.Count() == 0) { // If stream is closed, if (closedOriginatingTime.HasValue) { // Then no other value or better match will appear, so depending on orDefault, // either create a default value or return does not exist. return(this.orDefault ? InterpolationResult <T> .Create(this.defaultValue, DateTime.MinValue) : InterpolationResult <T> .DoesNotExist(DateTime.MinValue)); } else { // otherwise insufficient data return(InterpolationResult <T> .InsufficientData()); } } // Look for the first message in the window Message <T> foundMessage = default; bool found = false; foreach (var message in messages) { var delta = message.OriginatingTime - interpolationTime; // Determine if the message is on the right side of the window start var messageIsAfterWindowStart = this.relativeTimeInterval.LeftEndpoint.Inclusive ? delta >= this.relativeTimeInterval.Left : delta > this.relativeTimeInterval.Left; // Only consider messages that occur within the lookback window. if (messageIsAfterWindowStart) { // Determine if the message is outside the window end var messageIsOutsideWindowEnd = this.relativeTimeInterval.RightEndpoint.Inclusive ? delta > this.relativeTimeInterval.Right : delta >= this.relativeTimeInterval.Right; // We stop searching when we reach a message that is beyond the window end if (messageIsOutsideWindowEnd) { break; } // o/w we have found a message foundMessage = message; found = true; break; } } // If we found a first message if (found) { // Return the matching message value as the matched result. // All messages strictly before the matching message are obsolete. return(InterpolationResult <T> .Create(foundMessage.Data, foundMessage.OriginatingTime)); } // If we arrive here, it means no suitable match was found among the messages. This also // implies there is no message in the lookup window. If the stream is closed, or if the last // message is at or past the upper search bound then it is the case that no future messages // will match better. var lastMessageDelta = messages.Last().OriginatingTime - interpolationTime; var lastMessageIsOutsideWindowEnd = this.relativeTimeInterval.RightEndpoint.Inclusive ? lastMessageDelta > this.relativeTimeInterval.Right : lastMessageDelta >= this.relativeTimeInterval.Right; // If the stream was closed or last message is at or past the upper search bound, if (closedOriginatingTime.HasValue || lastMessageIsOutsideWindowEnd) { // Then no future messages will match better, so return DoesNotExist or the default value // (according to the parameter) var windowLeftDateTime = interpolationTime.BoundedAdd(this.relativeTimeInterval.Left); return(this.orDefault ? InterpolationResult <T> .Create(this.defaultValue, windowLeftDateTime) : InterpolationResult <T> .DoesNotExist(windowLeftDateTime)); } else { // Otherwise signal insufficient data. return(InterpolationResult <T> .InsufficientData()); } }