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 package androidx.constraintlayout.core;
17 
18 import java.util.Arrays;
19 
20 /**
21  * Store a set of variables and their values in an array-based linked list coupled
22  * with a custom hashmap.
23  */
24 public class SolverVariableValues implements ArrayRow.ArrayRowVariables {
25 
26     private static final boolean DEBUG = false;
27     @SuppressWarnings("unused") private static final boolean HASH = true;
28     private static float sEpsilon = 0.001f;
29     private final int mNone = -1;
30     private int mSize = 16;
31     private int mHashSize = 16;
32 
33     int[] mKeys = new int[mSize];
34     int[] mNextKeys = new int[mSize];
35 
36     int[] mVariables = new int[mSize];
37     float[] mValues = new float[mSize];
38     int[] mPrevious = new int[mSize];
39     int[] mNext = new int[mSize];
40     int mCount = 0;
41     int mHead = -1;
42 
43     private final ArrayRow mRow; // our owner
44     // pointer to the system-wide cache, allowing access to SolverVariables
45     protected final Cache mCache;
46 
SolverVariableValues(ArrayRow row, Cache cache)47     SolverVariableValues(ArrayRow row, Cache cache) {
48         mRow = row;
49         mCache = cache;
50         clear();
51     }
52 
53     @Override
getCurrentSize()54     public int getCurrentSize() {
55         return mCount;
56     }
57 
58     @Override
getVariable(int index)59     public SolverVariable getVariable(int index) {
60         final int count = mCount;
61         if (count == 0) {
62             return null;
63         }
64         int j = mHead;
65         for (int i = 0; i < count; i++) {
66             if (i == index && j != mNone) {
67                 return mCache.mIndexedVariables[mVariables[j]];
68             }
69             j = mNext[j];
70             if (j == mNone) {
71                 break;
72             }
73         }
74         return null;
75     }
76 
77     @Override
getVariableValue(int index)78     public float getVariableValue(int index) {
79         final int count = mCount;
80         int j = mHead;
81         for (int i = 0; i < count; i++) {
82             if (i == index) {
83                 return mValues[j];
84             }
85             j = mNext[j];
86             if (j == mNone) {
87                 break;
88             }
89         }
90         return 0;
91     }
92 
93     @Override
contains(SolverVariable variable)94     public boolean contains(SolverVariable variable) {
95         return indexOf(variable) != mNone;
96     }
97 
98     @Override
indexOf(SolverVariable variable)99     public int indexOf(SolverVariable variable) {
100         if (mCount == 0 || variable == null) {
101             return mNone;
102         }
103         int id = variable.id;
104         int key = id % mHashSize;
105         key = mKeys[key];
106         if (key == mNone) {
107             return mNone;
108         }
109         if (mVariables[key] == id) {
110             return key;
111         }
112         while (mNextKeys[key] != mNone && mVariables[mNextKeys[key]] != id) {
113             key = mNextKeys[key];
114         }
115         if (mNextKeys[key] == mNone) {
116             return mNone;
117         }
118         if (mVariables[mNextKeys[key]] == id) {
119             return mNextKeys[key];
120         }
121         return mNone;
122     }
123 
124     @Override
get(SolverVariable variable)125     public float get(SolverVariable variable) {
126         final int index = indexOf(variable);
127         if (index != mNone) {
128             return mValues[index];
129         }
130         return 0;
131     }
132 
133     @Override
display()134     public void display() {
135         final int count = mCount;
136         System.out.print("{ ");
137         for (int i = 0; i < count; i++) {
138             SolverVariable v = getVariable(i);
139             if (v == null) {
140                 continue;
141             }
142             System.out.print(v + " = " + getVariableValue(i) + " ");
143         }
144         System.out.println(" }");
145     }
146 
147     @Override
toString()148     public String toString() {
149         String str = hashCode() + " { ";
150         final int count = mCount;
151         for (int i = 0; i < count; i++) {
152             SolverVariable v = getVariable(i);
153             if (v == null) {
154                 continue;
155             }
156             str += v + " = " + getVariableValue(i) + " ";
157             int index = indexOf(v);
158             str += "[p: ";
159             if (mPrevious[index] != mNone) {
160                 str += mCache.mIndexedVariables[mVariables[mPrevious[index]]];
161             } else {
162                 str += "none";
163             }
164             str += ", n: ";
165             if (mNext[index] != mNone) {
166                 str += mCache.mIndexedVariables[mVariables[mNext[index]]];
167             } else {
168                 str += "none";
169             }
170             str += "]";
171         }
172         str += " }";
173         return str;
174     }
175 
176     @Override
clear()177     public void clear() {
178         if (DEBUG) {
179             System.out.println(this + " <clear>");
180         }
181         final int count = mCount;
182         for (int i = 0; i < count; i++) {
183             SolverVariable v = getVariable(i);
184             if (v != null) {
185                 v.removeFromRow(mRow);
186             }
187         }
188         for (int i = 0; i < mSize; i++) {
189             mVariables[i] = mNone;
190             mNextKeys[i] = mNone;
191         }
192         for (int i = 0; i < mHashSize; i++) {
193             mKeys[i] = mNone;
194         }
195         mCount = 0;
196         mHead = -1;
197     }
198 
increaseSize()199     private void increaseSize() {
200         int size = this.mSize * 2;
201         mVariables = Arrays.copyOf(mVariables, size);
202         mValues = Arrays.copyOf(mValues, size);
203         mPrevious = Arrays.copyOf(mPrevious, size);
204         mNext = Arrays.copyOf(mNext, size);
205         mNextKeys = Arrays.copyOf(mNextKeys, size);
206         for (int i = this.mSize; i < size; i++) {
207             mVariables[i] = mNone;
208             mNextKeys[i] = mNone;
209         }
210         this.mSize = size;
211     }
212 
addToHashMap(SolverVariable variable, int index)213     private void addToHashMap(SolverVariable variable, int index) {
214         if (DEBUG) {
215             System.out.println(this.hashCode() + " hash add " + variable.id + " @ " + index);
216         }
217         int hash = variable.id % mHashSize;
218         int key = mKeys[hash];
219         if (key == mNone) {
220             mKeys[hash] = index;
221             if (DEBUG) {
222                 System.out.println(this.hashCode() + " hash add "
223                         + variable.id + " @ " + index + " directly on keys " + hash);
224             }
225         } else {
226             while (mNextKeys[key] != mNone) {
227                 key = mNextKeys[key];
228             }
229             mNextKeys[key] = index;
230             if (DEBUG) {
231                 System.out.println(this.hashCode() + " hash add "
232                         + variable.id + " @ " + index + " as nextkey of " + key);
233             }
234         }
235         mNextKeys[index] = mNone;
236         if (DEBUG) {
237             displayHash();
238         }
239     }
240 
displayHash()241     private void displayHash() {
242         for (int i = 0; i < mHashSize; i++) {
243             if (mKeys[i] != mNone) {
244                 String str = this.hashCode() + " hash [" + i + "] => ";
245                 int key = mKeys[i];
246                 boolean done = false;
247                 while (!done) {
248                     str += " " + mVariables[key];
249                     if (mNextKeys[key] != mNone) {
250                         key = mNextKeys[key];
251                     } else {
252                         done = true;
253                     }
254                 }
255                 System.out.println(str);
256             }
257         }
258     }
259 
removeFromHashMap(SolverVariable variable)260     private void removeFromHashMap(SolverVariable variable) {
261         if (DEBUG) {
262             System.out.println(this.hashCode() + " hash remove " + variable.id);
263         }
264         int hash = variable.id % mHashSize;
265         int key = mKeys[hash];
266         if (key == mNone) {
267             if (DEBUG) {
268                 displayHash();
269             }
270             return;
271         }
272         int id = variable.id;
273         // let's first find it
274         if (mVariables[key] == id) {
275             mKeys[hash] = mNextKeys[key];
276             mNextKeys[key] = mNone;
277         } else {
278             while (mNextKeys[key] != mNone && mVariables[mNextKeys[key]] != id) {
279                 key = mNextKeys[key];
280             }
281             int currentKey = mNextKeys[key];
282             if (currentKey != mNone && mVariables[currentKey] == id) {
283                 mNextKeys[key] = mNextKeys[currentKey];
284                 mNextKeys[currentKey] = mNone;
285             }
286         }
287         if (DEBUG) {
288             displayHash();
289         }
290     }
291 
addVariable(int index, SolverVariable variable, float value)292     private void addVariable(int index, SolverVariable variable, float value) {
293         mVariables[index] = variable.id;
294         mValues[index] = value;
295         mPrevious[index] = mNone;
296         mNext[index] = mNone;
297         variable.addToRow(mRow);
298         variable.usageInRowCount++;
299         mCount++;
300     }
301 
findEmptySlot()302     private int findEmptySlot() {
303         for (int i = 0; i < mSize; i++) {
304             if (mVariables[i] == mNone) {
305                 return i;
306             }
307         }
308         return -1;
309     }
310 
insertVariable(int index, SolverVariable variable, float value)311     private void insertVariable(int index, SolverVariable variable, float value) {
312         int availableSlot = findEmptySlot();
313         addVariable(availableSlot, variable, value);
314         if (index != mNone) {
315             mPrevious[availableSlot] = index;
316             mNext[availableSlot] = mNext[index];
317             mNext[index] = availableSlot;
318         } else {
319             mPrevious[availableSlot] = mNone;
320             if (mCount > 0) {
321                 mNext[availableSlot] = mHead;
322                 mHead = availableSlot;
323             } else {
324                 mNext[availableSlot] = mNone;
325             }
326         }
327         if (mNext[availableSlot] != mNone) {
328             mPrevious[mNext[availableSlot]] = availableSlot;
329         }
330         addToHashMap(variable, availableSlot);
331     }
332 
333     @Override
put(SolverVariable variable, float value)334     public void put(SolverVariable variable, float value) {
335         if (DEBUG) {
336             System.out.println(this + " <put> " + variable.id + " = " + value);
337         }
338         if (value > -sEpsilon && value < sEpsilon) {
339             remove(variable, true);
340             return;
341         }
342         if (mCount == 0) {
343             addVariable(0, variable, value);
344             addToHashMap(variable, 0);
345             mHead = 0;
346         } else {
347             final int index = indexOf(variable);
348             if (index != mNone) {
349                 mValues[index] = value;
350             } else {
351                 if (mCount + 1 >= mSize) {
352                     increaseSize();
353                 }
354                 final int count = mCount;
355                 int previousItem = -1;
356                 int j = mHead;
357                 for (int i = 0; i < count; i++) {
358                     if (mVariables[j] == variable.id) {
359                         mValues[j] = value;
360                         return;
361                     }
362                     if (mVariables[j] < variable.id) {
363                         previousItem = j;
364                     }
365                     j = mNext[j];
366                     if (j == mNone) {
367                         break;
368                     }
369                 }
370                 insertVariable(previousItem, variable, value);
371             }
372         }
373     }
374 
375     @Override
sizeInBytes()376     public int sizeInBytes() {
377         return 0;
378     }
379 
380     @Override
remove(SolverVariable v, boolean removeFromDefinition)381     public float remove(SolverVariable v, boolean removeFromDefinition) {
382         if (DEBUG) {
383             System.out.println(this + " <remove> " + v.id);
384         }
385         int index = indexOf(v);
386         if (index == mNone) {
387             return 0;
388         }
389         removeFromHashMap(v);
390         float value = mValues[index];
391         if (mHead == index) {
392             mHead = mNext[index];
393         }
394         mVariables[index] = mNone;
395         if (mPrevious[index] != mNone) {
396             mNext[mPrevious[index]] = mNext[index];
397         }
398         if (mNext[index] != mNone) {
399             mPrevious[mNext[index]] = mPrevious[index];
400         }
401         mCount--;
402         v.usageInRowCount--;
403         if (removeFromDefinition) {
404             v.removeFromRow(mRow);
405         }
406         return value;
407     }
408 
409     @Override
add(SolverVariable v, float value, boolean removeFromDefinition)410     public void add(SolverVariable v, float value, boolean removeFromDefinition) {
411         if (DEBUG) {
412             System.out.println(this + " <add> " + v.id + " = " + value);
413         }
414         if (value > -sEpsilon && value < sEpsilon) {
415             return;
416         }
417         final int index = indexOf(v);
418         if (index == mNone) {
419             put(v, value);
420         } else {
421             mValues[index] += value;
422             if (mValues[index] > -sEpsilon && mValues[index] < sEpsilon) {
423                 mValues[index] = 0;
424                 remove(v, removeFromDefinition);
425             }
426         }
427     }
428 
429     @Override
use(ArrayRow definition, boolean removeFromDefinition)430     public float use(ArrayRow definition, boolean removeFromDefinition) {
431         float value = get(definition.mVariable);
432         remove(definition.mVariable, removeFromDefinition);
433         if (false) {
434             ArrayRow.ArrayRowVariables definitionVariables = definition.variables;
435             int definitionSize = definitionVariables.getCurrentSize();
436             for (int i = 0; i < definitionSize; i++) {
437                 SolverVariable definitionVariable = definitionVariables.getVariable(i);
438                 float definitionValue = definitionVariables.get(definitionVariable);
439                 this.add(definitionVariable, definitionValue * value, removeFromDefinition);
440             }
441             return value;
442         }
443         SolverVariableValues localDef = (SolverVariableValues) definition.variables;
444         final int definitionSize = localDef.getCurrentSize();
445         int j = localDef.mHead;
446         if (false) {
447             for (int i = 0; i < definitionSize; i++) {
448                 float definitionValue = localDef.mValues[j];
449                 SolverVariable definitionVariable =
450                         mCache.mIndexedVariables[localDef.mVariables[j]];
451                 add(definitionVariable, definitionValue * value, removeFromDefinition);
452                 j = localDef.mNext[j];
453                 if (j == mNone) {
454                     break;
455                 }
456             }
457         } else {
458             j = 0;
459             for (int i = 0; j < definitionSize; i++) {
460                 if (localDef.mVariables[i] != mNone) {
461                     float definitionValue = localDef.mValues[i];
462                     SolverVariable definitionVariable =
463                             mCache.mIndexedVariables[localDef.mVariables[i]];
464                     add(definitionVariable, definitionValue * value, removeFromDefinition);
465                     j++;
466                 }
467             }
468         }
469         return value;
470     }
471 
472     @Override
invert()473     public void invert() {
474         final int count = mCount;
475         int j = mHead;
476         for (int i = 0; i < count; i++) {
477             mValues[j] *= -1;
478             j = mNext[j];
479             if (j == mNone) {
480                 break;
481             }
482         }
483     }
484 
485     @Override
divideByAmount(float amount)486     public void divideByAmount(float amount) {
487         final int count = mCount;
488         int j = mHead;
489         for (int i = 0; i < count; i++) {
490             mValues[j] /= amount;
491             j = mNext[j];
492             if (j == mNone) {
493                 break;
494             }
495         }
496     }
497 
498 }
499