Example #1
0
        /// <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));
Example #2
0
        /// <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());
            }
        }
Example #3
0
        /// <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());
            }
        }
Example #4
0
        /// <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());
            }
        }