/*
 * 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 com.google.common.base.Preconditions.checkNotNull;

import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.j2objc.annotations.Weak;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;

/**
 * The {@code CycleDetectingLockFactory} creates {@link ReentrantLock} instances and {@link
 * ReentrantReadWriteLock} instances that detect potential deadlock by checking for cycles in lock
 * acquisition order.
 *
 *
Potential deadlocks detected when calling the {@code lock()}, {@code lockInterruptibly()}, or
 * {@code tryLock()} methods will result in the execution of the {@link Policy} specified when
 * creating the factory. The currently available policies are:
 *
 *

 *
DISABLED
 *
WARN
 *
THROW
 *

 *
 *
The locks created by a factory instance will detect lock acquisition cycles with locks created
 * by other {@code CycleDetectingLockFactory} instances (except those with {@code Policy.DISABLED}).
 * A lock's behavior when a cycle is detected, however, is defined by the {@code Policy} of the
 * factory that created it. This allows detection of cycles across components while delegating
 * control over lock behavior to individual components.
 *
 *
Applications are encouraged to use a {@code CycleDetectingLockFactory} to create any locks for
 * which external/unmanaged code is executed while the lock is held. (See caveats under
 * Performance).
 *
 *
Cycle Detection
 *
 *
Deadlocks can arise when locks are acquired in an order that forms a cycle. In a simple
 * example involving two locks and two threads, deadlock occurs when one thread acquires Lock A, and
 * then Lock B, while another thread acquires Lock B, and then Lock A:
 *
 *

 * Thread1: acquire(LockA) --X acquire(LockB)
 * Thread2: acquire(LockB) --X acquire(LockA)
 *

 *
 *
Neither thread will progress because each is waiting for the other. In more complex
 * applications, cycles can arise from interactions among more than 2 locks:
 *
 *

 * Thread1: acquire(LockA) --X acquire(LockB)
 * Thread2: acquire(LockB) --X acquire(LockC)
 * ...
 * ThreadN: acquire(LockN) --X acquire(LockA)
 *

 *
 *
The implementation detects cycles by constructing a directed graph in which each lock
 * represents a node and each edge represents an acquisition ordering between two locks.
 *
 *

 *
Each lock adds (and removes) itself to/from a ThreadLocal Set of acquired locks when the
 *       Thread acquires its first hold (and releases its last remaining hold).
 *
Before the lock is acquired, the lock is checked against the current set of acquired
 *       locks---to each of the acquired locks, an edge from the soon-to-be-acquired lock is either
 *       verified or created.
 *
If a new edge needs to be created, the outgoing edges of the acquired locks are traversed
 *       to check for a cycle that reaches the lock to be acquired. If no cycle is detected, a new
 *       "safe" edge is created.
 *
If a cycle is detected, an "unsafe" (cyclic) edge is created to represent a potential
 *       deadlock situation, and the appropriate Policy is executed.
 *

 *
 *
Note that detection of potential deadlock does not necessarily indicate that deadlock will
 * happen, as it is possible that higher level application logic prevents the cyclic lock
 * acquisition from occurring. One example of a false positive is:
 *
 *

 * LockA -> LockB -> LockC
 * LockA -> LockC -> LockB
 *

 *
 *
ReadWriteLocks
 *
 *
While {@code ReadWriteLock} instances have different properties and can form cycles without
 * potential deadlock, this class treats {@code ReadWriteLock} instances as equivalent to
 * traditional exclusive locks. Although this increases the false positives that the locks detect
 * (i.e. cycles that will not actually result in deadlock), it simplifies the algorithm and
 * implementation considerably. The assumption is that a user of this factory wishes to eliminate
 * any cyclic acquisition ordering.
 *
 *
Explicit Lock Acquisition Ordering
 *
 *
The {@link CycleDetectingLockFactory.WithExplicitOrdering} class can be used to enforce an
 * application-specific ordering in addition to performing general cycle detection.
 *
 *
Garbage Collection
 *
 *
In order to allow proper garbage collection of unused locks, the edges of the lock graph are
 * weak references.
 *
 *
Performance
 *
 *
The extra bookkeeping done by cycle detecting locks comes at some cost to performance.
 * Benchmarks (as of December 2011) show that:
 *
 *

 *
for an unnested {@code lock()} and {@code unlock()}, a cycle detecting lock takes 38ns as
 *       opposed to the 24ns taken by a plain lock.
 *
for nested locking, the cost increases with the depth of the nesting:
 *

 *
2 levels: average of 64ns per lock()/unlock()
 *
3 levels: average of 77ns per lock()/unlock()
 *
4 levels: average of 99ns per lock()/unlock()
 *
5 levels: average of 103ns per lock()/unlock()
 *
10 levels: average of 184ns per lock()/unlock()
 *
20 levels: average of 393ns per lock()/unlock()
 *

 *

 *
 *
As such, the CycleDetectingLockFactory may not be suitable for performance-critical
 * applications which involve tightly-looped or deeply-nested locking algorithms.
 *
 * @author Darick Tong
 * @since 13.0
 */
@Beta
@CanIgnoreReturnValue // TODO(cpovirk): Consider being more strict.
@GwtIncompatible
public class CycleDetectingLockFactory {

  /**
   * Encapsulates the action to be taken when a potential deadlock is encountered. Clients can use
   * one of the predefined {@link Policies} or specify a custom implementation. Implementations must
   * be thread-safe.
   *
   * @since 13.0
   */
  @Beta
  public interface Policy {

    /**
     * Called when a potential deadlock is encountered. Implementations can throw the given {@code
     * exception} and/or execute other desired logic.
     *
     *
Note that the method will be called even upon an invocation of {@code tryLock()}. Although
     * {@code tryLock()} technically recovers from deadlock by eventually timing out, this behavior
     * is chosen based on the assumption that it is the application's wish to prohibit any cyclical
     * lock acquisitions.
     */
    void handlePotentialDeadlock(PotentialDeadlockException exception);
  }

  /**
   * Pre-defined {@link Policy} implementations.
   *
   * @since 13.0
   */
  @Beta
  public enum Policies implements Policy {
    /**
     * When potential deadlock is detected, this policy results in the throwing of the {@code
     * PotentialDeadlockException} indicating the potential deadlock, which includes stack traces
     * illustrating the cycle in lock acquisition order.
     */
    THROW {
      @Override
      public void handlePotentialDeadlock(PotentialDeadlockException e) {
        throw e;
      }
    },

    /**
     * When potential deadlock is detected, this policy results in the logging of a {@link
     * Level#SEVERE} message indicating the potential deadlock, which includes stack traces
     * illustrating the cycle in lock acquisition order.
     */
    WARN {
      @Override
      public void handlePotentialDeadlock(PotentialDeadlockException e) {
        logger.log(Level.SEVERE, "Detected potential deadlock", e);
      }
    },

    /**
     * Disables cycle detection. This option causes the factory to return unmodified lock
     * implementations provided by the JDK, and is provided to allow applications to easily
     * parameterize when cycle detection is enabled.
     *
     *
Note that locks created by a factory with this policy will not participate the
     * cycle detection performed by locks created by other factories.
     */
    DISABLED {
      @Override
      public void handlePotentialDeadlock(PotentialDeadlockException e) {}
    };
  }

  /** Creates a new factory with the specified policy. */
  public static CycleDetectingLockFactory newInstance(Policy policy) {
    return new CycleDetectingLockFactory(policy);
  }

  /** Equivalent to {@code newReentrantLock(lockName, false)}. */
  public ReentrantLock newReentrantLock(String lockName) {
    return newReentrantLock(lockName, false);
  }

  /**
   * Creates a {@link ReentrantLock} with the given fairness policy. The {@code lockName} is used in
   * the warning or exception output to help identify the locks involved in the detected deadlock.
   */
  public ReentrantLock newReentrantLock(String lockName, boolean fair) {
    return policy == Policies.DISABLED
        ? new ReentrantLock(fair)
        : new CycleDetectingReentrantLock(new LockGraphNode(lockName), fair);
  }

  /** Equivalent to {@code newReentrantReadWriteLock(lockName, false)}. */
  public ReentrantReadWriteLock newReentrantReadWriteLock(String lockName) {
    return newReentrantReadWriteLock(lockName, false);
  }

  /**
   * Creates a {@link ReentrantReadWriteLock} with the given fairness policy. The {@code lockName}
   * is used in the warning or exception output to help identify the locks involved in the detected
   * deadlock.
   */
  public ReentrantReadWriteLock newReentrantReadWriteLock(String lockName, boolean fair) {
    return policy == Policies.DISABLED
        ? new ReentrantReadWriteLock(fair)
        : new CycleDetectingReentrantReadWriteLock(new LockGraphNode(lockName), fair);
  }

  // A static mapping from an Enum type to its set of LockGraphNodes.
  private static final ConcurrentMap<Class<? extends Enum>, Map<? extends Enum, LockGraphNode>>
      lockGraphNodesPerType = new MapMaker().weakKeys().makeMap();

  /** Creates a {@code CycleDetectingLockFactory.WithExplicitOrdering<E>}. */
  public static <E extends Enum<E>> WithExplicitOrdering<E> newInstanceWithExplicitOrdering(
      Class<E> enumClass, Policy policy) {
    // createNodes maps each enumClass to a Map with the corresponding enum key
    // type.
    checkNotNull(enumClass);
    checkNotNull(policy);
    @SuppressWarnings("unchecked")
    Map<E, LockGraphNode> lockGraphNodes = (Map<E, LockGraphNode>) getOrCreateNodes(enumClass);
    return new WithExplicitOrdering<E>(policy, lockGraphNodes);
  }

  private static Map<? extends Enum, LockGraphNode> getOrCreateNodes(Class<? extends Enum> clazz) {
    Map<? extends Enum, LockGraphNode> existing = lockGraphNodesPerType.get(clazz);
    if (existing != null) {
      return existing;
    }
    Map<? extends Enum, LockGraphNode> created = createNodes(clazz);
    existing = lockGraphNodesPerType.putIfAbsent(clazz, created);
    return MoreObjects.firstNonNull(existing, created);
  }

  /**
   * For a given Enum type, creates an immutable map from each of the Enum's values to a
   * corresponding LockGraphNode, with the {@code allowedPriorLocks} and {@code
   * disallowedPriorLocks} prepopulated with nodes according to the natural ordering of the
   * associated Enum values.
   */
  @VisibleForTesting
  static <E extends Enum<E>> Map<E, LockGraphNode> createNodes(Class<E> clazz) {
    EnumMap<E, LockGraphNode> map = Maps.newEnumMap(clazz);
    E[] keys = clazz.getEnumConstants();
    final int numKeys = keys.length;
    ArrayList<LockGraphNode> nodes = Lists.newArrayListWithCapacity(numKeys);
    // Create a LockGraphNode for each enum value.
    for (E key : keys) {
      LockGraphNode node = new LockGraphNode(getLockName(key));
      nodes.add(node);
      map.put(key, node);
    }
    // Pre-populate all allowedPriorLocks with nodes of smaller ordinal.
    for (int i = 1; i < numKeys; i++) {
      nodes.get(i).checkAcquiredLocks(Policies.THROW, nodes.subList(0, i));
    }
    // Pre-populate all disallowedPriorLocks with nodes of larger ordinal.
    for (int i = 0; i < numKeys - 1; i++) {
      nodes.get(i).checkAcquiredLocks(Policies.DISABLED, nodes.subList(i + 1, numKeys));
    }
    return Collections.unmodifiableMap(map);
  }

  /**
   * For the given Enum value {@code rank}, returns the value's {@code "EnumClass.name"}, which is
   * used in exception and warning output.
   */
  private static String getLockName(Enum<?> rank) {
    return rank.getDeclaringClass().getSimpleName() + "." + rank.name();
  }

  /**
   * A {@code CycleDetectingLockFactory.WithExplicitOrdering} provides the additional enforcement of
   * an application-specified ordering of lock acquisitions. The application defines the allowed
   * ordering with an {@code Enum} whose values each correspond to a lock type. The order in which
   * the values are declared dictates the allowed order of lock acquisition. In other words, locks
   * corresponding to smaller values of {@link Enum#ordinal()} should only be acquired before locks
   * with larger ordinals. Example:
   *
   *
{@code
   * enum MyLockOrder {
   *   FIRST, SECOND, THIRD;
   * }
   *
   * CycleDetectingLockFactory.WithExplicitOrdering<MyLockOrder> factory =
   *   CycleDetectingLockFactory.newInstanceWithExplicitOrdering(Policies.THROW);
   *
   * Lock lock1 = factory.newReentrantLock(MyLockOrder.FIRST);
   * Lock lock2 = factory.newReentrantLock(MyLockOrder.SECOND);
   * Lock lock3 = factory.newReentrantLock(MyLockOrder.THIRD);
   *
   * lock1.lock();
   * lock3.lock();
   * lock2.lock();  // will throw an IllegalStateException
   * }

   *
   *
As with all locks created by instances of {@code CycleDetectingLockFactory} explicitly
   * ordered locks participate in general cycle detection with all other cycle detecting locks, and
   * a lock's behavior when detecting a cyclic lock acquisition is defined by the {@code Policy} of
   * the factory that created it.
   *
   *
Note, however, that although multiple locks can be created for a given Enum value, whether
   * it be through separate factory instances or through multiple calls to the same factory,
   * attempting to acquire multiple locks with the same Enum value (within the same thread) will
   * result in an IllegalStateException regardless of the factory's policy. For example:
   *
   *
{@code
   * CycleDetectingLockFactory.WithExplicitOrdering<MyLockOrder> factory1 =
   *   CycleDetectingLockFactory.newInstanceWithExplicitOrdering(...);
   * CycleDetectingLockFactory.WithExplicitOrdering<MyLockOrder> factory2 =
   *   CycleDetectingLockFactory.newInstanceWithExplicitOrdering(...);
   *
   * Lock lockA = factory1.newReentrantLock(MyLockOrder.FIRST);
   * Lock lockB = factory1.newReentrantLock(MyLockOrder.FIRST);
   * Lock lockC = factory2.newReentrantLock(MyLockOrder.FIRST);
   *
   * lockA.lock();
   *
   * lockB.lock();  // will throw an IllegalStateException
   * lockC.lock();  // will throw an IllegalStateException
   *
   * lockA.lock();  // reentrant acquisition is okay
   * }

   *
   *
It is the responsibility of the application to ensure that multiple lock instances with the
   * same rank are never acquired in the same thread.
   *
   * @param <E> The Enum type representing the explicit lock ordering.
   * @since 13.0
   */
  @Beta
  public static final class WithExplicitOrdering<E extends Enum<E>>
      extends CycleDetectingLockFactory {

    private final Map<E, LockGraphNode> lockGraphNodes;

    @VisibleForTesting
    WithExplicitOrdering(Policy policy, Map<E, LockGraphNode> lockGraphNodes) {
      super(policy);
      this.lockGraphNodes = lockGraphNodes;
    }

    /** Equivalent to {@code newReentrantLock(rank, false)}. */
    public ReentrantLock newReentrantLock(E rank) {
      return newReentrantLock(rank, false);
    }

    /**
     * Creates a {@link ReentrantLock} with the given fairness policy and rank. The values returned
     * by {@link Enum#getDeclaringClass()} and {@link Enum#name()} are used to describe the lock in
     * warning or exception output.
     *
     * @throws IllegalStateException If the factory has already created a {@code Lock} with the
     *     specified rank.
     */
    public ReentrantLock newReentrantLock(E rank, boolean fair) {
      return policy == Policies.DISABLED
          ? new ReentrantLock(fair)
          : new CycleDetectingReentrantLock(lockGraphNodes.get(rank), fair);
    }

    /** Equivalent to {@code newReentrantReadWriteLock(rank, false)}. */
    public ReentrantReadWriteLock newReentrantReadWriteLock(E rank) {
      return newReentrantReadWriteLock(rank, false);
    }

    /**
     * Creates a {@link ReentrantReadWriteLock} with the given fairness policy and rank. The values
     * returned by {@link Enum#getDeclaringClass()} and {@link Enum#name()} are used to describe the
     * lock in warning or exception output.
     *
     * @throws IllegalStateException If the factory has already created a {@code Lock} with the
     *     specified rank.
     */
    public ReentrantReadWriteLock newReentrantReadWriteLock(E rank, boolean fair) {
      return policy == Policies.DISABLED
          ? new ReentrantReadWriteLock(fair)
          : new CycleDetectingReentrantReadWriteLock(lockGraphNodes.get(rank), fair);
    }
  }

  //////// Implementation /////////

  private static final Logger logger = Logger.getLogger(CycleDetectingLockFactory.class.getName());

  final Policy policy;

  private CycleDetectingLockFactory(Policy policy) {
    this.policy = checkNotNull(policy);
  }

  /**
   * Tracks the currently acquired locks for each Thread, kept up to date by calls to {@link
   * #aboutToAcquire(CycleDetectingLock)} and {@link #lockStateChanged(CycleDetectingLock)}.
   */
  // This is logically a Set, but an ArrayList is used to minimize the amount
  // of allocation done on lock()/unlock().
  private static final ThreadLocal<ArrayList<LockGraphNode>> acquiredLocks =
      new ThreadLocal<ArrayList<LockGraphNode>>() {
        @Override
        protected ArrayList<LockGraphNode> initialValue() {
          return Lists.<LockGraphNode>newArrayListWithCapacity(3);
        }
      };

  /**
   * A Throwable used to record a stack trace that illustrates an example of a specific lock
   * acquisition ordering. The top of the stack trace is truncated such that it starts with the
   * acquisition of the lock in question, e.g.
   *
   *

   * com...ExampleStackTrace: LockB -> LockC
   *   at com...CycleDetectingReentrantLock.lock(CycleDetectingLockFactory.java:443)
   *   at ...
   *   at ...
   *   at com...MyClass.someMethodThatAcquiresLockB(MyClass.java:123)
   *

   */
  private static class ExampleStackTrace extends IllegalStateException {

    static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];

    static final ImmutableSet<String> EXCLUDED_CLASS_NAMES =
        ImmutableSet.of(
            CycleDetectingLockFactory.class.getName(),
            ExampleStackTrace.class.getName(),
            LockGraphNode.class.getName());

    ExampleStackTrace(LockGraphNode node1, LockGraphNode node2) {
      super(node1.getLockName() + " -> " + node2.getLockName());
      StackTraceElement[] origStackTrace = getStackTrace();
      for (int i = 0, n = origStackTrace.length; i < n; i++) {
        if (WithExplicitOrdering.class.getName().equals(origStackTrace[i].getClassName())) {
          // For pre-populated disallowedPriorLocks edges, omit the stack trace.
          setStackTrace(EMPTY_STACK_TRACE);
          break;
        }
        if (!EXCLUDED_CLASS_NAMES.contains(origStackTrace[i].getClassName())) {
          setStackTrace(Arrays.copyOfRange(origStackTrace, i, n));
          break;
        }
      }
    }
  }

  /**
   * Represents a detected cycle in lock acquisition ordering. The exception includes a causal chain
   * of {@code ExampleStackTrace} instances to illustrate the cycle, e.g.
   *
   *

   * com....PotentialDeadlockException: Potential Deadlock from LockC -> ReadWriteA
   *   at ...
   *   at ...
   * Caused by: com...ExampleStackTrace: LockB -> LockC
   *   at ...
   *   at ...
   * Caused by: com...ExampleStackTrace: ReadWriteA -> LockB
   *   at ...
   *   at ...
   *

   *
   *
Instances are logged for the {@code Policies.WARN}, and thrown for {@code Policies.THROW}.
   *
   * @since 13.0
   */
  @Beta
  public static final class PotentialDeadlockException extends ExampleStackTrace {

    private final ExampleStackTrace conflictingStackTrace;

    private PotentialDeadlockException(
        LockGraphNode node1, LockGraphNode node2, ExampleStackTrace conflictingStackTrace) {
      super(node1, node2);
      this.conflictingStackTrace = conflictingStackTrace;
      initCause(conflictingStackTrace);
    }

    public ExampleStackTrace getConflictingStackTrace() {
      return conflictingStackTrace;
    }

    /**
     * Appends the chain of messages from the {@code conflictingStackTrace} to the original {@code
     * message}.
     */
    @Override
    public String getMessage() {
      StringBuilder message = new StringBuilder(super.getMessage());
      for (Throwable t = conflictingStackTrace; t != null; t = t.getCause()) {
        message.append(", ").append(t.getMessage());
      }
      return message.toString();
    }
  }

  /**
   * Internal Lock implementations implement the {@code CycleDetectingLock} interface, allowing the
   * detection logic to treat all locks in the same manner.
   */
  private interface CycleDetectingLock {

    /** @return the {@link LockGraphNode} associated with this lock. */
    LockGraphNode getLockGraphNode();

    /** @return {@code true} if the current thread has acquired this lock. */
    boolean isAcquiredByCurrentThread();
  }

  /**
   * A {@code LockGraphNode} associated with each lock instance keeps track of the directed edges in
   * the lock acquisition graph.
   */
  private static class LockGraphNode {

    /**
     * The map tracking the locks that are known to be acquired before this lock, each associated
     * with an example stack trace. Locks are weakly keyed to allow proper garbage collection when
     * they are no longer referenced.
     */
    final Map<LockGraphNode, ExampleStackTrace> allowedPriorLocks =
        new MapMaker().weakKeys().makeMap();

    /**
     * The map tracking lock nodes that can cause a lock acquisition cycle if acquired before this
     * node.
     */
    final Map<LockGraphNode, PotentialDeadlockException> disallowedPriorLocks =
        new MapMaker().weakKeys().makeMap();

    final String lockName;

    LockGraphNode(String lockName) {
      this.lockName = Preconditions.checkNotNull(lockName);
    }

    String getLockName() {
      return lockName;
    }

    void checkAcquiredLocks(Policy policy, List<LockGraphNode> acquiredLocks) {
      for (int i = 0, size = acquiredLocks.size(); i < size; i++) {
        checkAcquiredLock(policy, acquiredLocks.get(i));
      }
    }

    /**
     * Checks the acquisition-ordering between {@code this}, which is about to be acquired, and the
     * specified {@code acquiredLock}.
     *
     *
When this method returns, the {@code acquiredLock} should be in either the {@code
     * preAcquireLocks} map, for the case in which it is safe to acquire {@code this} after the
     * {@code acquiredLock}, or in the {@code disallowedPriorLocks} map, in which case it is not
     * safe.
     */
    void checkAcquiredLock(Policy policy, LockGraphNode acquiredLock) {
      // checkAcquiredLock() should never be invoked by a lock that has already
      // been acquired. For unordered locks, aboutToAcquire() ensures this by
      // checking isAcquiredByCurrentThread(). For ordered locks, however, this
      // can happen because multiple locks may share the same LockGraphNode. In
      // this situation, throw an IllegalStateException as defined by contract
      // described in the documentation of WithExplicitOrdering.
      Preconditions.checkState(
          this != acquiredLock,
          "Attempted to acquire multiple locks with the same rank %s",
          acquiredLock.getLockName());

      if (allowedPriorLocks.containsKey(acquiredLock)) {
        // The acquisition ordering from "acquiredLock" to "this" has already
        // been verified as safe. In a properly written application, this is
        // the common case.
        return;
      }
      PotentialDeadlockException previousDeadlockException = disallowedPriorLocks.get(acquiredLock);
      if (previousDeadlockException != null) {
        // Previously determined to be an unsafe lock acquisition.
        // Create a new PotentialDeadlockException with the same causal chain
        // (the example cycle) as that of the cached exception.
        PotentialDeadlockException exception =
            new PotentialDeadlockException(
                acquiredLock, this, previousDeadlockException.getConflictingStackTrace());
        policy.handlePotentialDeadlock(exception);
        return;
      }
      // Otherwise, it's the first time seeing this lock relationship. Look for
      // a path from the acquiredLock to this.
      Set<LockGraphNode> seen = Sets.newIdentityHashSet();
      ExampleStackTrace path = acquiredLock.findPathTo(this, seen);

      if (path == null) {
        // this can be safely acquired after the acquiredLock.
        //
        // Note that there is a race condition here which can result in missing
        // a cyclic edge: it's possible for two threads to simultaneous find
        // "safe" edges which together form a cycle. Preventing this race
        // condition efficiently without _introducing_ deadlock is probably
        // tricky. For now, just accept the race condition---missing a warning
        // now and then is still better than having no deadlock detection.
        allowedPriorLocks.put(acquiredLock, new ExampleStackTrace(acquiredLock, this));
      } else {
        // Unsafe acquisition order detected. Create and cache a
        // PotentialDeadlockException.
        PotentialDeadlockException exception =
            new PotentialDeadlockException(acquiredLock, this, path);
        disallowedPriorLocks.put(acquiredLock, exception);
        policy.handlePotentialDeadlock(exception);
      }
    }

    /**
     * Performs a depth-first traversal of the graph edges defined by each node's {@code
     * allowedPriorLocks} to find a path between {@code this} and the specified {@code lock}.
     *
     * @return If a path was found, a chained {@link ExampleStackTrace} illustrating the path to the
     *     {@code lock}, or {@code null} if no path was found.
     */
    @NullableDecl
    private ExampleStackTrace findPathTo(LockGraphNode node, Set<LockGraphNode> seen) {
      if (!seen.add(this)) {
        return null; // Already traversed this node.
      }
      ExampleStackTrace found = allowedPriorLocks.get(node);
      if (found != null) {
        return found; // Found a path ending at the node!
      }
      // Recurse the edges.
      for (Entry<LockGraphNode, ExampleStackTrace> entry : allowedPriorLocks.entrySet()) {
        LockGraphNode preAcquiredLock = entry.getKey();
        found = preAcquiredLock.findPathTo(node, seen);
        if (found != null) {
          // One of this node's allowedPriorLocks found a path. Prepend an
          // ExampleStackTrace(preAcquiredLock, this) to the returned chain of
          // ExampleStackTraces.
          ExampleStackTrace path = new ExampleStackTrace(preAcquiredLock, this);
          path.setStackTrace(entry.getValue().getStackTrace());
          path.initCause(found);
          return path;
        }
      }
      return null;
    }
  }

  /**
   * CycleDetectingLock implementations must call this method before attempting to acquire the lock.
   */
  private void aboutToAcquire(CycleDetectingLock lock) {
    if (!lock.isAcquiredByCurrentThread()) {
      ArrayList<LockGraphNode> acquiredLockList = acquiredLocks.get();
      LockGraphNode node = lock.getLockGraphNode();
      node.checkAcquiredLocks(policy, acquiredLockList);
      acquiredLockList.add(node);
    }
  }

  /**
   * CycleDetectingLock implementations must call this method in a {@code finally} clause after any
   * attempt to change the lock state, including both lock and unlock attempts. Failure to do so can
   * result in corrupting the acquireLocks set.
   */
  private static void lockStateChanged(CycleDetectingLock lock) {
    if (!lock.isAcquiredByCurrentThread()) {
      ArrayList<LockGraphNode> acquiredLockList = acquiredLocks.get();
      LockGraphNode node = lock.getLockGraphNode();
      // Iterate in reverse because locks are usually locked/unlocked in a
      // LIFO order.
      for (int i = acquiredLockList.size() - 1; i >= 0; i--) {
        if (acquiredLockList.get(i) == node) {
          acquiredLockList.remove(i);
          break;
        }
      }
    }
  }

  final class CycleDetectingReentrantLock extends ReentrantLock implements CycleDetectingLock {

    private final LockGraphNode lockGraphNode;

    private CycleDetectingReentrantLock(LockGraphNode lockGraphNode, boolean fair) {
      super(fair);
      this.lockGraphNode = Preconditions.checkNotNull(lockGraphNode);
    }

    ///// CycleDetectingLock methods. /////

    @Override
    public LockGraphNode getLockGraphNode() {
      return lockGraphNode;
    }

    @Override
    public boolean isAcquiredByCurrentThread() {
      return isHeldByCurrentThread();
    }

    ///// Overridden ReentrantLock methods. /////

    @Override
    public void lock() {
      aboutToAcquire(this);
      try {
        super.lock();
      } finally {
        lockStateChanged(this);
      }
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
      aboutToAcquire(this);
      try {
        super.lockInterruptibly();
      } finally {
        lockStateChanged(this);
      }
    }

    @Override
    public boolean tryLock() {
      aboutToAcquire(this);
      try {
        return super.tryLock();
      } finally {
        lockStateChanged(this);
      }
    }

    @Override
    public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
      aboutToAcquire(this);
      try {
        return super.tryLock(timeout, unit);
      } finally {
        lockStateChanged(this);
      }
    }

    @Override
    public void unlock() {
      try {
        super.unlock();
      } finally {
        lockStateChanged(this);
      }
    }
  }

  final class CycleDetectingReentrantReadWriteLock extends ReentrantReadWriteLock
      implements CycleDetectingLock {

    // These ReadLock/WriteLock implementations shadow those in the
    // ReentrantReadWriteLock superclass. They are simply wrappers around the
    // internal Sync object, so this is safe since the shadowed locks are never
    // exposed or used.
    private final CycleDetectingReentrantReadLock readLock;
    private final CycleDetectingReentrantWriteLock writeLock;

    private final LockGraphNode lockGraphNode;

    private CycleDetectingReentrantReadWriteLock(LockGraphNode lockGraphNode, boolean fair) {
      super(fair);
      this.readLock = new CycleDetectingReentrantReadLock(this);
      this.writeLock = new CycleDetectingReentrantWriteLock(this);
      this.lockGraphNode = Preconditions.checkNotNull(lockGraphNode);
    }

    ///// Overridden ReentrantReadWriteLock methods. /////

    @Override
    public ReadLock readLock() {
      return readLock;
    }

    @Override
    public WriteLock writeLock() {
      return writeLock;
    }

    ///// CycleDetectingLock methods. /////

    @Override
    public LockGraphNode getLockGraphNode() {
      return lockGraphNode;
    }

    @Override
    public boolean isAcquiredByCurrentThread() {
      return isWriteLockedByCurrentThread() || getReadHoldCount() > 0;
    }
  }

  private class CycleDetectingReentrantReadLock extends ReentrantReadWriteLock.ReadLock {

    @Weak final CycleDetectingReentrantReadWriteLock readWriteLock;

    CycleDetectingReentrantReadLock(CycleDetectingReentrantReadWriteLock readWriteLock) {
      super(readWriteLock);
      this.readWriteLock = readWriteLock;
    }

    @Override
    public void lock() {
      aboutToAcquire(readWriteLock);
      try {
        super.lock();
      } finally {
        lockStateChanged(readWriteLock);
      }
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
      aboutToAcquire(readWriteLock);
      try {
        super.lockInterruptibly();
      } finally {
        lockStateChanged(readWriteLock);
      }
    }

    @Override
    public boolean tryLock() {
      aboutToAcquire(readWriteLock);
      try {
        return super.tryLock();
      } finally {
        lockStateChanged(readWriteLock);
      }
    }

    @Override
    public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
      aboutToAcquire(readWriteLock);
      try {
        return super.tryLock(timeout, unit);
      } finally {
        lockStateChanged(readWriteLock);
      }
    }

    @Override
    public void unlock() {
      try {
        super.unlock();
      } finally {
        lockStateChanged(readWriteLock);
      }
    }
  }

  private class CycleDetectingReentrantWriteLock extends ReentrantReadWriteLock.WriteLock {

    @Weak final CycleDetectingReentrantReadWriteLock readWriteLock;

    CycleDetectingReentrantWriteLock(CycleDetectingReentrantReadWriteLock readWriteLock) {
      super(readWriteLock);
      this.readWriteLock = readWriteLock;
    }

    @Override
    public void lock() {
      aboutToAcquire(readWriteLock);
      try {
        super.lock();
      } finally {
        lockStateChanged(readWriteLock);
      }
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
      aboutToAcquire(readWriteLock);
      try {
        super.lockInterruptibly();
      } finally {
        lockStateChanged(readWriteLock);
      }
    }

    @Override
    public boolean tryLock() {
      aboutToAcquire(readWriteLock);
      try {
        return super.tryLock();
      } finally {
        lockStateChanged(readWriteLock);
      }
    }

    @Override
    public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
      aboutToAcquire(readWriteLock);
      try {
        return super.tryLock(timeout, unit);
      } finally {
        lockStateChanged(readWriteLock);
      }
    }

    @Override
    public void unlock() {
      try {
        super.unlock();
      } finally {
        lockStateChanged(readWriteLock);
      }
    }
  }
}


/*
 * Copyright (C) 2015 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.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.annotations.GwtCompatible;
import java.util.Comparator;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntConsumer;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.stream.IntStream;
import org.checkerframework.checker.nullness.qual.Nullable;

/** Spliterator utilities for {@code common.collect} internals. */
@GwtCompatible
final class CollectSpliterators {
  private CollectSpliterators() {}

  static <T> Spliterator<T> indexed(int size, int extraCharacteristics, IntFunction<T> function) {
    return indexed(size, extraCharacteristics, function, null);
  }

  static <T> Spliterator<T> indexed(
      int size,
      int extraCharacteristics,
      IntFunction<T> function,
      Comparator<? super T> comparator) {
    if (comparator != null) {
      checkArgument((extraCharacteristics & Spliterator.SORTED) != 0);
    }
    class WithCharacteristics implements Spliterator<T> {
      private final Spliterator.OfInt delegate;

      WithCharacteristics(Spliterator.OfInt delegate) {
        this.delegate = delegate;
      }

      @Override
      public boolean tryAdvance(Consumer<? super T> action) {
        return delegate.tryAdvance((IntConsumer) i -> action.accept(function.apply(i)));
      }

      @Override
      public void forEachRemaining(Consumer<? super T> action) {
        delegate.forEachRemaining((IntConsumer) i -> action.accept(function.apply(i)));
      }

      @Override
      public @Nullable Spliterator<T> trySplit() {
        Spliterator.OfInt split = delegate.trySplit();
        return (split == null) ? null : new WithCharacteristics(split);
      }

      @Override
      public long estimateSize() {
        return delegate.estimateSize();
      }

      @Override
      public int characteristics() {
        return Spliterator.ORDERED
            | Spliterator.SIZED
            | Spliterator.SUBSIZED
            | extraCharacteristics;
      }

      @Override
      public Comparator<? super T> getComparator() {
        if (hasCharacteristics(Spliterator.SORTED)) {
          return comparator;
        } else {
          throw new IllegalStateException();
        }
      }
    }
    return new WithCharacteristics(IntStream.range(0, size).spliterator());
  }

  /**
   * Returns a {@code Spliterator} over the elements of {@code fromSpliterator} mapped by {@code
   * function}.
   */
  static <F, T> Spliterator<T> map(
      Spliterator<F> fromSpliterator, Function<? super F, ? extends T> function) {
    checkNotNull(fromSpliterator);
    checkNotNull(function);
    return new Spliterator<T>() {

      @Override
      public boolean tryAdvance(Consumer<? super T> action) {
        return fromSpliterator.tryAdvance(
            fromElement -> action.accept(function.apply(fromElement)));
      }

      @Override
      public void forEachRemaining(Consumer<? super T> action) {
        fromSpliterator.forEachRemaining(fromElement -> action.accept(function.apply(fromElement)));
      }

      @Override
      public Spliterator<T> trySplit() {
        Spliterator<F> fromSplit = fromSpliterator.trySplit();
        return (fromSplit != null) ? map(fromSplit, function) : null;
      }

      @Override
      public long estimateSize() {
        return fromSpliterator.estimateSize();
      }

      @Override
      public int characteristics() {
        return fromSpliterator.characteristics()
            & ~(Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.SORTED);
      }
    };
  }

  /** Returns a {@code Spliterator} filtered by the specified predicate. */
  static <T> Spliterator<T> filter(Spliterator<T> fromSpliterator, Predicate<? super T> predicate) {
    checkNotNull(fromSpliterator);
    checkNotNull(predicate);
    class Splitr implements Spliterator<T>, Consumer<T> {
      T holder = null;

      @Override
      public void accept(T t) {
        this.holder = t;
      }

      @Override
      public boolean tryAdvance(Consumer<? super T> action) {
        while (fromSpliterator.tryAdvance(this)) {
          try {
            if (predicate.test(holder)) {
              action.accept(holder);
              return true;
            }
          } finally {
            holder = null;
          }
        }
        return false;
      }

      @Override
      public Spliterator<T> trySplit() {
        Spliterator<T> fromSplit = fromSpliterator.trySplit();
        return (fromSplit == null) ? null : filter(fromSplit, predicate);
      }

      @Override
      public long estimateSize() {
        return fromSpliterator.estimateSize() / 2;
      }

      @Override
      public Comparator<? super T> getComparator() {
        return fromSpliterator.getComparator();
      }

      @Override
      public int characteristics() {
        return fromSpliterator.characteristics()
            & (Spliterator.DISTINCT
                | Spliterator.NONNULL
                | Spliterator.ORDERED
                | Spliterator.SORTED);
      }
    }
    return new Splitr();
  }

  /**
   * Returns a {@code Spliterator} that iterates over the elements of the spliterators generated by
   * applying {@code function} to the elements of {@code fromSpliterator}.
   */
  static <F, T> Spliterator<T> flatMap(
      Spliterator<F> fromSpliterator,
      Function<? super F, Spliterator<T>> function,
      int topCharacteristics,
      long topSize) {
    checkArgument(
        (topCharacteristics & Spliterator.SUBSIZED) == 0,
        "flatMap does not support SUBSIZED characteristic");
    checkArgument(
        (topCharacteristics & Spliterator.SORTED) == 0,
        "flatMap does not support SORTED characteristic");
    checkNotNull(fromSpliterator);
    checkNotNull(function);
    class FlatMapSpliterator implements Spliterator<T> {
      @Nullable Spliterator<T> prefix;
      final Spliterator<F> from;
      int characteristics;
      long estimatedSize;

      FlatMapSpliterator(
          Spliterator<T> prefix, Spliterator<F> from, int characteristics, long estimatedSize) {
        this.prefix = prefix;
        this.from = from;
        this.characteristics = characteristics;
        this.estimatedSize = estimatedSize;
      }

      @Override
      public boolean tryAdvance(Consumer<? super T> action) {
        while (true) {
          if (prefix != null && prefix.tryAdvance(action)) {
            if (estimatedSize != Long.MAX_VALUE) {
              estimatedSize--;
            }
            return true;
          } else {
            prefix = null;
          }
          if (!from.tryAdvance(fromElement -> prefix = function.apply(fromElement))) {
            return false;
          }
        }
      }

      @Override
      public void forEachRemaining(Consumer<? super T> action) {
        if (prefix != null) {
          prefix.forEachRemaining(action);
          prefix = null;
        }
        from.forEachRemaining(fromElement -> function.apply(fromElement).forEachRemaining(action));
        estimatedSize = 0;
      }

      @Override
      public Spliterator<T> trySplit() {
        Spliterator<F> fromSplit = from.trySplit();
        if (fromSplit != null) {
          int splitCharacteristics = characteristics & ~Spliterator.SIZED;
          long estSplitSize = estimateSize();
          if (estSplitSize < Long.MAX_VALUE) {
            estSplitSize /= 2;
            this.estimatedSize -= estSplitSize;
            this.characteristics = splitCharacteristics;
          }
          Spliterator<T> result =
              new FlatMapSpliterator(this.prefix, fromSplit, splitCharacteristics, estSplitSize);
          this.prefix = null;
          return result;
        } else if (prefix != null) {
          Spliterator<T> result = prefix;
          this.prefix = null;
          return result;
        } else {
          return null;
        }
      }

      @Override
      public long estimateSize() {
        if (prefix != null) {
          estimatedSize = Math.max(estimatedSize, prefix.estimateSize());
        }
        return Math.max(estimatedSize, 0);
      }

      @Override
      public int characteristics() {
        return characteristics;
      }
    }
    return new FlatMapSpliterator(null, fromSpliterator, topCharacteristics, topSize);
  }
}


/*
 * Copyright (C) 2012 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.math;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.math.DoubleUtils.isFinite;
import static java.lang.Double.NaN;

import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtIncompatible;
import com.google.errorprone.annotations.concurrent.LazyInit;

/**
 * The representation of a linear transformation between real numbers {@code x} and {@code y}.
 * Graphically, this is the specification of a straight line on a plane. The transformation can be
 * expressed as {@code y = m * x + c} for finite {@code m} and {@code c}, unless it is a vertical
 * transformation in which case {@code x} has a constant value for all {@code y}. In the
 * non-vertical case, {@code m} is the slope of the transformation (and a horizontal transformation
 * has zero slope).
 *
 * @author Pete Gillin
 * @since 20.0
 */
@Beta
@GwtIncompatible
public abstract class LinearTransformation {

  /**
   * Start building an instance which maps {@code x = x1} to {@code y = y1}. Both arguments must be
   * finite. Call either {@link LinearTransformationBuilder#and} or {@link
   * LinearTransformationBuilder#withSlope} on the returned object to finish building the instance.
   */
  public static LinearTransformationBuilder mapping(double x1, double y1) {
    checkArgument(isFinite(x1) && isFinite(y1));
    return new LinearTransformationBuilder(x1, y1);
  }

  /**
   * This is an intermediate stage in the construction process. It is returned by {@link
   * LinearTransformation#mapping}. You almost certainly don't want to keep instances around, but
   * instead use method chaining. This represents a single point mapping, i.e. a mapping between one
   * {@code x} and {@code y} value pair.
   *
   * @since 20.0
   */
  public static final class LinearTransformationBuilder {

    private final double x1;
    private final double y1;

    private LinearTransformationBuilder(double x1, double y1) {
      this.x1 = x1;
      this.y1 = y1;
    }

    /**
     * Finish building an instance which also maps {@code x = x2} to {@code y = y2}. These values
     * must not both be identical to the values given in the first mapping. If only the {@code x}
     * values are identical, the transformation is vertical. If only the {@code y} values are
     * identical, the transformation is horizontal (i.e. the slope is zero).
     */
    public LinearTransformation and(double x2, double y2) {
      checkArgument(isFinite(x2) && isFinite(y2));
      if (x2 == x1) {
        checkArgument(y2 != y1);
        return new VerticalLinearTransformation(x1);
      } else {
        return withSlope((y2 - y1) / (x2 - x1));
      }
    }

    /**
     * Finish building an instance with the given slope, i.e. the rate of change of {@code y} with
     * respect to {@code x}. The slope must not be {@code NaN}. It may be infinite, in which case
     * the transformation is vertical. (If it is zero, the transformation is horizontal.)
     */
    public LinearTransformation withSlope(double slope) {
      checkArgument(!Double.isNaN(slope));
      if (isFinite(slope)) {
        double yIntercept = y1 - x1 * slope;
        return new RegularLinearTransformation(slope, yIntercept);
      } else {
        return new VerticalLinearTransformation(x1);
      }
    }
  }

  /**
   * Builds an instance representing a vertical transformation with a constant value of {@code x}.
   * (The inverse of this will be a horizontal transformation.)
   */
  public static LinearTransformation vertical(double x) {
    checkArgument(isFinite(x));
    return new VerticalLinearTransformation(x);
  }

  /**
   * Builds an instance representing a horizontal transformation with a constant value of {@code y}.
   * (The inverse of this will be a vertical transformation.)
   */
  public static LinearTransformation horizontal(double y) {
    checkArgument(isFinite(y));
    double slope = 0.0;
    return new RegularLinearTransformation(slope, y);
  }

  /**
   * Builds an instance for datasets which contains {@link Double#NaN}. The {@link #isHorizontal}
   * and {@link #isVertical} methods return {@code false} and the {@link #slope}, and {@link
   * #transform} methods all return {@link Double#NaN}. The {@link #inverse} method returns the same
   * instance.
   */
  public static LinearTransformation forNaN() {
    return NaNLinearTransformation.INSTANCE;
  }

  /** Returns whether this is a vertical transformation. */
  public abstract boolean isVertical();

  /** Returns whether this is a horizontal transformation. */
  public abstract boolean isHorizontal();

  /**
   * Returns the slope of the transformation, i.e. the rate of change of {@code y} with respect to
   * {@code x}. This must not be called on a vertical transformation (i.e. when {@link
   * #isVertical()} is true).
   */
  public abstract double slope();

  /**
   * Returns the {@code y} corresponding to the given {@code x}. This must not be called on a
   * vertical transformation (i.e. when {@link #isVertical()} is true).
   */
  public abstract double transform(double x);

  /**
   * Returns the inverse linear transformation. The inverse of a horizontal transformation is a
   * vertical transformation, and vice versa. The inverse of the {@link #forNaN} transformation is
   * itself. In all other cases, the inverse is a transformation such that applying both the
   * original transformation and its inverse to a value gives you the original value give-or-take
   * numerical errors. Calling this method multiple times on the same instance will always return
   * the same instance. Calling this method on the result of calling this method on an instance will
   * always return that original instance.
   */
  public abstract LinearTransformation inverse();

  private static final class RegularLinearTransformation extends LinearTransformation {

    final double slope;
    final double yIntercept;

    @LazyInit LinearTransformation inverse;

    RegularLinearTransformation(double slope, double yIntercept) {
      this.slope = slope;
      this.yIntercept = yIntercept;
      this.inverse = null; // to be lazily initialized
    }

    RegularLinearTransformation(double slope, double yIntercept, LinearTransformation inverse) {
      this.slope = slope;
      this.yIntercept = yIntercept;
      this.inverse = inverse;
    }

    @Override
    public boolean isVertical() {
      return false;
    }

    @Override
    public boolean isHorizontal() {
      return (slope == 0.0);
    }

    @Override
    public double slope() {
      return slope;
    }

    @Override
    public double transform(double x) {
      return x * slope + yIntercept;
    }

    @Override
    public LinearTransformation inverse() {
      LinearTransformation result = inverse;
      return (result == null) ? inverse = createInverse() : result;
    }

    @Override
    public String toString() {
      return String.format("y = %g * x + %g", slope, yIntercept);
    }

    private LinearTransformation createInverse() {
      if (slope != 0.0) {
        return new RegularLinearTransformation(1.0 / slope, -1.0 * yIntercept / slope, this);
      } else {
        return new VerticalLinearTransformation(yIntercept, this);
      }
    }
  }

  private static final class VerticalLinearTransformation extends LinearTransformation {

    final double x;

    @LazyInit LinearTransformation inverse;

    VerticalLinearTransformation(double x) {
      this.x = x;
      this.inverse = null; // to be lazily initialized
    }

    VerticalLinearTransformation(double x, LinearTransformation inverse) {
      this.x = x;
      this.inverse = inverse;
    }

    @Override
    public boolean isVertical() {
      return true;
    }

    @Override
    public boolean isHorizontal() {
      return false;
    }

    @Override
    public double slope() {
      throw new IllegalStateException();
    }

    @Override
    public double transform(double x) {
      throw new IllegalStateException();
    }

    @Override
    public LinearTransformation inverse() {
      LinearTransformation result = inverse;
      return (result == null) ? inverse = createInverse() : result;
    }

    @Override
    public String toString() {
      return String.format("x = %g", x);
    }

    private LinearTransformation createInverse() {
      return new RegularLinearTransformation(0.0, x, this);
    }
  }

  private static final class NaNLinearTransformation extends LinearTransformation {

    static final NaNLinearTransformation INSTANCE = new NaNLinearTransformation();

    @Override
    public boolean isVertical() {
      return false;
    }

    @Override
    public boolean isHorizontal() {
      return false;
    }

    @Override
    public double slope() {
      return NaN;
    }

    @Override
    public double transform(double x) {
      return NaN;
    }

    @Override
    public LinearTransformation inverse() {
      return this;
    }

    @Override
    public String toString() {
      return "NaN";
    }
  }
}


/*
 * Copyright (C) 2009 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.base.MoreObjects;
import com.google.common.collect.Tables.AbstractCell;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Spliterator;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collector;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * A {@link Table} whose contents will never change, with many other important properties detailed
 * at {@link ImmutableCollection}.
 *
 *
See the Guava User Guide article on  immutable collections.
 *
 * @author Gregory Kick
 * @since 11.0
 */
@GwtCompatible
public abstract class ImmutableTable<R, C, V> extends AbstractTable<R, C, V>
    implements Serializable {

  /**
   * Returns a {@code Collector} that accumulates elements into an {@code ImmutableTable}. Each
   * input element is mapped to one cell in the returned table, with the rows, columns, and values
   * generated by applying the specified functions.
   *
   *
The returned {@code Collector} will throw a {@code NullPointerException} at collection time
   * if the row, column, or value functions return null on any input.
   *
   * @since 21.0
   */
  public static <T, R, C, V> Collector<T, ?, ImmutableTable<R, C, V>> toImmutableTable(
      Function<? super T, ? extends R> rowFunction,
      Function<? super T, ? extends C> columnFunction,
      Function<? super T, ? extends V> valueFunction) {
    checkNotNull(rowFunction, "rowFunction");
    checkNotNull(columnFunction, "columnFunction");
    checkNotNull(valueFunction, "valueFunction");
    return Collector.of(
        () -> new ImmutableTable.Builder<R, C, V>(),
        (builder, t) ->
            builder.put(rowFunction.apply(t), columnFunction.apply(t), valueFunction.apply(t)),
        (b1, b2) -> b1.combine(b2),
        b -> b.build());
  }

  /**
   * Returns a {@code Collector} that accumulates elements into an {@code ImmutableTable}. Each
   * input element is mapped to one cell in the returned table, with the rows, columns, and values
   * generated by applying the specified functions. If multiple inputs are mapped to the same row
   * and column pair, they will be combined with the specified merging function in encounter order.
   *
   *
The returned {@code Collector} will throw a {@code NullPointerException} at collection time
   * if the row, column, value, or merging functions return null on any input.
   *
   * @since 21.0
   */
  public static <T, R, C, V> Collector<T, ?, ImmutableTable<R, C, V>> toImmutableTable(
      Function<? super T, ? extends R> rowFunction,
      Function<? super T, ? extends C> columnFunction,
      Function<? super T, ? extends V> valueFunction,
      BinaryOperator<V> mergeFunction) {

    checkNotNull(rowFunction, "rowFunction");
    checkNotNull(columnFunction, "columnFunction");
    checkNotNull(valueFunction, "valueFunction");
    checkNotNull(mergeFunction, "mergeFunction");

    /*
     * No mutable Table exactly matches the insertion order behavior of ImmutableTable.Builder, but
     * the Builder can't efficiently support merging of duplicate values.  Getting around this
     * requires some work.
     */

    return Collector.of(
        () -> new CollectorState<R, C, V>()
        /* GWT isn't currently playing nicely with constructor references? */ ,
        (state, input) ->
            state.put(
                rowFunction.apply(input),
                columnFunction.apply(input),
                valueFunction.apply(input),
                mergeFunction),
        (s1, s2) -> s1.combine(s2, mergeFunction),
        state -> state.toTable());
  }

  private static final class CollectorState<R, C, V> {
    final List<MutableCell<R, C, V>> insertionOrder = new ArrayList<>();
    final Table<R, C, MutableCell<R, C, V>> table = HashBasedTable.create();

    void put(R row, C column, V value, BinaryOperator<V> merger) {
      MutableCell<R, C, V> oldCell = table.get(row, column);
      if (oldCell == null) {
        MutableCell<R, C, V> cell = new MutableCell<>(row, column, value);
        insertionOrder.add(cell);
        table.put(row, column, cell);
      } else {
        oldCell.merge(value, merger);
      }
    }

    CollectorState<R, C, V> combine(CollectorState<R, C, V> other, BinaryOperator<V> merger) {
      for (MutableCell<R, C, V> cell : other.insertionOrder) {
        put(cell.getRowKey(), cell.getColumnKey(), cell.getValue(), merger);
      }
      return this;
    }

    ImmutableTable<R, C, V> toTable() {
      return copyOf(insertionOrder);
    }
  }

  private static final class MutableCell<R, C, V> extends AbstractCell<R, C, V> {
    private final R row;
    private final C column;
    private V value;

    MutableCell(R row, C column, V value) {
      this.row = checkNotNull(row, "row");
      this.column = checkNotNull(column, "column");
      this.value = checkNotNull(value, "value");
    }

    @Override
    public R getRowKey() {
      return row;
    }

    @Override
    public C getColumnKey() {
      return column;
    }

    @Override
    public V getValue() {
      return value;
    }

    void merge(V value, BinaryOperator<V> mergeFunction) {
      checkNotNull(value, "value");
      this.value = checkNotNull(mergeFunction.apply(this.value, value), "mergeFunction.apply");
    }
  }

  /** Returns an empty immutable table. */
  @SuppressWarnings("unchecked")
  public static <R, C, V> ImmutableTable<R, C, V> of() {
    return (ImmutableTable<R, C, V>) SparseImmutableTable.EMPTY;
  }

  /** Returns an immutable table containing a single cell. */
  public static <R, C, V> ImmutableTable<R, C, V> of(R rowKey, C columnKey, V value) {
    return new SingletonImmutableTable<>(rowKey, columnKey, value);
  }

  /**
   * Returns an immutable copy of the provided table.
   *
   *
The {@link Table#cellSet()} iteration order of the provided table determines the iteration
   * ordering of all views in the returned table. Note that some views of the original table and the
   * copied table may have different iteration orders. For more control over the ordering, create a
   * {@link Builder} and call {@link Builder#orderRowsBy}, {@link Builder#orderColumnsBy}, and
   * {@link Builder#putAll}
   *
   *
Despite the method name, this method attempts to avoid actually copying the data when it is
   * safe to do so. The exact circumstances under which a copy will or will not be performed are
   * undocumented and subject to change.
   */
  public static <R, C, V> ImmutableTable<R, C, V> copyOf(
      Table<? extends R, ? extends C, ? extends V> table) {
    if (table instanceof ImmutableTable) {
      @SuppressWarnings("unchecked")
      ImmutableTable<R, C, V> parameterizedTable = (ImmutableTable<R, C, V>) table;
      return parameterizedTable;
    } else {
      return copyOf(table.cellSet());
    }
  }

  private static <R, C, V> ImmutableTable<R, C, V> copyOf(
      Iterable<? extends Cell<? extends R, ? extends C, ? extends V>> cells) {
    ImmutableTable.Builder<R, C, V> builder = ImmutableTable.builder();
    for (Cell<? extends R, ? extends C, ? extends V> cell : cells) {
      builder.put(cell);
    }
    return builder.build();
  }

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

  /**
   * Verifies that {@code rowKey}, {@code columnKey} and {@code value} are non-null, and returns a
   * new entry with those values.
   */
  static <R, C, V> Cell<R, C, V> cellOf(R rowKey, C columnKey, V value) {
    return Tables.immutableCell(
        checkNotNull(rowKey, "rowKey"),
        checkNotNull(columnKey, "columnKey"),
        checkNotNull(value, "value"));
  }

  /**
   * A builder for creating immutable table instances, especially {@code public static final} tables
   * ("constant tables"). Example:
   *
   *
{@code
   * static final ImmutableTable<Integer, Character, String> SPREADSHEET =
   *     new ImmutableTable.Builder<Integer, Character, String>()
   *         .put(1, 'A', "foo")
   *         .put(1, 'B', "bar")
   *         .put(2, 'A', "baz")
   *         .build();
   * }

   *
   *
By default, the order in which cells are added to the builder determines the iteration
   * ordering of all views in the returned table, with {@link #putAll} following the {@link
   * Table#cellSet()} iteration order. However, if {@link #orderRowsBy} or {@link #orderColumnsBy}
   * is called, the views are sorted by the supplied comparators.
   *
   *
For empty or single-cell immutable tables, {@link #of()} and {@link #of(Object, Object,
   * Object)} are even more convenient.
   *
   *
Builder instances can be reused - it is safe to call {@link #build} multiple times to build
   * multiple tables in series. Each table is a superset of the tables created before it.
   *
   * @since 11.0
   */
  public static final class Builder<R, C, V> {
    private final List<Cell<R, C, V>> cells = Lists.newArrayList();
    @MonotonicNonNull private Comparator<? super R> rowComparator;
    @MonotonicNonNull private Comparator<? super C> columnComparator;

    /**
     * Creates a new builder. The returned builder is equivalent to the builder generated by {@link
     * ImmutableTable#builder}.
     */
    public Builder() {}

    /** Specifies the ordering of the generated table's rows. */
    @CanIgnoreReturnValue
    public Builder<R, C, V> orderRowsBy(Comparator<? super R> rowComparator) {
      this.rowComparator = checkNotNull(rowComparator, "rowComparator");
      return this;
    }

    /** Specifies the ordering of the generated table's columns. */
    @CanIgnoreReturnValue
    public Builder<R, C, V> orderColumnsBy(Comparator<? super C> columnComparator) {
      this.columnComparator = checkNotNull(columnComparator, "columnComparator");
      return this;
    }

    /**
     * Associates the ({@code rowKey}, {@code columnKey}) pair with {@code value} in the built
     * table. Duplicate key pairs are not allowed and will cause {@link #build} to fail.
     */
    @CanIgnoreReturnValue
    public Builder<R, C, V> put(R rowKey, C columnKey, V value) {
      cells.add(cellOf(rowKey, columnKey, value));
      return this;
    }

    /**
     * Adds the given {@code cell} to the table, making it immutable if necessary. Duplicate key
     * pairs are not allowed and will cause {@link #build} to fail.
     */
    @CanIgnoreReturnValue
    public Builder<R, C, V> put(Cell<? extends R, ? extends C, ? extends V> cell) {
      if (cell instanceof Tables.ImmutableCell) {
        checkNotNull(cell.getRowKey(), "row");
        checkNotNull(cell.getColumnKey(), "column");
        checkNotNull(cell.getValue(), "value");
        @SuppressWarnings("unchecked") // all supported methods are covariant
        Cell<R, C, V> immutableCell = (Cell<R, C, V>) cell;
        cells.add(immutableCell);
      } else {
        put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
      }
      return this;
    }

    /**
     * Associates all of the given table's keys and values in the built table. Duplicate row key
     * column key pairs are not allowed, and will cause {@link #build} to fail.
     *
     * @throws NullPointerException if any key or value in {@code table} is null
     */
    @CanIgnoreReturnValue
    public Builder<R, C, V> putAll(Table<? extends R, ? extends C, ? extends V> table) {
      for (Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) {
        put(cell);
      }
      return this;
    }

    Builder<R, C, V> combine(Builder<R, C, V> other) {
      this.cells.addAll(other.cells);
      return this;
    }

    /**
     * Returns a newly-created immutable table.
     *
     * @throws IllegalArgumentException if duplicate key pairs were added
     */
    public ImmutableTable<R, C, V> build() {
      int size = cells.size();
      switch (size) {
        case 0:
          return of();
        case 1:
          return new SingletonImmutableTable<>(Iterables.getOnlyElement(cells));
        default:
          return RegularImmutableTable.forCells(cells, rowComparator, columnComparator);
      }
    }
  }

  ImmutableTable() {}

  @Override
  public ImmutableSet<Cell<R, C, V>> cellSet() {
    return (ImmutableSet<Cell<R, C, V>>) super.cellSet();
  }

  @Override
  abstract ImmutableSet<Cell<R, C, V>> createCellSet();

  @Override
  final UnmodifiableIterator<Cell<R, C, V>> cellIterator() {
    throw new AssertionError("should never be called");
  }

  @Override
  final Spliterator<Cell<R, C, V>> cellSpliterator() {
    throw new AssertionError("should never be called");
  }

  @Override
  public ImmutableCollection<V> values() {
    return (ImmutableCollection<V>) super.values();
  }

  @Override
  abstract ImmutableCollection<V> createValues();

  @Override
  final Iterator<V> valuesIterator() {
    throw new AssertionError("should never be called");
  }

  /**
   * {@inheritDoc}
   *
   * @throws NullPointerException if {@code columnKey} is {@code null}
   */
  @Override
  public ImmutableMap<R, V> column(C columnKey) {
    checkNotNull(columnKey, "columnKey");
    return MoreObjects.firstNonNull(
        (ImmutableMap<R, V>) columnMap().get(columnKey), ImmutableMap.<R, V>of());
  }

  @Override
  public ImmutableSet<C> columnKeySet() {
    return columnMap().keySet();
  }

  /**
   * {@inheritDoc}
   *
   *
The value {@code Map<R, V>} instances in the returned map are {@link ImmutableMap} instances
   * as well.
   */
  @Override
  public abstract ImmutableMap<C, Map<R, V>> columnMap();

  /**
   * {@inheritDoc}
   *
   * @throws NullPointerException if {@code rowKey} is {@code null}
   */
  @Override
  public ImmutableMap<C, V> row(R rowKey) {
    checkNotNull(rowKey, "rowKey");
    return MoreObjects.firstNonNull(
        (ImmutableMap<C, V>) rowMap().get(rowKey), ImmutableMap.<C, V>of());
  }

  @Override
  public ImmutableSet<R> rowKeySet() {
    return rowMap().keySet();
  }

  /**
   * {@inheritDoc}
   *
   *
The value {@code Map<C, V>} instances in the returned map are {@link ImmutableMap} instances
   * as well.
   */
  @Override
  public abstract ImmutableMap<R, Map<C, V>> rowMap();

  @Override
  public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) {
    return get(rowKey, columnKey) != null;
  }

  @Override
  public boolean containsValue(@Nullable Object value) {
    return values().contains(value);
  }

  /**
   * Guaranteed to throw an exception and leave the table unmodified.
   *
   * @throws UnsupportedOperationException always
   * @deprecated Unsupported operation.
   */
  @Deprecated
  @Override
  public final void clear() {
    throw new UnsupportedOperationException();
  }

  /**
   * Guaranteed to throw an exception and leave the table unmodified.
   *
   * @throws UnsupportedOperationException always
   * @deprecated Unsupported operation.
   */
  @CanIgnoreReturnValue
  @Deprecated
  @Override
  public final V put(R rowKey, C columnKey, V value) {
    throw new UnsupportedOperationException();
  }

  /**
   * Guaranteed to throw an exception and leave the table unmodified.
   *
   * @throws UnsupportedOperationException always
   * @deprecated Unsupported operation.
   */
  @Deprecated
  @Override
  public final void putAll(Table<? extends R, ? extends C, ? extends V> table) {
    throw new UnsupportedOperationException();
  }

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

  /** Creates the common serialized form for this table. */
  abstract SerializedForm createSerializedForm();

  /**
   * Serialized type for all ImmutableTable instances. It captures the logical contents and
   * preserves iteration order of all views.
   */
  static final class SerializedForm implements Serializable {
    private final Object[] rowKeys;
    private final Object[] columnKeys;

    private final Object[] cellValues;
    private final int[] cellRowIndices;
    private final int[] cellColumnIndices;

    private SerializedForm(
        Object[] rowKeys,
        Object[] columnKeys,
        Object[] cellValues,
        int[] cellRowIndices,
        int[] cellColumnIndices) {
      this.rowKeys = rowKeys;
      this.columnKeys = columnKeys;
      this.cellValues = cellValues;
      this.cellRowIndices = cellRowIndices;
      this.cellColumnIndices = cellColumnIndices;
    }

    static SerializedForm create(
        ImmutableTable<?, ?, ?> table, int[] cellRowIndices, int[] cellColumnIndices) {
      return new SerializedForm(
          table.rowKeySet().toArray(),
          table.columnKeySet().toArray(),
          table.values().toArray(),
          cellRowIndices,
          cellColumnIndices);
    }

    Object readResolve() {
      if (cellValues.length == 0) {
        return of();
      }
      if (cellValues.length == 1) {
        return of(rowKeys[0], columnKeys[0], cellValues[0]);
      }
      ImmutableList.Builder<Cell<Object, Object, Object>> cellListBuilder =
          new ImmutableList.Builder<>(cellValues.length);
      for (int i = 0; i < cellValues.length; i++) {
        cellListBuilder.add(
            cellOf(rowKeys[cellRowIndices[i]], columnKeys[cellColumnIndices[i]], cellValues[i]));
      }
      return RegularImmutableTable.forOrderedComponents(
          cellListBuilder.build(), ImmutableSet.copyOf(rowKeys), ImmutableSet.copyOf(columnKeys));
    }

    private static final long serialVersionUID = 0;
  }

  final Object writeReplace() {
    return createSerializedForm();
  }
}


/*
 * Copyright (C) 2012 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.checkArgument;
import static com.google.common.base.Preconditions.checkElementIndex;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_HIGHER;
import static com.google.common.collect.SortedLists.KeyAbsentBehavior.NEXT_LOWER;
import static com.google.common.collect.SortedLists.KeyPresentBehavior.ANY_PRESENT;

import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.collect.SortedLists.KeyAbsentBehavior;
import com.google.common.collect.SortedLists.KeyPresentBehavior;
import com.google.common.primitives.Ints;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.concurrent.LazyInit;
import java.io.Serializable;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import org.checkerframework.checker.nullness.compatqual.MonotonicNonNullDecl;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;

/**
 * A {@link RangeSet} whose contents will never change, with many other important properties
 * detailed at {@link ImmutableCollection}.
 *
 * @author Louis Wasserman
 * @since 14.0
 */
@Beta
@GwtIncompatible
public final class ImmutableRangeSet<C extends Comparable> extends AbstractRangeSet<C>
    implements Serializable {

  private static final ImmutableRangeSet<Comparable<?>> EMPTY =
      new ImmutableRangeSet<>(ImmutableList.<Range<Comparable<?>>>of());

  private static final ImmutableRangeSet<Comparable<?>> ALL =
      new ImmutableRangeSet<>(ImmutableList.of(Range.<Comparable<?>>all()));

  /** Returns an empty immutable range set. */
  @SuppressWarnings("unchecked")
  public static <C extends Comparable> ImmutableRangeSet<C> of() {
    return (ImmutableRangeSet<C>) EMPTY;
  }

  /**
   * Returns an immutable range set containing the specified single range. If {@link Range#isEmpty()
   * range.isEmpty()}, this is equivalent to {@link ImmutableRangeSet#of()}.
   */
  public static <C extends Comparable> ImmutableRangeSet<C> of(Range<C> range) {
    checkNotNull(range);
    if (range.isEmpty()) {
      return of();
    } else if (range.equals(Range.all())) {
      return all();
    } else {
      return new ImmutableRangeSet<C>(ImmutableList.of(range));
    }
  }

  /** Returns an immutable range set containing the single range {@link Range#all()}. */
  @SuppressWarnings("unchecked")
  static <C extends Comparable> ImmutableRangeSet<C> all() {
    return (ImmutableRangeSet<C>) ALL;
  }

  /** Returns an immutable copy of the specified {@code RangeSet}. */
  public static <C extends Comparable> ImmutableRangeSet<C> copyOf(RangeSet<C> rangeSet) {
    checkNotNull(rangeSet);
    if (rangeSet.isEmpty()) {
      return of();
    } else if (rangeSet.encloses(Range.<C>all())) {
      return all();
    }

    if (rangeSet instanceof ImmutableRangeSet) {
      ImmutableRangeSet<C> immutableRangeSet = (ImmutableRangeSet<C>) rangeSet;
      if (!immutableRangeSet.isPartialView()) {
        return immutableRangeSet;
      }
    }
    return new ImmutableRangeSet<C>(ImmutableList.copyOf(rangeSet.asRanges()));
  }

  /**
   * Returns an {@code ImmutableRangeSet} containing each of the specified disjoint ranges.
   * Overlapping ranges and empty ranges are forbidden, though adjacent ranges are permitted and
   * will be merged.
   *
   * @throws IllegalArgumentException if any ranges overlap or are empty
   * @since 21.0
   */
  public static <C extends Comparable<?>> ImmutableRangeSet<C> copyOf(Iterable<Range<C>> ranges) {
    return new ImmutableRangeSet.Builder<C>().addAll(ranges).build();
  }

  /**
   * Returns an {@code ImmutableRangeSet} representing the union of the specified ranges.
   *
   *
This is the smallest {@code RangeSet} which encloses each of the specified ranges. Duplicate
   * or connected ranges are permitted, and will be coalesced in the result.
   *
   * @since 21.0
   */
  public static <C extends Comparable<?>> ImmutableRangeSet<C> unionOf(Iterable<Range<C>> ranges) {
    return copyOf(TreeRangeSet.create(ranges));
  }

  ImmutableRangeSet(ImmutableList<Range<C>> ranges) {
    this.ranges = ranges;
  }

  private ImmutableRangeSet(ImmutableList<Range<C>> ranges, ImmutableRangeSet<C> complement) {
    this.ranges = ranges;
    this.complement = complement;
  }

  private final transient ImmutableList<Range<C>> ranges;

  @Override
  public boolean intersects(Range<C> otherRange) {
    int ceilingIndex =
        SortedLists.binarySearch(
            ranges,
            Range.<C>lowerBoundFn(),
            otherRange.lowerBound,
            Ordering.natural(),
            ANY_PRESENT,
            NEXT_HIGHER);
    if (ceilingIndex < ranges.size()
        && ranges.get(ceilingIndex).isConnected(otherRange)
        && !ranges.get(ceilingIndex).intersection(otherRange).isEmpty()) {
      return true;
    }
    return ceilingIndex > 0
        && ranges.get(ceilingIndex - 1).isConnected(otherRange)
        && !ranges.get(ceilingIndex - 1).intersection(otherRange).isEmpty();
  }

  @Override
  public boolean encloses(Range<C> otherRange) {
    int index =
        SortedLists.binarySearch(
            ranges,
            Range.<C>lowerBoundFn(),
            otherRange.lowerBound,
            Ordering.natural(),
            ANY_PRESENT,
            NEXT_LOWER);
    return index != -1 && ranges.get(index).encloses(otherRange);
  }

  @Override
  public Range<C> rangeContaining(C value) {
    int index =
        SortedLists.binarySearch(
            ranges,
            Range.<C>lowerBoundFn(),
            Cut.belowValue(value),
            Ordering.natural(),
            ANY_PRESENT,
            NEXT_LOWER);
    if (index != -1) {
      Range<C> range = ranges.get(index);
      return range.contains(value) ? range : null;
    }
    return null;
  }

  @Override
  public Range<C> span() {
    if (ranges.isEmpty()) {
      throw new NoSuchElementException();
    }
    return Range.create(ranges.get(0).lowerBound, ranges.get(ranges.size() - 1).upperBound);
  }

  @Override
  public boolean isEmpty() {
    return ranges.isEmpty();
  }

  /**
   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
   *
   * @throws UnsupportedOperationException always
   * @deprecated Unsupported operation.
   */
  @Deprecated
  @Override
  public void add(Range<C> range) {
    throw new UnsupportedOperationException();
  }

  /**
   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
   *
   * @throws UnsupportedOperationException always
   * @deprecated Unsupported operation.
   */
  @Deprecated
  @Override
  public void addAll(RangeSet<C> other) {
    throw new UnsupportedOperationException();
  }

  /**
   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
   *
   * @throws UnsupportedOperationException always
   * @deprecated Unsupported operation.
   */
  @Deprecated
  @Override
  public void addAll(Iterable<Range<C>> other) {
    throw new UnsupportedOperationException();
  }

  /**
   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
   *
   * @throws UnsupportedOperationException always
   * @deprecated Unsupported operation.
   */
  @Deprecated
  @Override
  public void remove(Range<C> range) {
    throw new UnsupportedOperationException();
  }

  /**
   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
   *
   * @throws UnsupportedOperationException always
   * @deprecated Unsupported operation.
   */
  @Deprecated
  @Override
  public void removeAll(RangeSet<C> other) {
    throw new UnsupportedOperationException();
  }

  /**
   * Guaranteed to throw an exception and leave the {@code RangeSet} unmodified.
   *
   * @throws UnsupportedOperationException always
   * @deprecated Unsupported operation.
   */
  @Deprecated
  @Override
  public void removeAll(Iterable<Range<C>> other) {
    throw new UnsupportedOperationException();
  }

  @Override
  public ImmutableSet<Range<C>> asRanges() {
    if (ranges.isEmpty()) {
      return ImmutableSet.of();
    }
    return new RegularImmutableSortedSet<>(ranges, Range.<C>rangeLexOrdering());
  }

  @Override
  public ImmutableSet<Range<C>> asDescendingSetOfRanges() {
    if (ranges.isEmpty()) {
      return ImmutableSet.of();
    }
    return new RegularImmutableSortedSet<>(ranges.reverse(), Range.<C>rangeLexOrdering().reverse());
  }

  @LazyInit private transient ImmutableRangeSet<C> complement;

  private final class ComplementRanges extends ImmutableList<Range<C>> {
    // True if the "positive" range set is empty or bounded below.
    private final boolean positiveBoundedBelow;

    // True if the "positive" range set is empty or bounded above.
    private final boolean positiveBoundedAbove;

    private final int size;

    ComplementRanges() {
      this.positiveBoundedBelow = ranges.get(0).hasLowerBound();
      this.positiveBoundedAbove = Iterables.getLast(ranges).hasUpperBound();

      int size = ranges.size() - 1;
      if (positiveBoundedBelow) {
        size++;
      }
      if (positiveBoundedAbove) {
        size++;
      }
      this.size = size;
    }

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

    @Override
    public Range<C> get(int index) {
      checkElementIndex(index, size);

      Cut<C> lowerBound;
      if (positiveBoundedBelow) {
        lowerBound = (index == 0) ? Cut.<C>belowAll() : ranges.get(index - 1).upperBound;
      } else {
        lowerBound = ranges.get(index).upperBound;
      }

      Cut<C> upperBound;
      if (positiveBoundedAbove && index == size - 1) {
        upperBound = Cut.<C>aboveAll();
      } else {
        upperBound = ranges.get(index + (positiveBoundedBelow ? 0 : 1)).lowerBound;
      }

      return Range.create(lowerBound, upperBound);
    }

    @Override
    boolean isPartialView() {
      return true;
    }
  }

  @Override
  public ImmutableRangeSet<C> complement() {
    ImmutableRangeSet<C> result = complement;
    if (result != null) {
      return result;
    } else if (ranges.isEmpty()) {
      return complement = all();
    } else if (ranges.size() == 1 && ranges.get(0).equals(Range.all())) {
      return complement = of();
    } else {
      ImmutableList<Range<C>> complementRanges = new ComplementRanges();
      result = complement = new ImmutableRangeSet<C>(complementRanges, this);
    }
    return result;
  }

  /**
   * Returns a new range set consisting of the union of this range set and {@code other}.
   *
   *
This is essentially the same as {@code TreeRangeSet.create(this).addAll(other)} except it
   * returns an {@code ImmutableRangeSet}.
   *
   * @since 21.0
   */
  public ImmutableRangeSet<C> union(RangeSet<C> other) {
    return unionOf(Iterables.concat(asRanges(), other.asRanges()));
  }

  /**
   * Returns a new range set consisting of the intersection of this range set and {@code other}.
   *
   *
This is essentially the same as {@code
   * TreeRangeSet.create(this).removeAll(other.complement())} except it returns an {@code
   * ImmutableRangeSet}.
   *
   * @since 21.0
   */
  public ImmutableRangeSet<C> intersection(RangeSet<C> other) {
    RangeSet<C> copy = TreeRangeSet.create(this);
    copy.removeAll(other.complement());
    return copyOf(copy);
  }

  /**
   * Returns a new range set consisting of the difference of this range set and {@code other}.
   *
   *
This is essentially the same as {@code TreeRangeSet.create(this).removeAll(other)} except it
   * returns an {@code ImmutableRangeSet}.
   *
   * @since 21.0
   */
  public ImmutableRangeSet<C> difference(RangeSet<C> other) {
    RangeSet<C> copy = TreeRangeSet.create(this);
    copy.removeAll(other);
    return copyOf(copy);
  }

  /**
   * Returns a list containing the nonempty intersections of {@code range} with the ranges in this
   * range set.
   */
  private ImmutableList<Range<C>> intersectRanges(final Range<C> range) {
    if (ranges.isEmpty() || range.isEmpty()) {
      return ImmutableList.of();
    } else if (range.encloses(span())) {
      return ranges;
    }

    final int fromIndex;
    if (range.hasLowerBound()) {
      fromIndex =
          SortedLists.binarySearch(
              ranges,
              Range.<C>upperBoundFn(),
              range.lowerBound,
              KeyPresentBehavior.FIRST_AFTER,
              KeyAbsentBehavior.NEXT_HIGHER);
    } else {
      fromIndex = 0;
    }

    int toIndex;
    if (range.hasUpperBound()) {
      toIndex =
          SortedLists.binarySearch(
              ranges,
              Range.<C>lowerBoundFn(),
              range.upperBound,
              KeyPresentBehavior.FIRST_PRESENT,
              KeyAbsentBehavior.NEXT_HIGHER);
    } else {
      toIndex = ranges.size();
    }
    final int length = toIndex - fromIndex;
    if (length == 0) {
      return ImmutableList.of();
    } else {
      return new ImmutableList<Range<C>>() {
        @Override
        public int size() {
          return length;
        }

        @Override
        public Range<C> get(int index) {
          checkElementIndex(index, length);
          if (index == 0 || index == length - 1) {
            return ranges.get(index + fromIndex).intersection(range);
          } else {
            return ranges.get(index + fromIndex);
          }
        }

        @Override
        boolean isPartialView() {
          return true;
        }
      };
    }
  }

  /** Returns a view of the intersection of this range set with the given range. */
  @Override
  public ImmutableRangeSet<C> subRangeSet(Range<C> range) {
    if (!isEmpty()) {
      Range<C> span = span();
      if (range.encloses(span)) {
        return this;
      } else if (range.isConnected(span)) {
        return new ImmutableRangeSet<C>(intersectRanges(range));
      }
    }
    return of();
  }

  /**
   * Returns an {@link ImmutableSortedSet} containing the same values in the given domain
   * {@linkplain RangeSet#contains contained} by this range set.
   *
   *
Note: {@code a.asSet(d).equals(b.asSet(d))} does not imply {@code a.equals(b)}! For
   * example, {@code a} and {@code b} could be {@code [2..4]} and {@code (1..5)}, or the empty
   * ranges {@code [3..3)} and {@code [4..4)}.
   *
   *
Warning: Be extremely careful what you do with the {@code asSet} view of a large
   * range set (such as {@code ImmutableRangeSet.of(Range.greaterThan(0))}). Certain operations on
   * such a set can be performed efficiently, but others (such as {@link Set#hashCode} or {@link
   * Collections#frequency}) can cause major performance problems.
   *
   *
The returned set's {@link Object#toString} method returns a short-hand form of the set's
   * contents, such as {@code "[1..100]}"}.
   *
   * @throws IllegalArgumentException if neither this range nor the domain has a lower bound, or if
   *     neither has an upper bound
   */
  public ImmutableSortedSet<C> asSet(DiscreteDomain<C> domain) {
    checkNotNull(domain);
    if (isEmpty()) {
      return ImmutableSortedSet.of();
    }
    Range<C> span = span().canonical(domain);
    if (!span.hasLowerBound()) {
      // according to the spec of canonical, neither this ImmutableRangeSet nor
      // the range have a lower bound
      throw new IllegalArgumentException(
          "Neither the DiscreteDomain nor this range set are bounded below");
    } else if (!span.hasUpperBound()) {
      try {
        domain.maxValue();
      } catch (NoSuchElementException e) {
        throw new IllegalArgumentException(
            "Neither the DiscreteDomain nor this range set are bounded above");
      }
    }

    return new AsSet(domain);
  }

  private final class AsSet extends ImmutableSortedSet<C> {
    private final DiscreteDomain<C> domain;

    AsSet(DiscreteDomain<C> domain) {
      super(Ordering.natural());
      this.domain = domain;
    }

    @MonotonicNonNullDecl private transient Integer size;

    @Override
    public int size() {
      // racy single-check idiom
      Integer result = size;
      if (result == null) {
        long total = 0;
        for (Range<C> range : ranges) {
          total += ContiguousSet.create(range, domain).size();
          if (total >= Integer.MAX_VALUE) {
            break;
          }
        }
        result = size = Ints.saturatedCast(total);
      }
      return result.intValue();
    }

    @Override
    public UnmodifiableIterator<C> iterator() {
      return new AbstractIterator<C>() {
        final Iterator<Range<C>> rangeItr = ranges.iterator();
        Iterator<C> elemItr = Iterators.emptyIterator();

        @Override
        protected C computeNext() {
          while (!elemItr.hasNext()) {
            if (rangeItr.hasNext()) {
              elemItr = ContiguousSet.create(rangeItr.next(), domain).iterator();
            } else {
              return endOfData();
            }
          }
          return elemItr.next();
        }
      };
    }

    @Override
    @GwtIncompatible("NavigableSet")
    public UnmodifiableIterator<C> descendingIterator() {
      return new AbstractIterator<C>() {
        final Iterator<Range<C>> rangeItr = ranges.reverse().iterator();
        Iterator<C> elemItr = Iterators.emptyIterator();

        @Override
        protected C computeNext() {
          while (!elemItr.hasNext()) {
            if (rangeItr.hasNext()) {
              elemItr = ContiguousSet.create(rangeItr.next(), domain).descendingIterator();
            } else {
              return endOfData();
            }
          }
          return elemItr.next();
        }
      };
    }

    ImmutableSortedSet<C> subSet(Range<C> range) {
      return subRangeSet(range).asSet(domain);
    }

    @Override
    ImmutableSortedSet<C> headSetImpl(C toElement, boolean inclusive) {
      return subSet(Range.upTo(toElement, BoundType.forBoolean(inclusive)));
    }

    @Override
    ImmutableSortedSet<C> subSetImpl(
        C fromElement, boolean fromInclusive, C toElement, boolean toInclusive) {
      if (!fromInclusive && !toInclusive && Range.compareOrThrow(fromElement, toElement) == 0) {
        return ImmutableSortedSet.of();
      }
      return subSet(
          Range.range(
              fromElement, BoundType.forBoolean(fromInclusive),
              toElement, BoundType.forBoolean(toInclusive)));
    }

    @Override
    ImmutableSortedSet<C> tailSetImpl(C fromElement, boolean inclusive) {
      return subSet(Range.downTo(fromElement, BoundType.forBoolean(inclusive)));
    }

    @Override
    public boolean contains(@NullableDecl Object o) {
      if (o == null) {
        return false;
      }
      try {
        @SuppressWarnings("unchecked") // we catch CCE's
        C c = (C) o;
        return ImmutableRangeSet.this.contains(c);
      } catch (ClassCastException e) {
        return false;
      }
    }

    @Override
    int indexOf(Object target) {
      if (contains(target)) {
        @SuppressWarnings("unchecked") // if it's contained, it's definitely a C
        C c = (C) target;
        long total = 0;
        for (Range<C> range : ranges) {
          if (range.contains(c)) {
            return Ints.saturatedCast(total + ContiguousSet.create(range, domain).indexOf(c));
          } else {
            total += ContiguousSet.create(range, domain).size();
          }
        }
        throw new AssertionError("impossible");
      }
      return -1;
    }

    @Override
    ImmutableSortedSet<C> createDescendingSet() {
      return new DescendingImmutableSortedSet<C>(this);
    }

    @Override
    boolean isPartialView() {
      return ranges.isPartialView();
    }

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

    @Override
    Object writeReplace() {
      return new AsSetSerializedForm<C>(ranges, domain);
    }
  }

  private static class AsSetSerializedForm<C extends Comparable> implements Serializable {
    private final ImmutableList<Range<C>> ranges;
    private final DiscreteDomain<C> domain;

    AsSetSerializedForm(ImmutableList<Range<C>> ranges, DiscreteDomain<C> domain) {
      this.ranges = ranges;
      this.domain = domain;
    }

    Object readResolve() {
      return new ImmutableRangeSet<C>(ranges).asSet(domain);
    }
  }

  /**
   * Returns {@code true} if this immutable range set's implementation contains references to
   * user-created objects that aren't accessible via this range set's methods. This is generally
   * used to determine whether {@code copyOf} implementations should make an explicit copy to avoid
   * memory leaks.
   */
  boolean isPartialView() {
    return ranges.isPartialView();
  }

  /** Returns a new builder for an immutable range set. */
  public static <C extends Comparable<?>> Builder<C> builder() {
    return new Builder<C>();
  }

  /**
   * A builder for immutable range sets.
   *
   * @since 14.0
   */
  public static class Builder<C extends Comparable<?>> {
    private final List<Range<C>> ranges;

    public Builder() {
      this.ranges = Lists.newArrayList();
    }

    // TODO(lowasser): consider adding union, in addition to add, that does allow overlap

    /**
     * Add the specified range to this builder. Adjacent ranges are permitted and will be merged,
     * but overlapping ranges will cause an exception when {@link #build()} is called.
     *
     * @throws IllegalArgumentException if {@code range} is empty
     */
    @CanIgnoreReturnValue
    public Builder<C> add(Range<C> range) {
      checkArgument(!range.isEmpty(), "range must not be empty, but was %s", range);
      ranges.add(range);
      return this;
    }

    /**
     * Add all ranges from the specified range set to this builder. Adjacent ranges are permitted
     * and will be merged, but overlapping ranges will cause an exception when {@link #build()} is
     * called.
     */
    @CanIgnoreReturnValue
    public Builder<C> addAll(RangeSet<C> ranges) {
      return addAll(ranges.asRanges());
    }

    /**
     * Add all of the specified ranges to this builder. Adjacent ranges are permitted and will be
     * merged, but overlapping ranges will cause an exception when {@link #build()} is called.
     *
     * @throws IllegalArgumentException if any inserted ranges are empty
     * @since 21.0
     */
    @CanIgnoreReturnValue
    public Builder<C> addAll(Iterable<Range<C>> ranges) {
      for (Range<C> range : ranges) {
        add(range);
      }
      return this;
    }

    /**
     * Returns an {@code ImmutableRangeSet} containing the ranges added to this builder.
     *
     * @throws IllegalArgumentException if any input ranges have nonempty overlap
     */
    public ImmutableRangeSet<C> build() {
      ImmutableList.Builder<Range<C>> mergedRangesBuilder =
          new ImmutableList.Builder<>(ranges.size());
      Collections.sort(ranges, Range.<C>rangeLexOrdering());
      PeekingIterator<Range<C>> peekingItr = Iterators.peekingIterator(ranges.iterator());
      while (peekingItr.hasNext()) {
        Range<C> range = peekingItr.next();
        while (peekingItr.hasNext()) {
          Range<C> nextRange = peekingItr.peek();
          if (range.isConnected(nextRange)) {
            checkArgument(
                range.intersection(nextRange).isEmpty(),
                "Overlapping ranges not permitted but found %s overlapping %s",
                range,
                nextRange);
            range = range.span(peekingItr.next());
          } else {
            break;
          }
        }
        mergedRangesBuilder.add(range);
      }
      ImmutableList<Range<C>> mergedRanges = mergedRangesBuilder.build();
      if (mergedRanges.isEmpty()) {
        return of();
      } else if (mergedRanges.size() == 1
          && Iterables.getOnlyElement(mergedRanges).equals(Range.all())) {
        return all();
      } else {
        return new ImmutableRangeSet<C>(mergedRanges);
      }
    }
  }

  private static final class SerializedForm<C extends Comparable> implements Serializable {
    private final ImmutableList<Range<C>> ranges;

    SerializedForm(ImmutableList<Range<C>> ranges) {
      this.ranges = ranges;
    }

    Object readResolve() {
      if (ranges.isEmpty()) {
        return of();
      } else if (ranges.equals(ImmutableList.of(Range.all()))) {
        return all();
      } else {
        return new ImmutableRangeSet<C>(ranges);
      }
    }
  }

  Object writeReplace() {
    return new SerializedForm<C>(ranges);
  }
}