content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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 accord.utils;
  
  import java.util.Comparator;
  import java.util.function.Consumer;
  import java.util.function.Function;
  import java.util.function.Predicate;
  import java.util.function.Supplier;
  import javax.annotation.Nullable;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import accord.api.Tracing;
  import net.nicoulaj.compilecommand.annotations.Inline;
  
  public class Invariants
  {
      public enum Paranoia
      {
          NONE, CONSTANT, LINEAR, SUPERLINEAR, QUADRATIC
      }
  
      public enum ParanoiaCostFactor
      {
          LOW, HIGH
      }
  
      private static final Logger logger = LoggerFactory.getLogger(Invariants.class);
      public static final String KEY_PARANOIA_CPU = "accord.paranoia.cpu";
      public static final String KEY_PARANOIA_MEMORY = "accord.paranoia.memory";
      public static final String KEY_PARANOIA_COSTFACTOR = "accord.paranoia.costfactor";
      private static final int PARANOIA_COMPUTE = Paranoia.valueOf(System.getProperty(KEY_PARANOIA_CPU, "NONE").toUpperCase()).ordinal();
      private static final int PARANOIA_MEMORY = Paranoia.valueOf(System.getProperty(KEY_PARANOIA_MEMORY, "NONE").toUpperCase()).ordinal();
      private static final int PARANOIA_FACTOR = ParanoiaCostFactor.valueOf(System.getProperty(KEY_PARANOIA_COSTFACTOR, "LOW").toUpperCase()).ordinal();
      private static boolean IS_PARANOID = PARANOIA_COMPUTE > 0 || PARANOIA_MEMORY > 0;
      private static Consumer<RuntimeException> onUnexpected = System.getProperty("accord.testing", "false").equals("true")
                                                               ? fail -> { throw fail; }
                                                               : fail -> logger.error("Invariant failed", fail);
      private static final boolean DEBUG = System.getProperty("accord.debug", "false").equals("true");
  
      public static boolean isParanoid()
      {
          return IS_PARANOID;
      }
  
      public static boolean testParanoia(Paranoia compute, Paranoia memory, ParanoiaCostFactor factor)
      {
          return PARANOIA_COMPUTE >= compute.ordinal() && PARANOIA_MEMORY >= memory.ordinal() && PARANOIA_FACTOR >= factor.ordinal();
      }
  
      public static boolean debug()
      {
          return DEBUG;
      }
  
      public static IllegalStateException createIllegalState(String msg)
      {
          return new IllegalStateException(msg);
      }
  
      public static IllegalStateException illegalState(String msg)
      {
           throw createIllegalState(msg);
      }
  
      private static String format(String fmt, Object ... args)
      {
          return Tracing.safeFormat(fmt, args);
      }
  
      public static IllegalStateException illegalState(String fmt, Object... args)
      {
          return illegalState(format(fmt, args));
      }
  
      public static IllegalStateException illegalState()
      {
          throw illegalState(null);
      }
  
      public static IllegalArgumentException illegalArgument(String msg)
      {
          throw new IllegalArgumentException(msg);
      }
  
      public static IllegalArgumentException illegalArgument(String fmt, Object ... args)
      {
          throw illegalArgument(String.format(fmt, args));
      }
  
      private static IllegalArgumentException illegalArgument()
      {
          throw illegalArgument(null);
      }
  
      public static <T1, T2 extends T1> T2 requireType(T1 cast)
      {
          return (T2)cast;
      }
  
      public static <T1, T2 extends T1> T2 requireType(Class<T2> to, T1 cast)
      {
          if (cast != null && !to.isInstance(cast))
              illegalState();
          return (T2)cast;
      }
  
      public static <T1, T2 extends T1> T2 requireType(Class<T2> to, T1 cast, String msg)
      {
          if (cast != null && !to.isInstance(cast))
              illegalState(msg);
          return (T2)cast;
      }
  
      public static void paranoid(boolean condition)
      {
          if (isParanoid() && !condition)
              throw illegalState();
      }
  
      public static boolean expect(boolean condition)
      {
          if (!condition)
              onUnexpected.accept(createIllegalState(null));
          return condition;
      }
  
      public static boolean expect(boolean condition, String msg)
      {
          if (!condition)
              onUnexpected.accept(createIllegalState(msg));
          return condition;
      }
  
      public static boolean expect(boolean condition, String fmt, int p1)
      {
          if (!condition)
              onUnexpected.accept(createIllegalState(format(fmt, p1)));
          return condition;
      }
  
      public static boolean expect(boolean condition, String fmt, int p1, int p2)
      {
          if (!condition)
              onUnexpected.accept(createIllegalState(format(fmt, p1, p2)));
          return condition;
      }
  
      public static boolean expect(boolean condition, String fmt, long p1)
      {
          if (!condition)
              onUnexpected.accept(createIllegalState(format(fmt, p1)));
          return condition;
      }
  
      public static boolean expect(boolean condition, String fmt, long p1, long p2)
      {
          if (!condition)
              onUnexpected.accept(createIllegalState(format(fmt, p1, p2)));
          return condition;
      }
  
      public static boolean expect(boolean condition, String fmt, @Nullable Object p1)
      {
          if (!condition)
              onUnexpected.accept(createIllegalState(format(fmt, p1)));
          return condition;
      }
  
      public static <P> boolean expect(boolean condition, String fmt, @Nullable P p1, Function<? super P, ?> transformP)
      {
          if (!condition)
              onUnexpected.accept(createIllegalState(format(fmt, transformP.apply(p1))));
          return condition;
      }
  
      public static boolean expect(boolean condition, String fmt, @Nullable Object p1, @Nullable Object p2)
      {
          if (!condition)
              onUnexpected.accept(createIllegalState(format(fmt, p1, p2)));
          return condition;
      }
  
      public static <P> void expect(boolean condition, String fmt, @Nullable Object p1, @Nullable P p2, Function<? super P, ?> transformP2)
      {
          if (!condition)
              onUnexpected.accept(createIllegalState(format(fmt, p1, transformP2.apply(p2))));
      }
  
  
      public static void expect(boolean condition, String fmt, @Nullable Object p1, @Nullable Object p2, @Nullable Object p3)
      {
          if (!condition)
              onUnexpected.accept(createIllegalState(format(fmt, p1, p2, p3)));
      }
  
      public static void expect(boolean condition, String fmt, @Nullable Object p1, @Nullable Object p2, long p3)
      {
          if (!condition)
              onUnexpected.accept(createIllegalState(format(fmt, p1, p2, p3)));
      }
  
      public static <P> void expect(boolean condition, String fmt, @Nullable Object p1, @Nullable Object p2, @Nullable P p3, Function<? super P, Object> transformP3)
      {
          if (!condition)
              onUnexpected.accept(createIllegalState(format(fmt, p1, p2, transformP3.apply(p3))));
      }
  
      public static void expect(boolean condition, String fmt, Object... args)
      {
          if (!condition)
              onUnexpected.accept(createIllegalState(format(fmt, args)));
      }
  
      public static void require(boolean condition)
      {
          if (!condition)
              throw illegalState();
      }
  
      public static void require(boolean condition, Supplier<String> msg)
      {
          if (!condition)
              throw illegalState(msg.get());
      }
  
      public static void require(boolean condition, String msg)
      {
          if (!condition)
              throw illegalState(msg);
      }
  
      public static void require(boolean condition, String fmt, int p1)
      {
          if (!condition)
              throw illegalState(format(fmt, p1));
      }
  
      public static void require(boolean condition, String fmt, int p1, int p2)
      {
          if (!condition)
              throw illegalState(format(fmt, p1, p2));
      }
  
      public static void require(boolean condition, String fmt, int p1, int p2, Object p3)
      {
          if (!condition)
              throw illegalState(format(fmt, p1, p2, p3));
      }
  
      public static void require(boolean condition, String fmt, long p1)
      {
          if (!condition)
              throw illegalState(format(fmt, p1));
      }
  
      public static void require(boolean condition, String fmt, long p1, long p2)
      {
          if (!condition)
              throw illegalState(format(fmt, p1, p2));
      }
  
      public static void require(boolean condition, String fmt, long p1, long p2, long p3)
      {
          if (!condition)
              throw illegalState(format(fmt, p1, p2, p3));
      }
  
      public static void require(boolean condition, String fmt, long p1, long p2, Object p3)
      {
          if (!condition)
              throw illegalState(format(fmt, p1, p2, p3));
      }
  
      public static void require(boolean condition, String fmt, long p1, long p2, long p3, Object p4)
      {
          if (!condition)
              throw illegalState(format(fmt, p1, p2, p3, p4));
      }
  
      public static void require(boolean condition, String fmt, long p1, long p2, long p3, Object p4, Object p5)
      {
          if (!condition)
              throw illegalState(format(fmt, p1, p2, p3, p4, p5));
      }
  
      public static void require(boolean condition, String fmt, @Nullable Object p1)
      {
          if (!condition)
              throw illegalState(format(fmt, p1));
      }
  
      public static <P> void require(boolean condition, String fmt, @Nullable P p1, Function<? super P, ?> transformP)
      {
          if (!condition)
              throw illegalState(format(fmt, transformP.apply(p1)));
      }
  
      public static void require(boolean condition, String fmt, @Nullable Object p1, @Nullable Object p2)
      {
          if (!condition)
              throw illegalState(format(fmt, p1, p2));
      }
  
      public static <P> void require(boolean condition, String fmt, @Nullable Object p1, @Nullable P p2, Function<? super P, ?> transformP2)
      {
          if (!condition)
              throw illegalState(format(fmt, p1, transformP2.apply(p2)));
      }
  
  
      public static void require(boolean condition, String fmt, @Nullable Object p1, @Nullable Object p2, @Nullable Object p3)
      {
          if (!condition)
              throw illegalState(format(fmt, p1, p2, p3));
      }
  
      public static void require(boolean condition, String fmt, int p1, @Nullable Object p2, @Nullable Object p3)
      {
          if (!condition)
              throw illegalState(format(fmt, p1, p2, p3));
      }
  
      public static <P> void require(boolean condition, String fmt, @Nullable Object p1, @Nullable Object p2, @Nullable P p3, Function<? super P, Object> transformP3)
      {
          if (!condition)
              throw illegalState(format(fmt, p1, p2, transformP3.apply(p3)));
      }
  
      public static void require(boolean condition, String fmt, Object... args)
      {
          if (!condition)
              throw illegalState(format(fmt, args));
      }
  
      public static <T> T nonNull(T param)
      {
          if (param == null)
              throw new NullPointerException();
          return param;
      }
  
      public static <T> T nonNull(T param, String message)
      {
          if (param == null)
              throw new NullPointerException(message);
          return param;
      }
  
      public static <T> T nonNull(T param, String fmt, Object... args)
      {
          if (param == null)
              throw new NullPointerException(format(fmt, args));
          return param;
      }
  
      public static int isNatural(int input)
      {
          if (input < 0)
              throw illegalState();
          return input;
      }
  
      public static long isNatural(long input)
      {
          if (input < 0)
              throw illegalState();
          return input;
      }
  
      public static void requireArgument(boolean condition)
      {
          if (!condition)
              throw illegalArgument();
      }
  
      public static void requireArgument(boolean condition, String msg)
      {
          if (!condition)
              throw illegalArgument(msg);
      }
  
      public static void requireArgument(boolean condition, String fmt, int p1)
      {
          if (!condition)
              illegalArgument(format(fmt, p1));
      }
  
      public static void requireArgument(boolean condition, String fmt, int p1, int p2)
      {
          if (!condition)
              illegalArgument(format(fmt, p1, p2));
      }
  
      public static void requireArgument(boolean condition, String fmt, long p1)
      {
          if (!condition)
              illegalArgument(format(fmt, p1));
      }
  
      public static void requireArgument(boolean condition, String fmt, long p1, long p2)
      {
          if (!condition)
              illegalArgument(format(fmt, p1, p2));
      }
  
      public static void requireArgument(boolean condition, String fmt, @Nullable Object p1)
      {
          if (!condition)
              illegalArgument(format(fmt, p1));
      }
  
      public static void requireArgument(boolean condition, String fmt, @Nullable Object p1, @Nullable Object p2)
      {
          if (!condition)
              illegalArgument(format(fmt, p1, p2));
      }
  
      public static void requireArgument(boolean condition, String fmt, @Nullable Object p1, @Nullable Object p2, @Nullable Object p3)
      {
          if (!condition)
              illegalArgument(format(fmt, p1, p2, p3));
      }
  
      public static void requireArgument(boolean condition, String fmt, @Nullable Object p1, @Nullable Object p2, @Nullable Object p3, @Nullable Object p4)
      {
          if (!condition)
              illegalArgument(format(fmt, p1, p2, p3, p4));
      }
  
      public static void requireArgument(boolean condition, String fmt, Object... args)
      {
          if (!condition)
              illegalArgument(format(fmt, args));
      }
  
      public static <T> T requireArgument(T param, boolean condition)
      {
          if (!condition)
              illegalArgument();
          return param;
      }
  
      public static <T> T requireArgument(T param, boolean condition, String msg)
      {
          if (!condition)
              illegalArgument(msg);
          return param;
      }
  
      public static <T> T requireArgument(T param, boolean condition, String fmt, int p1)
      {
          if (!condition)
              illegalArgument(format(fmt, p1));
          return param;
      }
  
      public static <T> T requireArgument(T param, boolean condition, String fmt, int p1, int p2)
      {
          if (!condition)
              illegalArgument(format(fmt, p1, p2));
          return param;
      }
  
      public static <T> T requireArgument(T param, boolean condition, String fmt, long p1)
      {
          if (!condition)
              illegalArgument(format(fmt, p1));
          return param;
      }
  
      public static <T> T requireArgument(T param, boolean condition, String fmt, long p1, long p2)
      {
          if (!condition)
              illegalArgument(format(fmt, p1, p2));
          return param;
      }
  
      public static <T> T requireArgument(T param, boolean condition, String fmt, @Nullable Object p1)
      {
          if (!condition)
              illegalArgument(format(fmt, p1));
          return param;
      }
  
      public static <T> T requireArgument(T param, boolean condition, String fmt, @Nullable Object p1, @Nullable Object p2)
      {
          if (!condition)
              illegalArgument(format(fmt, p1, p2));
          return param;
      }
  
      public static <T> T requireArgument(T param, boolean condition, String fmt, Object... args)
      {
          if (!condition)
              illegalArgument(format(fmt, args));
          return param;
      }
  
      @Inline
      public static <T> T requireArgument(T param, Predicate<T> condition)
      {
          if (!condition.test(param))
              illegalArgument();
          return param;
      }
  
      @Inline
      public static <T> T requireArgument(T param, Predicate<T> condition, String msg)
      {
          if (!condition.test(param))
              illegalArgument(msg);
          return param;
      }
  
      @Inline
      public static int requireNonNegative(int index)
      {
          if (index < 0)
              throw illegalState("Index %d expected to be non-negative", index);
          return index;
      }
  
      public static void requireArgumentSorted(int[] array, int start, int end)
      {
          Invariants.requireArgument(SortedArrays.isSorted(array, start, end));
      }
  
      public static void requireArgumentSorted(long[] array, int start, int end)
      {
          Invariants.requireArgument(SortedArrays.isSorted(array, start, end));
      }
  
      public static <O> O cast(Object o, Class<O> klass)
      {
          try
          {
              return klass.cast(o);
          }
          catch (ClassCastException e)
          {
              throw illegalArgument(format("Unable to cast %s to %s", o, klass.getName()));
          }
      }
  
      public static void requireIndex(int index, int length)
      {
          if (!(index >= 0 && index < length))
          {
              if (index < 0)
                  throw new IndexOutOfBoundsException("Index " + index + " must not be negative");
              if (length < 0)
                  throw new IndexOutOfBoundsException("Length " + length + " must not be negative");
              throw new IndexOutOfBoundsException(String.format("%d must be less than %d", index, length));
          }
      }
  
      public static void requireIndexInBounds(int realLength, int offset, int length)
      {
          if (realLength == 0 || length == 0)
              throw new IndexOutOfBoundsException("Unable to access offset " + offset + "; empty");
          if (offset < 0)
              throw new IndexOutOfBoundsException("Offset " + offset + " must not be negative");
          if (length < 0)
              throw new IndexOutOfBoundsException("Length " + length + " must not be negative");
          int endOffset = offset + length;
          if (endOffset > realLength)
              throw new IndexOutOfBoundsException(String.format("Offset %d, length = %d; real length was %d", offset, length, realLength));
      }
  
      public static <T extends Comparable<? super T>> void requirePartiallyOrdered(T... vs)
      {
          for (int i = 1 ; i < vs.length ; ++i)
              Invariants.requireArgument(vs[i - 1].compareTo(vs[i]) <= 0);
      }
  
      public static <T extends Comparable<? super T>> void requireStrictlyOrdered(T... vs)
      {
          requireStrictlyOrdered(Comparable::compareTo, vs);
      }
  
      public static <T> void requireStrictlyOrdered(Comparator<? super T> comparator, T... vs)
      {
          for (int i = 1 ; i < vs.length ; ++i)
              Invariants.requireArgument(comparator.compare(vs[i - 1], vs[i]) < 0);
      }
  
      public static <T extends Comparable<? super T>> void requireOrdered(T... vs)
      {
          for (int i = 1 ; i < vs.length ; ++i)
              Invariants.requireArgument(vs[i - 1].compareTo(vs[i]) < 0);
      }
  }
  