import java.util.concurrent.*;
import java.util.function.*;
import java.util.stream.Collectors;
import java.util.List;

public class AsyncUtils {
    
    // Simple async execution
    public static <T> CompletableFuture<T> supplyAsync(Supplier<T> supplier) {
        return CompletableFuture.supplyAsync(supplier);
    }
    
    // Async execution with custom executor
    public static <T> CompletableFuture<T> supplyAsync(Supplier<T> supplier, ExecutorService executor) {
        return CompletableFuture.supplyAsync(supplier, executor);
    }
    
    // Transform result
    public static <T, R> CompletableFuture<R> thenApply(CompletableFuture<T> future, Function<T, R> mapper) {
        return future.thenApply(mapper);
    }
    
    // Chain futures
    public static <T, R> CompletableFuture<R> thenCompose(CompletableFuture<T> future, Function<T, CompletableFuture<R>> mapper) {
        return future.thenCompose(mapper);
    }
    
    // Combine two futures
    public static <T, U, R> CompletableFuture<R> thenCombine(CompletableFuture<T> future1, CompletableFuture<U> future2, BiFunction<T, U, R> combiner) {
        return future1.thenCombine(future2, combiner);
    }
    
    // Wait for all futures
    public static CompletableFuture<Void> allOf(List<CompletableFuture<?>> futures) {
        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
    }
    
    // Wait for any future
    public static <T> CompletableFuture<Object> anyOf(List<CompletableFuture<T>> futures) {
        return CompletableFuture.anyOf(futures.toArray(new CompletableFuture[0]));
    }
    
    // Handle exceptions
    public static <T> CompletableFuture<T> handle(CompletableFuture<T> future, BiFunction<T, Throwable, T> handler) {
        return future.handle(handler);
    }
}