import java.util.Iterator;

/**
 * Multiset Interface. A Multiset is a generalization of a set that allows duplicate elements.
 * 
 * This is inspired by the Guava Multiset interface:
 * 
 * https://guava.dev/releases/18.0/api/docs/com/google/common/collect/Multiset.html
 *
 * @author CS 240 Instructors
 * @param <E> the type of elements maintained by this multiset
 *
 */
public interface Multiset<E> extends Iterable<E> {


  /**
   * Add a single occurrence of an item to the multiset.
   *
   * @param item The item to add
   * @return True if the item was added, false if there is no space left in the Multiset.
   */
  public boolean add(E item);

  /**
   * Add some number of occurrences for an item.
   *
   * @param item The item to add
   * @param occurrences The number of repeats to add
   * @return True if the item was added, false if there is no space left in the Multiset.
   */
  public boolean add(E item, int occurrences);

  /**
   * Remove all elements.
   */
  public void clear();

  /**
   * Return true if at least one instance of the provided item is contained in the Multiset.
   *
   * @param item The item to check
   * @return true if the item is in the Multiset, false otherwise
   */
  public boolean contains(Object item);


  /**
   * Return a count of the number of occurrences of the provided item in the Multiset.
   *
   * @param item The item to count
   * @return The number of occurrences
   */
  public int getCount(Object item);

  /**
   * Return true if the provided object is equal to this Multiset. Two Multisets are considered to
   * be equal if they contain the same elements with the same counts.
   *
   * @param other The object to check for equality
   * @return true if the object is equal to this Multiset
   */
  @Override
  public boolean equals(Object other);


  /**
   * Return an iterator for this Multiset. Repeated elements will be returned the appropriate number
   * of times by the iterator.
   *
   * @return The iterator
   */
  @Override
  public Iterator<E> iterator();

  /**
   * Reduce the count of the provided element by one (remove a single occurrence).
   * Remove the element entirely if the count reaches 0.
   *
   * @param item The element to remove
   * @return true if the element was contained in the Multiset, false otherwise
   */
  public boolean remove(Object item);

  /**
   * Return the total number of elements stored in the Multiset, taking into account the number of
   * occurrences of each element.
   *
   * @return The total number of elements
   */
  public int size();

  /**
   * Return a String representation of this Multiset. Each element will be listed explicitly,
   * with duplicate elements appearing multiple times. For example, a Multiset containing two 'a's 
   * and one 'b' would be represented as "[a, a, b]". The order of the elements is not specified.
   *
   * @return A string representation of the Multiset
   */
  @Override
  public String toString();


}
