/*
 * Copyright (C) 2020-2022 Sebastiano Vigna
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package PACKAGE;

import java.util.function.UnaryOperator;

/** A type-specific {@link UnaryOperator}; provides methods operating both on objects
 * and on primitives.
 *
 * @see UnaryOperator
 * @since 8.5.0
 */

@FunctionalInterface
#if ! KEY_CLASS_Boolean
public interface KEY_UNARY_OPERATOR KEY_GENERIC extends UnaryOperator<KEY_GENERIC_CLASS>, JDK_PRIMITIVE_UNARY_OPERATOR {
#else
public interface KEY_UNARY_OPERATOR KEY_GENERIC extends UnaryOperator<KEY_GENERIC_CLASS> {
#endif

	/**
	 * Computes the operator on the given input.
	 *
	 * @param x the input.
	 * @return the output of the operator on the given input.
	 */
	KEY_TYPE apply(KEY_TYPE x);

	/**
	 * Returns a {@code UnaryOperator} that always returns the input unmodified.
	 * @see java.util.function.UnaryOperator#identity()
	 */
	public static KEY_UNARY_OPERATOR identity() {
		// Java is smart enough to see this lambda is stateless and will return the same instance every time.
		return i -> i;
	}

#if KEY_CLASS_Boolean
	/** Returns a {@code UnaryOperator} that always returns the logical negation of the input. */
	public static KEY_UNARY_OPERATOR negation() {
		return i -> !i;
	}
#elif !KEY_CLASS_Character
	/**
	 * Returns a {@code UnaryOperator} that always returns the arithmetic negation of the input.
	 * @implNote As with all negation, be wary of unexpected behavior near the minimum value of the data type.
	 * For example, -{@link Integer#MIN_VALUE} will result in {@link Integer#MIN_VALUE} (still negative),
	 * as the positive value of {@link Integer#MIN_VALUE} is too big for {@code int}
	 * (it would be 1 greater then {@link Integer#MAX_VALUE}).
	 */
	public static KEY_UNARY_OPERATOR negation() {
#if KEYS_BYTE_CHAR_SHORT_FLOAT && !KEY_CLASS_Float
		// Annoyingly, negating a byte (or similar) results in an int.
		return i -> (KEY_TYPE) -i;
#else
		return i -> -i;
#endif
	
	}
#endif

#if KEYS_INT_LONG_DOUBLE
	/** {@inheritDoc}
	 *
	 * @implSpec This default implementation delegates to {@link #apply}.
	 * @deprecated Please use {@link #apply}.
	 */
#else
	/** {@inheritDoc}
	 *
	 * @implSpec This default implementation delegates to
	 * {@link #apply} after narrowing down the argument to the
	 * actual key type, throwing an exception if the argument cannot be
	 * represented in the restricted domain. This is done for interoperability
	 * with the Java 8 function environment. The use of this method discouraged, as
	 * unexpected errors can occur.
	 *
	 * @throws IllegalArgumentException If the given operands are not an element of the key domain.
	 * @since 8.5.0
	 * @deprecated Please use {@link #apply}.
	 */
#endif

#if ! KEY_CLASS_Boolean
	@Deprecated
	@Override
	default KEY_TYPE_WIDENED JDK_PRIMITIVE_KEY_APPLY(final KEY_TYPE_WIDENED x) {
		return apply(KEY_NARROWING(x));
	}
#endif

	/** {@inheritDoc}
	 * @deprecated Please use the corresponding type-specific method instead. */
	@Deprecated
	@Override
	@SuppressWarnings("boxing")
	default KEY_CLASS apply(final KEY_CLASS x) {
		return apply(x.KEY_VALUE());
	}
}
