/**
  * Returns a composed {@link ThrowableToDoubleBiFunction} that first applies the {@code before}
  * functions to its input, and then applies this function to the result.
  *
  * @param <A> The type of the argument to the first given function, and of composed function
  * @param <B> The type of the argument to the second given function, and of composed function
  * @param before1 The first function to apply before this function is applied
  * @param before2 The second function to apply before this function is applied
  * @return A composed {@code ThrowableToDoubleBiFunction} that first applies the {@code before}
  *     functions to its input, and then applies this function to the result.
  * @throws NullPointerException If given argument is {@code null}
  * @implSpec The input argument of this method is able to handle every type.
  */
 @Nonnull
 default <A, B> ThrowableToDoubleBiFunction<A, B, X> compose(
     @Nonnull final ThrowableFunction<? super A, ? extends T, ? extends X> before1,
     @Nonnull final ThrowableToCharFunction<? super B, ? extends X> before2) {
   Objects.requireNonNull(before1);
   Objects.requireNonNull(before2);
   return (a, b) -> applyAsDoubleThrows(before1.applyThrows(a), before2.applyAsCharThrows(b));
 }
 /**
  * Returns a composed {@link ThrowableToByteTriFunction} that first applies the {@code before}
  * functions to its input, and then applies this function to the result.
  *
  * @param <A> The type of the argument to the first given function, and of composed function
  * @param <B> The type of the argument to the second given predicate, and of composed function
  * @param <C> The type of the argument to the third given predicate, and of composed function
  * @param before1 The first function to apply before this function is applied
  * @param before2 The second predicate to apply before this function is applied
  * @param before3 The third predicate to apply before this function is applied
  * @return A composed {@code ThrowableToByteTriFunction} that first applies the {@code before}
  *     functions to its input, and then applies this function to the result.
  * @throws NullPointerException If given argument is {@code null}
  * @implSpec The input argument of this method is able to handle every type.
  */
 @Nonnull
 default <A, B, C> ThrowableToByteTriFunction<A, B, C, X> compose(
     @Nonnull final ThrowableFunction<? super A, ? extends T, ? extends X> before1,
     @Nonnull final ThrowablePredicate<? super B, ? extends X> before2,
     @Nonnull final ThrowablePredicate<? super C, ? extends X> before3) {
   Objects.requireNonNull(before1);
   Objects.requireNonNull(before2);
   Objects.requireNonNull(before3);
   return (a, b, c) ->
       applyAsByteThrows(before1.applyThrows(a), before2.testThrows(b), before3.testThrows(c));
 }
 /**
  * Returns a composed {@link ThrowableToFloatTriFunction} that first applies the {@code before}
  * functions to its input, and then applies this function to the result.
  *
  * @param <A> The type of the argument to the first given function, and of composed function
  * @param <B> The type of the argument to the second given function, and of composed function
  * @param <C> The type of the argument to the third given function, and of composed function
  * @param before1 The first function to apply before this function is applied
  * @param before2 The second function to apply before this function is applied
  * @param before3 The third function to apply before this function is applied
  * @return A composed {@code ThrowableToFloatTriFunction} that first applies the {@code before}
  *     functions to its input, and then applies this function to the result.
  * @throws NullPointerException If given argument is {@code null}
  * @implSpec The input argument of this method is able to handle every type.
  */
 @Nonnull
 default <A, B, C> ThrowableToFloatTriFunction<A, B, C, X> compose(
     @Nonnull final ThrowableFunction<? super A, ? extends T, ? extends X> before1,
     @Nonnull final ThrowableToLongFunction<? super B, ? extends X> before2,
     @Nonnull final ThrowableToLongFunction<? super C, ? extends X> before3) {
   Objects.requireNonNull(before1);
   Objects.requireNonNull(before2);
   Objects.requireNonNull(before3);
   return (a, b, c) ->
       applyAsFloatThrows(
           before1.applyThrows(a), before2.applyAsLongThrows(b), before3.applyAsLongThrows(c));
 }
 /**
  * Returns a memoized (caching) version of this {@link ThrowableShortUnaryOperator}. Whenever it
  * is called, the mapping between the input parameter and the return value is preserved in a
  * cache, making subsequent calls returning the memoized value instead of computing the return
  * value again.
  *
  * <p>Unless the operator and therefore the used cache will be garbage-collected, it will keep all
  * memoized values forever.
  *
  * @return A memoized (caching) version of this {@code ThrowableShortUnaryOperator}.
  * @implSpec This implementation does not allow the input parameter or return value to be {@code
  *     null} for the resulting memoized operator, as the cache used internally does not permit
  *     {@code null} keys or values.
  * @implNote The returned memoized operator can be safely used concurrently from multiple threads
  *     which makes it thread-safe.
  */
 @Nonnull
 default ThrowableShortUnaryOperator<X> memoized() {
   if (isMemoized()) {
     return this;
   } else {
     final Map<Short, Short> cache = new ConcurrentHashMap<>();
     final Object lock = new Object();
     return (ThrowableShortUnaryOperator<X> & Memoized)
         (value) -> {
           final short returnValue;
           synchronized (lock) {
             returnValue =
                 cache.computeIfAbsent(value, ThrowableFunction.of(this::applyAsShortThrows));
           }
           return returnValue;
         };
   }
 }
 /**
  * Returns a memoized (caching) version of this {@link ThrowableBiByteToIntFunction}. Whenever it
  * is called, the mapping between the input parameters and the return value is preserved in a
  * cache, making subsequent calls returning the memoized value instead of computing the return
  * value again.
  *
  * <p>Unless the function and therefore the used cache will be garbage-collected, it will keep all
  * memoized values forever.
  *
  * @return A memoized (caching) version of this {@code ThrowableBiByteToIntFunction}.
  * @implSpec This implementation does not allow the input parameters or return value to be {@code
  *     null} for the resulting memoized function, as the cache used internally does not permit
  *     {@code null} keys or values.
  * @implNote The returned memoized function can be safely used concurrently from multiple threads
  *     which makes it thread-safe.
  */
 @Nonnull
 default ThrowableBiByteToIntFunction<X> memoized() {
   if (isMemoized()) {
     return this;
   } else {
     final Map<Pair<Byte, Byte>, Integer> cache = new ConcurrentHashMap<>();
     final Object lock = new Object();
     return (ThrowableBiByteToIntFunction<X> & Memoized)
         (value1, value2) -> {
           final int returnValue;
           synchronized (lock) {
             returnValue =
                 cache.computeIfAbsent(
                     Pair.of(value1, value2),
                     ThrowableFunction.of(key -> applyAsIntThrows(key.getLeft(), key.getRight())));
           }
           return returnValue;
         };
   }
 }
 /**
  * Returns a memoized (caching) version of this {@link ThrowableObjCharToDoubleFunction}. Whenever
  * it is called, the mapping between the input parameters and the return value is preserved in a
  * cache, making subsequent calls returning the memoized value instead of computing the return
  * value again.
  *
  * <p>Unless the function and therefore the used cache will be garbage-collected, it will keep all
  * memoized values forever.
  *
  * @return A memoized (caching) version of this {@code ThrowableObjCharToDoubleFunction}.
  * @implSpec This implementation does not allow the input parameters or return value to be {@code
  *     null} for the resulting memoized function, as the cache used internally does not permit
  *     {@code null} keys or values.
  * @implNote The returned memoized function can be safely used concurrently from multiple threads
  *     which makes it thread-safe.
  */
 @Nonnull
 default ThrowableObjCharToDoubleFunction<T, X> memoized() {
   if (isMemoized()) {
     return this;
   } else {
     final Map<Pair<T, Character>, Double> cache = new ConcurrentHashMap<>();
     final Object lock = new Object();
     return (ThrowableObjCharToDoubleFunction<T, X> & Memoized)
         (t, value) -> {
           final double returnValue;
           synchronized (lock) {
             returnValue =
                 cache.computeIfAbsent(
                     Pair.of(t, value),
                     ThrowableFunction.of(
                         key -> applyAsDoubleThrows(key.getLeft(), key.getRight())));
           }
           return returnValue;
         };
   }
 }
 /**
  * Returns a memoized (caching) version of this {@link ThrowableBiCharToShortFunction}. Whenever
  * it is called, the mapping between the input parameters and the return value is preserved in a
  * cache, making subsequent calls returning the memoized value instead of computing the return
  * value again.
  *
  * <p>Unless the function and therefore the used cache will be garbage-collected, it will keep all
  * memoized values forever.
  *
  * @return A memoized (caching) version of this {@code ThrowableBiCharToShortFunction}.
  * @implSpec This implementation does not allow the input parameters or return value to be {@code
  *     null} for the resulting memoized function, as the cache used internally does not permit
  *     {@code null} keys or values.
  * @implNote The returned memoized function can be safely used concurrently from multiple threads
  *     which makes it thread-safe.
  */
 @Nonnull
 default ThrowableBiCharToShortFunction<X> memoized() {
   if (isMemoized()) {
     return this;
   } else {
     final Map<Pair<Character, Character>, Short> cache = new ConcurrentHashMap<>();
     final Object lock = new Object();
     return (ThrowableBiCharToShortFunction<X> & Memoized)
         (value1, value2) -> {
           final short returnValue;
           synchronized (lock) {
             returnValue =
                 cache.computeIfAbsent(
                     Pair.of(value1, value2),
                     ThrowableFunction.of(
                         key -> applyAsShortThrows(key.getLeft(), key.getRight())));
           }
           return returnValue;
         };
   }
 }
 /**
  * Returns a memoized (caching) version of this {@link ThrowableObjBiBooleanToByteFunction}.
  * Whenever it is called, the mapping between the input parameters and the return value is
  * preserved in a cache, making subsequent calls returning the memoized value instead of computing
  * the return value again.
  *
  * <p>Unless the function and therefore the used cache will be garbage-collected, it will keep all
  * memoized values forever.
  *
  * @return A memoized (caching) version of this {@code ThrowableObjBiBooleanToByteFunction}.
  * @implSpec This implementation does not allow the input parameters or return value to be {@code
  *     null} for the resulting memoized function, as the cache used internally does not permit
  *     {@code null} keys or values.
  * @implNote The returned memoized function can be safely used concurrently from multiple threads
  *     which makes it thread-safe.
  */
 @Nonnull
 default ThrowableObjBiBooleanToByteFunction<T, X> memoized() {
   if (isMemoized()) {
     return this;
   } else {
     final Map<Triple<T, Boolean, Boolean>, Byte> cache = new ConcurrentHashMap<>();
     final Object lock = new Object();
     return (ThrowableObjBiBooleanToByteFunction<T, X> & Memoized)
         (t, value1, value2) -> {
           final byte returnValue;
           synchronized (lock) {
             returnValue =
                 cache.computeIfAbsent(
                     Triple.of(t, value1, value2),
                     ThrowableFunction.of(
                         key ->
                             applyAsByteThrows(key.getLeft(), key.getMiddle(), key.getRight())));
           }
           return returnValue;
         };
   }
 }
 /**
  * Returns a memoized (caching) version of this {@link ThrowableTriByteToFloatFunction}. Whenever
  * it is called, the mapping between the input parameters and the return value is preserved in a
  * cache, making subsequent calls returning the memoized value instead of computing the return
  * value again.
  *
  * <p>Unless the function and therefore the used cache will be garbage-collected, it will keep all
  * memoized values forever.
  *
  * @return A memoized (caching) version of this {@code ThrowableTriByteToFloatFunction}.
  * @implSpec This implementation does not allow the input parameters or return value to be {@code
  *     null} for the resulting memoized function, as the cache used internally does not permit
  *     {@code null} keys or values.
  * @implNote The returned memoized function can be safely used concurrently from multiple threads
  *     which makes it thread-safe.
  */
 @Nonnull
 default ThrowableTriByteToFloatFunction<X> memoized() {
   if (isMemoized()) {
     return this;
   } else {
     final Map<Triple<Byte, Byte, Byte>, Float> cache = new ConcurrentHashMap<>();
     final Object lock = new Object();
     return (ThrowableTriByteToFloatFunction<X> & Memoized)
         (value1, value2, value3) -> {
           final float returnValue;
           synchronized (lock) {
             returnValue =
                 cache.computeIfAbsent(
                     Triple.of(value1, value2, value3),
                     ThrowableFunction.of(
                         key ->
                             applyAsFloatThrows(key.getLeft(), key.getMiddle(), key.getRight())));
           }
           return returnValue;
         };
   }
 }