All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hu.akarnokd.rxjava2.Single Maven / Gradle / Ivy

There is a newer version: 2.0.0-RC3
Show newest version
/**
 * Copyright 2015 David Karnok and Netflix, Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
 * the License for the specific language governing permissions and limitations under the License.
 */

package hu.akarnokd.rxjava2;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

import org.reactivestreams.*;

import hu.akarnokd.rxjava2.disposables.*;
import hu.akarnokd.rxjava2.exceptions.CompositeException;
import hu.akarnokd.rxjava2.functions.*;
import hu.akarnokd.rxjava2.internal.disposables.EmptyDisposable;
import hu.akarnokd.rxjava2.internal.functions.Functions;
import hu.akarnokd.rxjava2.internal.functions.Objects;
import hu.akarnokd.rxjava2.internal.operators.single.*;
import hu.akarnokd.rxjava2.internal.subscriptions.*;
import hu.akarnokd.rxjava2.internal.util.*;
import hu.akarnokd.rxjava2.plugins.RxJavaPlugins;
import hu.akarnokd.rxjava2.schedulers.Schedulers;

/**
 * Represents a deferred computation and emission of a single value or exception.
 * 
 * @param  the value type
 */
public class Single {
    
    public interface SingleOnSubscribe extends Consumer> {
        
    }
    
    public interface SingleOperator extends Function, SingleSubscriber> {
        
    }
    
    public interface SingleSubscriber {
        
        
        void onSubscribe(Disposable d);
        
        void onSuccess(T value);

        void onError(Throwable e);
    }
    
    public interface SingleTransformer extends Function, Single> {
        
    }
    
    public static  Single amb(final Iterable> sources) {
        Objects.requireNonNull(sources, "sources is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                final AtomicBoolean once = new AtomicBoolean();
                final CompositeDisposable set = new CompositeDisposable();
                s.onSubscribe(set);
                
                int c = 0;
                Iterator> iterator;
                
                try {
                    iterator = sources.iterator();
                } catch (Throwable e) {
                    s.onError(e);
                    return;
                }
                
                if (iterator == null) {
                    s.onError(new NullPointerException("The iterator returned is null"));
                    return;
                }
                for (;;) {
                    if (once.get()) {
                        return;
                    }
                    
                    boolean b;
                    
                    try {
                        b = iterator.hasNext();
                    } catch (Throwable e) {
                        s.onError(e);
                        return;
                    }
                    
                    if (once.get()) {
                        return;
                    }

                    if (!b) {
                        break;
                    }
                    
                    Single s1;

                    if (once.get()) {
                        return;
                    }

                    try {
                        s1 = iterator.next();
                    } catch (Throwable e) {
                        set.dispose();
                        s.onError(e);
                        return;
                    }
                    
                    if (s1 == null) {
                        set.dispose();
                        s.onError(new NullPointerException("The single source returned by the iterator is null"));
                        return;
                    }
                    
                    s1.subscribe(new SingleSubscriber() {

                        @Override
                        public void onSubscribe(Disposable d) {
                            set.add(d);
                        }

                        @Override
                        public void onSuccess(T value) {
                            if (once.compareAndSet(false, true)) {
                                s.onSuccess(value);
                            }
                        }

                        @Override
                        public void onError(Throwable e) {
                            if (once.compareAndSet(false, true)) {
                                s.onError(e);
                            } else {
                                RxJavaPlugins.onError(e);
                            }
                        }
                        
                    });
                    c++;
                }
                
                if (c == 0 && !set.isDisposed()) {
                    s.onError(new NoSuchElementException());
                }
            }
        });
    }
    
    @SuppressWarnings("unchecked")
    public static  Single amb(final Single... sources) {
        if (sources.length == 0) {
            return error(new Supplier() {
                @Override
                public Throwable get() {
                    return new NoSuchElementException();
                }
            });
        }
        if (sources.length == 1) {
            return (Single)sources[0];
        }
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                final AtomicBoolean once = new AtomicBoolean();
                final CompositeDisposable set = new CompositeDisposable();
                s.onSubscribe(set);
                
                for (Single s1 : sources) {
                    if (once.get()) {
                        return;
                    }
                    
                    if (s1 == null) {
                        set.dispose();
                        Throwable e = new NullPointerException("One of the sources is null");
                        if (once.compareAndSet(false, true)) {
                            s.onError(e);
                        } else {
                            RxJavaPlugins.onError(e);
                        }
                        return;
                    }
                    
                    s1.subscribe(new SingleSubscriber() {

                        @Override
                        public void onSubscribe(Disposable d) {
                            set.add(d);
                        }

                        @Override
                        public void onSuccess(T value) {
                            if (once.compareAndSet(false, true)) {
                                s.onSuccess(value);
                            }
                        }

                        @Override
                        public void onError(Throwable e) {
                            if (once.compareAndSet(false, true)) {
                                s.onError(e);
                            } else {
                                RxJavaPlugins.onError(e);
                            }
                        }
                        
                    });
                }
            }
        });
    }

    public static  Observable concat(Iterable> sources) {
        return concat(Observable.fromIterable(sources));
    }
    
    public static  Observable concat(Observable> sources) {
        return sources.concatMap(new Function, Publisher>() {
            @Override 
            public Publisher apply(Single v){
                return v.toFlowable();
            }
        });
    }
    
    @SuppressWarnings("unchecked")
    public static  Observable concat(
            Single s1, Single s2
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        return concat(Observable.fromArray(s1, s2));
    }
    
    @SuppressWarnings("unchecked")
    public static  Observable concat(
            Single s1, Single s2,
            Single s3
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        return concat(Observable.fromArray(s1, s2, s3));
    }
    
    @SuppressWarnings("unchecked")
    public static  Observable concat(
            Single s1, Single s2,
            Single s3, Single s4
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        return concat(Observable.fromArray(s1, s2, s3, s4));
    }
    
    @SuppressWarnings("unchecked")
    public static  Observable concat(
            Single s1, Single s2,
            Single s3, Single s4,
            Single s5
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        Objects.requireNonNull(s5, "s5 is null");
        return concat(Observable.fromArray(s1, s2, s3, s4, s5));
    }
    
    @SuppressWarnings("unchecked")
    public static  Observable concat(
            Single s1, Single s2,
            Single s3, Single s4,
            Single s5, Single s6
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        Objects.requireNonNull(s5, "s5 is null");
        Objects.requireNonNull(s6, "s6 is null");
        return concat(Observable.fromArray(s1, s2, s3, s4, s5, s6));
    }
    
    @SuppressWarnings("unchecked")
    public static  Observable concat(
            Single s1, Single s2,
            Single s3, Single s4,
            Single s5, Single s6,
            Single s7
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        Objects.requireNonNull(s5, "s5 is null");
        Objects.requireNonNull(s6, "s6 is null");
        Objects.requireNonNull(s7, "s7 is null");
        return concat(Observable.fromArray(s1, s2, s3, s4, s5, s6, s7));
    }
    
    @SuppressWarnings("unchecked")
    public static  Observable concat(
            Single s1, Single s2,
            Single s3, Single s4,
            Single s5, Single s6,
            Single s7, Single s8
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        Objects.requireNonNull(s5, "s5 is null");
        Objects.requireNonNull(s6, "s6 is null");
        Objects.requireNonNull(s7, "s7 is null");
        Objects.requireNonNull(s8, "s8 is null");
        return concat(Observable.fromArray(s1, s2, s3, s4, s5, s6, s7, s8));
    }
    
    @SuppressWarnings("unchecked")
    public static  Observable concat(
            Single s1, Single s2,
            Single s3, Single s4,
            Single s5, Single s6,
            Single s7, Single s8,
            Single s9
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        Objects.requireNonNull(s5, "s5 is null");
        Objects.requireNonNull(s6, "s6 is null");
        Objects.requireNonNull(s7, "s7 is null");
        Objects.requireNonNull(s8, "s8 is null");
        Objects.requireNonNull(s9, "s9 is null");
        return concat(Observable.fromArray(s1, s2, s3, s4, s5, s6, s7, s8, s9));
    }
    
    public static  Single create(SingleOnSubscribe onSubscribe) {
        Objects.requireNonNull(onSubscribe, "onSubscribe is null");
        // TODO plugin wrapper
        return new Single(onSubscribe);
    }
    
    public static  Single defer(final Supplier> singleSupplier) {
        Objects.requireNonNull(singleSupplier, "singleSupplier is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(SingleSubscriber s) {
                Single next;
                
                try {
                    next = singleSupplier.get();
                } catch (Throwable e) {
                    s.onSubscribe(EmptyDisposable.INSTANCE);
                    s.onError(e);
                    return;
                }
                
                if (next == null) {
                    s.onSubscribe(EmptyDisposable.INSTANCE);
                    s.onError(new NullPointerException("The Single supplied was null"));
                    return;
                }
                
                next.subscribe(s);
            }
        });
    }
    
    public static  Single error(final Supplier errorSupplier) {
        Objects.requireNonNull(errorSupplier, "errorSupplier is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(SingleSubscriber s) {
                Throwable error;
                
                try {
                    error = errorSupplier.get();
                } catch (Throwable e) {
                    error = e;
                }
                
                if (error == null) {
                    error = new NullPointerException();
                }
                
                s.onSubscribe(EmptyDisposable.INSTANCE);
                s.onError(error);
            }
        });
    }
    
    public static  Single error(final Throwable error) {
        Objects.requireNonNull(error, "error is null");
        return error(new Supplier() {
            @Override
            public Throwable get() {
                return error;
            }
        });
    }
    
    public static  Single fromCallable(final Callable callable) {
        Objects.requireNonNull(callable, "callable is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(SingleSubscriber s) {
                s.onSubscribe(EmptyDisposable.INSTANCE);
                try {
                    T v = callable.call();
                    if (v != null) {
                        s.onSuccess(v);
                    } else {
                        s.onError(new NullPointerException());
                    }
                } catch (Throwable e) {
                    s.onError(e);
                }
            }
        });
    }
    
    public static  Single fromFuture(Future future) {
        return Observable.fromFuture(future).toSingle();
    }

    public static  Single fromFuture(Future future, long timeout, TimeUnit unit) {
        return Observable.fromFuture(future, timeout, unit).toSingle();
    }

    public static  Single fromFuture(Future future, long timeout, TimeUnit unit, Scheduler scheduler) {
        return Observable.fromFuture(future, timeout, unit, scheduler).toSingle();
    }

    public static  Single fromFuture(Future future, Scheduler scheduler) {
        return Observable.fromFuture(future, scheduler).toSingle();
    }

    public static  Single fromPublisher(final Publisher publisher) {
        Objects.requireNonNull(publisher, "publisher is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                publisher.subscribe(new Subscriber() {
                    T value;
                    @Override
                    public void onComplete() {
                        T v = value;
                        value = null;
                        if (v != null) {
                            s.onSuccess(v);
                        } else {
                            s.onError(new NoSuchElementException());
                        }
                    }

                    @Override
                    public void onError(Throwable t) {
                        value = null;
                        s.onError(t);
                    }

                    @Override
                    public void onNext(T t) {
                        value = t;
                    }

                    @Override
                    public void onSubscribe(Subscription inner) {
                        s.onSubscribe(Disposables.from(inner));
                        inner.request(Long.MAX_VALUE);
                    }
                    
                });
            }
        });
    }

    public static  Single just(final T value) {
        Objects.requireNonNull(value, "value is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(SingleSubscriber s) {
                s.onSubscribe(EmptyDisposable.INSTANCE);
                s.onSuccess(value);
            }
        });
    }

    public static  Observable merge(Iterable> sources) {
        return merge(Observable.fromIterable(sources));
    }

    public static  Observable merge(Observable> sources) {
        return sources.flatMap(new Function, Publisher>() {
            @Override 
            public Publisher apply(Single v){
                return v.toFlowable();
            }
        });
    }
    
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static  Single merge(Single> source) {
        return source.flatMap((Function)Functions.identity());
    }
    
    @SuppressWarnings("unchecked")
    public static  Observable merge(
            Single s1, Single s2
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        return merge(Observable.fromArray(s1, s2));
    }
    
    @SuppressWarnings("unchecked")
    public static  Observable merge(
            Single s1, Single s2,
            Single s3
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        return merge(Observable.fromArray(s1, s2, s3));
    }
    
    @SuppressWarnings("unchecked")
    public static  Observable merge(
            Single s1, Single s2,
            Single s3, Single s4
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        return merge(Observable.fromArray(s1, s2, s3, s4));
    }
    
    @SuppressWarnings("unchecked")
    public static  Observable merge(
            Single s1, Single s2,
            Single s3, Single s4,
            Single s5
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        Objects.requireNonNull(s5, "s5 is null");
        return merge(Observable.fromArray(s1, s2, s3, s4, s5));
    }
    
    @SuppressWarnings("unchecked")
    public static  Observable merge(
            Single s1, Single s2,
            Single s3, Single s4,
            Single s5, Single s6
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        Objects.requireNonNull(s5, "s5 is null");
        Objects.requireNonNull(s6, "s6 is null");
        return merge(Observable.fromArray(s1, s2, s3, s4, s5, s6));
    }

    @SuppressWarnings("unchecked")
    public static  Observable merge(
            Single s1, Single s2,
            Single s3, Single s4,
            Single s5, Single s6,
            Single s7
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        Objects.requireNonNull(s5, "s5 is null");
        Objects.requireNonNull(s6, "s6 is null");
        Objects.requireNonNull(s7, "s7 is null");
        return merge(Observable.fromArray(s1, s2, s3, s4, s5, s6, s7));
    }

    @SuppressWarnings("unchecked")
    public static  Observable merge(
            Single s1, Single s2,
            Single s3, Single s4,
            Single s5, Single s6,
            Single s7, Single s8
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        Objects.requireNonNull(s5, "s5 is null");
        Objects.requireNonNull(s6, "s6 is null");
        Objects.requireNonNull(s7, "s7 is null");
        Objects.requireNonNull(s8, "s8 is null");
        return merge(Observable.fromArray(s1, s2, s3, s4, s5, s6, s7, s8));
    }

    @SuppressWarnings("unchecked")
    public static  Observable merge(
            Single s1, Single s2,
            Single s3, Single s4,
            Single s5, Single s6,
            Single s7, Single s8,
            Single s9
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        Objects.requireNonNull(s5, "s5 is null");
        Objects.requireNonNull(s6, "s6 is null");
        Objects.requireNonNull(s7, "s7 is null");
        Objects.requireNonNull(s8, "s8 is null");
        Objects.requireNonNull(s9, "s9 is null");
        return merge(Observable.fromArray(s1, s2, s3, s4, s5, s6, s7, s8, s9));
    }
    
    static final Single NEVER = create(new SingleOnSubscribe() {
        @Override
        public void accept(SingleSubscriber s) {
            s.onSubscribe(EmptyDisposable.INSTANCE);
        }
    });
    
    @SuppressWarnings("unchecked")
    public static  Single never() {
        return (Single)NEVER; 
    }
    
    public static Single timer(long delay, TimeUnit unit) {
        return timer(delay, unit, Schedulers.computation());
    }
    
    public static Single timer(final long delay, final TimeUnit unit, final Scheduler scheduler) {
        Objects.requireNonNull(unit, "unit is null");
        Objects.requireNonNull(scheduler, "scheduler is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                MultipleAssignmentDisposable mad = new MultipleAssignmentDisposable();
                
                s.onSubscribe(mad);
                
                mad.set(scheduler.scheduleDirect(new Runnable() {
                    @Override
                    public void run() {
                        s.onSuccess(0L);
                    }
                }, delay, unit));
            }
        });
    }
    
    public static  Single equals(final Single first, final Single second) {
        Objects.requireNonNull(first, "first is null");
        Objects.requireNonNull(second, "second is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                final AtomicInteger count = new AtomicInteger();
                final Object[] values = { null, null };
                
                final CompositeDisposable set = new CompositeDisposable();
                s.onSubscribe(set);
                
                class InnerSubscriber implements SingleSubscriber {
                    final int index;
                    public InnerSubscriber(int index) {
                        this.index = index;
                    }
                    @Override
                    public void onSubscribe(Disposable d) {
                        set.add(d);
                    }

                    @Override
                    public void onSuccess(T value) {
                        values[index] = value;
                        
                        if (count.incrementAndGet() == 2) {
                            s.onSuccess(Objects.equals(values[0], values[1]));
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        for (;;) {
                            int state = count.get();
                            if (state >= 2) {
                                RxJavaPlugins.onError(e);
                                return;
                            }
                            if (count.compareAndSet(state, 2)) {
                                s.onError(e);
                                return;
                            }
                        }
                    }
                    
                }
                
                first.subscribe(new InnerSubscriber(0));
                second.subscribe(new InnerSubscriber(1));
            }
        });
    }

    public static  Single using(Supplier resourceSupplier, 
            Function> singleFunction, Consumer disposer) {
        return using(resourceSupplier, singleFunction, disposer, true);
    }
        
    public static  Single using(
            final Supplier resourceSupplier, 
            final Function> singleFunction, 
            final Consumer disposer, 
            final boolean eager) {
        Objects.requireNonNull(resourceSupplier, "resourceSupplier is null");
        Objects.requireNonNull(singleFunction, "singleFunction is null");
        Objects.requireNonNull(disposer, "disposer is null");
        
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                final U resource;
                
                try {
                    resource = resourceSupplier.get();
                } catch (Throwable ex) {
                    s.onSubscribe(EmptyDisposable.INSTANCE);
                    s.onError(ex);
                    return;
                }
                
                Single s1;
                
                try {
                    s1 = singleFunction.apply(resource);
                } catch (Throwable ex) {
                    s.onSubscribe(EmptyDisposable.INSTANCE);
                    s.onError(ex);
                    return;
                }
                
                if (s1 == null) {
                    s.onSubscribe(EmptyDisposable.INSTANCE);
                    s.onError(new NullPointerException("The Single supplied by the function was null"));
                    return;
                }
                
                s1.subscribe(new SingleSubscriber() {

                    @Override
                    public void onSubscribe(Disposable d) {
                        if (eager) {
                            CompositeDisposable set = new CompositeDisposable();
                            set.add(d);
                            set.add(new Disposable() {
                                @Override
                                public void dispose() {
                                    try {
                                        disposer.accept(resource);
                                    } catch (Throwable e) {
                                        RxJavaPlugins.onError(e);
                                    }
                                }
                            });
                        } else {
                            s.onSubscribe(d);
                        }
                    }

                    @Override
                    public void onSuccess(T value) {
                        if (eager) {
                            try {
                                disposer.accept(resource);
                            } catch (Throwable e) {
                                s.onError(e);
                                return;
                            }
                        }
                        s.onSuccess(value);
                        if (!eager) {
                            try {
                                disposer.accept(resource);
                            } catch (Throwable e) {
                                RxJavaPlugins.onError(e);
                            }
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        if (eager) {
                            try {
                                disposer.accept(resource);
                            } catch (Throwable ex) {
                                e = new CompositeException(ex, e);
                            }
                        }
                        s.onError(e);
                        if (!eager) {
                            try {
                                disposer.accept(resource);
                            } catch (Throwable ex) {
                                RxJavaPlugins.onError(ex);
                            }
                        }
                    }
                    
                });
            }
        });
    }

    @SuppressWarnings("unchecked")
    public static  Single zip(final Iterable> sources, Function zipper) {
        Objects.requireNonNull(sources, "sources is null");
        
        Iterable> it = new Iterable>() {
            @Override
            public Iterator> iterator() {
                final Iterator> sit = sources.iterator();
                return new Iterator>() {

                    @Override
                    public boolean hasNext() {
                        return sit.hasNext();
                    }

                    @Override
                    public Observable next() {
                        return ((Observable)sit.next().toFlowable());
                    }
                    
                    @Override
                    public void remove() {
                        throw new UnsupportedOperationException();
                    }
                };
            }
        };
        return Observable.zipIterable(zipper, false, 1, it).toSingle();
    }

    @SuppressWarnings("unchecked")
    public static  Single zip(
            Single s1, Single s2,
            BiFunction zipper
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        return zipArray(Functions.toFunction(zipper), s1, s2);
    }

    @SuppressWarnings("unchecked")
    public static  Single zip(
            Single s1, Single s2,
            Single s3,
            Function3 zipper
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        return zipArray(Functions.toFunction(zipper), s1, s2, s3);
    }

    @SuppressWarnings("unchecked")
    public static  Single zip(
            Single s1, Single s2,
            Single s3, Single s4,
            Function4 zipper
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        return zipArray(Functions.toFunction(zipper), s1, s2, s3, s4);
    }

    @SuppressWarnings("unchecked")
    public static  Single zip(
            Single s1, Single s2,
            Single s3, Single s4,
            Single s5,
            Function5 zipper
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        Objects.requireNonNull(s5, "s5 is null");
        return zipArray(Functions.toFunction(zipper), s1, s2, s3, s4, s5);
    }

    @SuppressWarnings("unchecked")
    public static  Single zip(
            Single s1, Single s2,
            Single s3, Single s4,
            Single s5, Single s6,
            Function6 zipper
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        Objects.requireNonNull(s5, "s5 is null");
        Objects.requireNonNull(s6, "s6 is null");
        return zipArray(Functions.toFunction(zipper), s1, s2, s3, s4, s5, s6);
    }

    @SuppressWarnings("unchecked")
    public static  Single zip(
            Single s1, Single s2,
            Single s3, Single s4,
            Single s5, Single s6,
            Single s7, 
            Function7 zipper
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        Objects.requireNonNull(s5, "s5 is null");
        Objects.requireNonNull(s6, "s6 is null");
        Objects.requireNonNull(s7, "s7 is null");
        return zipArray(Functions.toFunction(zipper), s1, s2, s3, s4, s5, s6, s7);
    }
    
    @SuppressWarnings("unchecked")
    public static  Single zip(
            Single s1, Single s2,
            Single s3, Single s4,
            Single s5, Single s6,
            Single s7, Single s8,
            Function8 zipper
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        Objects.requireNonNull(s5, "s5 is null");
        Objects.requireNonNull(s6, "s6 is null");
        Objects.requireNonNull(s7, "s7 is null");
        Objects.requireNonNull(s8, "s8 is null");
        return zipArray(Functions.toFunction(zipper), s1, s2, s3, s4, s5, s6, s7, s8);
    }
    
    @SuppressWarnings("unchecked")
    public static  Single zip(
            Single s1, Single s2,
            Single s3, Single s4,
            Single s5, Single s6,
            Single s7, Single s8,
            Single s9,
            Function9 zipper
     ) {
        Objects.requireNonNull(s1, "s1 is null");
        Objects.requireNonNull(s2, "s2 is null");
        Objects.requireNonNull(s3, "s3 is null");
        Objects.requireNonNull(s4, "s4 is null");
        Objects.requireNonNull(s5, "s5 is null");
        Objects.requireNonNull(s6, "s6 is null");
        Objects.requireNonNull(s7, "s7 is null");
        Objects.requireNonNull(s8, "s8 is null");
        Objects.requireNonNull(s9, "s9 is null");
        return zipArray(Functions.toFunction(zipper), s1, s2, s3, s4, s5, s6, s7, s8, s9);
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    public static  Single zipArray(Function zipper, Single... sources) {
        Publisher[] sourcePublishers = new Publisher[sources.length];
        int i = 0;
        for (Single s : sources) {
            sourcePublishers[i] = s.toFlowable();
            i++;
        }
        return Observable.zipArray(zipper, false, 1, sourcePublishers).toSingle();
    }

    protected final SingleOnSubscribe onSubscribe;

    protected Single(SingleOnSubscribe onSubscribe) {
        this.onSubscribe = onSubscribe;
    }

    @SuppressWarnings("unchecked")
    public final Single ambWith(Single other) {
        Objects.requireNonNull(other, "other is null");
        return amb(this, other);
    }
    
    public final Single asSingle() {
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(SingleSubscriber s) {
                subscribe(s);
            }
        });
    }
    
    public final  Single compose(Function, ? extends Single> convert) {
        return to(convert);
    }

    public final Single cache() {
        final AtomicInteger wip = new AtomicInteger();
        final AtomicReference notification = new AtomicReference();
        final List> subscribers = new ArrayList>();
        
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(SingleSubscriber s) {
                Object o = notification.get();
                if (o != null) {
                    s.onSubscribe(EmptyDisposable.INSTANCE);
                    if (NotificationLite.isError(o)) {
                        s.onError(NotificationLite.getError(o));
                    } else {
                        s.onSuccess(NotificationLite.getValue(o));
                    }
                    return;
                }
                
                synchronized (subscribers) {
                    o = notification.get();
                    if (o == null) {
                        subscribers.add(s);
                    }
                }
                if (o != null) {
                    s.onSubscribe(EmptyDisposable.INSTANCE);
                    if (NotificationLite.isError(o)) {
                        s.onError(NotificationLite.getError(o));
                    } else {
                        s.onSuccess(NotificationLite.getValue(o));
                    }
                    return;
                }
                
                if (wip.getAndIncrement() != 0) {
                    return;
                }
                
                subscribe(new SingleSubscriber() {

                    @Override
                    public void onSubscribe(Disposable d) {
                        
                    }

                    @Override
                    public void onSuccess(T value) {
                        notification.set(NotificationLite.next(value));
                        List> list;
                        synchronized (subscribers) {
                            list = new ArrayList>(subscribers);
                            subscribers.clear();
                        }
                        for (SingleSubscriber s1 : list) {
                            s1.onSuccess(value);
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        notification.set(NotificationLite.error(e));
                        List> list;
                        synchronized (subscribers) {
                            list = new ArrayList>(subscribers);
                            subscribers.clear();
                        }
                        for (SingleSubscriber s1 : list) {
                            s1.onError(e);
                        }
                    }
                    
                });
            }
        });
    }
    
    public final  Single cast(final Class clazz) {
        Objects.requireNonNull(clazz, "clazz is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                subscribe(new SingleSubscriber() {

                    @Override
                    public void onSubscribe(Disposable d) {
                        s.onSubscribe(d);
                    }

                    @Override
                    public void onSuccess(T value) {
                        U v;
                        try {
                            v = clazz.cast(value);
                        } catch (ClassCastException ex) {
                            s.onError(ex);
                            return;
                        }
                        s.onSuccess(v);
                    }

                    @Override
                    public void onError(Throwable e) {
                        s.onError(e);
                    }
                    
                });
            }
        });
    }
    
    public final Observable concatWith(Single other) {
        return concat(this, other);
    }
    
    public final Single delay(long time, TimeUnit unit) {
        return delay(time, unit, Schedulers.computation());
    }
    
    public final Single delay(final long time, final TimeUnit unit, final Scheduler scheduler) {
        Objects.requireNonNull(unit, "unit is null");
        Objects.requireNonNull(scheduler, "scheduler is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                final MultipleAssignmentDisposable mad = new MultipleAssignmentDisposable();
                s.onSubscribe(mad);
                subscribe(new SingleSubscriber() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        mad.set(d);
                    }

                    @Override
                    public void onSuccess(final T value) {
                        mad.set(scheduler.scheduleDirect(new Runnable() {
                            @Override
                            public void run() {
                                s.onSuccess(value);
                            }
                        }, time, unit));
                    }

                    @Override
                    public void onError(Throwable e) {
                        s.onError(e);
                    }
                    
                });
            }
        });
    }
    
    public final Single doOnSubscribe(final Consumer onSubscribe) {
        Objects.requireNonNull(onSubscribe, "onSubscribe is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                subscribe(new SingleSubscriber() {
                    boolean done;
                    @Override
                    public void onSubscribe(Disposable d) {
                        try {
                            onSubscribe.accept(d);
                        } catch (Throwable ex) {
                            done = true;
                            d.dispose();
                            s.onSubscribe(EmptyDisposable.INSTANCE);
                            s.onError(ex);
                            return;
                        }
                        
                        s.onSubscribe(d);
                    }

                    @Override
                    public void onSuccess(T value) {
                        if (done) {
                            return;
                        }
                        s.onSuccess(value);
                    }

                    @Override
                    public void onError(Throwable e) {
                        if (done) {
                            RxJavaPlugins.onError(e);
                            return;
                        }
                        s.onError(e);
                    }
                    
                });
            }
        });
    }
    
    public final Single doOnSuccess(final Consumer onSuccess) {
        Objects.requireNonNull(onSuccess, "onSuccess is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                subscribe(new SingleSubscriber() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        s.onSubscribe(d);
                    }

                    @Override
                    public void onSuccess(T value) {
                        try {
                            onSuccess.accept(value);
                        } catch (Throwable ex) {
                            s.onError(ex);
                            return;
                        }
                        s.onSuccess(value);
                    }

                    @Override
                    public void onError(Throwable e) {
                        s.onError(e);
                    }
                    
                });
            }
        });
    }
    
    public final Single doOnError(final Consumer onError) {
        Objects.requireNonNull(onError, "onError is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                subscribe(new SingleSubscriber() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        s.onSubscribe(d);
                    }

                    @Override
                    public void onSuccess(T value) {
                        s.onSuccess(value);
                    }

                    @Override
                    public void onError(Throwable e) {
                        try {
                            onError.accept(e);
                        } catch (Throwable ex) {
                            e = new CompositeException(ex, e);
                        }
                        s.onError(e);
                    }
                    
                });
            }
        });
    }
    
    public final Single doOnCancel(final Runnable onCancel) {
        Objects.requireNonNull(onCancel, "onCancel is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                subscribe(new SingleSubscriber() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        CompositeDisposable set = new CompositeDisposable();
                        set.add(Disposables.from(onCancel));
                        set.add(d);
                        s.onSubscribe(set);
                    }

                    @Override
                    public void onSuccess(T value) {
                        s.onSuccess(value);
                    }

                    @Override
                    public void onError(Throwable e) {
                        s.onError(e);
                    }
                    
                });
            }
        });
    }

    public final  Single flatMap(Function> mapper) {
        Objects.requireNonNull(mapper, "mapper is null");
        return lift(new SingleOperatorFlatMap(mapper));   
    }

    public final  Observable flatMapPublisher(Function> mapper) {
        return toFlowable().flatMap(mapper);
    }
    
    public final T get() {
        final AtomicReference valueRef = new AtomicReference();
        final AtomicReference errorRef = new AtomicReference();
        final CountDownLatch cdl = new CountDownLatch(1);
        
        subscribe(new SingleSubscriber() {
            @Override
            public void onError(Throwable e) {
                errorRef.lazySet(e);
                cdl.countDown();
            }

            @Override
            public void onSubscribe(Disposable d) {
            }
            @Override
            public void onSuccess(T value) {
                valueRef.lazySet(value);
                cdl.countDown();
            }
        });
        
        if (cdl.getCount() != 0L) {
            try {
                cdl.await();
            } catch (InterruptedException ex) {
                throw new RuntimeException(ex);
            }
        }
        Throwable e = errorRef.get();
        if (e != null) {
            throw Exceptions.propagate(e);
        }
        return valueRef.get();
    }
    
    public final  Single lift(final SingleOperator onLift) {
        Objects.requireNonNull(onLift, "onLift is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(SingleSubscriber s) {
                try {
                    SingleSubscriber sr = onLift.apply(s);
                    
                    if (sr == null) {
                        throw new NullPointerException("The onLift returned a null subscriber");
                    }
                    // TODO plugin wrapper
                    onSubscribe.accept(sr);
                } catch (NullPointerException ex) {
                    throw ex;
                } catch (Throwable ex) {
                    RxJavaPlugins.onError(ex);
                    NullPointerException npe = new NullPointerException("Not really but can't throw other than NPE");
                    npe.initCause(ex);
                    throw npe;
                }
            }
        });
    }
    
    public final  Single map(Function mapper) {
        return lift(new SingleOperatorMap(mapper));
    }

    public final Single contains(Object value) {
        return contains(value, Objects.equalsPredicate());
    }

    public final Single contains(final Object value, final BiPredicate comparer) {
        Objects.requireNonNull(value, "value is null");
        Objects.requireNonNull(comparer, "comparer is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                subscribe(new SingleSubscriber() {

                    @Override
                    public void onSubscribe(Disposable d) {
                        s.onSubscribe(d);
                    }

                    @Override
                    public void onSuccess(T v) {
                        s.onSuccess(comparer.test(v, value));
                    }

                    @Override
                    public void onError(Throwable e) {
                        s.onError(e);
                    }
                    
                });
            }
        });
    }
    
    public final Observable mergeWith(Single other) {
        return merge(this, other);
    }
    
    public final Single> nest() {
        return just(this);
    }
    
    public final Single observeOn(final Scheduler scheduler) {
        Objects.requireNonNull(scheduler, "scheduler is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                final CompositeDisposable mad = new CompositeDisposable();
                s.onSubscribe(mad);
                
                subscribe(new SingleSubscriber() {

                    @Override
                    public void onError(final Throwable e) {
                        mad.add(scheduler.scheduleDirect(new Runnable() {
                            @Override
                            public void run() {
                                s.onError(e);
                            }
                        }));
                    }

                    @Override
                    public void onSubscribe(Disposable d) {
                        mad.add(d);
                    }

                    @Override
                    public void onSuccess(final T value) {
                        mad.add(scheduler.scheduleDirect(new Runnable() {
                            @Override
                            public void run() {
                                s.onSuccess(value);
                            }
                        }));
                    }
                    
                });
            }
        });
    }

    public final Single onErrorReturn(final Supplier valueSupplier) {
        Objects.requireNonNull(valueSupplier, "valueSupplier is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                subscribe(new SingleSubscriber() {

                    @Override
                    public void onError(Throwable e) {
                        T v;
                        
                        try {
                            v = valueSupplier.get();
                        } catch (Throwable ex) {
                            s.onError(new CompositeException(ex, e));
                            return;
                        }
                        
                        if (v == null) {
                            NullPointerException npe = new NullPointerException("Value supplied was null");
                            npe.initCause(e);
                            s.onError(npe);
                            return;
                        }
                        
                        s.onSuccess(v);
                    }

                    @Override
                    public void onSubscribe(Disposable d) {
                        s.onSubscribe(d);
                    }

                    @Override
                    public void onSuccess(T value) {
                        s.onSuccess(value);
                    }
                    
                });
            }
        });
    }
    
    public final Single onErrorReturn(final T value) {
        Objects.requireNonNull(value, "value is null");
        return onErrorReturn(new Supplier() {
            @Override
            public T get() {
                return value;
            }
        });
    }

    public final Single onErrorResumeNext(
            final Function> nextFunction) {
        Objects.requireNonNull(nextFunction, "nextFunction is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                final MultipleAssignmentDisposable mad = new MultipleAssignmentDisposable();
                s.onSubscribe(mad);
                
                subscribe(new SingleSubscriber() {

                    @Override
                    public void onSubscribe(Disposable d) {
                        mad.set(d);
                    }

                    @Override
                    public void onSuccess(T value) {
                        s.onSuccess(value);
                    }

                    @Override
                    public void onError(Throwable e) {
                        Single next;
                        
                        try {
                            next = nextFunction.apply(e);
                        } catch (Throwable ex) {
                            s.onError(new CompositeException(ex, e));
                            return;
                        }
                        
                        if (next == null) {
                            NullPointerException npe = new NullPointerException("The next Single supplied was null");
                            npe.initCause(e);
                            s.onError(npe);
                            return;
                        }
                        
                        next.subscribe(new SingleSubscriber() {

                            @Override
                            public void onSubscribe(Disposable d) {
                                mad.set(d);
                            }

                            @Override
                            public void onSuccess(T value) {
                                s.onSuccess(value);
                            }

                            @Override
                            public void onError(Throwable e) {
                                s.onError(e);
                            }
                            
                        });
                    }
                    
                });
            }
        });
    }
    
    public final Observable repeat() {
        return toFlowable().repeat();
    }
    
    public final Observable repeat(long times) {
        return toFlowable().repeat(times);
    }
    
    public final Observable repeatWhen(Function, ? extends Publisher> handler) {
        return toFlowable().repeatWhen(handler);
    }
    
    public final Observable repeatUntil(BooleanSupplier stop) {
        return toFlowable().repeatUntil(stop);
    }
    
    public final Single retry() {
        return toFlowable().retry().toSingle();
    }
    
    public final Single retry(long times) {
        return toFlowable().retry(times).toSingle();
    }
    
    public final Single retry(BiPredicate predicate) {
        return toFlowable().retry(predicate).toSingle();
    }
    
    public final Single retry(Predicate predicate) {
        return toFlowable().retry(predicate).toSingle();
    }
    
    public final Single retryWhen(Function, ? extends Publisher> handler) {
        return toFlowable().retryWhen(handler).toSingle();
    }
    
    public final void safeSubscribe(Subscriber s) {
        toFlowable().safeSubscribe(s);
    }
    
    public final Disposable subscribe() {
        return subscribe(Functions.emptyConsumer(), RxJavaPlugins.errorConsumer());
    }
    
    public final Disposable subscribe(final BiConsumer onCallback) {
        Objects.requireNonNull(onCallback, "onCallback is null");
        
        final MultipleAssignmentDisposable mad = new MultipleAssignmentDisposable();
        
        subscribe(new SingleSubscriber() {
            @Override
            public void onError(Throwable e) {
                onCallback.accept(null, e);
            }
            
            @Override
            public void onSubscribe(Disposable d) {
                mad.set(d);
            }
            
            @Override
            public void onSuccess(T value) {
                onCallback.accept(value, null);
            }
        });
        
        return mad;
    }
    
    public final Disposable subscribe(Consumer onSuccess) {
        return subscribe(onSuccess, RxJavaPlugins.errorConsumer());
    }
    
    public final Disposable subscribe(final Consumer onSuccess, final Consumer onError) {
        Objects.requireNonNull(onSuccess, "onSuccess is null");
        Objects.requireNonNull(onError, "onError is null");
        
        final MultipleAssignmentDisposable mad = new MultipleAssignmentDisposable();
        
        subscribe(new SingleSubscriber() {
            @Override
            public void onError(Throwable e) {
                onError.accept(e);
            }
            
            @Override
            public void onSubscribe(Disposable d) {
                mad.set(d);
            }
            
            @Override
            public void onSuccess(T value) {
                onSuccess.accept(value);
            }
        });
        
        return mad;
    }
    
    public final void subscribe(SingleSubscriber subscriber) {
        Objects.requireNonNull(subscriber, "subscriber is null");
        // TODO plugin wrapper
        onSubscribe.accept(subscriber);
    }
    
    public final void subscribe(Subscriber s) {
        toFlowable().subscribe(s);
    }
    
    public final Single subscribeOn(final Scheduler scheduler) {
        Objects.requireNonNull(scheduler, "scheduler is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                scheduler.scheduleDirect(new Runnable() {
                    @Override
                    public void run() {
                        subscribe(s);
                    }
                });
            }
        });
    }
    
    public final Single timeout(long timeout, TimeUnit unit) {
        return timeout0(timeout, unit, Schedulers.computation(), null);
    }
    
    public final Single timeout(long timeout, TimeUnit unit, Scheduler scheduler) {
        return timeout0(timeout, unit, scheduler, null);
    }

    public final Single timeout(long timeout, TimeUnit unit, Scheduler scheduler, Single other) {
        Objects.requireNonNull(other, "other is null");
        return timeout0(timeout, unit, scheduler, other);
    }

    public final Single timeout(long timeout, TimeUnit unit, Single other) {
        Objects.requireNonNull(other, "other is null");
        return timeout0(timeout, unit, Schedulers.computation(), other);
    }

    private Single timeout0(final long timeout, final TimeUnit unit, final Scheduler scheduler, final Single other) {
        Objects.requireNonNull(unit, "unit is null");
        Objects.requireNonNull(scheduler, "scheduler is null");
        return create(new SingleOnSubscribe() {
            @Override
            public void accept(final SingleSubscriber s) {
                final CompositeDisposable set = new CompositeDisposable();
                s.onSubscribe(set);
                
                final AtomicBoolean once = new AtomicBoolean();
                
                Disposable timer = scheduler.scheduleDirect(new Runnable() {
                    @Override
                    public void run() {
                        if (once.compareAndSet(false, true)) {
                            if (other != null) {
                                set.clear();
                                other.subscribe(new SingleSubscriber() {

                                    @Override
                                    public void onError(Throwable e) {
                                        set.dispose();
                                        s.onError(e);
                                    }

                                    @Override
                                    public void onSubscribe(Disposable d) {
                                        set.add(d);
                                    }

                                    @Override
                                    public void onSuccess(T value) {
                                        set.dispose();
                                        s.onSuccess(value);
                                    }
                                    
                                });
                            } else {
                                set.dispose();
                                s.onError(new TimeoutException());
                            }
                        }
                    }
                }, timeout, unit);
                
                set.add(timer);
                
                subscribe(new SingleSubscriber() {

                    @Override
                    public void onError(Throwable e) {
                        if (once.compareAndSet(false, true)) {
                            set.dispose();
                            s.onError(e);
                        }
                    }

                    @Override
                    public void onSubscribe(Disposable d) {
                        set.add(d);
                    }

                    @Override
                    public void onSuccess(T value) {
                        if (once.compareAndSet(false, true)) {
                            set.dispose();
                            s.onSuccess(value);
                        }
                    }
                    
                });
            }
        });
    }

    public final  R to(Function, R> convert) {
        return convert.apply(this);
    }
    
    public final Observable toFlowable() {
        return Observable.create(new Publisher() {
            @Override
            public void subscribe(final Subscriber s) {
                final ScalarAsyncSubscription sas = new ScalarAsyncSubscription(s);
                final AsyncSubscription as = new AsyncSubscription();
                as.setSubscription(sas);
                s.onSubscribe(as);
                
                Single.this.subscribe(new SingleSubscriber() {
                    @Override
                    public void onError(Throwable e) {
                        s.onError(e);
                    }

                    @Override
                    public void onSubscribe(Disposable d) {
                        as.setResource(d);
                    }

                    @Override
                    public void onSuccess(T value) {
                        sas.setValue(value);
                    }
                    
                });
            }
        });
    }
    
    public final void unsafeSubscribe(Subscriber s) {
        toFlowable().unsafeSubscribe(s);
    }
    
    public final  Single zipWith(Single other, BiFunction zipper) {
        return zip(this, other, zipper);
    }
}