1 /*
2  * Copyright (C) 2020 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 java.util.Arrays;
20 import java.util.Comparator;
21 
22 /**
23  * Implements a row containing goals taking in account priorities.
24  */
25 public class PriorityGoalRow extends ArrayRow {
26     private static final float EPSILON = 0.0001f;
27     @SuppressWarnings("unused") private static final boolean DEBUG = false;
28 
29     private int mTableSize = 128;
30     private SolverVariable[] mArrayGoals = new SolverVariable[mTableSize];
31     private SolverVariable[] mSortArray = new SolverVariable[mTableSize];
32     private int mNumGoals = 0;
33     GoalVariableAccessor mAccessor = new GoalVariableAccessor(this);
34 
35     class GoalVariableAccessor {
36         SolverVariable mVariable;
37         PriorityGoalRow mRow;
38 
GoalVariableAccessor(PriorityGoalRow row)39         GoalVariableAccessor(PriorityGoalRow row) {
40             this.mRow = row;
41         }
42 
init(SolverVariable variable)43         public void init(SolverVariable variable) {
44             this.mVariable = variable;
45         }
46 
addToGoal(SolverVariable other, float value)47         public boolean addToGoal(SolverVariable other, float value) {
48             if (mVariable.inGoal) {
49                 boolean empty = true;
50                 for (int i = 0; i < SolverVariable.MAX_STRENGTH; i++) {
51                     mVariable.mGoalStrengthVector[i] += other.mGoalStrengthVector[i] * value;
52                     float v = mVariable.mGoalStrengthVector[i];
53                     if (Math.abs(v) < EPSILON) {
54                         mVariable.mGoalStrengthVector[i] = 0;
55                     } else {
56                         empty = false;
57                     }
58                 }
59                 if (empty) {
60                     removeGoal(mVariable);
61                 }
62             } else {
63                 for (int i = 0; i < SolverVariable.MAX_STRENGTH; i++) {
64                     float strength = other.mGoalStrengthVector[i];
65                     if (strength != 0) {
66                         float v = value * strength;
67                         if (Math.abs(v) < EPSILON) {
68                             v = 0;
69                         }
70                         mVariable.mGoalStrengthVector[i] = v;
71                     } else {
72                         mVariable.mGoalStrengthVector[i] = 0;
73                     }
74                 }
75                 return true;
76             }
77             return false;
78         }
79 
add(SolverVariable other)80         public void add(SolverVariable other) {
81             for (int i = 0; i < SolverVariable.MAX_STRENGTH; i++) {
82                 mVariable.mGoalStrengthVector[i] += other.mGoalStrengthVector[i];
83                 float value = mVariable.mGoalStrengthVector[i];
84                 if (Math.abs(value) < EPSILON) {
85                     mVariable.mGoalStrengthVector[i] = 0;
86                 }
87             }
88         }
89 
isNegative()90         public final boolean isNegative() {
91             for (int i = SolverVariable.MAX_STRENGTH - 1; i >= 0; i--) {
92                 float value = mVariable.mGoalStrengthVector[i];
93                 if (value > 0) {
94                     return false;
95                 }
96                 if (value < 0) {
97                     return true;
98                 }
99             }
100             return false;
101         }
102 
isSmallerThan(SolverVariable other)103         public final boolean isSmallerThan(SolverVariable other) {
104             for (int i = SolverVariable.MAX_STRENGTH - 1; i >= 0; i--) {
105                 float comparedValue = other.mGoalStrengthVector[i];
106                 float value = mVariable.mGoalStrengthVector[i];
107                 if (value == comparedValue) {
108                     continue;
109                 }
110                 return value < comparedValue;
111             }
112             return false;
113         }
114 
isNull()115         public final boolean isNull() {
116             for (int i = 0; i < SolverVariable.MAX_STRENGTH; i++) {
117                 if (mVariable.mGoalStrengthVector[i] != 0) {
118                     return false;
119                 }
120             }
121             return true;
122         }
123 
reset()124         public void reset() {
125             Arrays.fill(mVariable.mGoalStrengthVector, 0);
126         }
127 
128         @Override
toString()129         public String toString() {
130             String result = "[ ";
131             if (mVariable != null) {
132                 for (int i = 0; i < SolverVariable.MAX_STRENGTH; i++) {
133                     result += mVariable.mGoalStrengthVector[i] + " ";
134                 }
135             }
136             result += "] " + mVariable;
137             return result;
138         }
139 
140     }
141 
142     @Override
clear()143     public void clear() {
144         mNumGoals = 0;
145         mConstantValue = 0;
146     }
147 
148     Cache mCache;
149 
PriorityGoalRow(Cache cache)150     public PriorityGoalRow(Cache cache) {
151         super(cache);
152         mCache = cache;
153     }
154 
155     @Override
isEmpty()156     public boolean isEmpty() {
157         return mNumGoals == 0;
158     }
159 
160     static final int NOT_FOUND = -1;
161 
162     @Override
getPivotCandidate(LinearSystem system, boolean[] avoid)163     public SolverVariable getPivotCandidate(LinearSystem system, boolean[] avoid) {
164         int pivot = NOT_FOUND;
165         for (int i = 0; i < mNumGoals; i++) {
166             SolverVariable variable = mArrayGoals[i];
167             if (avoid[variable.id]) {
168                 continue;
169             }
170             mAccessor.init(variable);
171             if (pivot == NOT_FOUND) {
172                 if (mAccessor.isNegative()) {
173                     pivot = i;
174                 }
175             } else if (mAccessor.isSmallerThan(mArrayGoals[pivot])) {
176                 pivot = i;
177             }
178         }
179         if (pivot == NOT_FOUND) {
180             return null;
181         }
182         return mArrayGoals[pivot];
183     }
184 
185     @Override
addError(SolverVariable error)186     public void addError(SolverVariable error) {
187         mAccessor.init(error);
188         mAccessor.reset();
189         error.mGoalStrengthVector[error.strength] = 1;
190         addToGoal(error);
191     }
192 
addToGoal(SolverVariable variable)193     private void addToGoal(SolverVariable variable) {
194         if (mNumGoals + 1 > mArrayGoals.length) {
195             mArrayGoals = Arrays.copyOf(mArrayGoals, mArrayGoals.length * 2);
196             mSortArray = Arrays.copyOf(mArrayGoals, mArrayGoals.length * 2);
197         }
198         mArrayGoals[mNumGoals] = variable;
199         mNumGoals++;
200 
201         if (mNumGoals > 1 && mArrayGoals[mNumGoals - 1].id > variable.id) {
202             for (int i = 0; i < mNumGoals; i++) {
203                 mSortArray[i] = mArrayGoals[i];
204             }
205             Arrays.sort(mSortArray, 0, mNumGoals, new Comparator<SolverVariable>() {
206                 @Override
207                 public int compare(SolverVariable variable1, SolverVariable variable2) {
208                     return variable1.id - variable2.id;
209                 }
210             });
211             for (int i = 0; i < mNumGoals; i++) {
212                 mArrayGoals[i] = mSortArray[i];
213             }
214         }
215 
216         variable.inGoal = true;
217         variable.addToRow(this);
218     }
219 
removeGoal(SolverVariable variable)220     private void removeGoal(SolverVariable variable) {
221         for (int i = 0; i < mNumGoals; i++) {
222             if (mArrayGoals[i] == variable) {
223                 for (int j = i; j < mNumGoals - 1; j++) {
224                     mArrayGoals[j] = mArrayGoals[j + 1];
225                 }
226                 mNumGoals--;
227                 variable.inGoal = false;
228                 return;
229             }
230         }
231     }
232 
233     @Override
updateFromRow(LinearSystem system, ArrayRow definition, boolean removeFromDefinition)234     public void updateFromRow(LinearSystem system,
235             ArrayRow definition,
236             boolean removeFromDefinition) {
237         SolverVariable goalVariable = definition.mVariable;
238         if (goalVariable == null) {
239             return;
240         }
241 
242         ArrayRowVariables rowVariables = definition.variables;
243         int currentSize = rowVariables.getCurrentSize();
244         for (int i = 0; i < currentSize; i++) {
245             SolverVariable solverVariable = rowVariables.getVariable(i);
246             float value = rowVariables.getVariableValue(i);
247             mAccessor.init(solverVariable);
248             if (mAccessor.addToGoal(goalVariable, value)) {
249                 addToGoal(solverVariable);
250             }
251             mConstantValue += definition.mConstantValue * value;
252         }
253         removeGoal(goalVariable);
254     }
255 
256     @Override
toString()257     public String toString() {
258         String result = "";
259         result += " goal -> (" + mConstantValue + ") : ";
260         for (int i = 0; i < mNumGoals; i++) {
261             SolverVariable v = mArrayGoals[i];
262             mAccessor.init(v);
263             result += mAccessor + " ";
264         }
265         return result;
266     }
267 }
268