content: package com.annimon.stream.function;
  
  import com.annimon.stream.Objects;
  import org.jetbrains.annotations.NotNull;
  
  /**
   * Represents a function which produces result from two input arguments.
   *
   * @param <T> the type of the first argument
   * @param <U> the type of the second argument
   * @param <R> the type of the result of the function
   */
  public interface BiFunction<T, U, R> {
  
      /**
       * Applies this function to the given arguments.
       *
       * @param value1  the first argument
       * @param value2  the second argument
       * @return the function result
       */
      R apply(T value1, U value2);
  
      class Util {
  
          private Util() { }
  
          /**
           * Composes {@code BiFunction} calls.
           *
           * <p>{@code f2.apply(f1.apply(t, u)) }
           *
           * @param <T> the type of the first argument
           * @param <U> the type of the second argument
           * @param <R> the type of the result of the {@code BiFunction f1}
           * @param <V> the type of the result of composed function {@code f2}
           * @param f1  the {@code BiFunction} which is called first
           * @param f2  the function for transform {@code BiFunction f1} result to the type {@code V}
           * @return the result of composed function
           * @throws NullPointerException if {@code f1} or {@code f2} is null
           */
          public static <T, U, R, V> BiFunction<T, U, V> andThen(
                  @NotNull final BiFunction<? super T, ? super U, ? extends R> f1,
                  @NotNull final Function<? super R, ? extends V> f2) {
              Objects.requireNonNull(f1, "f1");
              Objects.requireNonNull(f2, "f2");
              return new BiFunction<T, U, V>() {
  
                  @Override
                  public V apply(T t, U u) {
                      return f2.apply(f1.apply(t, u));
                  }
              };
          }
  
          /**
           * Returns a {@code BiFunction} that reverses the input arguments order
           * of the specified {@code BiFunction}.
           *
           * @param <T> the type of the first argument of the given function
           *            and the second argument of the reversed function
           * @param <U> the type of the second argument of the given function
           *            and the first argument of the reversed function
           * @param <R> the type of the result of the given function
           *            and of the reversed function
           * @param function  the {@code BiFunction} to reverse arguments
           * @return the reversed function
           * @throws NullPointerException if {@code function} is null
           * @since 1.1.6
           */
          public static <T, U, R> BiFunction<U, T, R> reverse(
                  @NotNull final BiFunction<? super T, ? super U, ? extends R> function) {
              Objects.requireNonNull(function);
              return new BiFunction<U, T, R>() {
                  @Override
                  public R apply(U u, T t) {
                      return function.apply(t, u);
                  }
              };
          }
      }
  }
  