class Connector<T, R> {
        CollapseSpliterator<T, R> lhs, rhs;
        T left = none(), right = none();
        R acc;

        Connector(CollapseSpliterator<T, R> lhs, R acc, CollapseSpliterator<T, R> rhs) {
            this.lhs = lhs;
            this.rhs = rhs;
            this.acc = acc;
        }

        R drain() {
            if (lhs != null)
                lhs.right = null;
            if (rhs != null)
                rhs.left = null;
            return acc;
        }

        R drainLeft() {
            return left == NONE ? drain() : none();
        }

        R drainRight() {
            return right == NONE ? drain() : none();
        }
    }

    CollapseSpliterator(BiPredicate<? super T, ? super T> mergeable, Function<T, R> mapper,
            BiFunction<R, T, R> accumulator, BinaryOperator<R> combiner, Spliterator<T> source) {
        super(none());
        this.source = source;
        this.mergeable = mergeable;
        this.mapper = mapper;
        this.accumulator = accumulator;
        this.combiner = combiner;
        this.root = this;
    }

    private CollapseSpliterator(CollapseSpliterator<T, R> root, Spliterator<T> source, Connector<T, R> left,
            Connector<T, R> right) {
        super(none());
        this.source = source;
        this.root = root;
        this.mergeable = root.mergeable;
        this.mapper = root.mapper;
        this.accumulator = root.accumulator;
        this.combiner = root.combiner;
        this.left = left;
        this.right = right;
        if (left != null)
            left.rhs = this;
        right.lhs = this;
    }

    @Override
    public boolean tryAdvance(Consumer<? super R> action) {
        if (left != null) {
            if (accept(handleLeft(), action)) {
                return true;
            }
        }
        if (a == NONE) { // start
            if (!source.tryAdvance(this)) {
                return accept(pushRight(none(), none()), action);
            }
        }
        T first = a;
        R acc = mapper.apply(a);
        T last = first;
        while (source.tryAdvance(this)) {
            if (!this.mergeable.test(last, a)) {
                action.accept(acc);
                return true;
            }
            last = a;
            acc = this.accumulator.apply(acc, last);
        }
        return accept(pushRight(acc, last), action);
    }

    @Override
    public void forEachRemaining(Consumer<? super R> action) {
        while (left != null) {
            accept(handleLeft(), action);
        }
        if (a != NONE) {
            acc = mapper.apply(a);
        }
        source.forEachRemaining(next -> {
            if (a == NONE) {
                acc = mapper.apply(next);
            } else if (!this.mergeable.test(a, next)) {
                action.accept(acc);
                acc = mapper.apply(next);
            } else {
                acc = accumulator.apply(acc, next);
            }
            a = next;
        });
        if (a == NONE) {
            accept(pushRight(none(), none()), action);
        } else if (accept(pushRight(acc, a), action)) {
            if (right != null) {
                action.accept(right.acc);
                right = null;
            }
        }
    }

    private boolean accept(R acc, Consumer<? super R> action) {
        if (acc != NONE) {
            action.accept(acc);
            return true;
        }
        return false;
    }

    private R handleLeft() {<FILL_FUNCTION_BODY>

        synchronized (root) {
            Connector<T, R> l = left;
            if (l == null) {
                return none();
            }
            if (l.left == NONE && l.right == NONE && l.acc != NONE) {
                return l.drain();
            }
        }
        if (source.tryAdvance(this)) {
            T first = this.a;
            T last = first;
            R acc = this.mapper.apply(first);
            while (source.tryAdvance(this)) {
                if (!this.mergeable.test(last, a))
                    return pushLeft(first, acc);
                last = a;
                acc = this.accumulator.apply(acc, last);
            }
            a = none();
            return connectOne(first, acc, last);
        }
        return connectEmpty();
    
<methods>public void accept(T) <variables>T a