// Path: core/src/main/java/com/github/phantomthief/util/ThrowableConsumer.java
// @FunctionalInterface
// public interface ThrowableConsumer<T, X extends Throwable> {
// 
//     void accept(T t) throws X;
// 
//     default ThrowableConsumer<T, X> andThen(ThrowableConsumer<? super T, X> after) {
//         requireNonNull(after);
//         return t -> {
//             accept(t);
//             after.accept(t);
//         };
//     }
// }
// 
// Path: core/src/main/java/com/github/phantomthief/util/ThrowableFunction.java
// @FunctionalInterface
// public interface ThrowableFunction<T, R, X extends Throwable> {
// 
//     static <T, X extends Throwable> ThrowableFunction<T, T, X> identity() {
//         return t -> t;
//     }
// 
//     R apply(T t) throws X;
// 
//     default <V> ThrowableFunction<V, R, X>
//             compose(ThrowableFunction<? super V, ? extends T, X> before) {
//         requireNonNull(before);
//         return (V v) -> apply(before.apply(v));
//     }
// 
//     default <V> ThrowableFunction<T, V, X>
//             andThen(ThrowableFunction<? super R, ? extends V, X> after) {
//         requireNonNull(after);
//         return (T t) -> after.apply(apply(t));
//     }
// }


import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.util.concurrent.MoreExecutors.shutdownAndAwaitTermination;
import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly;
import static java.util.concurrent.Executors.newFixedThreadPool;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadLocalRandom;
import javax.annotation.Nonnull;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.github.phantomthief.util.ThrowableConsumer;
import com.github.phantomthief.util.ThrowableFunction;
        ExecutorService executorService = newFixedThreadPool(50);
        Map<Integer, String> firstMapping = new ConcurrentHashMap<>();
        for (int i = 0; i < 20; i++) {
            int j = i;
            executorService.execute(() -> {
                run(keyAffinity, j, v -> {
                    firstMapping.put(j, v);
                    sleepUninterruptibly(10, SECONDS);
                });
            });
        }
        sleepUninterruptibly(1, SECONDS);
        for (int i = 0; i < 1000; i++) {
            executorService.execute(() -> {
                int key = ThreadLocalRandom.current().nextInt(20);
                run(keyAffinity, key, v -> {
                    String firstV = firstMapping.get(key);
                    assertEquals(firstV, v);
                });
            });
        }
        shutdownAndAwaitTermination(executorService, 1, DAYS);
    }

    @AfterEach
    void tearDown() throws Exception {
        keyAffinity.close();
    }

    private static <T, X extends Throwable, K, V> T supply(LazyKeyAffinity<K, V> keyAffinity, K key,
// Path: core/src/main/java/com/github/phantomthief/util/ThrowableConsumer.java
// @FunctionalInterface
// public interface ThrowableConsumer<T, X extends Throwable> {
// 
//     void accept(T t) throws X;
// 
//     default ThrowableConsumer<T, X> andThen(ThrowableConsumer<? super T, X> after) {
//         requireNonNull(after);
//         return t -> {
//             accept(t);
//             after.accept(t);
//         };
//     }
// }
// 
// Path: core/src/main/java/com/github/phantomthief/util/ThrowableFunction.java
// @FunctionalInterface
// public interface ThrowableFunction<T, R, X extends Throwable> {
// 
//     static <T, X extends Throwable> ThrowableFunction<T, T, X> identity() {
//         return t -> t;
//     }
// 
//     R apply(T t) throws X;
// 
//     default <V> ThrowableFunction<V, R, X>
//             compose(ThrowableFunction<? super V, ? extends T, X> before) {
//         requireNonNull(before);
//         return (V v) -> apply(before.apply(v));
//     }
// 
//     default <V> ThrowableFunction<T, V, X>
//             andThen(ThrowableFunction<? super R, ? extends V, X> after) {
//         requireNonNull(after);
//         return (T t) -> after.apply(apply(t));
//     }
// }


// Path: core/src/test/java/com/github/phantomthief/pool/impl/KeyAffinityTest.java
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.util.concurrent.MoreExecutors.shutdownAndAwaitTermination;
import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly;
import static java.util.concurrent.Executors.newFixedThreadPool;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadLocalRandom;
import javax.annotation.Nonnull;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.github.phantomthief.util.ThrowableConsumer;
import com.github.phantomthief.util.ThrowableFunction;

        ExecutorService executorService = newFixedThreadPool(50);
        Map<Integer, String> firstMapping = new ConcurrentHashMap<>();
        for (int i = 0; i < 20; i++) {
            int j = i;
            executorService.execute(() -> {
                run(keyAffinity, j, v -> {
                    firstMapping.put(j, v);
                    sleepUninterruptibly(10, SECONDS);
                });
            });
        }
        sleepUninterruptibly(1, SECONDS);
        for (int i = 0; i < 1000; i++) {
            executorService.execute(() -> {
                int key = ThreadLocalRandom.current().nextInt(20);
                run(keyAffinity, key, v -> {
                    String firstV = firstMapping.get(key);
                    assertEquals(firstV, v);
                });
            });
        }
        shutdownAndAwaitTermination(executorService, 1, DAYS);
    }

    @AfterEach
    void tearDown() throws Exception {
        keyAffinity.close();
    }

    private static <T, X extends Throwable, K, V> T supply(LazyKeyAffinity<K, V> keyAffinity, K key,
            @Nonnull ThrowableFunction<V, T, X> func) throws X {