/*
 * Copyright (C) 2011 The Guava Authors
 *
 * 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 com.google.common.util.concurrent;

import static java.util.concurrent.TimeUnit.NANOSECONDS;

import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Preconditions;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;

/**
 * Utilities for treating interruptible operations as uninterruptible. In all cases, if a thread is
 * interrupted during such a call, the call continues to block until the result is available or the
 * timeout elapses, and only then re-interrupts the thread.
 *
 * @author Anthony Zana
 * @since 10.0
 */
@GwtCompatible(emulated = true)
public final class Uninterruptibles {

  // Implementation Note: As of 3-7-11, the logic for each blocking/timeout
  // methods is identical, save for method being invoked.

  /** Invokes {@code latch.}{@link CountDownLatch#await() await()} uninterruptibly. */
  @GwtIncompatible // concurrency
  public static void awaitUninterruptibly(CountDownLatch latch) {
    boolean interrupted = false;
    try {
      while (true) {
        try {
          latch.await();
          return;
        } catch (InterruptedException e) {
          interrupted = true;
        }
      }
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }

  /**
   * Invokes {@code latch.}{@link CountDownLatch#await(long, TimeUnit) await(timeout, unit)}
   * uninterruptibly.
   */
  @CanIgnoreReturnValue // TODO(cpovirk): Consider being more strict.
  @GwtIncompatible // concurrency
  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
  public static boolean awaitUninterruptibly(CountDownLatch latch, long timeout, TimeUnit unit) {
    boolean interrupted = false;
    try {
      long remainingNanos = unit.toNanos(timeout);
      long end = System.nanoTime() + remainingNanos;

      while (true) {
        try {
          // CountDownLatch treats negative timeouts just like zero.
          return latch.await(remainingNanos, NANOSECONDS);
        } catch (InterruptedException e) {
          interrupted = true;
          remainingNanos = end - System.nanoTime();
        }
      }
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }

  /**
   * Invokes {@code condition.}{@link Condition#await(long, TimeUnit) await(timeout, unit)}
   * uninterruptibly.
   *
   * @since 23.6
   */
  @GwtIncompatible // concurrency
  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
  public static boolean awaitUninterruptibly(Condition condition, long timeout, TimeUnit unit) {
    boolean interrupted = false;
    try {
      long remainingNanos = unit.toNanos(timeout);
      long end = System.nanoTime() + remainingNanos;

      while (true) {
        try {
          return condition.await(remainingNanos, NANOSECONDS);
        } catch (InterruptedException e) {
          interrupted = true;
          remainingNanos = end - System.nanoTime();
        }
      }
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }

  /** Invokes {@code toJoin.}{@link Thread#join() join()} uninterruptibly. */
  @GwtIncompatible // concurrency
  public static void joinUninterruptibly(Thread toJoin) {
    boolean interrupted = false;
    try {
      while (true) {
        try {
          toJoin.join();
          return;
        } catch (InterruptedException e) {
          interrupted = true;
        }
      }
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }

  /**
   * Invokes {@code unit.}{@link TimeUnit#timedJoin(Thread, long) timedJoin(toJoin, timeout)}
   * uninterruptibly.
   */
  @GwtIncompatible // concurrency
  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
  public static void joinUninterruptibly(Thread toJoin, long timeout, TimeUnit unit) {
    Preconditions.checkNotNull(toJoin);
    boolean interrupted = false;
    try {
      long remainingNanos = unit.toNanos(timeout);
      long end = System.nanoTime() + remainingNanos;
      while (true) {
        try {
          // TimeUnit.timedJoin() treats negative timeouts just like zero.
          NANOSECONDS.timedJoin(toJoin, remainingNanos);
          return;
        } catch (InterruptedException e) {
          interrupted = true;
          remainingNanos = end - System.nanoTime();
        }
      }
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }

  /**
   * Invokes {@code future.}{@link Future#get() get()} uninterruptibly.
   *
   *
Similar methods:
   *
   *

   *
To retrieve a result from a {@code Future} that is already done, use {@link
   *       Futures#getDone Futures.getDone}.
   *
To treat {@link InterruptedException} uniformly with other exceptions, use {@link
   *       Futures#getChecked(Future, Class) Futures.getChecked}.
   *
To get uninterruptibility and remove checked exceptions, use {@link
   *       Futures#getUnchecked}.
   *

   *
   * @throws ExecutionException if the computation threw an exception
   * @throws CancellationException if the computation was cancelled
   */
  @CanIgnoreReturnValue
  public static <V> V getUninterruptibly(Future<V> future) throws ExecutionException {
    boolean interrupted = false;
    try {
      while (true) {
        try {
          return future.get();
        } catch (InterruptedException e) {
          interrupted = true;
        }
      }
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }

  /**
   * Invokes {@code future.}{@link Future#get(long, TimeUnit) get(timeout, unit)} uninterruptibly.
   *
   *
Similar methods:
   *
   *

   *
To retrieve a result from a {@code Future} that is already done, use {@link
   *       Futures#getDone Futures.getDone}.
   *
To treat {@link InterruptedException} uniformly with other exceptions, use {@link
   *       Futures#getChecked(Future, Class, long, TimeUnit) Futures.getChecked}.
   *
To get uninterruptibility and remove checked exceptions, use {@link
   *       Futures#getUnchecked}.
   *

   *
   * @throws ExecutionException if the computation threw an exception
   * @throws CancellationException if the computation was cancelled
   * @throws TimeoutException if the wait timed out
   */
  @CanIgnoreReturnValue
  @GwtIncompatible // TODO
  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
  public static <V> V getUninterruptibly(Future<V> future, long timeout, TimeUnit unit)
      throws ExecutionException, TimeoutException {
    boolean interrupted = false;
    try {
      long remainingNanos = unit.toNanos(timeout);
      long end = System.nanoTime() + remainingNanos;

      while (true) {
        try {
          // Future treats negative timeouts just like zero.
          return future.get(remainingNanos, NANOSECONDS);
        } catch (InterruptedException e) {
          interrupted = true;
          remainingNanos = end - System.nanoTime();
        }
      }
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }

  /** Invokes {@code queue.}{@link BlockingQueue#take() take()} uninterruptibly. */
  @GwtIncompatible // concurrency
  public static <E> E takeUninterruptibly(BlockingQueue<E> queue) {
    boolean interrupted = false;
    try {
      while (true) {
        try {
          return queue.take();
        } catch (InterruptedException e) {
          interrupted = true;
        }
      }
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }

  /**
   * Invokes {@code queue.}{@link BlockingQueue#put(Object) put(element)} uninterruptibly.
   *
   * @throws ClassCastException if the class of the specified element prevents it from being added
   *     to the given queue
   * @throws IllegalArgumentException if some property of the specified element prevents it from
   *     being added to the given queue
   */
  @GwtIncompatible // concurrency
  public static <E> void putUninterruptibly(BlockingQueue<E> queue, E element) {
    boolean interrupted = false;
    try {
      while (true) {
        try {
          queue.put(element);
          return;
        } catch (InterruptedException e) {
          interrupted = true;
        }
      }
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }

  // TODO(user): Support Sleeper somehow (wrapper or interface method)?
  /** Invokes {@code unit.}{@link TimeUnit#sleep(long) sleep(sleepFor)} uninterruptibly. */
  @GwtIncompatible // concurrency
  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
  public static void sleepUninterruptibly(long sleepFor, TimeUnit unit) {
    boolean interrupted = false;
    try {
      long remainingNanos = unit.toNanos(sleepFor);
      long end = System.nanoTime() + remainingNanos;
      while (true) {
        try {
          // TimeUnit.sleep() treats negative timeouts just like zero.
          NANOSECONDS.sleep(remainingNanos);
          return;
        } catch (InterruptedException e) {
          interrupted = true;
          remainingNanos = end - System.nanoTime();
        }
      }
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }

  /**
   * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(1,
   * timeout, unit)} uninterruptibly.
   *
   * @since 18.0
   */
  @GwtIncompatible // concurrency
  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
  public static boolean tryAcquireUninterruptibly(
      Semaphore semaphore, long timeout, TimeUnit unit) {
    return tryAcquireUninterruptibly(semaphore, 1, timeout, unit);
  }

  /**
   * Invokes {@code semaphore.}{@link Semaphore#tryAcquire(int, long, TimeUnit) tryAcquire(permits,
   * timeout, unit)} uninterruptibly.
   *
   * @since 18.0
   */
  @GwtIncompatible // concurrency
  @SuppressWarnings("GoodTime") // should accept a java.time.Duration
  public static boolean tryAcquireUninterruptibly(
      Semaphore semaphore, int permits, long timeout, TimeUnit unit) {
    boolean interrupted = false;
    try {
      long remainingNanos = unit.toNanos(timeout);
      long end = System.nanoTime() + remainingNanos;

      while (true) {
        try {
          // Semaphore treats negative timeouts just like zero.
          return semaphore.tryAcquire(permits, remainingNanos, NANOSECONDS);
        } catch (InterruptedException e) {
          interrupted = true;
          remainingNanos = end - System.nanoTime();
        }
      }
    } finally {
      if (interrupted) {
        Thread.currentThread().interrupt();
      }
    }
  }

  // TODO(user): Add support for waitUninterruptibly.

  private Uninterruptibles() {}
}


/*
 * Copyright (C) 2011 The Guava Authors
 *
 * 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 com.google.common.cache;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.common.annotations.GwtIncompatible;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.Splitter;
import com.google.common.cache.LocalCache.Strength;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * A specification of a {@link CacheBuilder} configuration.
 *
 *
{@code CacheBuilderSpec} supports parsing configuration off of a string, which makes it
 * especially useful for command-line configuration of a {@code CacheBuilder}.
 *
 *
The string syntax is a series of comma-separated keys or key-value pairs, each corresponding
 * to a {@code CacheBuilder} method.
 *
 *

 *
{@code concurrencyLevel=[integer]}: sets {@link CacheBuilder#concurrencyLevel}.
 *
{@code initialCapacity=[integer]}: sets {@link CacheBuilder#initialCapacity}.
 *
{@code maximumSize=[long]}: sets {@link CacheBuilder#maximumSize}.
 *
{@code maximumWeight=[long]}: sets {@link CacheBuilder#maximumWeight}.
 *
{@code expireAfterAccess=[duration]}: sets {@link CacheBuilder#expireAfterAccess}.
 *
{@code expireAfterWrite=[duration]}: sets {@link CacheBuilder#expireAfterWrite}.
 *
{@code refreshAfterWrite=[duration]}: sets {@link CacheBuilder#refreshAfterWrite}.
 *
{@code weakKeys}: sets {@link CacheBuilder#weakKeys}.
 *
{@code softValues}: sets {@link CacheBuilder#softValues}.
 *
{@code weakValues}: sets {@link CacheBuilder#weakValues}.
 *
{@code recordStats}: sets {@link CacheBuilder#recordStats}.
 *

 *
 *
The set of supported keys will grow as {@code CacheBuilder} evolves, but existing keys will
 * never be removed.
 *
 *
Durations are represented by an integer, followed by one of "d", "h", "m", or "s",
 * representing days, hours, minutes, or seconds respectively. (There is currently no syntax to
 * request expiration in milliseconds, microseconds, or nanoseconds.)
 *
 *
Whitespace before and after commas and equal signs is ignored. Keys may not be repeated; it is
 * also illegal to use the following pairs of keys in a single value:
 *
 *

 *
{@code maximumSize} and {@code maximumWeight}
 *
{@code softValues} and {@code weakValues}
 *

 *
 *
{@code CacheBuilderSpec} does not support configuring {@code CacheBuilder} methods with
 * non-value parameters. These must be configured in code.
 *
 *
A new {@code CacheBuilder} can be instantiated from a {@code CacheBuilderSpec} using {@link
 * CacheBuilder#from(CacheBuilderSpec)} or {@link CacheBuilder#from(String)}.
 *
 * @author Adam Winer
 * @since 12.0
 */
@SuppressWarnings("GoodTime") // lots of violations (nanosecond math)
@GwtIncompatible
public final class CacheBuilderSpec {
  /** Parses a single value. */
  private interface ValueParser {
    void parse(CacheBuilderSpec spec, String key, @Nullable String value);
  }

  /** Splits each key-value pair. */
  private static final Splitter KEYS_SPLITTER = Splitter.on(',').trimResults();

  /** Splits the key from the value. */
  private static final Splitter KEY_VALUE_SPLITTER = Splitter.on('=').trimResults();

  /** Map of names to ValueParser. */
  private static final ImmutableMap<String, ValueParser> VALUE_PARSERS =
      ImmutableMap.<String, ValueParser>builder()
          .put("initialCapacity", new InitialCapacityParser())
          .put("maximumSize", new MaximumSizeParser())
          .put("maximumWeight", new MaximumWeightParser())
          .put("concurrencyLevel", new ConcurrencyLevelParser())
          .put("weakKeys", new KeyStrengthParser(Strength.WEAK))
          .put("softValues", new ValueStrengthParser(Strength.SOFT))
          .put("weakValues", new ValueStrengthParser(Strength.WEAK))
          .put("recordStats", new RecordStatsParser())
          .put("expireAfterAccess", new AccessDurationParser())
          .put("expireAfterWrite", new WriteDurationParser())
          .put("refreshAfterWrite", new RefreshDurationParser())
          .put("refreshInterval", new RefreshDurationParser())
          .build();

  @MonotonicNonNull @VisibleForTesting Integer initialCapacity;
  @MonotonicNonNull @VisibleForTesting Long maximumSize;
  @MonotonicNonNull @VisibleForTesting Long maximumWeight;
  @MonotonicNonNull @VisibleForTesting Integer concurrencyLevel;
  @MonotonicNonNull @VisibleForTesting Strength keyStrength;
  @MonotonicNonNull @VisibleForTesting Strength valueStrength;
  @MonotonicNonNull @VisibleForTesting Boolean recordStats;
  @VisibleForTesting long writeExpirationDuration;
  @MonotonicNonNull @VisibleForTesting TimeUnit writeExpirationTimeUnit;
  @VisibleForTesting long accessExpirationDuration;
  @MonotonicNonNull @VisibleForTesting TimeUnit accessExpirationTimeUnit;
  @VisibleForTesting long refreshDuration;
  @MonotonicNonNull @VisibleForTesting TimeUnit refreshTimeUnit;
  /** Specification; used for toParseableString(). */
  private final String specification;

  private CacheBuilderSpec(String specification) {
    this.specification = specification;
  }

  /**
   * Creates a CacheBuilderSpec from a string.
   *
   * @param cacheBuilderSpecification the string form
   */
  public static CacheBuilderSpec parse(String cacheBuilderSpecification) {
    CacheBuilderSpec spec = new CacheBuilderSpec(cacheBuilderSpecification);
    if (!cacheBuilderSpecification.isEmpty()) {
      for (String keyValuePair : KEYS_SPLITTER.split(cacheBuilderSpecification)) {
        List<String> keyAndValue = ImmutableList.copyOf(KEY_VALUE_SPLITTER.split(keyValuePair));
        checkArgument(!keyAndValue.isEmpty(), "blank key-value pair");
        checkArgument(
            keyAndValue.size() <= 2,
            "key-value pair %s with more than one equals sign",
            keyValuePair);

        // Find the ValueParser for the current key.
        String key = keyAndValue.get(0);
        ValueParser valueParser = VALUE_PARSERS.get(key);
        checkArgument(valueParser != null, "unknown key %s", key);

        String value = keyAndValue.size() == 1 ? null : keyAndValue.get(1);
        valueParser.parse(spec, key, value);
      }
    }

    return spec;
  }

  /** Returns a CacheBuilderSpec that will prevent caching. */
  public static CacheBuilderSpec disableCaching() {
    // Maximum size of zero is one way to block caching
    return CacheBuilderSpec.parse("maximumSize=0");
  }

  /** Returns a CacheBuilder configured according to this instance's specification. */
  CacheBuilder<Object, Object> toCacheBuilder() {
    CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
    if (initialCapacity != null) {
      builder.initialCapacity(initialCapacity);
    }
    if (maximumSize != null) {
      builder.maximumSize(maximumSize);
    }
    if (maximumWeight != null) {
      builder.maximumWeight(maximumWeight);
    }
    if (concurrencyLevel != null) {
      builder.concurrencyLevel(concurrencyLevel);
    }
    if (keyStrength != null) {
      switch (keyStrength) {
        case WEAK:
          builder.weakKeys();
          break;
        default:
          throw new AssertionError();
      }
    }
    if (valueStrength != null) {
      switch (valueStrength) {
        case SOFT:
          builder.softValues();
          break;
        case WEAK:
          builder.weakValues();
          break;
        default:
          throw new AssertionError();
      }
    }
    if (recordStats != null && recordStats) {
      builder.recordStats();
    }
    if (writeExpirationTimeUnit != null) {
      builder.expireAfterWrite(writeExpirationDuration, writeExpirationTimeUnit);
    }
    if (accessExpirationTimeUnit != null) {
      builder.expireAfterAccess(accessExpirationDuration, accessExpirationTimeUnit);
    }
    if (refreshTimeUnit != null) {
      builder.refreshAfterWrite(refreshDuration, refreshTimeUnit);
    }

    return builder;
  }

  /**
   * Returns a string that can be used to parse an equivalent {@code CacheBuilderSpec}. The order
   * and form of this representation is not guaranteed, except that reparsing its output will
   * produce a {@code CacheBuilderSpec} equal to this instance.
   */
  public String toParsableString() {
    return specification;
  }

  /**
   * Returns a string representation for this CacheBuilderSpec instance. The form of this
   * representation is not guaranteed.
   */
  @Override
  public String toString() {
    return MoreObjects.toStringHelper(this).addValue(toParsableString()).toString();
  }

  @Override
  public int hashCode() {
    return Objects.hashCode(
        initialCapacity,
        maximumSize,
        maximumWeight,
        concurrencyLevel,
        keyStrength,
        valueStrength,
        recordStats,
        durationInNanos(writeExpirationDuration, writeExpirationTimeUnit),
        durationInNanos(accessExpirationDuration, accessExpirationTimeUnit),
        durationInNanos(refreshDuration, refreshTimeUnit));
  }

  @Override
  public boolean equals(@Nullable Object obj) {
    if (this == obj) {
      return true;
    }
    if (!(obj instanceof CacheBuilderSpec)) {
      return false;
    }
    CacheBuilderSpec that = (CacheBuilderSpec) obj;
    return Objects.equal(initialCapacity, that.initialCapacity)
        && Objects.equal(maximumSize, that.maximumSize)
        && Objects.equal(maximumWeight, that.maximumWeight)
        && Objects.equal(concurrencyLevel, that.concurrencyLevel)
        && Objects.equal(keyStrength, that.keyStrength)
        && Objects.equal(valueStrength, that.valueStrength)
        && Objects.equal(recordStats, that.recordStats)
        && Objects.equal(
            durationInNanos(writeExpirationDuration, writeExpirationTimeUnit),
            durationInNanos(that.writeExpirationDuration, that.writeExpirationTimeUnit))
        && Objects.equal(
            durationInNanos(accessExpirationDuration, accessExpirationTimeUnit),
            durationInNanos(that.accessExpirationDuration, that.accessExpirationTimeUnit))
        && Objects.equal(
            durationInNanos(refreshDuration, refreshTimeUnit),
            durationInNanos(that.refreshDuration, that.refreshTimeUnit));
  }

  /**
   * Converts an expiration duration/unit pair into a single Long for hashing and equality. Uses
   * nanos to match CacheBuilder implementation.
   */
  private static @Nullable Long durationInNanos(long duration, @Nullable TimeUnit unit) {
    return (unit == null) ? null : unit.toNanos(duration);
  }

  /** Base class for parsing integers. */
  abstract static class IntegerParser implements ValueParser {
    protected abstract void parseInteger(CacheBuilderSpec spec, int value);

    @Override
    public void parse(CacheBuilderSpec spec, String key, String value) {
      checkArgument(value != null && !value.isEmpty(), "value of key %s omitted", key);
      try {
        parseInteger(spec, Integer.parseInt(value));
      } catch (NumberFormatException e) {
        throw new IllegalArgumentException(
            format("key %s value set to %s, must be integer", key, value), e);
      }
    }
  }

  /** Base class for parsing integers. */
  abstract static class LongParser implements ValueParser {
    protected abstract void parseLong(CacheBuilderSpec spec, long value);

    @Override
    public void parse(CacheBuilderSpec spec, String key, String value) {
      checkArgument(value != null && !value.isEmpty(), "value of key %s omitted", key);
      try {
        parseLong(spec, Long.parseLong(value));
      } catch (NumberFormatException e) {
        throw new IllegalArgumentException(
            format("key %s value set to %s, must be integer", key, value), e);
      }
    }
  }

  /** Parse initialCapacity */
  static class InitialCapacityParser extends IntegerParser {
    @Override
    protected void parseInteger(CacheBuilderSpec spec, int value) {
      checkArgument(
          spec.initialCapacity == null,
          "initial capacity was already set to ",
          spec.initialCapacity);
      spec.initialCapacity = value;
    }
  }

  /** Parse maximumSize */
  static class MaximumSizeParser extends LongParser {
    @Override
    protected void parseLong(CacheBuilderSpec spec, long value) {
      checkArgument(spec.maximumSize == null, "maximum size was already set to ", spec.maximumSize);
      checkArgument(
          spec.maximumWeight == null, "maximum weight was already set to ", spec.maximumWeight);
      spec.maximumSize = value;
    }
  }

  /** Parse maximumWeight */
  static class MaximumWeightParser extends LongParser {
    @Override
    protected void parseLong(CacheBuilderSpec spec, long value) {
      checkArgument(
          spec.maximumWeight == null, "maximum weight was already set to ", spec.maximumWeight);
      checkArgument(spec.maximumSize == null, "maximum size was already set to ", spec.maximumSize);
      spec.maximumWeight = value;
    }
  }

  /** Parse concurrencyLevel */
  static class ConcurrencyLevelParser extends IntegerParser {
    @Override
    protected void parseInteger(CacheBuilderSpec spec, int value) {
      checkArgument(
          spec.concurrencyLevel == null,
          "concurrency level was already set to ",
          spec.concurrencyLevel);
      spec.concurrencyLevel = value;
    }
  }

  /** Parse weakKeys */
  static class KeyStrengthParser implements ValueParser {
    private final Strength strength;

    public KeyStrengthParser(Strength strength) {
      this.strength = strength;
    }

    @Override
    public void parse(CacheBuilderSpec spec, String key, @Nullable String value) {
      checkArgument(value == null, "key %s does not take values", key);
      checkArgument(spec.keyStrength == null, "%s was already set to %s", key, spec.keyStrength);
      spec.keyStrength = strength;
    }
  }

  /** Parse weakValues and softValues */
  static class ValueStrengthParser implements ValueParser {
    private final Strength strength;

    public ValueStrengthParser(Strength strength) {
      this.strength = strength;
    }

    @Override
    public void parse(CacheBuilderSpec spec, String key, @Nullable String value) {
      checkArgument(value == null, "key %s does not take values", key);
      checkArgument(
          spec.valueStrength == null, "%s was already set to %s", key, spec.valueStrength);

      spec.valueStrength = strength;
    }
  }

  /** Parse recordStats */
  static class RecordStatsParser implements ValueParser {

    @Override
    public void parse(CacheBuilderSpec spec, String key, @Nullable String value) {
      checkArgument(value == null, "recordStats does not take values");
      checkArgument(spec.recordStats == null, "recordStats already set");
      spec.recordStats = true;
    }
  }

  /** Base class for parsing times with durations */
  abstract static class DurationParser implements ValueParser {
    protected abstract void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit);

    @Override
    public void parse(CacheBuilderSpec spec, String key, String value) {
      checkArgument(value != null && !value.isEmpty(), "value of key %s omitted", key);
      try {
        char lastChar = value.charAt(value.length() - 1);
        TimeUnit timeUnit;
        switch (lastChar) {
          case 'd':
            timeUnit = TimeUnit.DAYS;
            break;
          case 'h':
            timeUnit = TimeUnit.HOURS;
            break;
          case 'm':
            timeUnit = TimeUnit.MINUTES;
            break;
          case 's':
            timeUnit = TimeUnit.SECONDS;
            break;
          default:
            throw new IllegalArgumentException(
                format(
                    "key %s invalid format.  was %s, must end with one of [dDhHmMsS]", key, value));
        }

        long duration = Long.parseLong(value.substring(0, value.length() - 1));
        parseDuration(spec, duration, timeUnit);
      } catch (NumberFormatException e) {
        throw new IllegalArgumentException(
            format("key %s value set to %s, must be integer", key, value));
      }
    }
  }

  /** Parse expireAfterAccess */
  static class AccessDurationParser extends DurationParser {
    @Override
    protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) {
      checkArgument(spec.accessExpirationTimeUnit == null, "expireAfterAccess already set");
      spec.accessExpirationDuration = duration;
      spec.accessExpirationTimeUnit = unit;
    }
  }

  /** Parse expireAfterWrite */
  static class WriteDurationParser extends DurationParser {
    @Override
    protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) {
      checkArgument(spec.writeExpirationTimeUnit == null, "expireAfterWrite already set");
      spec.writeExpirationDuration = duration;
      spec.writeExpirationTimeUnit = unit;
    }
  }

  /** Parse refreshAfterWrite */
  static class RefreshDurationParser extends DurationParser {
    @Override
    protected void parseDuration(CacheBuilderSpec spec, long duration, TimeUnit unit) {
      checkArgument(spec.refreshTimeUnit == null, "refreshAfterWrite already set");
      spec.refreshDuration = duration;
      spec.refreshTimeUnit = unit;
    }
  }

  private static String format(String format, Object... args) {
    return String.format(Locale.ROOT, format, args);
  }
}


/*
 * Copyright (C) 2011 The Guava Authors
 *
 * 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 com.google.common.primitives;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkPositionIndexes;

import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Comparator;

/**
 * Static utility methods pertaining to {@code long} primitives that interpret values as
 * unsigned (that is, any negative value {@code x} is treated as the positive value {@code
 * 2^64 + x}). The methods for which signedness is not an issue are in {@link Longs}, as well as
 * signed versions of methods for which signedness is an issue.
 *
 *
In addition, this class provides several static methods for converting a {@code long} to a
 * {@code String} and a {@code String} to a {@code long} that treat the {@code long} as an unsigned
 * number.
 *
 *
Users of these utilities must be extremely careful not to mix up signed and unsigned
 * {@code long} values. When possible, it is recommended that the {@link UnsignedLong} wrapper class
 * be used, at a small efficiency penalty, to enforce the distinction in the type system.
 *
 *
See the Guava User Guide article on unsigned
 * primitive utilities.
 *
 * @author Louis Wasserman
 * @author Brian Milch
 * @author Colin Evans
 * @since 10.0
 */
@Beta
@GwtCompatible
public final class UnsignedLongs {
  private UnsignedLongs() {}

  public static final long MAX_VALUE = -1L; // Equivalent to 2^64 - 1

  /**
   * A (self-inverse) bijection which converts the ordering on unsigned longs to the ordering on
   * longs, that is, {@code a <= b} as unsigned longs if and only if {@code flip(a) <= flip(b)} as
   * signed longs.
   */
  private static long flip(long a) {
    return a ^ Long.MIN_VALUE;
  }

  /**
   * Compares the two specified {@code long} values, treating them as unsigned values between {@code
   * 0} and {@code 2^64 - 1} inclusive.
   *
   *
Java 8 users: use {@link Long#compareUnsigned(long, long)} instead.
   *
   * @param a the first unsigned {@code long} to compare
   * @param b the second unsigned {@code long} to compare
   * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is
   *     greater than {@code b}; or zero if they are equal
   */
  public static int compare(long a, long b) {
    return Longs.compare(flip(a), flip(b));
  }

  /**
   * Returns the least value present in {@code array}, treating values as unsigned.
   *
   * @param array a nonempty array of unsigned {@code long} values
   * @return the value present in {@code array} that is less than or equal to every other value in
   *     the array according to {@link #compare}
   * @throws IllegalArgumentException if {@code array} is empty
   */
  public static long min(long... array) {
    checkArgument(array.length > 0);
    long min = flip(array[0]);
    for (int i = 1; i < array.length; i++) {
      long next = flip(array[i]);
      if (next < min) {
        min = next;
      }
    }
    return flip(min);
  }

  /**
   * Returns the greatest value present in {@code array}, treating values as unsigned.
   *
   * @param array a nonempty array of unsigned {@code long} values
   * @return the value present in {@code array} that is greater than or equal to every other value
   *     in the array according to {@link #compare}
   * @throws IllegalArgumentException if {@code array} is empty
   */
  public static long max(long... array) {
    checkArgument(array.length > 0);
    long max = flip(array[0]);
    for (int i = 1; i < array.length; i++) {
      long next = flip(array[i]);
      if (next > max) {
        max = next;
      }
    }
    return flip(max);
  }

  /**
   * Returns a string containing the supplied unsigned {@code long} values separated by {@code
   * separator}. For example, {@code join("-", 1, 2, 3)} returns the string {@code "1-2-3"}.
   *
   * @param separator the text that should appear between consecutive values in the resulting string
   *     (but not at the start or end)
   * @param array an array of unsigned {@code long} values, possibly empty
   */
  public static String join(String separator, long... array) {
    checkNotNull(separator);
    if (array.length == 0) {
      return "";
    }

    // For pre-sizing a builder, just get the right order of magnitude
    StringBuilder builder = new StringBuilder(array.length * 5);
    builder.append(toString(array[0]));
    for (int i = 1; i < array.length; i++) {
      builder.append(separator).append(toString(array[i]));
    }
    return builder.toString();
  }

  /**
   * Returns a comparator that compares two arrays of unsigned {@code long} values lexicographically. That is, it
   * compares, using {@link #compare(long, long)}), the first pair of values that follow any common
   * prefix, or when one array is a prefix of the other, treats the shorter array as the lesser. For
   * example, {@code [] < [1L] < [1L, 2L] < [2L] < [1L << 63]}.
   *
   *
The returned comparator is inconsistent with {@link Object#equals(Object)} (since arrays
   * support only identity equality), but it is consistent with {@link Arrays#equals(long[],
   * long[])}.
   */
  public static Comparator<long[]> lexicographicalComparator() {
    return LexicographicalComparator.INSTANCE;
  }

  enum LexicographicalComparator implements Comparator<long[]> {
    INSTANCE;

    @Override
    public int compare(long[] left, long[] right) {
      int minLength = Math.min(left.length, right.length);
      for (int i = 0; i < minLength; i++) {
        if (left[i] != right[i]) {
          return UnsignedLongs.compare(left[i], right[i]);
        }
      }
      return left.length - right.length;
    }

    @Override
    public String toString() {
      return "UnsignedLongs.lexicographicalComparator()";
    }
  }

  /**
   * Sorts the array, treating its elements as unsigned 64-bit integers.
   *
   * @since 23.1
   */
  public static void sort(long[] array) {
    checkNotNull(array);
    sort(array, 0, array.length);
  }

  /**
   * Sorts the array between {@code fromIndex} inclusive and {@code toIndex} exclusive, treating its
   * elements as unsigned 64-bit integers.
   *
   * @since 23.1
   */
  public static void sort(long[] array, int fromIndex, int toIndex) {
    checkNotNull(array);
    checkPositionIndexes(fromIndex, toIndex, array.length);
    for (int i = fromIndex; i < toIndex; i++) {
      array[i] = flip(array[i]);
    }
    Arrays.sort(array, fromIndex, toIndex);
    for (int i = fromIndex; i < toIndex; i++) {
      array[i] = flip(array[i]);
    }
  }

  /**
   * Sorts the elements of {@code array} in descending order, interpreting them as unsigned 64-bit
   * integers.
   *
   * @since 23.1
   */
  public static void sortDescending(long[] array) {
    checkNotNull(array);
    sortDescending(array, 0, array.length);
  }

  /**
   * Sorts the elements of {@code array} between {@code fromIndex} inclusive and {@code toIndex}
   * exclusive in descending order, interpreting them as unsigned 64-bit integers.
   *
   * @since 23.1
   */
  public static void sortDescending(long[] array, int fromIndex, int toIndex) {
    checkNotNull(array);
    checkPositionIndexes(fromIndex, toIndex, array.length);
    for (int i = fromIndex; i < toIndex; i++) {
      array[i] ^= Long.MAX_VALUE;
    }
    Arrays.sort(array, fromIndex, toIndex);
    for (int i = fromIndex; i < toIndex; i++) {
      array[i] ^= Long.MAX_VALUE;
    }
  }

  /**
   * Returns dividend / divisor, where the dividend and divisor are treated as unsigned 64-bit
   * quantities.
   *
   *
Java 8 users: use {@link Long#divideUnsigned(long, long)} instead.
   *
   * @param dividend the dividend (numerator)
   * @param divisor the divisor (denominator)
   * @throws ArithmeticException if divisor is 0
   */
  public static long divide(long dividend, long divisor) {
    if (divisor < 0) { // i.e., divisor >= 2^63:
      if (compare(dividend, divisor) < 0) {
        return 0; // dividend < divisor
      } else {
        return 1; // dividend >= divisor
      }
    }

    // Optimization - use signed division if dividend < 2^63
    if (dividend >= 0) {
      return dividend / divisor;
    }

    /*
     * Otherwise, approximate the quotient, check, and correct if necessary. Our approximation is
     * guaranteed to be either exact or one less than the correct value. This follows from fact that
     * floor(floor(x)/i) == floor(x/i) for any real x and integer i != 0. The proof is not quite
     * trivial.
     */
    long quotient = ((dividend >>> 1) / divisor) << 1;
    long rem = dividend - quotient * divisor;
    return quotient + (compare(rem, divisor) >= 0 ? 1 : 0);
  }

  /**
   * Returns dividend % divisor, where the dividend and divisor are treated as unsigned 64-bit
   * quantities.
   *
   *
Java 8 users: use {@link Long#remainderUnsigned(long, long)} instead.
   *
   * @param dividend the dividend (numerator)
   * @param divisor the divisor (denominator)
   * @throws ArithmeticException if divisor is 0
   * @since 11.0
   */
  public static long remainder(long dividend, long divisor) {
    if (divisor < 0) { // i.e., divisor >= 2^63:
      if (compare(dividend, divisor) < 0) {
        return dividend; // dividend < divisor
      } else {
        return dividend - divisor; // dividend >= divisor
      }
    }

    // Optimization - use signed modulus if dividend < 2^63
    if (dividend >= 0) {
      return dividend % divisor;
    }

    /*
     * Otherwise, approximate the quotient, check, and correct if necessary. Our approximation is
     * guaranteed to be either exact or one less than the correct value. This follows from the fact
     * that floor(floor(x)/i) == floor(x/i) for any real x and integer i != 0. The proof is not
     * quite trivial.
     */
    long quotient = ((dividend >>> 1) / divisor) << 1;
    long rem = dividend - quotient * divisor;
    return rem - (compare(rem, divisor) >= 0 ? divisor : 0);
  }

  /**
   * Returns the unsigned {@code long} value represented by the given decimal string.
   *
   *
Java 8 users: use {@link Long#parseUnsignedLong(String)} instead.
   *
   * @throws NumberFormatException if the string does not contain a valid unsigned {@code long}
   *     value
   * @throws NullPointerException if {@code string} is null (in contrast to {@link
   *     Long#parseLong(String)})
   */
  @CanIgnoreReturnValue
  public static long parseUnsignedLong(String string) {
    return parseUnsignedLong(string, 10);
  }

  /**
   * Returns the unsigned {@code long} value represented by a string with the given radix.
   *
   *
Java 8 users: use {@link Long#parseUnsignedLong(String, int)} instead.
   *
   * @param string the string containing the unsigned {@code long} representation to be parsed.
   * @param radix the radix to use while parsing {@code string}
   * @throws NumberFormatException if the string does not contain a valid unsigned {@code long} with
   *     the given radix, or if {@code radix} is not between {@link Character#MIN_RADIX} and {@link
   *     Character#MAX_RADIX}.
   * @throws NullPointerException if {@code string} is null (in contrast to {@link
   *     Long#parseLong(String)})
   */
  @CanIgnoreReturnValue
  public static long parseUnsignedLong(String string, int radix) {
    checkNotNull(string);
    if (string.length() == 0) {
      throw new NumberFormatException("empty string");
    }
    if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
      throw new NumberFormatException("illegal radix: " + radix);
    }

    int maxSafePos = ParseOverflowDetection.maxSafeDigits[radix] - 1;
    long value = 0;
    for (int pos = 0; pos < string.length(); pos++) {
      int digit = Character.digit(string.charAt(pos), radix);
      if (digit == -1) {
        throw new NumberFormatException(string);
      }
      if (pos > maxSafePos && ParseOverflowDetection.overflowInParse(value, digit, radix)) {
        throw new NumberFormatException("Too large for unsigned long: " + string);
      }
      value = (value * radix) + digit;
    }

    return value;
  }

  /**
   * Returns the unsigned {@code long} value represented by the given string.
   *
   *
Accepts a decimal, hexadecimal, or octal number given by specifying the following prefix:
   *
   *

   *
{@code 0x}HexDigits
   *
{@code 0X}HexDigits
   *
{@code #}HexDigits
   *
{@code 0}OctalDigits
   *

   *
   * @throws NumberFormatException if the string does not contain a valid unsigned {@code long}
   *     value
   * @since 13.0
   */
  @CanIgnoreReturnValue
  public static long decode(String stringValue) {
    ParseRequest request = ParseRequest.fromString(stringValue);

    try {
      return parseUnsignedLong(request.rawValue, request.radix);
    } catch (NumberFormatException e) {
      NumberFormatException decodeException =
          new NumberFormatException("Error parsing value: " + stringValue);
      decodeException.initCause(e);
      throw decodeException;
    }
  }

  /*
   * We move the static constants into this class so ProGuard can inline UnsignedLongs entirely
   * unless the user is actually calling a parse method.
   */
  private static final class ParseOverflowDetection {
    private ParseOverflowDetection() {}

    // calculated as 0xffffffffffffffff / radix
    static final long[] maxValueDivs = new long[Character.MAX_RADIX + 1];
    static final int[] maxValueMods = new int[Character.MAX_RADIX + 1];
    static final int[] maxSafeDigits = new int[Character.MAX_RADIX + 1];

    static {
      BigInteger overflow = new BigInteger("10000000000000000", 16);
      for (int i = Character.MIN_RADIX; i <= Character.MAX_RADIX; i++) {
        maxValueDivs[i] = divide(MAX_VALUE, i);
        maxValueMods[i] = (int) remainder(MAX_VALUE, i);
        maxSafeDigits[i] = overflow.toString(i).length() - 1;
      }
    }

    /**
     * Returns true if (current * radix) + digit is a number too large to be represented by an
     * unsigned long. This is useful for detecting overflow while parsing a string representation of
     * a number. Does not verify whether supplied radix is valid, passing an invalid radix will give
     * undefined results or an ArrayIndexOutOfBoundsException.
     */
    static boolean overflowInParse(long current, int digit, int radix) {
      if (current >= 0) {
        if (current < maxValueDivs[radix]) {
          return false;
        }
        if (current > maxValueDivs[radix]) {
          return true;
        }
        // current == maxValueDivs[radix]
        return (digit > maxValueMods[radix]);
      }

      // current < 0: high bit is set
      return true;
    }
  }

  /**
   * Returns a string representation of x, where x is treated as unsigned.
   *
   *
Java 8 users: use {@link Long#toUnsignedString(long)} instead.
   */
  public static String toString(long x) {
    return toString(x, 10);
  }

  /**
   * Returns a string representation of {@code x} for the given radix, where {@code x} is treated as
   * unsigned.
   *
   *
Java 8 users: use {@link Long#toUnsignedString(long, int)} instead.
   *
   * @param x the value to convert to a string.
   * @param radix the radix to use while working with {@code x}
   * @throws IllegalArgumentException if {@code radix} is not between {@link Character#MIN_RADIX}
   *     and {@link Character#MAX_RADIX}.
   */
  public static String toString(long x, int radix) {
    checkArgument(
        radix >= Character.MIN_RADIX && radix <= Character.MAX_RADIX,
        "radix (%s) must be between Character.MIN_RADIX and Character.MAX_RADIX",
        radix);
    if (x == 0) {
      // Simply return "0"
      return "0";
    } else if (x > 0) {
      return Long.toString(x, radix);
    } else {
      char[] buf = new char[64];
      int i = buf.length;
      if ((radix & (radix - 1)) == 0) {
        // Radix is a power of two so we can avoid division.
        int shift = Integer.numberOfTrailingZeros(radix);
        int mask = radix - 1;
        do {
          buf[--i] = Character.forDigit(((int) x) & mask, radix);
          x >>>= shift;
        } while (x != 0);
      } else {
        // Separate off the last digit using unsigned division. That will leave
        // a number that is nonnegative as a signed integer.
        long quotient;
        if ((radix & 1) == 0) {
          // Fast path for the usual case where the radix is even.
          quotient = (x >>> 1) / (radix >>> 1);
        } else {
          quotient = divide(x, radix);
        }
        long rem = x - quotient * radix;
        buf[--i] = Character.forDigit((int) rem, radix);
        x = quotient;
        // Simple modulo/division approach
        while (x > 0) {
          buf[--i] = Character.forDigit((int) (x % radix), radix);
          x /= radix;
        }
      }
      // Generate string
      return new String(buf, i, buf.length - i);
    }
  }
}


/*
 * Copyright (C) 2008 The Guava Authors
 *
 * 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 com.google.common.collect;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkPositionIndex;
import static com.google.common.collect.CollectPreconditions.checkEntryNotNull;
import static com.google.common.collect.ImmutableMapEntry.createEntryArray;

import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMapEntry.NonTerminalImmutableMapEntry;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.io.Serializable;
import java.util.function.BiConsumer;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * Implementation of {@link ImmutableMap} with two or more entries.
 *
 * @author Jesse Wilson
 * @author Kevin Bourrillion
 * @author Gregory Kick
 */
@GwtCompatible(serializable = true, emulated = true)
final class RegularImmutableMap<K, V> extends ImmutableMap<K, V> {
  @SuppressWarnings("unchecked")
  static final ImmutableMap<Object, Object> EMPTY =
      new RegularImmutableMap<>((Entry<Object, Object>[]) ImmutableMap.EMPTY_ENTRY_ARRAY, null, 0);

  /**
   * Closed addressing tends to perform well even with high load factors. Being conservative here
   * ensures that the table is still likely to be relatively sparse (hence it misses fast) while
   * saving space.
   */
  @VisibleForTesting static final double MAX_LOAD_FACTOR = 1.2;

  /**
   * Maximum allowed false positive probability of detecting a hash flooding attack given random
   * input.
   */
  @VisibleForTesting static final double HASH_FLOODING_FPP = 0.001;

  /**
   * Maximum allowed length of a hash table bucket before falling back to a j.u.HashMap based
   * implementation. Experimentally determined.
   */
  @VisibleForTesting static final int MAX_HASH_BUCKET_LENGTH = 8;

  // entries in insertion order
  @VisibleForTesting final transient Entry<K, V>[] entries;
  // array of linked lists of entries
  private final transient ImmutableMapEntry<K, V>[] table;
  // 'and' with an int to get a table index
  private final transient int mask;

  static <K, V> ImmutableMap<K, V> fromEntries(Entry<K, V>... entries) {
    return fromEntryArray(entries.length, entries);
  }

  /**
   * Creates an ImmutableMap from the first n entries in entryArray. This implementation may replace
   * the entries in entryArray with its own entry objects (though they will have the same key/value
   * contents), and may take ownership of entryArray.
   */
  static <K, V> ImmutableMap<K, V> fromEntryArray(int n, Entry<K, V>[] entryArray) {
    checkPositionIndex(n, entryArray.length);
    if (n == 0) {
      return (RegularImmutableMap<K, V>) EMPTY;
    }
    Entry<K, V>[] entries;
    if (n == entryArray.length) {
      entries = entryArray;
    } else {
      entries = createEntryArray(n);
    }
    int tableSize = Hashing.closedTableSize(n, MAX_LOAD_FACTOR);
    ImmutableMapEntry<K, V>[] table = createEntryArray(tableSize);
    int mask = tableSize - 1;
    for (int entryIndex = 0; entryIndex < n; entryIndex++) {
      Entry<K, V> entry = entryArray[entryIndex];
      K key = entry.getKey();
      V value = entry.getValue();
      checkEntryNotNull(key, value);
      int tableIndex = Hashing.smear(key.hashCode()) & mask;
      @Nullable ImmutableMapEntry<K, V> existing = table[tableIndex];
      // prepend, not append, so the entries can be immutable
      ImmutableMapEntry<K, V> newEntry =
          (existing == null)
              ? makeImmutable(entry, key, value)
              : new NonTerminalImmutableMapEntry<K, V>(key, value, existing);
      table[tableIndex] = newEntry;
      entries[entryIndex] = newEntry;
      int bucketSize = checkNoConflictInKeyBucket(key, newEntry, existing);
      if (bucketSize > MAX_HASH_BUCKET_LENGTH) {
        // probable hash flooding attack, fall back to j.u.HM based implementation and use its
        // implementation of hash flooding protection
        return JdkBackedImmutableMap.create(n, entryArray);
      }
    }
    return new RegularImmutableMap<>(entries, table, mask);
  }

  /** Makes an entry usable internally by a new ImmutableMap without rereading its contents. */
  static <K, V> ImmutableMapEntry<K, V> makeImmutable(Entry<K, V> entry, K key, V value) {
    boolean reusable =
        entry instanceof ImmutableMapEntry && ((ImmutableMapEntry<K, V>) entry).isReusable();
    return reusable ? (ImmutableMapEntry<K, V>) entry : new ImmutableMapEntry<K, V>(key, value);
  }

  /** Makes an entry usable internally by a new ImmutableMap. */
  static <K, V> ImmutableMapEntry<K, V> makeImmutable(Entry<K, V> entry) {
    return makeImmutable(entry, entry.getKey(), entry.getValue());
  }

  private RegularImmutableMap(Entry<K, V>[] entries, ImmutableMapEntry<K, V>[] table, int mask) {
    this.entries = entries;
    this.table = table;
    this.mask = mask;
  }

  /**
   * @return number of entries in this bucket
   * @throws IllegalArgumentException if another entry in the bucket has the same key
   */
  @CanIgnoreReturnValue
  static int checkNoConflictInKeyBucket(
      Object key, Entry<?, ?> entry, @Nullable ImmutableMapEntry<?, ?> keyBucketHead) {
    int bucketSize = 0;
    for (; keyBucketHead != null; keyBucketHead = keyBucketHead.getNextInKeyBucket()) {
      checkNoConflict(!key.equals(keyBucketHead.getKey()), "key", entry, keyBucketHead);
      bucketSize++;
    }
    return bucketSize;
  }

  @Override
  public V get(@Nullable Object key) {
    return get(key, table, mask);
  }

  static <V> @Nullable V get(
      @Nullable Object key, ImmutableMapEntry<?, V> @Nullable [] keyTable, int mask) {
    if (key == null || keyTable == null) {
      return null;
    }
    int index = Hashing.smear(key.hashCode()) & mask;
    for (ImmutableMapEntry<?, V> entry = keyTable[index];
        entry != null;
        entry = entry.getNextInKeyBucket()) {
      Object candidateKey = entry.getKey();

      /*
       * Assume that equals uses the == optimization when appropriate, and that
       * it would check hash codes as an optimization when appropriate. If we
       * did these things, it would just make things worse for the most
       * performance-conscious users.
       */
      if (key.equals(candidateKey)) {
        return entry.getValue();
      }
    }
    return null;
  }

  @Override
  public void forEach(BiConsumer<? super K, ? super V> action) {
    checkNotNull(action);
    for (Entry<K, V> entry : entries) {
      action.accept(entry.getKey(), entry.getValue());
    }
  }

  @Override
  public int size() {
    return entries.length;
  }

  @Override
  boolean isPartialView() {
    return false;
  }

  @Override
  ImmutableSet<Entry<K, V>> createEntrySet() {
    return new ImmutableMapEntrySet.RegularEntrySet<>(this, entries);
  }

  @Override
  ImmutableSet<K> createKeySet() {
    return new KeySet<>(this);
  }

  @GwtCompatible(emulated = true)
  private static final class KeySet<K, V> extends IndexedImmutableSet<K> {
    private final RegularImmutableMap<K, V> map;

    KeySet(RegularImmutableMap<K, V> map) {
      this.map = map;
    }

    @Override
    K get(int index) {
      return map.entries[index].getKey();
    }

    @Override
    public boolean contains(Object object) {
      return map.containsKey(object);
    }

    @Override
    boolean isPartialView() {
      return true;
    }

    @Override
    public int size() {
      return map.size();
    }

    @GwtIncompatible // serialization
    @Override
    Object writeReplace() {
      return new SerializedForm<K>(map);
    }

    @GwtIncompatible // serialization
    private static class SerializedForm<K> implements Serializable {
      final ImmutableMap<K, ?> map;

      SerializedForm(ImmutableMap<K, ?> map) {
        this.map = map;
      }

      Object readResolve() {
        return map.keySet();
      }

      private static final long serialVersionUID = 0;
    }
  }

  @Override
  ImmutableCollection<V> createValues() {
    return new Values<>(this);
  }

  @GwtCompatible(emulated = true)
  private static final class Values<K, V> extends ImmutableList<V> {
    final RegularImmutableMap<K, V> map;

    Values(RegularImmutableMap<K, V> map) {
      this.map = map;
    }

    @Override
    public V get(int index) {
      return map.entries[index].getValue();
    }

    @Override
    public int size() {
      return map.size();
    }

    @Override
    boolean isPartialView() {
      return true;
    }

    @GwtIncompatible // serialization
    @Override
    Object writeReplace() {
      return new SerializedForm<V>(map);
    }

    @GwtIncompatible // serialization
    private static class SerializedForm<V> implements Serializable {
      final ImmutableMap<?, V> map;

      SerializedForm(ImmutableMap<?, V> map) {
        this.map = map;
      }

      Object readResolve() {
        return map.values();
      }

      private static final long serialVersionUID = 0;
    }
  }

  // This class is never actually serialized directly, but we have to make the
  // warning go away (and suppressing would suppress for all nested classes too)
  private static final long serialVersionUID = 0;
}


/*
 * Copyright (C) 2008 The Guava Authors
 *
 * 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 com.google.common.collect;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.annotations.VisibleForTesting;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.concurrent.LazyInit;
import com.google.j2objc.annotations.WeakOuter;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.stream.Collector;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * A {@link Multiset} whose contents will never change, with many other important properties
 * detailed at {@link ImmutableCollection}.
 *
 *
Grouped iteration. In all current implementations, duplicate elements always appear
 * consecutively when iterating. Elements iterate in order by the first appearance of that
 * element when the multiset was created.
 *
 *
See the Guava User Guide article on  immutable collections.
 *
 * @author Jared Levy
 * @author Louis Wasserman
 * @since 2.0
 */
@GwtCompatible(serializable = true, emulated = true)
@SuppressWarnings("serial") // we're overriding default serialization
public abstract class ImmutableMultiset<E> extends ImmutableMultisetGwtSerializationDependencies<E>
    implements Multiset<E> {

  /**
   * Returns a {@code Collector} that accumulates the input elements into a new {@code
   * ImmutableMultiset}. Elements iterate in order by the first appearance of that element in
   * encounter order.
   *
   * @since 21.0
   */
  public static <E> Collector<E, ?, ImmutableMultiset<E>> toImmutableMultiset() {
    return toImmutableMultiset(Function.identity(), e -> 1);
  }

  /**
   * Returns a {@code Collector} that accumulates elements into an {@code ImmutableMultiset} whose
   * elements are the result of applying {@code elementFunction} to the inputs, with counts equal to
   * the result of applying {@code countFunction} to the inputs.
   *
   *
If the mapped elements contain duplicates (according to {@link Object#equals}), the first
   * occurrence in encounter order appears in the resulting multiset, with count equal to the sum of
   * the outputs of {@code countFunction.applyAsInt(t)} for each {@code t} mapped to that element.
   *
   * @since 22.0
   */
  public static <T, E> Collector<T, ?, ImmutableMultiset<E>> toImmutableMultiset(
      Function<? super T, ? extends E> elementFunction, ToIntFunction<? super T> countFunction) {
    checkNotNull(elementFunction);
    checkNotNull(countFunction);
    return Collector.of(
        LinkedHashMultiset::create,
        (multiset, t) ->
            multiset.add(checkNotNull(elementFunction.apply(t)), countFunction.applyAsInt(t)),
        (multiset1, multiset2) -> {
          multiset1.addAll(multiset2);
          return multiset1;
        },
        (Multiset<E> multiset) -> copyFromEntries(multiset.entrySet()));
  }

  /** Returns the empty immutable multiset. */
  @SuppressWarnings("unchecked") // all supported methods are covariant
  public static <E> ImmutableMultiset<E> of() {
    return (ImmutableMultiset<E>) RegularImmutableMultiset.EMPTY;
  }

  /**
   * Returns an immutable multiset containing a single element.
   *
   * @throws NullPointerException if {@code element} is null
   * @since 6.0 (source-compatible since 2.0)
   */
  @SuppressWarnings("unchecked") // generic array created but never written
  public static <E> ImmutableMultiset<E> of(E element) {
    return copyFromElements(element);
  }

  /**
   * Returns an immutable multiset containing the given elements, in order.
   *
   * @throws NullPointerException if any element is null
   * @since 6.0 (source-compatible since 2.0)
   */
  @SuppressWarnings("unchecked") //
  public static <E> ImmutableMultiset<E> of(E e1, E e2) {
    return copyFromElements(e1, e2);
  }

  /**
   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
   * described in the class documentation.
   *
   * @throws NullPointerException if any element is null
   * @since 6.0 (source-compatible since 2.0)
   */
  @SuppressWarnings("unchecked") //
  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3) {
    return copyFromElements(e1, e2, e3);
  }

  /**
   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
   * described in the class documentation.
   *
   * @throws NullPointerException if any element is null
   * @since 6.0 (source-compatible since 2.0)
   */
  @SuppressWarnings("unchecked") //
  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4) {
    return copyFromElements(e1, e2, e3, e4);
  }

  /**
   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
   * described in the class documentation.
   *
   * @throws NullPointerException if any element is null
   * @since 6.0 (source-compatible since 2.0)
   */
  @SuppressWarnings("unchecked") //
  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4, E e5) {
    return copyFromElements(e1, e2, e3, e4, e5);
  }

  /**
   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
   * described in the class documentation.
   *
   * @throws NullPointerException if any element is null
   * @since 6.0 (source-compatible since 2.0)
   */
  @SuppressWarnings("unchecked") //
  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E... others) {
    return new Builder<E>().add(e1).add(e2).add(e3).add(e4).add(e5).add(e6).add(others).build();
  }

  /**
   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
   * described in the class documentation.
   *
   * @throws NullPointerException if any of {@code elements} is null
   * @since 6.0
   */
  public static <E> ImmutableMultiset<E> copyOf(E[] elements) {
    return copyFromElements(elements);
  }

  /**
   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
   * described in the class documentation.
   *
   * @throws NullPointerException if any of {@code elements} is null
   */
  public static <E> ImmutableMultiset<E> copyOf(Iterable<? extends E> elements) {
    if (elements instanceof ImmutableMultiset) {
      @SuppressWarnings("unchecked") // all supported methods are covariant
      ImmutableMultiset<E> result = (ImmutableMultiset<E>) elements;
      if (!result.isPartialView()) {
        return result;
      }
    }

    Multiset<? extends E> multiset =
        (elements instanceof Multiset)
            ? Multisets.cast(elements)
            : LinkedHashMultiset.create(elements);

    return copyFromEntries(multiset.entrySet());
  }

  /**
   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
   * described in the class documentation.
   *
   * @throws NullPointerException if any of {@code elements} is null
   */
  public static <E> ImmutableMultiset<E> copyOf(Iterator<? extends E> elements) {
    Multiset<E> multiset = LinkedHashMultiset.create();
    Iterators.addAll(multiset, elements);
    return copyFromEntries(multiset.entrySet());
  }

  private static <E> ImmutableMultiset<E> copyFromElements(E... elements) {
    Multiset<E> multiset = LinkedHashMultiset.create();
    Collections.addAll(multiset, elements);
    return copyFromEntries(multiset.entrySet());
  }

  static <E> ImmutableMultiset<E> copyFromEntries(
      Collection<? extends Entry<? extends E>> entries) {
    if (entries.isEmpty()) {
      return of();
    } else {
      return RegularImmutableMultiset.create(entries);
    }
  }

  ImmutableMultiset() {}

  @Override
  public UnmodifiableIterator<E> iterator() {
    final Iterator<Entry<E>> entryIterator = entrySet().iterator();
    return new UnmodifiableIterator<E>() {
      int remaining;
      @MonotonicNonNull E element;

      @Override
      public boolean hasNext() {
        return (remaining > 0) || entryIterator.hasNext();
      }

      @Override
      public E next() {
        if (remaining <= 0) {
          Entry<E> entry = entryIterator.next();
          element = entry.getElement();
          remaining = entry.getCount();
        }
        remaining--;
        return element;
      }
    };
  }

  @LazyInit private transient ImmutableList<E> asList;

  @Override
  public ImmutableList<E> asList() {
    ImmutableList<E> result = asList;
    return (result == null) ? asList = super.asList() : result;
  }

  @Override
  public boolean contains(@Nullable Object object) {
    return count(object) > 0;
  }

  /**
   * Guaranteed to throw an exception and leave the collection unmodified.
   *
   * @throws UnsupportedOperationException always
   * @deprecated Unsupported operation.
   */
  @CanIgnoreReturnValue
  @Deprecated
  @Override
  public final int add(E element, int occurrences) {
    throw new UnsupportedOperationException();
  }

  /**
   * Guaranteed to throw an exception and leave the collection unmodified.
   *
   * @throws UnsupportedOperationException always
   * @deprecated Unsupported operation.
   */
  @CanIgnoreReturnValue
  @Deprecated
  @Override
  public final int remove(Object element, int occurrences) {
    throw new UnsupportedOperationException();
  }

  /**
   * Guaranteed to throw an exception and leave the collection unmodified.
   *
   * @throws UnsupportedOperationException always
   * @deprecated Unsupported operation.
   */
  @CanIgnoreReturnValue
  @Deprecated
  @Override
  public final int setCount(E element, int count) {
    throw new UnsupportedOperationException();
  }

  /**
   * Guaranteed to throw an exception and leave the collection unmodified.
   *
   * @throws UnsupportedOperationException always
   * @deprecated Unsupported operation.
   */
  @CanIgnoreReturnValue
  @Deprecated
  @Override
  public final boolean setCount(E element, int oldCount, int newCount) {
    throw new UnsupportedOperationException();
  }

  @GwtIncompatible // not present in emulated superclass
  @Override
  int copyIntoArray(Object[] dst, int offset) {
    for (Multiset.Entry<E> entry : entrySet()) {
      Arrays.fill(dst, offset, offset + entry.getCount(), entry.getElement());
      offset += entry.getCount();
    }
    return offset;
  }

  @Override
  public boolean equals(@Nullable Object object) {
    return Multisets.equalsImpl(this, object);
  }

  @Override
  public int hashCode() {
    return Sets.hashCodeImpl(entrySet());
  }

  @Override
  public String toString() {
    return entrySet().toString();
  }

  /** @since 21.0 (present with return type {@code Set} since 2.0) */
  @Override
  public abstract ImmutableSet<E> elementSet();

  @LazyInit private transient ImmutableSet<Entry<E>> entrySet;

  @Override
  public ImmutableSet<Entry<E>> entrySet() {
    ImmutableSet<Entry<E>> es = entrySet;
    return (es == null) ? (entrySet = createEntrySet()) : es;
  }

  private ImmutableSet<Entry<E>> createEntrySet() {
    return isEmpty() ? ImmutableSet.<Entry<E>>of() : new EntrySet();
  }

  abstract Entry<E> getEntry(int index);

  @WeakOuter
  private final class EntrySet extends IndexedImmutableSet<Entry<E>> {
    @Override
    boolean isPartialView() {
      return ImmutableMultiset.this.isPartialView();
    }

    @Override
    Entry<E> get(int index) {
      return getEntry(index);
    }

    @Override
    public int size() {
      return elementSet().size();
    }

    @Override
    public boolean contains(Object o) {
      if (o instanceof Entry) {
        Entry<?> entry = (Entry<?>) o;
        if (entry.getCount() <= 0) {
          return false;
        }
        int count = count(entry.getElement());
        return count == entry.getCount();
      }
      return false;
    }

    @Override
    public int hashCode() {
      return ImmutableMultiset.this.hashCode();
    }

    @GwtIncompatible
    @Override
    Object writeReplace() {
      return new EntrySetSerializedForm<E>(ImmutableMultiset.this);
    }

    private static final long serialVersionUID = 0;
  }

  @GwtIncompatible
  static class EntrySetSerializedForm<E> implements Serializable {
    final ImmutableMultiset<E> multiset;

    EntrySetSerializedForm(ImmutableMultiset<E> multiset) {
      this.multiset = multiset;
    }

    Object readResolve() {
      return multiset.entrySet();
    }
  }

  @GwtIncompatible
  @Override
  Object writeReplace() {
    return new SerializedForm(this);
  }

  /**
   * Returns a new builder. The generated builder is equivalent to the builder created by the {@link
   * Builder} constructor.
   */
  public static <E> Builder<E> builder() {
    return new Builder<E>();
  }

  /**
   * A builder for creating immutable multiset instances, especially {@code public static final}
   * multisets ("constant multisets"). Example:
   *
   *
{@code
   * public static final ImmutableMultiset<Bean> BEANS =
   *     new ImmutableMultiset.Builder<Bean>()
   *         .addCopies(Bean.COCOA, 4)
   *         .addCopies(Bean.GARDEN, 6)
   *         .addCopies(Bean.RED, 8)
   *         .addCopies(Bean.BLACK_EYED, 10)
   *         .build();
   * }

   *
   *
Builder instances can be reused; it is safe to call {@link #build} multiple times to build
   * multiple multisets in series.
   *
   * @since 2.0
   */
  public static class Builder<E> extends ImmutableCollection.Builder<E> {
    final Multiset<E> contents;

    /**
     * Creates a new builder. The returned builder is equivalent to the builder generated by {@link
     * ImmutableMultiset#builder}.
     */
    public Builder() {
      this(LinkedHashMultiset.<E>create());
    }

    Builder(Multiset<E> contents) {
      this.contents = contents;
    }

    /**
     * Adds {@code element} to the {@code ImmutableMultiset}.
     *
     * @param element the element to add
     * @return this {@code Builder} object
     * @throws NullPointerException if {@code element} is null
     */
    @CanIgnoreReturnValue
    @Override
    public Builder<E> add(E element) {
      contents.add(checkNotNull(element));
      return this;
    }

    /**
     * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
     *
     * @param elements the elements to add
     * @return this {@code Builder} object
     * @throws NullPointerException if {@code elements} is null or contains a null element
     */
    @CanIgnoreReturnValue
    @Override
    public Builder<E> add(E... elements) {
      super.add(elements);
      return this;
    }

    /**
     * Adds a number of occurrences of an element to this {@code ImmutableMultiset}.
     *
     * @param element the element to add
     * @param occurrences the number of occurrences of the element to add. May be zero, in which
     *     case no change will be made.
     * @return this {@code Builder} object
     * @throws NullPointerException if {@code element} is null
     * @throws IllegalArgumentException if {@code occurrences} is negative, or if this operation
     *     would result in more than {@link Integer#MAX_VALUE} occurrences of the element
     */
    @CanIgnoreReturnValue
    public Builder<E> addCopies(E element, int occurrences) {
      contents.add(checkNotNull(element), occurrences);
      return this;
    }

    /**
     * Adds or removes the necessary occurrences of an element such that the element attains the
     * desired count.
     *
     * @param element the element to add or remove occurrences of
     * @param count the desired count of the element in this multiset
     * @return this {@code Builder} object
     * @throws NullPointerException if {@code element} is null
     * @throws IllegalArgumentException if {@code count} is negative
     */
    @CanIgnoreReturnValue
    public Builder<E> setCount(E element, int count) {
      contents.setCount(checkNotNull(element), count);
      return this;
    }

    /**
     * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
     *
     * @param elements the {@code Iterable} to add to the {@code ImmutableMultiset}
     * @return this {@code Builder} object
     * @throws NullPointerException if {@code elements} is null or contains a null element
     */
    @CanIgnoreReturnValue
    @Override
    public Builder<E> addAll(Iterable<? extends E> elements) {
      if (elements instanceof Multiset) {
        Multiset<? extends E> multiset = Multisets.cast(elements);
        multiset.forEachEntry((e, n) -> contents.add(checkNotNull(e), n));
      } else {
        super.addAll(elements);
      }
      return this;
    }

    /**
     * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
     *
     * @param elements the elements to add to the {@code ImmutableMultiset}
     * @return this {@code Builder} object
     * @throws NullPointerException if {@code elements} is null or contains a null element
     */
    @CanIgnoreReturnValue
    @Override
    public Builder<E> addAll(Iterator<? extends E> elements) {
      super.addAll(elements);
      return this;
    }

    /**
     * Returns a newly-created {@code ImmutableMultiset} based on the contents of the {@code
     * Builder}.
     */
    @Override
    public ImmutableMultiset<E> build() {
      return copyOf(contents);
    }

    @VisibleForTesting
    ImmutableMultiset<E> buildJdkBacked() {
      if (contents.isEmpty()) {
        return of();
      }
      return JdkBackedImmutableMultiset.create(contents.entrySet());
    }
  }

  static final class ElementSet<E> extends ImmutableSet.Indexed<E> {
    private final List<Entry<E>> entries;
    // TODO(cpovirk): @Weak?
    private final Multiset<E> delegate;

    ElementSet(List<Entry<E>> entries, Multiset<E> delegate) {
      this.entries = entries;
      this.delegate = delegate;
    }

    @Override
    E get(int index) {
      return entries.get(index).getElement();
    }

    @Override
    public boolean contains(@Nullable Object object) {
      return delegate.contains(object);
    }

    @Override
    boolean isPartialView() {
      return true;
    }

    @Override
    public int size() {
      return entries.size();
    }
  }

  static final class SerializedForm implements Serializable {
    final Object[] elements;
    final int[] counts;

    SerializedForm(Multiset<?> multiset) {
      int distinct = multiset.entrySet().size();
      elements = new Object[distinct];
      counts = new int[distinct];
      int i = 0;
      for (Entry<?> entry : multiset.entrySet()) {
        elements[i] = entry.getElement();
        counts[i] = entry.getCount();
        i++;
      }
    }

    Object readResolve() {
      LinkedHashMultiset<Object> multiset = LinkedHashMultiset.create(elements.length);
      for (int i = 0; i < elements.length; i++) {
        multiset.add(elements[i], counts[i]);
      }
      return ImmutableMultiset.copyOf(multiset);
    }

    private static final long serialVersionUID = 0;
  }
}


/*
 * Copyright (C) 2014 The Guava Authors
 *
 * 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 com.google.common.graph;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.graph.GraphConstants.NODE_NOT_IN_GRAPH;

import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.Set;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * Static utility methods for {@link Graph}, {@link ValueGraph}, and {@link Network} instances.
 *
 * @author James Sexton
 * @author Joshua O'Madadhain
 * @since 20.0
 */
@Beta
public final class Graphs {

  private Graphs() {}

  // Graph query methods

  /**
   * Returns true if {@code graph} has at least one cycle. A cycle is defined as a non-empty subset
   * of edges in a graph arranged to form a path (a sequence of adjacent outgoing edges) starting
   * and ending with the same node.
   *
   *
This method will detect any non-empty cycle, including self-loops (a cycle of length 1).
   */
  public static <N> boolean hasCycle(Graph<N> graph) {
    int numEdges = graph.edges().size();
    if (numEdges == 0) {
      return false; // An edge-free graph is acyclic by definition.
    }
    if (!graph.isDirected() && numEdges >= graph.nodes().size()) {
      return true; // Optimization for the undirected case: at least one cycle must exist.
    }

    Map<Object, NodeVisitState> visitedNodes =
        Maps.newHashMapWithExpectedSize(graph.nodes().size());
    for (N node : graph.nodes()) {
      if (subgraphHasCycle(graph, visitedNodes, node, null)) {
        return true;
      }
    }
    return false;
  }

  /**
   * Returns true if {@code network} has at least one cycle. A cycle is defined as a non-empty
   * subset of edges in a graph arranged to form a path (a sequence of adjacent outgoing edges)
   * starting and ending with the same node.
   *
   *
This method will detect any non-empty cycle, including self-loops (a cycle of length 1).
   */
  public static boolean hasCycle(Network<?, ?> network) {
    // In a directed graph, parallel edges cannot introduce a cycle in an acyclic graph.
    // However, in an undirected graph, any parallel edge induces a cycle in the graph.
    if (!network.isDirected()
        && network.allowsParallelEdges()
        && network.edges().size() > network.asGraph().edges().size()) {
      return true;
    }
    return hasCycle(network.asGraph());
  }

  /**
   * Performs a traversal of the nodes reachable from {@code node}. If we ever reach a node we've
   * already visited (following only outgoing edges and without reusing edges), we know there's a
   * cycle in the graph.
   */
  private static <N> boolean subgraphHasCycle(
      Graph<N> graph, Map<Object, NodeVisitState> visitedNodes, N node, @Nullable N previousNode) {
    NodeVisitState state = visitedNodes.get(node);
    if (state == NodeVisitState.COMPLETE) {
      return false;
    }
    if (state == NodeVisitState.PENDING) {
      return true;
    }

    visitedNodes.put(node, NodeVisitState.PENDING);
    for (N nextNode : graph.successors(node)) {
      if (canTraverseWithoutReusingEdge(graph, nextNode, previousNode)
          && subgraphHasCycle(graph, visitedNodes, nextNode, node)) {
        return true;
      }
    }
    visitedNodes.put(node, NodeVisitState.COMPLETE);
    return false;
  }

  /**
   * Determines whether an edge has already been used during traversal. In the directed case a cycle
   * is always detected before reusing an edge, so no special logic is required. In the undirected
   * case, we must take care not to "backtrack" over an edge (i.e. going from A to B and then going
   * from B to A).
   */
  private static boolean canTraverseWithoutReusingEdge(
      Graph<?> graph, Object nextNode, @Nullable Object previousNode) {
    if (graph.isDirected() || !Objects.equal(previousNode, nextNode)) {
      return true;
    }
    // This falls into the undirected A->B->A case. The Graph interface does not support parallel
    // edges, so this traversal would require reusing the undirected AB edge.
    return false;
  }

  /**
   * Returns the transitive closure of {@code graph}. The transitive closure of a graph is another
   * graph with an edge connecting node A to node B if node B is {@link #reachableNodes(Graph,
   * Object) reachable} from node A.
   *
   *
This is a "snapshot" based on the current topology of {@code graph}, rather than a live view
   * of the transitive closure of {@code graph}. In other words, the returned {@link Graph} will not
   * be updated after modifications to {@code graph}.
   */
  // TODO(b/31438252): Consider potential optimizations for this algorithm.
  public static <N> Graph<N> transitiveClosure(Graph<N> graph) {
    MutableGraph<N> transitiveClosure = GraphBuilder.from(graph).allowsSelfLoops(true).build();
    // Every node is, at a minimum, reachable from itself. Since the resulting transitive closure
    // will have no isolated nodes, we can skip adding nodes explicitly and let putEdge() do it.

    if (graph.isDirected()) {
      // Note: works for both directed and undirected graphs, but we only use in the directed case.
      for (N node : graph.nodes()) {
        for (N reachableNode : reachableNodes(graph, node)) {
          transitiveClosure.putEdge(node, reachableNode);
        }
      }
    } else {
      // An optimization for the undirected case: for every node B reachable from node A,
      // node A and node B have the same reachability set.
      Set<N> visitedNodes = new HashSet<N>();
      for (N node : graph.nodes()) {
        if (!visitedNodes.contains(node)) {
          Set<N> reachableNodes = reachableNodes(graph, node);
          visitedNodes.addAll(reachableNodes);
          int pairwiseMatch = 1; // start at 1 to include self-loops
          for (N nodeU : reachableNodes) {
            for (N nodeV : Iterables.limit(reachableNodes, pairwiseMatch++)) {
              transitiveClosure.putEdge(nodeU, nodeV);
            }
          }
        }
      }
    }

    return transitiveClosure;
  }

  /**
   * Returns the set of nodes that are reachable from {@code node}. Node B is defined as reachable
   * from node A if there exists a path (a sequence of adjacent outgoing edges) starting at node A
   * and ending at node B. Note that a node is always reachable from itself via a zero-length path.
   *
   *
This is a "snapshot" based on the current topology of {@code graph}, rather than a live view
   * of the set of nodes reachable from {@code node}. In other words, the returned {@link Set} will
   * not be updated after modifications to {@code graph}.
   *
   * @throws IllegalArgumentException if {@code node} is not present in {@code graph}
   */
  public static <N> Set<N> reachableNodes(Graph<N> graph, N node) {
    checkArgument(graph.nodes().contains(node), NODE_NOT_IN_GRAPH, node);
    Set<N> visitedNodes = new LinkedHashSet<N>();
    Queue<N> queuedNodes = new ArrayDeque<N>();
    visitedNodes.add(node);
    queuedNodes.add(node);
    // Perform a breadth-first traversal rooted at the input node.
    while (!queuedNodes.isEmpty()) {
      N currentNode = queuedNodes.remove();
      for (N successor : graph.successors(currentNode)) {
        if (visitedNodes.add(successor)) {
          queuedNodes.add(successor);
        }
      }
    }
    return Collections.unmodifiableSet(visitedNodes);
  }

  // Graph mutation methods

  // Graph view methods

  /**
   * Returns a view of {@code graph} with the direction (if any) of every edge reversed. All other
   * properties remain intact, and further updates to {@code graph} will be reflected in the view.
   */
  public static <N> Graph<N> transpose(Graph<N> graph) {
    if (!graph.isDirected()) {
      return graph; // the transpose of an undirected graph is an identical graph
    }

    if (graph instanceof TransposedGraph) {
      return ((TransposedGraph<N>) graph).graph;
    }

    return new TransposedGraph<N>(graph);
  }

  /**
   * Returns a view of {@code graph} with the direction (if any) of every edge reversed. All other
   * properties remain intact, and further updates to {@code graph} will be reflected in the view.
   */
  public static <N, V> ValueGraph<N, V> transpose(ValueGraph<N, V> graph) {
    if (!graph.isDirected()) {
      return graph; // the transpose of an undirected graph is an identical graph
    }

    if (graph instanceof TransposedValueGraph) {
      return ((TransposedValueGraph<N, V>) graph).graph;
    }

    return new TransposedValueGraph<>(graph);
  }

  /**
   * Returns a view of {@code network} with the direction (if any) of every edge reversed. All other
   * properties remain intact, and further updates to {@code network} will be reflected in the view.
   */
  public static <N, E> Network<N, E> transpose(Network<N, E> network) {
    if (!network.isDirected()) {
      return network; // the transpose of an undirected network is an identical network
    }

    if (network instanceof TransposedNetwork) {
      return ((TransposedNetwork<N, E>) network).network;
    }

    return new TransposedNetwork<>(network);
  }

  static <N> EndpointPair<N> transpose(EndpointPair<N> endpoints) {
    if (endpoints.isOrdered()) {
      return EndpointPair.ordered(endpoints.target(), endpoints.source());
    }
    return endpoints;
  }

  // NOTE: this should work as long as the delegate graph's implementation of edges() (like that of
  // AbstractGraph) derives its behavior from calling successors().
  private static class TransposedGraph<N> extends ForwardingGraph<N> {
    private final Graph<N> graph;

    TransposedGraph(Graph<N> graph) {
      this.graph = graph;
    }

    @Override
    protected Graph<N> delegate() {
      return graph;
    }

    @Override
    public Set<N> predecessors(N node) {
      return delegate().successors(node); // transpose
    }

    @Override
    public Set<N> successors(N node) {
      return delegate().predecessors(node); // transpose
    }

    @Override
    public int inDegree(N node) {
      return delegate().outDegree(node); // transpose
    }

    @Override
    public int outDegree(N node) {
      return delegate().inDegree(node); // transpose
    }

    @Override
    public boolean hasEdgeConnecting(N nodeU, N nodeV) {
      return delegate().hasEdgeConnecting(nodeV, nodeU); // transpose
    }

    @Override
    public boolean hasEdgeConnecting(EndpointPair<N> endpoints) {
      return delegate().hasEdgeConnecting(transpose(endpoints));
    }
  }

  // NOTE: this should work as long as the delegate graph's implementation of edges() (like that of
  // AbstractValueGraph) derives its behavior from calling successors().
  private static class TransposedValueGraph<N, V> extends ForwardingValueGraph<N, V> {
    private final ValueGraph<N, V> graph;

    TransposedValueGraph(ValueGraph<N, V> graph) {
      this.graph = graph;
    }

    @Override
    protected ValueGraph<N, V> delegate() {
      return graph;
    }

    @Override
    public Set<N> predecessors(N node) {
      return delegate().successors(node); // transpose
    }

    @Override
    public Set<N> successors(N node) {
      return delegate().predecessors(node); // transpose
    }

    @Override
    public int inDegree(N node) {
      return delegate().outDegree(node); // transpose
    }

    @Override
    public int outDegree(N node) {
      return delegate().inDegree(node); // transpose
    }

    @Override
    public boolean hasEdgeConnecting(N nodeU, N nodeV) {
      return delegate().hasEdgeConnecting(nodeV, nodeU); // transpose
    }

    @Override
    public boolean hasEdgeConnecting(EndpointPair<N> endpoints) {
      return delegate().hasEdgeConnecting(transpose(endpoints));
    }

    @Override
    public Optional<V> edgeValue(N nodeU, N nodeV) {
      return delegate().edgeValue(nodeV, nodeU); // transpose
    }

    @Override
    public Optional<V> edgeValue(EndpointPair<N> endpoints) {
      return delegate().edgeValue(transpose(endpoints));
    }

    @Override
    public @Nullable V edgeValueOrDefault(N nodeU, N nodeV, @Nullable V defaultValue) {
      return delegate().edgeValueOrDefault(nodeV, nodeU, defaultValue); // transpose
    }

    @Override
    public @Nullable V edgeValueOrDefault(EndpointPair<N> endpoints, @Nullable V defaultValue) {
      return delegate().edgeValueOrDefault(transpose(endpoints), defaultValue);
    }
  }

  private static class TransposedNetwork<N, E> extends ForwardingNetwork<N, E> {
    private final Network<N, E> network;

    TransposedNetwork(Network<N, E> network) {
      this.network = network;
    }

    @Override
    protected Network<N, E> delegate() {
      return network;
    }

    @Override
    public Set<N> predecessors(N node) {
      return delegate().successors(node); // transpose
    }

    @Override
    public Set<N> successors(N node) {
      return delegate().predecessors(node); // transpose
    }

    @Override
    public int inDegree(N node) {
      return delegate().outDegree(node); // transpose
    }

    @Override
    public int outDegree(N node) {
      return delegate().inDegree(node); // transpose
    }

    @Override
    public Set<E> inEdges(N node) {
      return delegate().outEdges(node); // transpose
    }

    @Override
    public Set<E> outEdges(N node) {
      return delegate().inEdges(node); // transpose
    }

    @Override
    public EndpointPair<N> incidentNodes(E edge) {
      EndpointPair<N> endpointPair = delegate().incidentNodes(edge);
      return EndpointPair.of(network, endpointPair.nodeV(), endpointPair.nodeU()); // transpose
    }

    @Override
    public Set<E> edgesConnecting(N nodeU, N nodeV) {
      return delegate().edgesConnecting(nodeV, nodeU); // transpose
    }

    @Override
    public Set<E> edgesConnecting(EndpointPair<N> endpoints) {
      return delegate().edgesConnecting(transpose(endpoints));
    }

    @Override
    public Optional<E> edgeConnecting(N nodeU, N nodeV) {
      return delegate().edgeConnecting(nodeV, nodeU); // transpose
    }

    @Override
    public Optional<E> edgeConnecting(EndpointPair<N> endpoints) {
      return delegate().edgeConnecting(transpose(endpoints));
    }

    @Override
    public E edgeConnectingOrNull(N nodeU, N nodeV) {
      return delegate().edgeConnectingOrNull(nodeV, nodeU); // transpose
    }

    @Override
    public E edgeConnectingOrNull(EndpointPair<N> endpoints) {
      return delegate().edgeConnectingOrNull(transpose(endpoints));
    }

    @Override
    public boolean hasEdgeConnecting(N nodeU, N nodeV) {
      return delegate().hasEdgeConnecting(nodeV, nodeU); // transpose
    }

    @Override
    public boolean hasEdgeConnecting(EndpointPair<N> endpoints) {
      return delegate().hasEdgeConnecting(transpose(endpoints));
    }
  }

  // Graph copy methods

  /**
   * Returns the subgraph of {@code graph} induced by {@code nodes}. This subgraph is a new graph
   * that contains all of the nodes in {@code nodes}, and all of the {@link Graph#edges() edges}
   * from {@code graph} for which both nodes are contained by {@code nodes}.
   *
   * @throws IllegalArgumentException if any element in {@code nodes} is not a node in the graph
   */
  public static <N> MutableGraph<N> inducedSubgraph(Graph<N> graph, Iterable<? extends N> nodes) {
    MutableGraph<N> subgraph =
        (nodes instanceof Collection)
            ? GraphBuilder.from(graph).expectedNodeCount(((Collection) nodes).size()).build()
            : GraphBuilder.from(graph).build();
    for (N node : nodes) {
      subgraph.addNode(node);
    }
    for (N node : subgraph.nodes()) {
      for (N successorNode : graph.successors(node)) {
        if (subgraph.nodes().contains(successorNode)) {
          subgraph.putEdge(node, successorNode);
        }
      }
    }
    return subgraph;
  }

  /**
   * Returns the subgraph of {@code graph} induced by {@code nodes}. This subgraph is a new graph
   * that contains all of the nodes in {@code nodes}, and all of the {@link Graph#edges() edges}
   * (and associated edge values) from {@code graph} for which both nodes are contained by {@code
   * nodes}.
   *
   * @throws IllegalArgumentException if any element in {@code nodes} is not a node in the graph
   */
  public static <N, V> MutableValueGraph<N, V> inducedSubgraph(
      ValueGraph<N, V> graph, Iterable<? extends N> nodes) {
    MutableValueGraph<N, V> subgraph =
        (nodes instanceof Collection)
            ? ValueGraphBuilder.from(graph).expectedNodeCount(((Collection) nodes).size()).build()
            : ValueGraphBuilder.from(graph).build();
    for (N node : nodes) {
      subgraph.addNode(node);
    }
    for (N node : subgraph.nodes()) {
      for (N successorNode : graph.successors(node)) {
        if (subgraph.nodes().contains(successorNode)) {
          subgraph.putEdgeValue(
              node, successorNode, graph.edgeValueOrDefault(node, successorNode, null));
        }
      }
    }
    return subgraph;
  }

  /**
   * Returns the subgraph of {@code network} induced by {@code nodes}. This subgraph is a new graph
   * that contains all of the nodes in {@code nodes}, and all of the {@link Network#edges() edges}
   * from {@code network} for which the {@link Network#incidentNodes(Object) incident nodes} are
   * both contained by {@code nodes}.
   *
   * @throws IllegalArgumentException if any element in {@code nodes} is not a node in the graph
   */
  public static <N, E> MutableNetwork<N, E> inducedSubgraph(
      Network<N, E> network, Iterable<? extends N> nodes) {
    MutableNetwork<N, E> subgraph =
        (nodes instanceof Collection)
            ? NetworkBuilder.from(network).expectedNodeCount(((Collection) nodes).size()).build()
            : NetworkBuilder.from(network).build();
    for (N node : nodes) {
      subgraph.addNode(node);
    }
    for (N node : subgraph.nodes()) {
      for (E edge : network.outEdges(node)) {
        N successorNode = network.incidentNodes(edge).adjacentNode(node);
        if (subgraph.nodes().contains(successorNode)) {
          subgraph.addEdge(node, successorNode, edge);
        }
      }
    }
    return subgraph;
  }

  /** Creates a mutable copy of {@code graph} with the same nodes and edges. */
  public static <N> MutableGraph<N> copyOf(Graph<N> graph) {
    MutableGraph<N> copy = GraphBuilder.from(graph).expectedNodeCount(graph.nodes().size()).build();
    for (N node : graph.nodes()) {
      copy.addNode(node);
    }
    for (EndpointPair<N> edge : graph.edges()) {
      copy.putEdge(edge.nodeU(), edge.nodeV());
    }
    return copy;
  }

  /** Creates a mutable copy of {@code graph} with the same nodes, edges, and edge values. */
  public static <N, V> MutableValueGraph<N, V> copyOf(ValueGraph<N, V> graph) {
    MutableValueGraph<N, V> copy =
        ValueGraphBuilder.from(graph).expectedNodeCount(graph.nodes().size()).build();
    for (N node : graph.nodes()) {
      copy.addNode(node);
    }
    for (EndpointPair<N> edge : graph.edges()) {
      copy.putEdgeValue(
          edge.nodeU(), edge.nodeV(), graph.edgeValueOrDefault(edge.nodeU(), edge.nodeV(), null));
    }
    return copy;
  }

  /** Creates a mutable copy of {@code network} with the same nodes and edges. */
  public static <N, E> MutableNetwork<N, E> copyOf(Network<N, E> network) {
    MutableNetwork<N, E> copy =
        NetworkBuilder.from(network)
            .expectedNodeCount(network.nodes().size())
            .expectedEdgeCount(network.edges().size())
            .build();
    for (N node : network.nodes()) {
      copy.addNode(node);
    }
    for (E edge : network.edges()) {
      EndpointPair<N> endpointPair = network.incidentNodes(edge);
      copy.addEdge(endpointPair.nodeU(), endpointPair.nodeV(), edge);
    }
    return copy;
  }

  @CanIgnoreReturnValue
  static int checkNonNegative(int value) {
    checkArgument(value >= 0, "Not true that %s is non-negative.", value);
    return value;
  }

  @CanIgnoreReturnValue
  static long checkNonNegative(long value) {
    checkArgument(value >= 0, "Not true that %s is non-negative.", value);
    return value;
  }

  @CanIgnoreReturnValue
  static int checkPositive(int value) {
    checkArgument(value > 0, "Not true that %s is positive.", value);
    return value;
  }

  @CanIgnoreReturnValue
  static long checkPositive(long value) {
    checkArgument(value > 0, "Not true that %s is positive.", value);
    return value;
  }

  /**
   * An enum representing the state of a node during DFS. {@code PENDING} means that the node is on
   * the stack of the DFS, while {@code COMPLETE} means that the node and all its successors have
   * been already explored. Any node that has not been explored will not have a state at all.
   */
  private enum NodeVisitState {
    PENDING,
    COMPLETE
  }
}