1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.constraintlayout.core;
18 
19 import static androidx.constraintlayout.core.LinearSystem.FULL_DEBUG;
20 
21 import java.util.Arrays;
22 import java.util.HashSet;
23 
24 /**
25  * Represents a given variable used in the {@link LinearSystem linear expression solver}.
26  */
27 public class SolverVariable implements Comparable<SolverVariable> {
28 
29     private static final boolean INTERNAL_DEBUG = FULL_DEBUG;
30     private static final boolean VAR_USE_HASH = false;
31     private static final boolean DO_NOT_USE = false;
32 
33 
34     @SuppressWarnings("WeakerAccess")
35     public static final int STRENGTH_NONE = 0;
36     public static final int STRENGTH_LOW = 1;
37     public static final int STRENGTH_MEDIUM = 2;
38     public static final int STRENGTH_HIGH = 3;
39     @SuppressWarnings("WeakerAccess")
40     public static final int STRENGTH_HIGHEST = 4;
41     public static final int STRENGTH_EQUALITY = 5;
42     public static final int STRENGTH_BARRIER = 6;
43     public static final int STRENGTH_CENTERING = 7;
44     public static final int STRENGTH_FIXED = 8;
45 
46     private static int sUniqueSlackId = 1;
47     private static int sUniqueErrorId = 1;
48     private static int sUniqueUnrestrictedId = 1;
49     private static int sUniqueConstantId = 1;
50     private static int sUniqueId = 1;
51     public boolean inGoal;
52 
53     private String mName;
54 
55     public int id = -1;
56     int mDefinitionId = -1;
57     public int strength = 0;
58     public float computedValue;
59     public boolean isFinalValue = false;
60 
61     static final int MAX_STRENGTH = 9;
62     float[] mStrengthVector = new float[MAX_STRENGTH];
63     float[] mGoalStrengthVector = new float[MAX_STRENGTH];
64 
65     Type mType;
66 
67     ArrayRow[] mClientEquations = new ArrayRow[16];
68     int mClientEquationsCount = 0;
69     public int usageInRowCount = 0;
70     boolean mIsSynonym = false;
71     int mSynonym = -1;
72     float mSynonymDelta = 0;
73 
74     /**
75      * Type of variables
76      */
77     public enum Type {
78         /**
79          * The variable can take negative or positive values
80          */
81         UNRESTRICTED,
82         /**
83          * The variable is actually not a variable :) , but a constant number
84          */
85         CONSTANT,
86         /**
87          * The variable is restricted to positive values and represents a slack
88          */
89         SLACK,
90         /**
91          * The variable is restricted to positive values and represents an error
92          */
93         ERROR,
94         /**
95          * Unknown (invalid) type.
96          */
97         UNKNOWN
98     }
99 
increaseErrorId()100     static void increaseErrorId() {
101         sUniqueErrorId++;
102     }
103 
getUniqueName(Type type, String prefix)104     private static String getUniqueName(Type type, String prefix) {
105         if (prefix != null) {
106             return prefix + sUniqueErrorId;
107         }
108         switch (type) {
109             case UNRESTRICTED:
110                 return "U" + ++sUniqueUnrestrictedId;
111             case CONSTANT:
112                 return "C" + ++sUniqueConstantId;
113             case SLACK:
114                 return "S" + ++sUniqueSlackId;
115             case ERROR: {
116                 return "e" + ++sUniqueErrorId;
117             }
118             case UNKNOWN:
119                 return "V" + ++sUniqueId;
120         }
121         throw new AssertionError(type.name());
122     }
123 
124     /**
125      * Base constructor
126      *
127      * @param name the variable name
128      * @param type the type of the variable
129      */
SolverVariable(String name, Type type)130     public SolverVariable(String name, Type type) {
131         mName = name;
132         mType = type;
133     }
134 
SolverVariable(Type type, String prefix)135     public SolverVariable(Type type, String prefix) {
136         mType = type;
137         if (INTERNAL_DEBUG) {
138             //mName = getUniqueName(type, prefix);
139         }
140     }
141 
clearStrengths()142     void clearStrengths() {
143         for (int i = 0; i < MAX_STRENGTH; i++) {
144             mStrengthVector[i] = 0;
145         }
146     }
147 
strengthsToString()148     String strengthsToString() {
149         String representation = this + "[";
150         boolean negative = false;
151         boolean empty = true;
152         for (int j = 0; j < mStrengthVector.length; j++) {
153             representation += mStrengthVector[j];
154             if (mStrengthVector[j] > 0) {
155                 negative = false;
156             } else if (mStrengthVector[j] < 0) {
157                 negative = true;
158             }
159             if (mStrengthVector[j] != 0) {
160                 empty = false;
161             }
162             if (j < mStrengthVector.length - 1) {
163                 representation += ", ";
164             } else {
165                 representation += "] ";
166             }
167         }
168         if (negative) {
169             representation += " (-)";
170         }
171         if (empty) {
172             representation += " (*)";
173         }
174         // representation += " {id: " + id + "}";
175         return representation;
176     }
177 
178     HashSet<ArrayRow> mInRows = VAR_USE_HASH ? new HashSet<ArrayRow>() : null;
179 
180     // @TODO: add description
addToRow(ArrayRow row)181     public final void addToRow(ArrayRow row) {
182         if (VAR_USE_HASH) {
183             mInRows.add(row);
184         } else {
185             for (int i = 0; i < mClientEquationsCount; i++) {
186                 if (mClientEquations[i] == row) {
187                     return;
188                 }
189             }
190             if (mClientEquationsCount >= mClientEquations.length) {
191                 mClientEquations = Arrays.copyOf(mClientEquations, mClientEquations.length * 2);
192             }
193             mClientEquations[mClientEquationsCount] = row;
194             mClientEquationsCount++;
195         }
196     }
197 
198     // @TODO: add description
removeFromRow(ArrayRow row)199     public final void removeFromRow(ArrayRow row) {
200         if (VAR_USE_HASH) {
201             mInRows.remove(row);
202         } else {
203             final int count = mClientEquationsCount;
204             for (int i = 0; i < count; i++) {
205                 if (mClientEquations[i] == row) {
206                     for (int j = i; j < count - 1; j++) {
207                         mClientEquations[j] = mClientEquations[j + 1];
208                     }
209                     mClientEquationsCount--;
210                     return;
211                 }
212             }
213         }
214     }
215 
216     // @TODO: add description
updateReferencesWithNewDefinition(LinearSystem system, ArrayRow definition)217     public final void updateReferencesWithNewDefinition(LinearSystem system, ArrayRow definition) {
218         if (VAR_USE_HASH) {
219             for (ArrayRow row : mInRows) {
220                 row.updateFromRow(system, definition, false);
221             }
222             mInRows.clear();
223         } else {
224             final int count = mClientEquationsCount;
225             for (int i = 0; i < count; i++) {
226                 mClientEquations[i].updateFromRow(system, definition, false);
227             }
228             mClientEquationsCount = 0;
229         }
230     }
231 
232     // @TODO: add description
setFinalValue(LinearSystem system, float value)233     public void setFinalValue(LinearSystem system, float value) {
234         if (DO_NOT_USE && INTERNAL_DEBUG) {
235             System.out.println("Set final value for " + this + " of " + value);
236         }
237         computedValue = value;
238         isFinalValue = true;
239         mIsSynonym = false;
240         mSynonym = -1;
241         mSynonymDelta = 0;
242         final int count = mClientEquationsCount;
243         mDefinitionId = -1;
244         for (int i = 0; i < count; i++) {
245             mClientEquations[i].updateFromFinalVariable(system, this, false);
246         }
247         mClientEquationsCount = 0;
248     }
249 
250     // @TODO: add description
setSynonym(LinearSystem system, SolverVariable synonymVariable, float value)251     public void setSynonym(LinearSystem system, SolverVariable synonymVariable, float value) {
252         if (INTERNAL_DEBUG) {
253             System.out.println("Set synonym for " + this + " = " + synonymVariable + " + " + value);
254         }
255         mIsSynonym = true;
256         mSynonym = synonymVariable.id;
257         mSynonymDelta = value;
258         final int count = mClientEquationsCount;
259         mDefinitionId = -1;
260         for (int i = 0; i < count; i++) {
261             mClientEquations[i].updateFromSynonymVariable(system, this, false);
262         }
263         mClientEquationsCount = 0;
264         system.displayReadableRows();
265     }
266 
267     // @TODO: add description
reset()268     public void reset() {
269         mName = null;
270         mType = Type.UNKNOWN;
271         strength = SolverVariable.STRENGTH_NONE;
272         id = -1;
273         mDefinitionId = -1;
274         computedValue = 0;
275         isFinalValue = false;
276         mIsSynonym = false;
277         mSynonym = -1;
278         mSynonymDelta = 0;
279         if (VAR_USE_HASH) {
280             mInRows.clear();
281         } else {
282             final int count = mClientEquationsCount;
283             for (int i = 0; i < count; i++) {
284                 mClientEquations[i] = null;
285             }
286             mClientEquationsCount = 0;
287         }
288         usageInRowCount = 0;
289         inGoal = false;
290         Arrays.fill(mGoalStrengthVector, 0);
291     }
292 
293     /**
294      * Accessor for the name
295      *
296      * @return the name of the variable
297      */
getName()298     public String getName() {
299         return mName;
300     }
301 
setName(String name)302     public void setName(String name) {
303         mName = name;
304     }
305 
306     // @TODO: add description
setType(Type type, String prefix)307     public void setType(Type type, String prefix) {
308         mType = type;
309         if (INTERNAL_DEBUG && mName == null) {
310             mName = getUniqueName(type, prefix);
311         }
312     }
313 
314     @Override
compareTo(SolverVariable v)315     public int compareTo(SolverVariable v) {
316         return this.id - v.id;
317     }
318 
319     /**
320      * Override the toString() method to display the variable
321      */
322     @Override
toString()323     public String toString() {
324         String result = "";
325         if (INTERNAL_DEBUG) {
326             result += mName + "(" + id + "):" + strength;
327             if (mIsSynonym) {
328                 result += ":S(" + mSynonym + ")";
329             }
330             if (isFinalValue) {
331                 result += ":F(" + computedValue + ")";
332             }
333         } else {
334             if (mName != null) {
335                 result += mName;
336             } else {
337                 result += id;
338             }
339         }
340         return result;
341     }
342 
343 }
344