content: /*
   * Copyright (C) 2015 The Android Open Source Project
   *
   * 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 androidx.constraintlayout.core;
  
  import static androidx.constraintlayout.core.LinearSystem.FULL_DEBUG;
  
  import java.util.Arrays;
  import java.util.HashSet;
  
  /**
   * Represents a given variable used in the {@link LinearSystem linear expression solver}.
   */
  public class SolverVariable implements Comparable<SolverVariable> {
  
      private static final boolean INTERNAL_DEBUG = FULL_DEBUG;
      private static final boolean VAR_USE_HASH = false;
      private static final boolean DO_NOT_USE = false;
  
  
      @SuppressWarnings("WeakerAccess")
      public static final int STRENGTH_NONE = 0;
      public static final int STRENGTH_LOW = 1;
      public static final int STRENGTH_MEDIUM = 2;
      public static final int STRENGTH_HIGH = 3;
      @SuppressWarnings("WeakerAccess")
      public static final int STRENGTH_HIGHEST = 4;
      public static final int STRENGTH_EQUALITY = 5;
      public static final int STRENGTH_BARRIER = 6;
      public static final int STRENGTH_CENTERING = 7;
      public static final int STRENGTH_FIXED = 8;
  
      private static int sUniqueSlackId = 1;
      private static int sUniqueErrorId = 1;
      private static int sUniqueUnrestrictedId = 1;
      private static int sUniqueConstantId = 1;
      private static int sUniqueId = 1;
      public boolean inGoal;
  
      private String mName;
  
      public int id = -1;
      int mDefinitionId = -1;
      public int strength = 0;
      public float computedValue;
      public boolean isFinalValue = false;
  
      static final int MAX_STRENGTH = 9;
      float[] mStrengthVector = new float[MAX_STRENGTH];
      float[] mGoalStrengthVector = new float[MAX_STRENGTH];
  
      Type mType;
  
      ArrayRow[] mClientEquations = new ArrayRow[16];
      int mClientEquationsCount = 0;
      public int usageInRowCount = 0;
      boolean mIsSynonym = false;
      int mSynonym = -1;
      float mSynonymDelta = 0;
  
      /**
       * Type of variables
       */
      public enum Type {
          /**
           * The variable can take negative or positive values
           */
          UNRESTRICTED,
          /**
           * The variable is actually not a variable :) , but a constant number
           */
          CONSTANT,
          /**
           * The variable is restricted to positive values and represents a slack
           */
          SLACK,
          /**
           * The variable is restricted to positive values and represents an error
           */
          ERROR,
          /**
           * Unknown (invalid) type.
           */
          UNKNOWN
      }
  
      static void increaseErrorId() {
          sUniqueErrorId++;
      }
  
      private static String getUniqueName(Type type, String prefix) {
          if (prefix != null) {
              return prefix + sUniqueErrorId;
          }
          switch (type) {
              case UNRESTRICTED:
                  return "U" + ++sUniqueUnrestrictedId;
              case CONSTANT:
                  return "C" + ++sUniqueConstantId;
              case SLACK:
                  return "S" + ++sUniqueSlackId;
              case ERROR: {
                  return "e" + ++sUniqueErrorId;
              }
              case UNKNOWN:
                  return "V" + ++sUniqueId;
          }
          throw new AssertionError(type.name());
      }
  
      /**
       * Base constructor
       *
       * @param name the variable name
       * @param type the type of the variable
       */
      public SolverVariable(String name, Type type) {
          mName = name;
          mType = type;
      }
  
      public SolverVariable(Type type, String prefix) {
          mType = type;
          if (INTERNAL_DEBUG) {
              //mName = getUniqueName(type, prefix);
          }
      }
  
      void clearStrengths() {
          for (int i = 0; i < MAX_STRENGTH; i++) {
              mStrengthVector[i] = 0;
          }
      }
  
      String strengthsToString() {
          String representation = this + "[";
          boolean negative = false;
          boolean empty = true;
          for (int j = 0; j < mStrengthVector.length; j++) {
              representation += mStrengthVector[j];
              if (mStrengthVector[j] > 0) {
                  negative = false;
              } else if (mStrengthVector[j] < 0) {
                  negative = true;
              }
              if (mStrengthVector[j] != 0) {
                  empty = false;
              }
              if (j < mStrengthVector.length - 1) {
                  representation += ", ";
              } else {
                  representation += "] ";
              }
          }
          if (negative) {
              representation += " (-)";
          }
          if (empty) {
              representation += " (*)";
          }
          // representation += " {id: " + id + "}";
          return representation;
      }
  
      HashSet<ArrayRow> mInRows = VAR_USE_HASH ? new HashSet<ArrayRow>() : null;
  
      // @TODO: add description
      public final void addToRow(ArrayRow row) {
          if (VAR_USE_HASH) {
              mInRows.add(row);
          } else {
              for (int i = 0; i < mClientEquationsCount; i++) {
                  if (mClientEquations[i] == row) {
                      return;
                  }
              }
              if (mClientEquationsCount >= mClientEquations.length) {
                  mClientEquations = Arrays.copyOf(mClientEquations, mClientEquations.length * 2);
              }
              mClientEquations[mClientEquationsCount] = row;
              mClientEquationsCount++;
          }
      }
  
      // @TODO: add description
      public final void removeFromRow(ArrayRow row) {
          if (VAR_USE_HASH) {
              mInRows.remove(row);
          } else {
              final int count = mClientEquationsCount;
              for (int i = 0; i < count; i++) {
                  if (mClientEquations[i] == row) {
                      for (int j = i; j < count - 1; j++) {
                          mClientEquations[j] = mClientEquations[j + 1];
                      }
                      mClientEquationsCount--;
                      return;
                  }
              }
          }
      }
  
      // @TODO: add description
      public final void updateReferencesWithNewDefinition(LinearSystem system, ArrayRow definition) {
          if (VAR_USE_HASH) {
              for (ArrayRow row : mInRows) {
                  row.updateFromRow(system, definition, false);
              }
              mInRows.clear();
          } else {
              final int count = mClientEquationsCount;
              for (int i = 0; i < count; i++) {
                  mClientEquations[i].updateFromRow(system, definition, false);
              }
              mClientEquationsCount = 0;
          }
      }
  
      // @TODO: add description
      public void setFinalValue(LinearSystem system, float value) {
          if (DO_NOT_USE && INTERNAL_DEBUG) {
              System.out.println("Set final value for " + this + " of " + value);
          }
          computedValue = value;
          isFinalValue = true;
          mIsSynonym = false;
          mSynonym = -1;
          mSynonymDelta = 0;
          final int count = mClientEquationsCount;
          mDefinitionId = -1;
          for (int i = 0; i < count; i++) {
              mClientEquations[i].updateFromFinalVariable(system, this, false);
          }
          mClientEquationsCount = 0;
      }
  
      // @TODO: add description
      public void setSynonym(LinearSystem system, SolverVariable synonymVariable, float value) {
          if (INTERNAL_DEBUG) {
              System.out.println("Set synonym for " + this + " = " + synonymVariable + " + " + value);
          }
          mIsSynonym = true;
          mSynonym = synonymVariable.id;
          mSynonymDelta = value;
          final int count = mClientEquationsCount;
          mDefinitionId = -1;
          for (int i = 0; i < count; i++) {
              mClientEquations[i].updateFromSynonymVariable(system, this, false);
          }
          mClientEquationsCount = 0;
          system.displayReadableRows();
      }
  
      // @TODO: add description
      public void reset() {
          mName = null;
          mType = Type.UNKNOWN;
          strength = SolverVariable.STRENGTH_NONE;
          id = -1;
          mDefinitionId = -1;
          computedValue = 0;
          isFinalValue = false;
          mIsSynonym = false;
          mSynonym = -1;
          mSynonymDelta = 0;
          if (VAR_USE_HASH) {
              mInRows.clear();
          } else {
              final int count = mClientEquationsCount;
              for (int i = 0; i < count; i++) {
                  mClientEquations[i] = null;
              }
              mClientEquationsCount = 0;
          }
          usageInRowCount = 0;
          inGoal = false;
          Arrays.fill(mGoalStrengthVector, 0);
      }
  
      /**
       * Accessor for the name
       *
       * @return the name of the variable
       */
      public String getName() {
          return mName;
      }
  
      public void setName(String name) {
          mName = name;
      }
  
      // @TODO: add description
      public void setType(Type type, String prefix) {
          mType = type;
          if (INTERNAL_DEBUG && mName == null) {
              mName = getUniqueName(type, prefix);
          }
      }
  
      @Override
      public int compareTo(SolverVariable v) {
          return this.id - v.id;
      }
  
      /**
       * Override the toString() method to display the variable
       */
      @Override
      public String toString() {
          String result = "";
          if (INTERNAL_DEBUG) {
              result += mName + "(" + id + "):" + strength;
              if (mIsSynonym) {
                  result += ":S(" + mSynonym + ")";
              }
              if (isFinalValue) {
                  result += ":F(" + computedValue + ")";
              }
          } else {
              if (mName != null) {
                  result += mName;
              } else {
                  result += id;
              }
          }
          return result;
      }
  
  }
  