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