{
        healthCheckProcessorMap.putAll(processors.stream().filter(processor -> processor.getType() != null).collect(Collectors.toMap(HealthCheckProcessorV2::getType, processor -> processor)));
    }

package com.alibaba.nacos.naming.healthcheck.v2.processor;

import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata;
import com.alibaba.nacos.naming.core.v2.pojo.Service;
import com.alibaba.nacos.naming.healthcheck.NoneHealthCheckProcessor;
import com.alibaba.nacos.naming.healthcheck.extend.HealthCheckExtendProvider;
import com.alibaba.nacos.naming.healthcheck.v2.HealthCheckTaskV2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Delegate of health check v2.x.
 *
 * @author nacos
 */
@Component("healthCheckDelegateV2")
public class HealthCheckProcessorV2Delegate implements HealthCheckProcessorV2 {

    private final Map<String, HealthCheckProcessorV2> healthCheckProcessorMap = new HashMap<>();

    public HealthCheckProcessorV2Delegate(HealthCheckExtendProvider provider) {
        provider.init();
    }

    @Autowired
    public void addProcessor(Collection<HealthCheckProcessorV2> processors)

    @Override
    public void process(HealthCheckTaskV2 task, Service service, ClusterMetadata metadata) {
        String type = metadata.getHealthyCheckType();
        HealthCheckProcessorV2 processor = healthCheckProcessorMap.get(type);
        if (processor == null) {
            processor = healthCheckProcessorMap.get(NoneHealthCheckProcessor.TYPE);
        }
        processor.process(task, service, metadata);
    }

    @Override
    public String getType() {
        return null;
    }
}


String type = metadata.getHealthyCheckType();

package com.alibaba.nacos.naming.healthcheck.v2.processor;

import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata;
import com.alibaba.nacos.naming.core.v2.pojo.Service;
import com.alibaba.nacos.naming.healthcheck.NoneHealthCheckProcessor;
import com.alibaba.nacos.naming.healthcheck.extend.HealthCheckExtendProvider;
import com.alibaba.nacos.naming.healthcheck.v2.HealthCheckTaskV2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Delegate of health check v2.x.
 *
 * @author nacos
 */
@Component("healthCheckDelegateV2")
public class HealthCheckProcessorV2Delegate implements HealthCheckProcessorV2 {

    private final Map<String, HealthCheckProcessorV2> healthCheckProcessorMap = new HashMap<>();

    public HealthCheckProcessorV2Delegate(HealthCheckExtendProvider provider) {
        provider.init();
    }

    @Autowired
    public void addProcessor(Collection<HealthCheckProcessorV2> processors) {
        healthCheckProcessorMap.putAll(processors.stream().filter(processor -> processor.getType() != null).collect(Collectors.toMap(HealthCheckProcessorV2::getType, processor -> processor)));
    }

    @Override
    public void process(HealthCheckTaskV2 task, Service service, ClusterMetadata metadata) {

        HealthCheckProcessorV2 processor = healthCheckProcessorMap.get(type);
        if (processor == null) {
            processor = healthCheckProcessorMap.get(NoneHealthCheckProcessor.TYPE);
        }
        processor.process(task, service, metadata);
    }

    @Override
    public String getType() {
        return null;
    }
}


HealthCheckProcessorV2 processor = healthCheckProcessorMap.get(type);
        if (processor == null) {
            processor = healthCheckProcessorMap.get(NoneHealthCheckProcessor.TYPE);
        }

package com.alibaba.nacos.naming.healthcheck.v2.processor;

import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata;
import com.alibaba.nacos.naming.core.v2.pojo.Service;
import com.alibaba.nacos.naming.healthcheck.NoneHealthCheckProcessor;
import com.alibaba.nacos.naming.healthcheck.extend.HealthCheckExtendProvider;
import com.alibaba.nacos.naming.healthcheck.v2.HealthCheckTaskV2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Delegate of health check v2.x.
 *
 * @author nacos
 */
@Component("healthCheckDelegateV2")
public class HealthCheckProcessorV2Delegate implements HealthCheckProcessorV2 {

    private final Map<String, HealthCheckProcessorV2> healthCheckProcessorMap = new HashMap<>();

    public HealthCheckProcessorV2Delegate(HealthCheckExtendProvider provider) {
        provider.init();
    }

    @Autowired
    public void addProcessor(Collection<HealthCheckProcessorV2> processors) {
        healthCheckProcessorMap.putAll(processors.stream().filter(processor -> processor.getType() != null).collect(Collectors.toMap(HealthCheckProcessorV2::getType, processor -> processor)));
    }

    @Override
    public void process(HealthCheckTaskV2 task, Service service, ClusterMetadata metadata) {
        String type = metadata.getHealthyCheckType();

        processor.process(task, service, metadata);
    }

    @Override
    public String getType() {
        return null;
    }
}


processor.process(task, service, metadata);
    }

package com.alibaba.nacos.naming.healthcheck.v2.processor;

import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata;
import com.alibaba.nacos.naming.core.v2.pojo.Service;
import com.alibaba.nacos.naming.healthcheck.NoneHealthCheckProcessor;
import com.alibaba.nacos.naming.healthcheck.extend.HealthCheckExtendProvider;
import com.alibaba.nacos.naming.healthcheck.v2.HealthCheckTaskV2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Delegate of health check v2.x.
 *
 * @author nacos
 */
@Component("healthCheckDelegateV2")
public class HealthCheckProcessorV2Delegate implements HealthCheckProcessorV2 {

    private final Map<String, HealthCheckProcessorV2> healthCheckProcessorMap = new HashMap<>();

    public HealthCheckProcessorV2Delegate(HealthCheckExtendProvider provider) {
        provider.init();
    }

    @Autowired
    public void addProcessor(Collection<HealthCheckProcessorV2> processors) {
        healthCheckProcessorMap.putAll(processors.stream().filter(processor -> processor.getType() != null).collect(Collectors.toMap(HealthCheckProcessorV2::getType, processor -> processor)));
    }

    @Override
    public void process(HealthCheckTaskV2 task, Service service, ClusterMetadata metadata) {
        String type = metadata.getHealthyCheckType();
        HealthCheckProcessorV2 processor = healthCheckProcessorMap.get(type);
        if (processor == null) {
            processor = healthCheckProcessorMap.get(NoneHealthCheckProcessor.TYPE);
        }

    @Override
    public String getType() {
        return null;
    }
}


package com.alibaba.nacos.naming.healthcheck.v2.processor;

import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata;
import com.alibaba.nacos.naming.core.v2.pojo.Service;
import com.alibaba.nacos.naming.healthcheck.NoneHealthCheckProcessor;
import com.alibaba.nacos.naming.healthcheck.extend.HealthCheckExtendProvider;
import com.alibaba.nacos.naming.healthcheck.v2.HealthCheckTaskV2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Delegate of health check v2.x.
 *
 * @author nacos
 */
@Component("healthCheckDelegateV2")
public class HealthCheckProcessorV2Delegate implements HealthCheckProcessorV2 {

    private final Map<String, HealthCheckProcessorV2> healthCheckProcessorMap = new HashMap<>();

    public HealthCheckProcessorV2Delegate(HealthCheckExtendProvider provider) {
        provider.init();
    }

    @Autowired
    public void addProcessor(Collection<HealthCheckProcessorV2> processors) {
        healthCheckProcessorMap.putAll(processors.stream().filter(processor -> processor.getType() != null).collect(Collectors.toMap(HealthCheckProcessorV2::getType, processor -> processor)));
    }

    @Override
    public void process(HealthCheckTaskV2 task, Service service, ClusterMetadata metadata) {
        String type = metadata.getHealthyCheckType();
        HealthCheckProcessorV2 processor = healthCheckProcessorMap.

        if (processor == null) {
            processor = healthCheckProcessorMap.get(NoneHealthCheckProcessor.TYPE);
        }
        processor.process(task, service, metadata);
    }

    @Override
    public String getType() {
        return null;
    }
}


if (processor == null) {
            processor = healthCheckProcessorMap.get(NoneHealthCheckProcessor.TYPE);
        }

package com.alibaba.nacos.naming.healthcheck.v2.processor;

import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata;
import com.alibaba.nacos.naming.core.v2.pojo.Service;
import com.alibaba.nacos.naming.healthcheck.NoneHealthCheckProcessor;
import com.alibaba.nacos.naming.healthcheck.extend.HealthCheckExtendProvider;
import com.alibaba.nacos.naming.healthcheck.v2.HealthCheckTaskV2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Delegate of health check v2.x.
 *
 * @author nacos
 */
@Component("healthCheckDelegateV2")
public class HealthCheckProcessorV2Delegate implements HealthCheckProcessorV2 {

    private final Map<String, HealthCheckProcessorV2> healthCheckProcessorMap = new HashMap<>();

    public HealthCheckProcessorV2Delegate(HealthCheckExtendProvider provider) {
        provider.init();
    }

    @Autowired
    public void addProcessor(Collection<HealthCheckProcessorV2> processors) {
        healthCheckProcessorMap.putAll(processors.stream().filter(processor -> processor.getType() != null).collect(Collectors.toMap(HealthCheckProcessorV2::getType, processor -> processor)));
    }

    @Override
    public void process(HealthCheckTaskV2 task, Service service, ClusterMetadata metadata) {
        String type = metadata.getHealthyCheckType();
        HealthCheckProcessorV2 processor = healthCheckProcessorMap.get(type);


package com.datatorrent.stram.util;

import java.util.Comparator;

/**
 * Used to wrap around long int values safely

 *

 * Needed to ensure that windowId wrap around safely

 * {@see StablePriorityQueue}

 *

 *
 * @since 0.3.2
 */
class StableWrapper<E> {

    public final int id;

    public final E object;

    public StableWrapper(E o, int id) {
        this.id = id;
        this.object = o;
    }

    static class NaturalComparator<E> implements Comparator<StableWrapper<E>> {

        @Override
        public int compare(StableWrapper<E> o1, StableWrapper<E> o2) {
            @SuppressWarnings({ "unchecked", "rawtypes" })
            int ret = ((Comparable) o1.object).compareTo(o2.object);
            if (ret == 0) {
                if (o1.id > o2.id) {
                    ret = 1;
                } else if (o1.id < o2.id) {
                    ret = -1;
                }
            }
            return ret;
        }
    }

    static class ProvidedComparator<E> implements Comparator<StableWrapper<E>> {

        public final Comparator<? super E> comparator;

        public ProvidedComparator(Comparator<? super E> comparator) {
            this.comparator = comparator;
        }

{
            int ret = comparator.compare(o1.object, o2.object);
            if (ret == 0) {
                if (o1.id > o2.id) {
                    ret = 1;
                } else if (o1.id < o2.id) {
                    ret = -1;
                }
            }
            return ret;
        }
    }
}


{
            @SuppressWarnings({ "unchecked", "rawtypes" })
            int ret = ((Comparable) o1.object).compareTo(o2.object);
            if (ret == 0) {
                if (o1.id > o2.id) {
                    ret = 1;
                } else if (o1.id < o2.id) {
                    ret = -1;
                }
            }
            return ret;
        }

package com.datatorrent.stram.util;

import java.util.Comparator;

/**
 * Used to wrap around long int values safely

 *

 * Needed to ensure that windowId wrap around safely

 * {@see StablePriorityQueue}

 *

 *
 * @since 0.3.2
 */
class StableWrapper<E> {

    public final int id;

    public final E object;

    public StableWrapper(E o, int id) {
        this.id = id;
        this.object = o;
    }

    static class NaturalComparator<E> implements Comparator<StableWrapper<E>> {

        @Override
        public int compare(StableWrapper<E> o1, StableWrapper<E> o2)

    }

    static class ProvidedComparator<E> implements Comparator<StableWrapper<E>> {

        public final Comparator<? super E> comparator;

        public ProvidedComparator(Comparator<? super E> comparator) {
            this.comparator = comparator;
        }

        @Override
        public int compare(StableWrapper<E> o1, StableWrapper<E> o2) {
            int ret = comparator.compare(o1.object, o2.object);
            if (ret == 0) {
                if (o1.id > o2.id) {
                    ret = 1;
                } else if (o1.id < o2.id) {
                    ret = -1;
                }
            }
            return ret;
        }
    }
}


            @SuppressWarnings({ "unchecked", "rawtypes" })
            int ret = ((Comparable) o1.object).compareTo(o2.object);

package com.datatorrent.stram.util;

import java.util.Comparator;

/**
 * Used to wrap around long int values safely

 *

 * Needed to ensure that windowId wrap around safely

 * {@see StablePriorityQueue}

 *

 *
 * @since 0.3.2
 */
class StableWrapper<E> {

    public final int id;

    public final E object;

    public StableWrapper(E o, int id) {
        this.id = id;
        this.object = o;
    }

    static class NaturalComparator<E> implements Comparator<StableWrapper<E>> {

        @Override
        public int compare(StableWrapper<E> o1, StableWrapper<E> o2) {

            if (ret == 0) {
                if (o1.id > o2.id) {
                    ret = 1;
                } else if (o1.id < o2.id) {
                    ret = -1;
                }
            }
            return ret;
        }
    }

    static class ProvidedComparator<E> implements Comparator<StableWrapper<E>> {

        public final Comparator<? super E> comparator;

        public ProvidedComparator(Comparator<? super E> comparator) {
            this.comparator = comparator;
        }

        @Override
        public int compare(StableWrapper<E> o1, StableWrapper<E> o2) {
            int ret = comparator.compare(o1.object, o2.object);
            if (ret == 0) {
                if (o1.id > o2.id) {
                    ret = 1;
                } else if (o1.id < o2.id) {
                    ret = -1;
                }
            }
            return ret;
        }
    }
}


package com.datatorrent.stram.util;

import java.util.Comparator;

/**
 * Used to wrap around long int values safely

 *

 * Needed to ensure that windowId wrap around safely

 * {@see StablePriorityQueue}

 *

 *
 * @since 0.3.2
 */
class StableWrapper<E> {

    public final int id;

    public final E object;

    public StableWrapper(E o, int id) {
        this.id = id;
        this.object = o;
    }

    static class NaturalComparator<E> implements Comparator<StableWrapper<E>> {

        @Override
        public int compare(StableWrapper<E> o1, StableWrapper<E> o2) {
            @SuppressWarnings({ "unchecked", "rawtypes" })
            int ret = ((Comparable) o1.object).compareTo(o2.object);

            return ret;
        }
    }

    static class ProvidedComparator<E> implements Comparator<StableWrapper<E>> {

        public final Comparator<? super E> comparator;

        public ProvidedComparator(Comparator<? super E> comparator) {
            this.comparator = comparator;
        }

        @Override
        public int compare(StableWrapper<E> o1, StableWrapper<E> o2) {
            int ret = comparator.compare(o1.object, o2.object);
            if (ret == 0) {
                if (o1.id > o2.id) {
                    ret = 1;
                } else if (o1.id < o2.id) {
                    ret = -1;
                }
            }
            return ret;
        }
    }
}


package com.datatorrent.stram.util;

import java.util.Comparator;

/**
 * Used to wrap around long int values safely

 *

 * Needed to ensure that windowId wrap around safely

 * {@see StablePriorityQueue}

 *

 *
 * @since 0.3.2
 */
class StableWrapper<E> {

    public final int id;

    public final E object;

    public StableWrapper(E o, int id) {
        this.id = id;
        this.object = o;
    }

    static class NaturalComparator<E> implements Comparator<StableWrapper<E>> {

        @Override
        public int compare(StableWrapper<E> o1, StableWrapper<E> o2) {
            @SuppressWarnings({ "unchecked", "rawtypes" })
            int ret = ((Comparable) o1.object).compareTo(o2.object);
            if (ret == 0) {
                if (o1.id > o2.id) {
                    ret = 1;
                } else if (o1.id < o2.id) {
                    ret = -1;
                }
            }


package com.datatorrent.stram.util;

import java.util.Comparator;

/**
 * Used to wrap around long int values safely

 *

 * Needed to ensure that windowId wrap around safely

 * {@see StablePriorityQueue}

 *

 *
 * @since 0.3.2
 */
class StableWrapper<E> {

    public final int id;

    public final E object;

    public StableWrapper(E o, int id) {
        this.id = id;
        this.object = o;
    }

    static class NaturalComparator<E> implements Comparator<StableWrapper<E>> {

        @Override
        public int compare(StableWrapper<E> o1, StableWrapper<E> o2) {
            @SuppressWarnings({ "unchecked", "rawtypes" })
            int ret = ((Comparable) o1.object).compareTo(o2.object);
            if (ret == 0) {
                if (o1.id > o2.id) {
                    ret = 1;
                } else if (o1.id < o2.id) {
                    ret = -1;
                }
            }
            return r

        }
    }

    static class ProvidedComparator<E> implements Comparator<StableWrapper<E>> {

        public final Comparator<? super E> comparator;

        public ProvidedComparator(Comparator<? super E> comparator) {
            this.comparator = comparator;
        }

        @Override
        public int compare(StableWrapper<E> o1, StableWrapper<E> o2) {
            int ret = comparator.compare(o1.object, o2.object);
            if (ret == 0) {
                if (o1.id > o2.id) {
                    ret = 1;
                } else if (o1.id < o2.id) {
                    ret = -1;
                }
            }
            return ret;
        }
    }
}


package com.datatorrent.stram.util;

import java.util.Comparator;

/**
 * Used to wrap around long int values safely

 *

 * Needed to ensure that windowId wrap around safely

 * {@see StablePriorityQueue}

 *

 *
 * @since 0.3.2
 */
class StableWrapper<E> {

    public final int id;

    public final E object;

    public StableWrapper(E o, int id) {
        this.id = id;
        this.object = o;
    }

    static class NaturalComparator<E> implements Comparator<StableWrapper<E>> {

        @Override
        public int compare(StableWrapper<E> o1, StableWrapper<E> o2) {
            @SuppressWarnings({ "unchecked", "rawtypes" })
            int ret = ((Comparable) o1.object).compareTo(o2.object);
            if (ret == 0) {
                if (o1.id > o2.id) {
                    ret = 1;
                } else if (o1.id < o2.id) {
                    ret = -1;
                }
            }
            return ret;
        }
    }

    static class ProvidedComparator<E> implements Comparator<StableWrapper<E>> {

        public final Comparator<? super E> comparator;

        public ProvidedComparator(Comparator<? super E> comparator) {
            this.comparator = comparator;
        }

        @Override
        public int compare(StableWrapper<E> o1, StableWrapper<E> o2) {
            int ret = comparator.compare(o1.object, o2.object);
            if (ret == 0) {
                if (o1.id > o2.id) {
                    ret = 1;
                } else

            }
            return ret;
        }
    }
}


@Override
        public int compare(IQuantity o1, IQuantity o2)

/*
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 *
 */
package org.openjdk.jmc.flightrecorder.rules.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.FutureTask;
import org.openjdk.jmc.common.item.IItem;
import org.openjdk.jmc.common.item.IItemCollection;
import org.openjdk.jmc.common.item.IItemFilter;
import org.openjdk.jmc.common.item.IMemberAccessor;
import org.openjdk.jmc.common.item.ItemFilters;
import org.openjdk.jmc.common.unit.IQuantity;
import org.openjdk.jmc.common.unit.IRange;
import org.openjdk.jmc.common.unit.QuantityRange;
import org.openjdk.jmc.common.util.Pair;
import org.openjdk.jmc.flightrecorder.JfrAttributes;
import org.openjdk.jmc.flightrecorder.rules.IResult;

/**
 * Utility functions and interfaces for doing sliding window calculations.
 */
public class SlidingWindowToolkit {

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowOrdered}
     */
    public interface IOrderedWindowVisitor {

        void visitWindow(Iterator<IItem> items);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, looping through items once, removing from the start
     * and adding and the end of a windowItem set to match the current time window. items.
     * <p>
     * Suitable if the items are guaranteed to be ordered.
     *
     * @param callback
     *            method that can do calculations on the items in the window
     * @param items
     *            input items
     * @param posAccessor
     *            an accessor that should give a position value that is used with windowsSize and
     *            slideSize
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be, if slideSize is {@code null}, it will slide one item
     *            at a time
     */
    public static void slidingWindowOrdered(IOrderedWindowVisitor callback, Iterator<IItem> items, IMemberAccessor<IQuantity, IItem> posAccessor, IQuantity windowSize, IQuantity slideSize) {
        IQuantity windowStart = null;
        List<IItem> windowItems = new ArrayList<>();
        for (Iterator<IItem> iterator = items; iterator.hasNext() && callback.shouldContinue(); ) {
            IItem item = iterator.next();
            if (windowItems.isEmpty()) {
                windowStart = posAccessor.getMember(item);
            } else {
                windowStart = posAccessor.getMember(windowItems.get(0));
            }
            windowItems.add(item);
            IQuantity windowEnd = posAccessor.getMember(item);
            while (iterator.hasNext() && windowEnd.subtract(windowSize).compareTo(windowStart) < 0 && callback.shouldContinue()) {
                IItem next = iterator.next();
                windowEnd = posAccessor.getMember(next);
                windowItems.add(next);
            }
            callback.visitWindow(windowItems.iterator());
            // FIXME: What should we do about empty time intervals?
            if (slideSize == null) {
                if (windowItems.size() > 1) {
                    windowItems.remove(0);
                }
            } else {
                IQuantity newStart = windowStart.add(slideSize);
                for (Iterator<IItem> it = windowItems.iterator(); it.hasNext() && callback.shouldContinue(); ) {
                    IItem wi = it.next();
                    if (posAccessor.getMember(wi).compareTo(newStart) < 0) {
                        it.remove();
                    } else {
                        break;
                    }
                }
            }
        }
    }

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowUnordered}
     */
    public interface IUnorderedWindowVisitor {

        void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize) {
        slidingWindowUnordered(callback, items, windowSize, slideSize, false);
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     * @param includeIntersecting
     *            if the window filter should include events intersecting the window, or just those
     *            with end time in the window. It's up to the caller to take this into account and
     *            for example cap event duration to the window timespan.
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize, boolean includeIntersecting) {
        IQuantity first = includeIntersecting ? RulesToolkit.getEarliestStartTime(items) : RulesToolkit.getEarliestEndTime(items);
        IQuantity last = RulesToolkit.getLatestEndTime(items);
        if (first == null) {
            return;
        }
        IQuantity windowStart = first;
        IQuantity windowEnd = windowStart.add(windowSize);
        do {
            IItemFilter window = includeIntersecting ? ItemFilters.rangeIntersects(JfrAttributes.LIFETIME, QuantityRange.createWithEnd(windowStart, windowEnd)) : ItemFilters.interval(JfrAttributes.END_TIME, windowStart, true, windowEnd, true);
            IItemCollection windowItems = items.apply(window);
            callback.visitWindow(windowItems, windowStart, windowEnd);
            windowStart = windowStart.add(slideSize);
            windowEnd = windowEnd.add(slideSize);
        } while (windowStart.compareTo(last) < 0 && callback.shouldContinue());
    }

    /**
     * Value function used when calling
     * {@link SlidingWindowToolkit#slidingWindowUnorderedMinMaxValue}
     */
    public interface IUnorderedWindowValueFunction<V> {

        V getValue(IItemCollection items, IQuantity startTime, IQuantity endTime);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, max, includeIntersecting);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, QUANTITY_COMPARATOR, max, includeIntersecting);
    }

    private static final Comparator<IQuantity> QUANTITY_COMPARATOR = new Comparator<IQuantity>() {

{
            return o1.compareTo(o2);
        }
    };

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, valueComparator, max, includeIntersecting);
    }

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        final List<Pair<V, IRange<IQuantity>>> valueList = new ArrayList<>();
        slidingWindowUnordered(new IUnorderedWindowVisitor() {

            @Override
            public void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime) {
                V value = valueFunction.getValue(items, startTime, endTime);
                if (value != null) {
                    valueList.add(new Pair<>(value, QuantityRange.createWithEnd(startTime, endTime)));
                }
            }

            @Override
            public boolean shouldContinue() {
                return !cancellationSupplier.isCancelled();
            }
        }, items, windowSize, slideSize, includeIntersecting);
        Comparator<Pair<V, IRange<IQuantity>>> comparator = new Comparator<Pair<V, IRange<IQuantity>>>() {

            @Override
            public int compare(Pair<V, IRange<IQuantity>> o1, Pair<V, IRange<IQuantity>> o2) {
                return valueComparator.compare(o1.left, o2.left);
            }
        };
        if (valueList.isEmpty()) {
            return null;
        }
        Pair<V, IRange<IQuantity>> minMaxWindow = max ? Collections.max(valueList, comparator) : Collections.min(valueList, comparator);
        return minMaxWindow;
    }
}


{
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, valueComparator, max, includeIntersecting);
    }

/*
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 *
 */
package org.openjdk.jmc.flightrecorder.rules.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.FutureTask;
import org.openjdk.jmc.common.item.IItem;
import org.openjdk.jmc.common.item.IItemCollection;
import org.openjdk.jmc.common.item.IItemFilter;
import org.openjdk.jmc.common.item.IMemberAccessor;
import org.openjdk.jmc.common.item.ItemFilters;
import org.openjdk.jmc.common.unit.IQuantity;
import org.openjdk.jmc.common.unit.IRange;
import org.openjdk.jmc.common.unit.QuantityRange;
import org.openjdk.jmc.common.util.Pair;
import org.openjdk.jmc.flightrecorder.JfrAttributes;
import org.openjdk.jmc.flightrecorder.rules.IResult;

/**
 * Utility functions and interfaces for doing sliding window calculations.
 */
public class SlidingWindowToolkit {

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowOrdered}
     */
    public interface IOrderedWindowVisitor {

        void visitWindow(Iterator<IItem> items);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, looping through items once, removing from the start
     * and adding and the end of a windowItem set to match the current time window. items.
     * <p>
     * Suitable if the items are guaranteed to be ordered.
     *
     * @param callback
     *            method that can do calculations on the items in the window
     * @param items
     *            input items
     * @param posAccessor
     *            an accessor that should give a position value that is used with windowsSize and
     *            slideSize
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be, if slideSize is {@code null}, it will slide one item
     *            at a time
     */
    public static void slidingWindowOrdered(IOrderedWindowVisitor callback, Iterator<IItem> items, IMemberAccessor<IQuantity, IItem> posAccessor, IQuantity windowSize, IQuantity slideSize) {
        IQuantity windowStart = null;
        List<IItem> windowItems = new ArrayList<>();
        for (Iterator<IItem> iterator = items; iterator.hasNext() && callback.shouldContinue(); ) {
            IItem item = iterator.next();
            if (windowItems.isEmpty()) {
                windowStart = posAccessor.getMember(item);
            } else {
                windowStart = posAccessor.getMember(windowItems.get(0));
            }
            windowItems.add(item);
            IQuantity windowEnd = posAccessor.getMember(item);
            while (iterator.hasNext() && windowEnd.subtract(windowSize).compareTo(windowStart) < 0 && callback.shouldContinue()) {
                IItem next = iterator.next();
                windowEnd = posAccessor.getMember(next);
                windowItems.add(next);
            }
            callback.visitWindow(windowItems.iterator());
            // FIXME: What should we do about empty time intervals?
            if (slideSize == null) {
                if (windowItems.size() > 1) {
                    windowItems.remove(0);
                }
            } else {
                IQuantity newStart = windowStart.add(slideSize);
                for (Iterator<IItem> it = windowItems.iterator(); it.hasNext() && callback.shouldContinue(); ) {
                    IItem wi = it.next();
                    if (posAccessor.getMember(wi).compareTo(newStart) < 0) {
                        it.remove();
                    } else {
                        break;
                    }
                }
            }
        }
    }

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowUnordered}
     */
    public interface IUnorderedWindowVisitor {

        void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize) {
        slidingWindowUnordered(callback, items, windowSize, slideSize, false);
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     * @param includeIntersecting
     *            if the window filter should include events intersecting the window, or just those
     *            with end time in the window. It's up to the caller to take this into account and
     *            for example cap event duration to the window timespan.
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize, boolean includeIntersecting) {
        IQuantity first = includeIntersecting ? RulesToolkit.getEarliestStartTime(items) : RulesToolkit.getEarliestEndTime(items);
        IQuantity last = RulesToolkit.getLatestEndTime(items);
        if (first == null) {
            return;
        }
        IQuantity windowStart = first;
        IQuantity windowEnd = windowStart.add(windowSize);
        do {
            IItemFilter window = includeIntersecting ? ItemFilters.rangeIntersects(JfrAttributes.LIFETIME, QuantityRange.createWithEnd(windowStart, windowEnd)) : ItemFilters.interval(JfrAttributes.END_TIME, windowStart, true, windowEnd, true);
            IItemCollection windowItems = items.apply(window);
            callback.visitWindow(windowItems, windowStart, windowEnd);
            windowStart = windowStart.add(slideSize);
            windowEnd = windowEnd.add(slideSize);
        } while (windowStart.compareTo(last) < 0 && callback.shouldContinue());
    }

    /**
     * Value function used when calling
     * {@link SlidingWindowToolkit#slidingWindowUnorderedMinMaxValue}
     */
    public interface IUnorderedWindowValueFunction<V> {

        V getValue(IItemCollection items, IQuantity startTime, IQuantity endTime);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, max, includeIntersecting);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, QUANTITY_COMPARATOR, max, includeIntersecting);
    }

    private static final Comparator<IQuantity> QUANTITY_COMPARATOR = new Comparator<IQuantity>() {

        @Override
        public int compare(IQuantity o1, IQuantity o2) {
            return o1.compareTo(o2);
        }
    };

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting)

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        final List<Pair<V, IRange<IQuantity>>> valueList = new ArrayList<>();
        slidingWindowUnordered(new IUnorderedWindowVisitor() {

            @Override
            public void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime) {
                V value = valueFunction.getValue(items, startTime, endTime);
                if (value != null) {
                    valueList.add(new Pair<>(value, QuantityRange.createWithEnd(startTime, endTime)));
                }
            }

            @Override
            public boolean shouldContinue() {
                return !cancellationSupplier.isCancelled();
            }
        }, items, windowSize, slideSize, includeIntersecting);
        Comparator<Pair<V, IRange<IQuantity>>> comparator = new Comparator<Pair<V, IRange<IQuantity>>>() {

            @Override
            public int compare(Pair<V, IRange<IQuantity>> o1, Pair<V, IRange<IQuantity>> o2) {
                return valueComparator.compare(o1.left, o2.left);
            }
        };
        if (valueList.isEmpty()) {
            return null;
        }
        Pair<V, IRange<IQuantity>> minMaxWindow = max ? Collections.max(valueList, comparator) : Collections.min(valueList, comparator);
        return minMaxWindow;
    }
}


/*
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 *
 */
package org.openjdk.jmc.flightrecorder.rules.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.FutureTask;
import org.openjdk.jmc.common.item.IItem;
import org.openjdk.jmc.common.item.IItemCollection;
import org.openjdk.jmc.common.item.IItemFilter;
import org.openjdk.jmc.common.item.IMemberAccessor;
import org.openjdk.jmc.common.item.ItemFilters;
import org.openjdk.jmc.common.unit.IQuantity;
import org.openjdk.jmc.common.unit.IRange;
import org.openjdk.jmc.common.unit.QuantityRange;
import org.openjdk.jmc.common.util.Pair;
import org.openjdk.jmc.flightrecorder.JfrAttributes;
import org.openjdk.jmc.flightrecorder.rules.IResult;

/**
 * Utility functions and interfaces for doing sliding window calculations.
 */
public class SlidingWindowToolkit {

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowOrdered}
     */
    public interface IOrderedWindowVisitor {

        void visitWindow(Iterator<IItem> items);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, looping through items once, removing from the start
     * and adding and the end of a windowItem set to match the current time window. items.
     * <p>
     * Suitable if the items are guaranteed to be ordered.
     *
     * @param callback
     *            method that can do calculations on the items in the window
     * @param items
     *            input items
     * @param posAccessor
     *            an accessor that should give a position value that is used with windowsSize and
     *            slideSize
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be, if slideSize is {@code null}, it will slide one item
     *            at a time
     */
    public static void slidingWindowOrdered(IOrderedWindowVisitor callback, Iterator<IItem> items, IMemberAccessor<IQuantity, IItem> posAccessor, IQuantity windowSize, IQuantity slideSize) {

        List<IItem> windowItems = new ArrayList<>();
        for (Iterator<IItem> iterator = items; iterator.hasNext() && callback.shouldContinue(); ) {
            IItem item = iterator.next();
            if (windowItems.isEmpty()) {
                windowStart = posAccessor.getMember(item);
            } else {
                windowStart = posAccessor.getMember(windowItems.get(0));
            }
            windowItems.add(item);
            IQuantity windowEnd = posAccessor.getMember(item);
            while (iterator.hasNext() && windowEnd.subtract(windowSize).compareTo(windowStart) < 0 && callback.shouldContinue()) {
                IItem next = iterator.next();
                windowEnd = posAccessor.getMember(next);
                windowItems.add(next);
            }
            callback.visitWindow(windowItems.iterator());
            // FIXME: What should we do about empty time intervals?
            if (slideSize == null) {
                if (windowItems.size() > 1) {
                    windowItems.remove(0);
                }
            } else {
                IQuantity newStart = windowStart.add(slideSize);
                for (Iterator<IItem> it = windowItems.iterator(); it.hasNext() && callback.shouldContinue(); ) {
                    IItem wi = it.next();
                    if (posAccessor.getMember(wi).compareTo(newStart) < 0) {
                        it.remove();
                    } else {
                        break;
                    }
                }
            }
        }
    }

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowUnordered}
     */
    public interface IUnorderedWindowVisitor {

        void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize) {
        slidingWindowUnordered(callback, items, windowSize, slideSize, false);
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     * @param includeIntersecting
     *            if the window filter should include events intersecting the window, or just those
     *            with end time in the window. It's up to the caller to take this into account and
     *            for example cap event duration to the window timespan.
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize, boolean includeIntersecting) {
        IQuantity first = includeIntersecting ? RulesToolkit.getEarliestStartTime(items) : RulesToolkit.getEarliestEndTime(items);
        IQuantity last = RulesToolkit.getLatestEndTime(items);
        if (first == null) {
            return;
        }
        IQuantity windowStart = first;
        IQuantity windowEnd = windowStart.add(windowSize);
        do {
            IItemFilter window = includeIntersecting ? ItemFilters.rangeIntersects(JfrAttributes.LIFETIME, QuantityRange.createWithEnd(windowStart, windowEnd)) : ItemFilters.interval(JfrAttributes.END_TIME, windowStart, true, windowEnd, true);
            IItemCollection windowItems = items.apply(window);
            callback.visitWindow(windowItems, windowStart, windowEnd);
            windowStart = windowStart.add(slideSize);
            windowEnd = windowEnd.add(slideSize);
        } while (windowStart.compareTo(last) < 0 && callback.shouldContinue());
    }

    /**
     * Value function used when calling
     * {@link SlidingWindowToolkit#slidingWindowUnorderedMinMaxValue}
     */
    public interface IUnorderedWindowValueFunction<V> {

        V getValue(IItemCollection items, IQuantity startTime, IQuantity endTime);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, max, includeIntersecting);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, QUANTITY_COMPARATOR, max, includeIntersecting);
    }

    private static final Comparator<IQuantity> QUANTITY_COMPARATOR = new Comparator<IQuantity>() {

        @Override
        public int compare(IQuantity o1, IQuantity o2) {
            return o1.compareTo(o2);
        }
    };

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, valueComparator, max, includeIntersecting);
    }

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        final List<Pair<V, IRange<IQuantity>>> valueList = new ArrayList<>();
        slidingWindowUnordered(new IUnorderedWindowVisitor() {

            @Override
            public void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime) {
                V value = valueFunction.getValue(items, startTime, endTime);
                if (value != null) {
                    valueList.add(new Pair<>(value, QuantityRange.createWithEnd(startTime, endTime)));
                }
            }

            @Override
            public boolean shouldContinue() {
                return !cancellationSupplier.isCancelled();
            }
        }, items, windowSize, slideSize, includeIntersecting);
        Comparator<Pair<V, IRange<IQuantity>>> comparator = new Comparator<Pair<V, IRange<IQuantity>>>() {

            @Override
            public int compare(Pair<V, IRange<IQuantity>> o1, Pair<V, IRange<IQuantity>> o2) {
                return valueComparator.compare(o1.left, o2.left);
            }
        };
        if (valueList.isEmpty()) {
            return null;
        }
        Pair<V, IRange<IQuantity>> minMaxWindow = max ? Collections.max(valueList, comparator) : Collections.min(valueList, comparator);
        return minMaxWindow;
    }
}


List<IItem> windowItems = new ArrayList<>();

/*
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 *
 */
package org.openjdk.jmc.flightrecorder.rules.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.FutureTask;
import org.openjdk.jmc.common.item.IItem;
import org.openjdk.jmc.common.item.IItemCollection;
import org.openjdk.jmc.common.item.IItemFilter;
import org.openjdk.jmc.common.item.IMemberAccessor;
import org.openjdk.jmc.common.item.ItemFilters;
import org.openjdk.jmc.common.unit.IQuantity;
import org.openjdk.jmc.common.unit.IRange;
import org.openjdk.jmc.common.unit.QuantityRange;
import org.openjdk.jmc.common.util.Pair;
import org.openjdk.jmc.flightrecorder.JfrAttributes;
import org.openjdk.jmc.flightrecorder.rules.IResult;

/**
 * Utility functions and interfaces for doing sliding window calculations.
 */
public class SlidingWindowToolkit {

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowOrdered}
     */
    public interface IOrderedWindowVisitor {

        void visitWindow(Iterator<IItem> items);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, looping through items once, removing from the start
     * and adding and the end of a windowItem set to match the current time window. items.
     * <p>
     * Suitable if the items are guaranteed to be ordered.
     *
     * @param callback
     *            method that can do calculations on the items in the window
     * @param items
     *            input items
     * @param posAccessor
     *            an accessor that should give a position value that is used with windowsSize and
     *            slideSize
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be, if slideSize is {@code null}, it will slide one item
     *            at a time
     */
    public static void slidingWindowOrdered(IOrderedWindowVisitor callback, Iterator<IItem> items, IMemberAccessor<IQuantity, IItem> posAccessor, IQuantity windowSize, IQuantity slideSize) {
        IQuantity windowStart = null;

        for (Iterator<IItem> iterator = items; iterator.hasNext() && callback.shouldContinue(); ) {
            IItem item = iterator.next();
            if (windowItems.isEmpty()) {
                windowStart = posAccessor.getMember(item);
            } else {
                windowStart = posAccessor.getMember(windowItems.get(0));
            }
            windowItems.add(item);
            IQuantity windowEnd = posAccessor.getMember(item);
            while (iterator.hasNext() && windowEnd.subtract(windowSize).compareTo(windowStart) < 0 && callback.shouldContinue()) {
                IItem next = iterator.next();
                windowEnd = posAccessor.getMember(next);
                windowItems.add(next);
            }
            callback.visitWindow(windowItems.iterator());
            // FIXME: What should we do about empty time intervals?
            if (slideSize == null) {
                if (windowItems.size() > 1) {
                    windowItems.remove(0);
                }
            } else {
                IQuantity newStart = windowStart.add(slideSize);
                for (Iterator<IItem> it = windowItems.iterator(); it.hasNext() && callback.shouldContinue(); ) {
                    IItem wi = it.next();
                    if (posAccessor.getMember(wi).compareTo(newStart) < 0) {
                        it.remove();
                    } else {
                        break;
                    }
                }
            }
        }
    }

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowUnordered}
     */
    public interface IUnorderedWindowVisitor {

        void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize) {
        slidingWindowUnordered(callback, items, windowSize, slideSize, false);
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     * @param includeIntersecting
     *            if the window filter should include events intersecting the window, or just those
     *            with end time in the window. It's up to the caller to take this into account and
     *            for example cap event duration to the window timespan.
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize, boolean includeIntersecting) {
        IQuantity first = includeIntersecting ? RulesToolkit.getEarliestStartTime(items) : RulesToolkit.getEarliestEndTime(items);
        IQuantity last = RulesToolkit.getLatestEndTime(items);
        if (first == null) {
            return;
        }
        IQuantity windowStart = first;
        IQuantity windowEnd = windowStart.add(windowSize);
        do {
            IItemFilter window = includeIntersecting ? ItemFilters.rangeIntersects(JfrAttributes.LIFETIME, QuantityRange.createWithEnd(windowStart, windowEnd)) : ItemFilters.interval(JfrAttributes.END_TIME, windowStart, true, windowEnd, true);
            IItemCollection windowItems = items.apply(window);
            callback.visitWindow(windowItems, windowStart, windowEnd);
            windowStart = windowStart.add(slideSize);
            windowEnd = windowEnd.add(slideSize);
        } while (windowStart.compareTo(last) < 0 && callback.shouldContinue());
    }

    /**
     * Value function used when calling
     * {@link SlidingWindowToolkit#slidingWindowUnorderedMinMaxValue}
     */
    public interface IUnorderedWindowValueFunction<V> {

        V getValue(IItemCollection items, IQuantity startTime, IQuantity endTime);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, max, includeIntersecting);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, QUANTITY_COMPARATOR, max, includeIntersecting);
    }

    private static final Comparator<IQuantity> QUANTITY_COMPARATOR = new Comparator<IQuantity>() {

        @Override
        public int compare(IQuantity o1, IQuantity o2) {
            return o1.compareTo(o2);
        }
    };

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, valueComparator, max, includeIntersecting);
    }

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        final List<Pair<V, IRange<IQuantity>>> valueList = new ArrayList<>();
        slidingWindowUnordered(new IUnorderedWindowVisitor() {

            @Override
            public void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime) {
                V value = valueFunction.getValue(items, startTime, endTime);
                if (value != null) {
                    valueList.add(new Pair<>(value, QuantityRange.createWithEnd(startTime, endTime)));
                }
            }

            @Override
            public boolean shouldContinue() {
                return !cancellationSupplier.isCancelled();
            }
        }, items, windowSize, slideSize, includeIntersecting);
        Comparator<Pair<V, IRange<IQuantity>>> comparator = new Comparator<Pair<V, IRange<IQuantity>>>() {

            @Override
            public int compare(Pair<V, IRange<IQuantity>> o1, Pair<V, IRange<IQuantity>> o2) {
                return valueComparator.compare(o1.left, o2.left);
            }
        };
        if (valueList.isEmpty()) {
            return null;
        }
        Pair<V, IRange<IQuantity>> minMaxWindow = max ? Collections.max(valueList, comparator) : Collections.min(valueList, comparator);
        return minMaxWindow;
    }
}


for (Iterator<IItem> iterator = items; iterator.hasNext() && callback.shouldContinue(); ) {
            IItem item = iterator.next();
            if (windowItems.isEmpty()) {
                windowStart = posAccessor.getMember(item);
            } else {
                windowStart = posAccessor.getMember(windowItems.get(0));
            }
            windowItems.add(item);
            IQuantity windowEnd = posAccessor.getMember(item);
            while (iterator.hasNext() && windowEnd.subtract(windowSize).compareTo(windowStart) < 0 && callback.shouldContinue()) {
                IItem next = iterator.next();
                windowEnd = posAccessor.getMember(next);
                windowItems.add(next);
            }
            callback.visitWindow(windowItems.iterator());
            // FIXME: What should we do about empty time intervals?
            if (slideSize == null) {
                if (windowItems.size() > 1) {
                    windowItems.remove(0);
                }
            } else {
                IQuantity newStart = windowStart.add(slideSize);
                for (Iterator<IItem> it = windowItems.iterator(); it.hasNext() && callback.shouldContinue(); ) {
                    IItem wi = it.next();
                    if (posAccessor.getMember(wi).compareTo(newStart) < 0) {
                        it.remove();
                    } else {
                        break;
                    }
                }
            }
        }
    }

/*
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 *
 */
package org.openjdk.jmc.flightrecorder.rules.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.FutureTask;
import org.openjdk.jmc.common.item.IItem;
import org.openjdk.jmc.common.item.IItemCollection;
import org.openjdk.jmc.common.item.IItemFilter;
import org.openjdk.jmc.common.item.IMemberAccessor;
import org.openjdk.jmc.common.item.ItemFilters;
import org.openjdk.jmc.common.unit.IQuantity;
import org.openjdk.jmc.common.unit.IRange;
import org.openjdk.jmc.common.unit.QuantityRange;
import org.openjdk.jmc.common.util.Pair;
import org.openjdk.jmc.flightrecorder.JfrAttributes;
import org.openjdk.jmc.flightrecorder.rules.IResult;

/**
 * Utility functions and interfaces for doing sliding window calculations.
 */
public class SlidingWindowToolkit {

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowOrdered}
     */
    public interface IOrderedWindowVisitor {

        void visitWindow(Iterator<IItem> items);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, looping through items once, removing from the start
     * and adding and the end of a windowItem set to match the current time window. items.
     * <p>
     * Suitable if the items are guaranteed to be ordered.
     *
     * @param callback
     *            method that can do calculations on the items in the window
     * @param items
     *            input items
     * @param posAccessor
     *            an accessor that should give a position value that is used with windowsSize and
     *            slideSize
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be, if slideSize is {@code null}, it will slide one item
     *            at a time
     */
    public static void slidingWindowOrdered(IOrderedWindowVisitor callback, Iterator<IItem> items, IMemberAccessor<IQuantity, IItem> posAccessor, IQuantity windowSize, IQuantity slideSize) {
        IQuantity windowStart = null;
        List<IItem> windowItems = new ArrayList<>();

    /**
     * Visitor interface used when calling {@link SlidingWindowToolkit#slidingWindowUnordered}
     */
    public interface IUnorderedWindowVisitor {

        void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime);

        boolean shouldContinue();
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize) {
        slidingWindowUnordered(callback, items, windowSize, slideSize, false);
    }

    /**
     * Runs a sliding window through all items, by calculating the window start and end and filter
     * through all the items. Suitable if the items are not guaranteed to be ordered, but is slower
     * than the ordered version.
     *
     * @param callback
     *            method that can do calculations on the items in the window.
     * @param items
     *            input items
     * @param windowSize
     *            size for the sliding window
     * @param slideSize
     *            how big the slide should be
     * @param includeIntersecting
     *            if the window filter should include events intersecting the window, or just those
     *            with end time in the window. It's up to the caller to take this into account and
     *            for example cap event duration to the window timespan.
     */
    public static void slidingWindowUnordered(IUnorderedWindowVisitor callback, IItemCollection items, IQuantity windowSize, IQuantity slideSize, boolean includeIntersecting) {
        IQuantity first = includeIntersecting ? RulesToolkit.getEarliestStartTime(items) : RulesToolkit.getEarliestEndTime(items);
        IQuantity last = RulesToolkit.getLatestEndTime(items);
        if (first == null) {
            return;
        }
        IQuantity windowStart = first;
        IQuantity windowEnd = windowStart.add(windowSize);
        do {
            IItemFilter window = includeIntersecting ? ItemFilters.rangeIntersects(JfrAttributes.LIFETIME, QuantityRange.createWithEnd(windowStart, windowEnd)) : ItemFilters.interval(JfrAttributes.END_TIME, windowStart, true, windowEnd, true);
            IItemCollection windowItems = items.apply(window);
            callback.visitWindow(windowItems, windowStart, windowEnd);
            windowStart = windowStart.add(slideSize);
            windowEnd = windowEnd.add(slideSize);
        } while (windowStart.compareTo(last) < 0 && callback.shouldContinue());
    }

    /**
     * Value function used when calling
     * {@link SlidingWindowToolkit#slidingWindowUnorderedMinMaxValue}
     */
    public interface IUnorderedWindowValueFunction<V> {

        V getValue(IItemCollection items, IQuantity startTime, IQuantity endTime);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, max, includeIntersecting);
    }

    /**
     * Calculates max/min window quantity value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static Pair<IQuantity, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<IQuantity> valueFunction, boolean max, boolean includeIntersecting) {
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, QUANTITY_COMPARATOR, max, includeIntersecting);
    }

    private static final Comparator<IQuantity> QUANTITY_COMPARATOR = new Comparator<IQuantity>() {

        @Override
        public int compare(IQuantity o1, IQuantity o2) {
            return o1.compareTo(o2);
        }
    };

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        IQuantity slideSize = windowSize.getUnit().quantity(windowSize.ratioTo(windowSize.getUnit().quantity(2)));
        return slidingWindowUnorderedMinMaxValue(items, windowSize, slideSize, cancellationSupplier, valueFunction, valueComparator, max, includeIntersecting);
    }

    /**
     * Calculates max/min window value of items.
     *
     * @param items
     *            items to use for evaluation
     * @param windowSize
     *            window size
     * @param slideSize
     *            window slide size
     * @param cancellationSupplier
     *            if the evaluation should be cancelled
     * @param valueFunction
     *            provides the window value for items
     * @param valueComparator
     *            compares values
     * @param max
     *            true to get the max value, false to get min value
     * @param includeIntersecting
     *            true to include also intersecting items, false to only include contained items. If
     *            set to true, it's up to the valueFunction to only use the duration events that is
     *            actually included in the window.
     * @return min/max window value and range
     */
    public static <V> Pair<V, IRange<IQuantity>> slidingWindowUnorderedMinMaxValue(IItemCollection items, IQuantity windowSize, IQuantity slideSize, final FutureTask<IResult> cancellationSupplier, final IUnorderedWindowValueFunction<V> valueFunction, final Comparator<V> valueComparator, boolean max, boolean includeIntersecting) {
        final List<Pair<V, IRange<IQuantity>>> valueList = new ArrayList<>();
        slidingWindowUnordered(new IUnorderedWindowVisitor() {

            @Override
            public void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime) {
                V value = valueFunction.getValue(items, startTime, endTime);
                if (value != null) {
                    valueList.add(new Pair<>(value, QuantityRange.createWithEnd(startTime, endTime)));
                }
            }

            @Override
            public boolean shouldContinue() {
                return !cancellationSupplier.isCancelled();
            }
        }, items, windowSize, slideSize, includeIntersecting);
        Comparator<Pair<V, IRange<IQuantity>>> comparator = new Comparator<Pair<V, IRange<IQuantity>>>() {

            @Override
            public int compare(Pair<V, IRange<IQuantity>> o1, Pair<V, IRange<IQuantity>> o2) {
                return valueComparator.compare(o1.left, o2.left);
            }
        };
        if (valueList.isEmpty()) {
            return null;
        }
        Pair<V, IRange<IQuantity>> minMaxWindow = max ? Collections.max(valueList, comparator) : Collections.min(valueList, comparator);
        return minMaxWindow;
    }
}