/*
 * Copyright (C) 2002-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.Spliterator;
import java.util.Set;
import static it.unimi.dsi.fastutil.Size64.sizeOf;

#if !KEYS_USE_REFERENCE_EQUALITY
/** A type-specific {@link Set}; provides some additional methods that use polymorphism to avoid (un)boxing.
 *
 * <p>Additionally, this interface strengthens (again) {@link #iterator()}.
 *
 * @see Set
 */
#else
/** A type-specific {@link Set}; provides some additional methods that use polymorphism to avoid (un)boxing.
 *
 * <p>Additionally, this interface strengthens (again) {@link #iterator()}.
 *
 * <p>This interface specifies reference equality semantics (members will be compared equal with
 * {@code ==} instead of {@link Object#equals(Object) equals}), which may result in breaks in contract
 * if attempted to be used with non reference-equality semantics based {@link Set}s. For example, a
 * {@code aReferenceSet.equals(aObjectSet)} may return different a different result then
 * {@code aObjectSet.equals(aReferenceSet)}, in violation of {@link Object#equals equals}'s contract
 * requiring it being symmetric.
 *
 * @see Set
 */
#endif
public interface SET KEY_GENERIC extends COLLECTION KEY_GENERIC, Set<KEY_GENERIC_CLASS> {

	/** Returns a type-specific iterator on the elements of this set.
	 *
	 * @apiNote This specification strengthens the one given in {@link java.lang.Iterable#iterator()},
	 * which was already strengthened in the corresponding type-specific class,
	 * but was weakened by the fact that this interface extends {@link Set}.
	 * <p>Also, this is generally the only {@code iterator} method subclasses should override.
	 *
	 * @return a type-specific iterator on the elements of this set.
	 */
	@Override
	KEY_ITERATOR KEY_GENERIC iterator();

	/**
	 * Returns a type-specific spliterator on the elements of this set.
	 *
	 * <p>Set spliterators must report at least {@link Spliterator#DISTINCT}.
	 *
	 * <p>See {@link java.util.Set#spliterator()} for more documentation on the requirements
	 * of the returned spliterator.
	 *
	 * @apiNote This specification strengthens the one given in
	 * {@link java.util.Collection#spliterator()}, which was already
	 * strengthened in the corresponding type-specific class,
	 * but was weakened by the fact that this interface extends {@link Set}.
	 * <p>Also, this is generally the only {@code spliterator} method subclasses should override.
	 *
	 * @implSpec The default implementation returns a late-binding spliterator (see
	 * {@link Spliterator} for documentation on what binding policies mean)
	 * that wraps this instance's type specific {@link #iterator}.
	 * <p>Additionally, it reports {@link Spliterator#SIZED} and {@link Spliterator#DISTINCT}.
	 *
	 * @implNote As this default implementation wraps the iterator, and {@link java.util.Iterator}
	 * is an inherently linear API, the returned spliterator will yield limited performance gains
	 * when run in parallel contexts, as the returned spliterator's
	 * {@link Spliterator#trySplit() trySplit()} will have linear runtime.
	 *
	 * @return {@inheritDoc}
	 * @since 8.5.0
	 */
	@Override
#if SPLITERATOR_ASSURE_OVERRIDE
	abstract KEY_SPLITERATOR KEY_GENERIC spliterator();
#else
	default KEY_SPLITERATOR KEY_GENERIC spliterator() {
		return SPLITERATORS.asSpliterator(
				iterator(), sizeOf(this), SPLITERATORS.SET_SPLITERATOR_CHARACTERISTICS);
	}
#endif

#if KEYS_PRIMITIVE
	/** Removes an element from this set.
	 *
	 * @apiNote Note that the corresponding method of a type-specific collection is {@code rem()}.
	 * This unfortunate situation is caused by the clash
	 * with the similarly named index-based method in the {@link java.util.List} interface.
	 *
	 * @see java.util.Collection#remove(Object)
	 */
	boolean remove(KEY_TYPE k);

	/** {@inheritDoc}
	 * @deprecated Please use the corresponding type-specific method instead.
	 */
	@SuppressWarnings("deprecation")
	@Deprecated
	@Override
	default boolean remove(final Object o) {
		return COLLECTION.super.remove(o);
	}

	/** {@inheritDoc}
	 * @deprecated Please use the corresponding type-specific method instead.
	 */
	@SuppressWarnings("deprecation")
	@Deprecated
	@Override
	default boolean add(final KEY_GENERIC_CLASS o) {
		return COLLECTION.super.add(o);
	}

	/** {@inheritDoc}
	 * @deprecated Please use the corresponding type-specific method instead.
	 */
	@SuppressWarnings("deprecation")
	@Deprecated
	@Override
	default boolean contains(final Object o) {
		return COLLECTION.super.contains(o);
	}

	/** Removes an element from this set.
	 *
	 * <p>This method is inherited from the type-specific collection this
	 * type-specific set is based on, but it should not used as
	 * this interface reinstates {@code remove()} as removal method.
	 *
	 * @deprecated Please use {@code remove()} instead.
	 */
	@Deprecated
	@Override
	default boolean rem(KEY_TYPE k) {
		return remove(k);
	}

#endif

	/** Returns an immutable empty set.
	 *
	 * @return an immutable empty set.
	 */
	SUPPRESS_WARNINGS_KEY_UNCHECKED
	public static KEY_GENERIC SET KEY_GENERIC of() {
		return SETS.UNMODIFIABLE_EMPTY_SET;
	}

	/** Returns an immutable set with the element given.
	 *
	 * @param e an element.
	 * @return an immutable set containing {@code e}.
	 */
	public static KEY_GENERIC SET KEY_GENERIC of(KEY_GENERIC_TYPE e) {
		return SETS.singleton(e);
	}

	/**
	 * Returns an immutable set with the elements given.
	 *
	 * @param e0 the first element.
	 * @param e1 the second element.
	 * @return an immutable set containing {@code e0} and {@code e1}.
	 * @throws IllegalArgumentException if there were duplicate entries.
	 */
	public static KEY_GENERIC SET KEY_GENERIC of(KEY_GENERIC_TYPE e0, KEY_GENERIC_TYPE e1) {
		ARRAY_SET KEY_GENERIC innerSet = new ARRAY_SET KEY_GENERIC_DIAMOND(2);
		innerSet.add(e0);
		if (!innerSet.add(e1)) {
			throw new IllegalArgumentException("Duplicate element: " + e1);
		}
		return SETS.unmodifiable(innerSet);
	}

	/**
	 * Returns an immutable set with the elements given.
	 *
	 * @param e0 the first element.
	 * @param e1 the second element.
	 * @param e2 the third element.
	 * @return an immutable set containing {@code e0}, {@code e1}, and {@code e2}.
	 * @throws IllegalArgumentException if there were duplicate entries.
	 */
	public static KEY_GENERIC SET KEY_GENERIC of(KEY_GENERIC_TYPE e0, KEY_GENERIC_TYPE e1, KEY_GENERIC_TYPE e2) {
		ARRAY_SET KEY_GENERIC innerSet = new ARRAY_SET KEY_GENERIC_DIAMOND(3);
		innerSet.add(e0);
		if (!innerSet.add(e1)) {
			throw new IllegalArgumentException("Duplicate element: " + e1);
		}
		if (!innerSet.add(e2)) {
			throw new IllegalArgumentException("Duplicate element: " + e2);
		}
		return SETS.unmodifiable(innerSet);
	}

	/**
	 * Returns an immutable list with the elements given.
	 *
	 * @param a the list of elements that will be in the final set.
	 * @return an immutable set containing the elements in {@code a}.
	 * @throws IllegalArgumentException if there are any duplicate entries.
	 */
	SAFE_VARARGS
	public static KEY_GENERIC SET KEY_GENERIC of(KEY_GENERIC_TYPE... a) {
		switch(a.length) {
			case 0:
				return of();
			case 1:
				return of(a[0]);
			case 2:
				return of(a[0], a[1]);
			case 3:
				return of(a[0], a[1], a[2]);
			default:
				// fall through
		}
		// Will copy, but that is the only way we assure immutability.
		SET KEY_GENERIC innerSet = a.length <= SETS.ARRAY_SET_CUTOFF ?
			new ARRAY_SET KEY_GENERIC_DIAMOND(a.length) :
			new OPEN_HASH_SET KEY_GENERIC_DIAMOND(a.length);
		for (KEY_GENERIC_TYPE element : a) {
			if (!innerSet.add(element)) {
				throw new IllegalArgumentException("Duplicate element: " + element);
			}
		}
		return SETS.unmodifiable(innerSet);
	}

}
