docs: @SuppressWarnings("unchecked")
  	@PublicEvolving
  	public static <IN1, IN2, OUT> TypeInformation<OUT> getBinaryOperatorReturnType(
  		Function function,
  		Class<?> baseClass,
  		int input1TypeArgumentIndex,
  		int input2TypeArgumentIndex,
  		int outputTypeArgumentIndex,
  		int[] lambdaOutputTypeArgumentIndices,
  		TypeInformation<IN1> in1Type,
  		TypeInformation<IN2> in2Type,
  		String functionName,
  		boolean allowMissing) {
  
  		Preconditions.checkArgument(in1Type == null || input1TypeArgumentIndex >= 0, "Input 1 type argument index was not provided");
  		Preconditions.checkArgument(in2Type == null || input2TypeArgumentIndex >= 0, "Input 2 type argument index was not provided");
  		Preconditions.checkArgument(outputTypeArgumentIndex >= 0, "Output type argument index was not provided");
  		Preconditions.checkArgument(
  			lambdaOutputTypeArgumentIndices != null,
  			"Indices for output type arguments within lambda not provided");
  
  		// explicit result type has highest precedence
  		if (function instanceof ResultTypeQueryable) {
  			return ((ResultTypeQueryable<OUT>) function).getProducedType();
  		}
  
  		// perform extraction
  		try {
  			final LambdaExecutable exec;
  			try {
  				exec = checkAndExtractLambda(function);
  			} catch (TypeExtractionException e) {
  				throw new InvalidTypesException("Internal error occurred.", e);
  			}
  			if (exec != null) {
  
  				final Method sam = TypeExtractionUtils.getSingleAbstractMethod(baseClass);
  				final int baseParametersLen = sam.getParameterTypes().length;
  
  				// parameters must be accessed from behind, since JVM can add additional parameters e.g. when using local variables inside lambda function
  				final int paramLen = exec.getParameterTypes().length;
  
  				final Type output;
  				if (lambdaOutputTypeArgumentIndices.length > 0) {
  					output = TypeExtractionUtils.extractTypeFromLambda(
  						baseClass,
  						exec,
  						lambdaOutputTypeArgumentIndices,
  						paramLen,
  						baseParametersLen);
  				} else {
  					output = exec.getReturnType();
  					TypeExtractionUtils.validateLambdaType(baseClass, output);
  				}
  
  				return new TypeExtractor().privateCreateTypeInfo(
  					output,
  					in1Type,
  					in2Type);
  			}
  			else {
  				if (in1Type != null) {
  					validateInputType(baseClass, function.getClass(), input1TypeArgumentIndex, in1Type);
  				}
  				if (in2Type != null) {
  					validateInputType(baseClass, function.getClass(), input2TypeArgumentIndex, in2Type);
  				}
  				return new TypeExtractor().privateCreateTypeInfo(baseClass, function.getClass(), outputTypeArgumentIndex, in1Type, in2Type);
  			}
  		}
  		catch (InvalidTypesException e) {
  			if (allowMissing) {
  				return (TypeInformation<OUT>) new MissingTypeInfo(functionName != null ? functionName : function.toString(), e);
  			} else {
  				throw e;
  			}
  		}
  	}