// Path: guava/src/com/google/common/collect/MapMaker.java
// enum RemovalCause {
//   /**
//    * The entry was manually removed by the user. This can result from the user invoking
//    * {@link Map#remove}, {@link ConcurrentMap#remove}, or {@link java.util.Iterator#remove}.
//    */
//   EXPLICIT {
//     @Override
//     boolean wasEvicted() {
//       return false;
//     }
//   },
// 
//   /**
//    * The entry itself was not actually removed, but its value was replaced by the user. This can
//    * result from the user invoking {@link Map#put}, {@link Map#putAll},
//    * {@link ConcurrentMap#replace(Object, Object)}, or
//    * {@link ConcurrentMap#replace(Object, Object, Object)}.
//    */
//   REPLACED {
//     @Override
//     boolean wasEvicted() {
//       return false;
//     }
//   },
// 
//   /**
//    * The entry was removed automatically because its key or value was garbage-collected. This
//    * can occur when using {@link #softKeys}, {@link #softValues}, {@link #weakKeys}, or {@link
//    * #weakValues}.
//    */
//   COLLECTED {
//     @Override
//     boolean wasEvicted() {
//       return true;
//     }
//   },
// 
//   /**
//    * The entry's expiration timestamp has passed. This can occur when using {@link
//    * #expireAfterWrite} or {@link #expireAfterAccess}.
//    */
//   EXPIRED {
//     @Override
//     boolean wasEvicted() {
//       return true;
//     }
//   },
// 
//   /**
//    * The entry was evicted due to size constraints. This can occur when using {@link
//    * #maximumSize}.
//    */
//   SIZE {
//     @Override
//     boolean wasEvicted() {
//       return true;
//     }
//   };
// 
//   /**
//    * Returns {@code true} if there was an automatic removal due to eviction (the cause is neither
//    * {@link #EXPLICIT} nor {@link #REPLACED}).
//    */
//   abstract boolean wasEvicted();
// }
// 
// Path: guava/src/com/google/common/collect/MapMaker.java
// interface RemovalListener<K, V> {
//   /**
//    * Notifies the listener that a removal occurred at some point in the past.
//    */
//   void onRemoval(RemovalNotification<K, V> notification);
// }
// 
// Path: guava/src/com/google/common/collect/MapMaker.java
// static final class RemovalNotification<K, V> extends ImmutableEntry<K, V> {
//   private static final long serialVersionUID = 0;
// 
//   private final RemovalCause cause;
// 
//   RemovalNotification(@Nullable K key, @Nullable V value, RemovalCause cause) {
//     super(key, value);
//     this.cause = cause;
//   }
// 
//   /**
//    * Returns the cause for which the entry was removed.
//    */
//   public RemovalCause getCause() {
//     return cause;
//   }
// 
//   /**
//    * Returns {@code true} if there was an automatic removal due to eviction (the cause is neither
//    * {@link RemovalCause#EXPLICIT} nor {@link RemovalCause#REPLACED}).
//    */
//   public boolean wasEvicted() {
//     return cause.wasEvicted();
//   }
// }


import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Equivalence;
import com.google.common.base.Equivalences;
import com.google.common.base.Ticker;
import com.google.common.collect.GenericMapMaker.NullListener;
import com.google.common.collect.MapMaker.RemovalCause;
import com.google.common.collect.MapMaker.RemovalListener;
import com.google.common.collect.MapMaker.RemovalNotification;
import com.google.common.primitives.Ints;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractQueue;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
      // might overflow, but that's okay (see isExpired())
      entry.setExpirationTime(map.ticker.read() + expirationNanos);
    }

    /**
     * Cleanup expired entries when the lock is available.
     */
    void tryExpireEntries() {
      if (tryLock()) {
        try {
          expireEntries();
        } finally {
          unlock();
          // don't call postWriteCleanup as we're in a read
        }
      }
    }

    @GuardedBy("Segment.this")
    void expireEntries() {
      drainRecencyQueue();

      if (expirationQueue.isEmpty()) {
        // There's no point in calling nanoTime() if we have no entries to
        // expire.
        return;
      }
      long now = map.ticker.read();
      ReferenceEntry<K, V> e;
      while ((e = expirationQueue.peek()) != null && map.isExpired(e, now)) {
// Path: guava/src/com/google/common/collect/MapMaker.java
// enum RemovalCause {
//   /**
//    * The entry was manually removed by the user. This can result from the user invoking
//    * {@link Map#remove}, {@link ConcurrentMap#remove}, or {@link java.util.Iterator#remove}.
//    */
//   EXPLICIT {
//     @Override
//     boolean wasEvicted() {
//       return false;
//     }
//   },
// 
//   /**
//    * The entry itself was not actually removed, but its value was replaced by the user. This can
//    * result from the user invoking {@link Map#put}, {@link Map#putAll},
//    * {@link ConcurrentMap#replace(Object, Object)}, or
//    * {@link ConcurrentMap#replace(Object, Object, Object)}.
//    */
//   REPLACED {
//     @Override
//     boolean wasEvicted() {
//       return false;
//     }
//   },
// 
//   /**
//    * The entry was removed automatically because its key or value was garbage-collected. This
//    * can occur when using {@link #softKeys}, {@link #softValues}, {@link #weakKeys}, or {@link
//    * #weakValues}.
//    */
//   COLLECTED {
//     @Override
//     boolean wasEvicted() {
//       return true;
//     }
//   },
// 
//   /**
//    * The entry's expiration timestamp has passed. This can occur when using {@link
//    * #expireAfterWrite} or {@link #expireAfterAccess}.
//    */
//   EXPIRED {
//     @Override
//     boolean wasEvicted() {
//       return true;
//     }
//   },
// 
//   /**
//    * The entry was evicted due to size constraints. This can occur when using {@link
//    * #maximumSize}.
//    */
//   SIZE {
//     @Override
//     boolean wasEvicted() {
//       return true;
//     }
//   };
// 
//   /**
//    * Returns {@code true} if there was an automatic removal due to eviction (the cause is neither
//    * {@link #EXPLICIT} nor {@link #REPLACED}).
//    */
//   abstract boolean wasEvicted();
// }
// 
// Path: guava/src/com/google/common/collect/MapMaker.java
// interface RemovalListener<K, V> {
//   /**
//    * Notifies the listener that a removal occurred at some point in the past.
//    */
//   void onRemoval(RemovalNotification<K, V> notification);
// }
// 
// Path: guava/src/com/google/common/collect/MapMaker.java
// static final class RemovalNotification<K, V> extends ImmutableEntry<K, V> {
//   private static final long serialVersionUID = 0;
// 
//   private final RemovalCause cause;
// 
//   RemovalNotification(@Nullable K key, @Nullable V value, RemovalCause cause) {
//     super(key, value);
//     this.cause = cause;
//   }
// 
//   /**
//    * Returns the cause for which the entry was removed.
//    */
//   public RemovalCause getCause() {
//     return cause;
//   }
// 
//   /**
//    * Returns {@code true} if there was an automatic removal due to eviction (the cause is neither
//    * {@link RemovalCause#EXPLICIT} nor {@link RemovalCause#REPLACED}).
//    */
//   public boolean wasEvicted() {
//     return cause.wasEvicted();
//   }
// }


// Path: guava/src/com/google/common/collect/MapMakerInternalMap.java
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Equivalence;
import com.google.common.base.Equivalences;
import com.google.common.base.Ticker;
import com.google.common.collect.GenericMapMaker.NullListener;
import com.google.common.collect.MapMaker.RemovalCause;
import com.google.common.collect.MapMaker.RemovalListener;
import com.google.common.collect.MapMaker.RemovalNotification;
import com.google.common.primitives.Ints;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractQueue;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;

      // might overflow, but that's okay (see isExpired())
      entry.setExpirationTime(map.ticker.read() + expirationNanos);
    }

    /**
     * Cleanup expired entries when the lock is available.
     */
    void tryExpireEntries() {
      if (tryLock()) {
        try {
          expireEntries();
        } finally {
          unlock();
          // don't call postWriteCleanup as we're in a read
        }
      }
    }

    @GuardedBy("Segment.this")
    void expireEntries() {
      drainRecencyQueue();

      if (expirationQueue.isEmpty()) {
        // There's no point in calling nanoTime() if we have no entries to
        // expire.
        return;
      }
      long now = map.ticker.read();
      ReferenceEntry<K, V> e;
      while ((e = expirationQueue.peek()) != null && map.isExpired(e, now)) {
        if (!removeEntry(e, e.getHash(), RemovalCause.EXPIRED)) {