OMCompiler/Compiler/Util/UnorderedMap.mo
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file is part of OpenModelica. | ||
| 3 | * | ||
| 4 | * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC), | ||
| 5 | * c/o Linköpings universitet, Department of Computer and Information Science, | ||
| 6 | * SE-58183 Linköping, Sweden. | ||
| 7 | * | ||
| 8 | * All rights reserved. | ||
| 9 | * | ||
| 10 | * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF AGPL VERSION 3 LICENSE OR | ||
| 11 | * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8. | ||
| 12 | * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES | ||
| 13 | * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GNU AGPL | ||
| 14 | * VERSION 3, ACCORDING TO RECIPIENTS CHOICE. | ||
| 15 | * | ||
| 16 | * The OpenModelica software and the OSMC (Open Source Modelica Consortium) | ||
| 17 | * Public License (OSMC-PL) are obtained from OSMC, either from the above | ||
| 18 | * address, from the URLs: | ||
| 19 | * http://www.openmodelica.org or | ||
| 20 | * https://github.com/OpenModelica/ or | ||
| 21 | * http://www.ida.liu.se/projects/OpenModelica, | ||
| 22 | * and in the OpenModelica distribution. | ||
| 23 | * | ||
| 24 | * GNU AGPL version 3 is obtained from: | ||
| 25 | * https://www.gnu.org/licenses/licenses.html#GPL | ||
| 26 | * | ||
| 27 | * This program is distributed WITHOUT ANY WARRANTY; without | ||
| 28 | * even the implied warranty of MERCHANTABILITY or FITNESS | ||
| 29 | * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH | ||
| 30 | * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL. | ||
| 31 | * | ||
| 32 | * See the full OSMC Public License conditions for more details. | ||
| 33 | * | ||
| 34 | */ | ||
| 35 | |||
| 36 | encapsulated uniontype UnorderedMap<K, V> | ||
| 37 | "An implementation of a generic unordered map, a.k.a. hash map. | ||
| 38 | |||
| 39 | This implementation uses separate chaining and automatically rehashes the map | ||
| 40 | when the load factor becomes too large to keep the performance up." | ||
| 41 | |||
| 42 | import Vector; | ||
| 43 | |||
| 44 | protected | ||
| 45 | import Error; | ||
| 46 | import List; | ||
| 47 | import MetaModelica.Dangerous.*; | ||
| 48 | import Util; | ||
| 49 | import IOStream; | ||
| 50 | import UnorderedSet; | ||
| 51 | |||
| 52 | public | ||
| 53 | partial function Hash | ||
| 54 | input K key; | ||
| 55 | output Integer hash; | ||
| 56 | end Hash; | ||
| 57 | |||
| 58 | partial function KeyEq | ||
| 59 | input K key1; | ||
| 60 | input K key2; | ||
| 61 | output Boolean equal; | ||
| 62 | end KeyEq; | ||
| 63 | |||
| 64 | partial function KeyStringFn | ||
| 65 | input K key; | ||
| 66 | output String str; | ||
| 67 | end KeyStringFn; | ||
| 68 | |||
| 69 | partial function ValueStringFn | ||
| 70 | input V value; | ||
| 71 | output String str; | ||
| 72 | end ValueStringFn; | ||
| 73 | |||
| 74 | record UNORDERED_MAP | ||
| 75 | Vector<list<Integer>> buckets; | ||
| 76 | Vector<K> keys; | ||
| 77 | Vector<Integer> hashes "hashFn of each key"; | ||
| 78 | Vector<V> values; | ||
| 79 | Hash hashFn; | ||
| 80 | KeyEq eqFn; | ||
| 81 | end UNORDERED_MAP; | ||
| 82 | |||
| 83 | function new<V> | ||
| 84 | "Creates a new map given a hash function, key equality function, and | ||
| 85 | optional desired bucket count. An appropriate bucket count is | ||
| 86 | Util.nextPrime(number of elements that will be added), but starting with a | ||
| 87 | low bucket count is also fine if the number of elements is unknown since | ||
| 88 | the map rehashes as needed." | ||
| 89 | input Hash hash; | ||
| 90 | input KeyEq keyEq; | ||
| 91 | input Integer bucketCount = 1; | ||
| 92 | output UnorderedMap<K, V> map; | ||
| 93 | algorithm | ||
| 94 | 683348 | map := UNORDERED_MAP( | |
| 95 | Vector.newFill(bucketCount, {}), | ||
| 96 | Vector.new<K>(), | ||
| 97 | Vector.new<Integer>(), | ||
| 98 | Vector.new<V>(), | ||
| 99 | hash, | ||
| 100 | keyEq | ||
| 101 | ); | ||
| 102 | end new; | ||
| 103 | |||
| 104 | function fromLists<V> | ||
| 105 | "Creates a new map from a list of keys and a corresponding list of values. | ||
| 106 | Fails if the two lists do not have the same size." | ||
| 107 | input list<K> keys; | ||
| 108 | input list<V> values; | ||
| 109 | input Hash hash; | ||
| 110 | input KeyEq keyEq; | ||
| 111 | output UnorderedMap<K, V> map; | ||
| 112 | protected | ||
| 113 | Integer key_count, bucket_count; | ||
| 114 | V v; | ||
| 115 | list<V> rest_v = values; | ||
| 116 | algorithm | ||
| 117 | 1371 | key_count := listLength(keys); | |
| 118 | 1371 | bucket_count := Util.nextPrime(key_count); | |
| 119 | |||
| 120 | 1371 | map := UNORDERED_MAP( | |
| 121 | Vector.newFill(bucket_count, {}), | ||
| 122 | Vector.new<K>(key_count), | ||
| 123 | Vector.new<Integer>(key_count), | ||
| 124 | Vector.new<V>(key_count), | ||
| 125 | hash, | ||
| 126 | keyEq | ||
| 127 | ); | ||
| 128 | |||
| 129 |
2/2✓ Branch 0 taken 1822 times.
✓ Branch 1 taken 1371 times.
|
3193 | for k in keys loop |
| 130 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1822 times.
|
1822 | v :: rest_v := rest_v; |
| 131 | 1822 | add(k, v, map); | |
| 132 | end for; | ||
| 133 | end fromLists; | ||
| 134 | |||
| 135 | function copy | ||
| 136 | "Returns a copy of the map." | ||
| 137 | input UnorderedMap<K, V> map; | ||
| 138 | output UnorderedMap<K, V> outMap; | ||
| 139 | algorithm | ||
| 140 | 665 | outMap := UNORDERED_MAP( | |
| 141 | Vector.copy(map.buckets), | ||
| 142 | Vector.copy(map.keys), | ||
| 143 | Vector.copy(map.hashes), | ||
| 144 | Vector.copy(map.values), | ||
| 145 | map.hashFn, | ||
| 146 | map.eqFn | ||
| 147 | ); | ||
| 148 | end copy; | ||
| 149 | |||
| 150 | function deepCopy | ||
| 151 | "Returns a deep copy of the map using the given copy function for values." | ||
| 152 | input UnorderedMap<K, V> map; | ||
| 153 | input CopyFn fn; | ||
| 154 | output UnorderedMap<K, V> outMap; | ||
| 155 | |||
| 156 | partial function CopyFn | ||
| 157 | input output V value; | ||
| 158 | end CopyFn; | ||
| 159 | algorithm | ||
| 160 | ✗ | outMap := UNORDERED_MAP( | |
| 161 | Vector.copy(map.buckets), | ||
| 162 | Vector.copy(map.keys), | ||
| 163 | Vector.copy(map.hashes), | ||
| 164 | Vector.deepCopy(map.values, fn), | ||
| 165 | map.hashFn, | ||
| 166 | map.eqFn | ||
| 167 | ); | ||
| 168 | end deepCopy; | ||
| 169 | |||
| 170 | function add | ||
| 171 | "Adds a key and associated value to the map, or updates the value if the key | ||
| 172 | already exists in the map. Might trigger a rehash." | ||
| 173 | input K key; | ||
| 174 | input V value; | ||
| 175 | input UnorderedMap<K, V> map; | ||
| 176 | protected | ||
| 177 | Integer index, hash; | ||
| 178 | algorithm | ||
| 179 | 2593050 | (index, hash) := find(key, map); | |
| 180 | |||
| 181 |
2/2✓ Branch 0 taken 245495 times.
✓ Branch 1 taken 2347555 times.
|
2593050 | if index > 0 then |
| 182 | 245495 | Vector.update(map.values, index, value); | |
| 183 | else | ||
| 184 | 2347555 | addEntry(key, value, hash, map); | |
| 185 | end if; | ||
| 186 | end add; | ||
| 187 | |||
| 188 | function addNew | ||
| 189 | "Adds a key and associated value to the map without checking if it already | ||
| 190 | exists. Faster than add since it doesn't need to check if the key exists, | ||
| 191 | but will lead to duplicate keys if it actually does exist in the map | ||
| 192 | already. Might trigger a rehash." | ||
| 193 | input K key; | ||
| 194 | input V value; | ||
| 195 | input UnorderedMap<K, V> map; | ||
| 196 | protected | ||
| 197 | Hash hashfn = map.hashFn; | ||
| 198 | algorithm | ||
| 199 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 364305 times.
|
364305 | addEntry(key, value, hashfn(key), map); |
| 200 | end addNew; | ||
| 201 | |||
| 202 | function addUnique | ||
| 203 | "Adds a key and associated value to the map, but fails if the key already | ||
| 204 | exists. Might trigger a rehash." | ||
| 205 | input K key; | ||
| 206 | input V value; | ||
| 207 | input UnorderedMap<K, V> map; | ||
| 208 | protected | ||
| 209 | Integer index, hash; | ||
| 210 | algorithm | ||
| 211 | 8302 | (index, hash) := find(key, map); | |
| 212 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8302 times.
|
8302 | false := index > 0; |
| 213 | 8302 | addEntry(key, value, hash, map); | |
| 214 | end addUnique; | ||
| 215 | |||
| 216 | function tryAdd | ||
| 217 | "Adds a key and associated value to the map if the key doesn't already | ||
| 218 | exist, otherwise does nothing. Returns the value associated with the key if | ||
| 219 | the key already existed, otherwise the new value. Might trigger a rehash." | ||
| 220 | input K key; | ||
| 221 | input V value; | ||
| 222 | input UnorderedMap<K, V> map; | ||
| 223 | output V outValue; | ||
| 224 | protected | ||
| 225 | Integer index, hash; | ||
| 226 | algorithm | ||
| 227 | 58115 | (index, hash) := find(key, map); | |
| 228 | |||
| 229 |
2/2✓ Branch 0 taken 9446 times.
✓ Branch 1 taken 48669 times.
|
58115 | if index > 0 then |
| 230 | 9446 | outValue := Vector.getNoBounds(map.values, index); | |
| 231 | else | ||
| 232 | outValue := value; | ||
| 233 | 48669 | addEntry(key, value, hash, map); | |
| 234 | end if; | ||
| 235 | end tryAdd; | ||
| 236 | |||
| 237 | function tryUpdate | ||
| 238 | "Updates the value associated with a key if the key exists in the map, | ||
| 239 | otherwise does nothing. Returns whether the value was updated or not, | ||
| 240 | i.e. if the key exists in the map or not." | ||
| 241 | input K key; | ||
| 242 | input V value; | ||
| 243 | input UnorderedMap<K, V> map; | ||
| 244 | output Boolean updated; | ||
| 245 | protected | ||
| 246 | Integer index, hash; | ||
| 247 | algorithm | ||
| 248 | 11 | (index, hash) := find(key, map); | |
| 249 | 11 | updated := index > 0; | |
| 250 | |||
| 251 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | if updated then |
| 252 | 11 | Vector.update(map.values, index, value); | |
| 253 | end if; | ||
| 254 | end tryUpdate; | ||
| 255 | |||
| 256 | function addUpdate | ||
| 257 | "Adds a key and associated value to the map, where the value is generated by | ||
| 258 | calling the given function. If the key already exists the function is given | ||
| 259 | the old value. The generated value is returned by this function. | ||
| 260 | |||
| 261 | This function can be used to e.g. append to an existing value or create a | ||
| 262 | new value if none exists. This is faster than trying to fetch the value, | ||
| 263 | updating it and then readding it, since the key only needs to be hashed | ||
| 264 | once." | ||
| 265 | input K key; | ||
| 266 | input UpdateFn fn; | ||
| 267 | input UnorderedMap<K, V> map; | ||
| 268 | output V value; | ||
| 269 | |||
| 270 | partial function UpdateFn | ||
| 271 | input Option<V> oldValue; | ||
| 272 | output V value; | ||
| 273 | end UpdateFn; | ||
| 274 | protected | ||
| 275 | Integer index, hash; | ||
| 276 | algorithm | ||
| 277 | 225243 | (index, hash) := find(key, map); | |
| 278 | |||
| 279 |
2/2✓ Branch 0 taken 10112 times.
✓ Branch 1 taken 215131 times.
|
225243 | if index > 0 then |
| 280 |
2/2✓ Branch 0 taken 8340 times.
✓ Branch 1 taken 1772 times.
|
20224 | value := fn(SOME(Vector.getNoBounds(map.values, index))); |
| 281 | 10112 | Vector.updateNoBounds(map.values, index, value); | |
| 282 | else | ||
| 283 |
2/2✓ Branch 0 taken 209398 times.
✓ Branch 1 taken 5733 times.
|
215131 | value := fn(NONE()); |
| 284 | 215131 | addEntry(key, value, hash, map); | |
| 285 | end if; | ||
| 286 | end addUpdate; | ||
| 287 | |||
| 288 | function addMerge | ||
| 289 | "Adds a key and associated value to the map. If the key already exists the | ||
| 290 | merge function is called with the new and the old value and its result is | ||
| 291 | stored. Unlike addUpdate this takes a plain function rather than a closure, | ||
| 292 | which matters on paths that add millions of entries." | ||
| 293 | input K key; | ||
| 294 | input V value; | ||
| 295 | input MergeFn fn; | ||
| 296 | input UnorderedMap<K, V> map; | ||
| 297 | |||
| 298 | partial function MergeFn | ||
| 299 | input V newValue; | ||
| 300 | input V oldValue; | ||
| 301 | output V value; | ||
| 302 | end MergeFn; | ||
| 303 | protected | ||
| 304 | Integer index, hash; | ||
| 305 | algorithm | ||
| 306 | ✗ | (index, hash) := find(key, map); | |
| 307 | |||
| 308 | ✗ | if index > 0 then | |
| 309 | ✗ | Vector.updateNoBounds(map.values, index, fn(value, Vector.getNoBounds(map.values, index))); | |
| 310 | else | ||
| 311 | ✗ | addEntry(key, value, hash, map); | |
| 312 | end if; | ||
| 313 | end addMerge; | ||
| 314 | |||
| 315 | function tryAddUpdate | ||
| 316 | "Adds a key and associated value to the map, where the value is generated by | ||
| 317 | calling the given function. If the key already exists the function is given | ||
| 318 | the old value. The generated value is returned by this function. If the key | ||
| 319 | does not exist nothing happens. | ||
| 320 | |||
| 321 | This function can be used to e.g. append to an existing key, value pair. | ||
| 322 | This is faster than trying to fetch the value, | ||
| 323 | updating it and then readding it, since the key only needs to be hashed | ||
| 324 | once." | ||
| 325 | input K key; | ||
| 326 | input UpdateFn fn; | ||
| 327 | input UnorderedMap<K, V> map; | ||
| 328 | output Boolean updated; | ||
| 329 | |||
| 330 | partial function UpdateFn | ||
| 331 | input Option<V> oldValue; | ||
| 332 | output V value; | ||
| 333 | end UpdateFn; | ||
| 334 | protected | ||
| 335 | Integer index, hash; | ||
| 336 | V value; | ||
| 337 | algorithm | ||
| 338 | 38 | (index, hash) := find(key, map); | |
| 339 | 38 | updated := index > 0; | |
| 340 | |||
| 341 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
|
38 | if updated then |
| 342 |
1/2✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
|
76 | value := fn(SOME(Vector.getNoBounds(map.values, index))); |
| 343 | 38 | Vector.updateNoBounds(map.values, index, value); | |
| 344 | end if; | ||
| 345 | end tryAddUpdate; | ||
| 346 | |||
| 347 | function remove | ||
| 348 | "Removes a key from the map. Returns true if the key existed in the map and | ||
| 349 | was removed, or false if the key did not exist in the map. This function is | ||
| 350 | O(N) since it will remove the key/value from the key/value arrays. | ||
| 351 | |||
| 352 | Will not trigger a rehash, so rehash must be called manually if shrinking | ||
| 353 | the map is desirable (probably not a good idea unless the load factor is | ||
| 354 | very low, i.e. less than 0.25 or so)." | ||
| 355 | input K key; | ||
| 356 | input UnorderedMap<K, V> map; | ||
| 357 | output Boolean removed; | ||
| 358 | protected | ||
| 359 | Integer hash, index; | ||
| 360 | list<Integer> bucket; | ||
| 361 | |||
| 362 | function update_indices | ||
| 363 | input list<Integer> bucket; | ||
| 364 | input Integer removedIndex; | ||
| 365 | output list<Integer> outBucket; | ||
| 366 | algorithm | ||
| 367 |
6/6✓ Branch 0 taken 6697 times.
✓ Branch 1 taken 17889 times.
✓ Branch 2 taken 6697 times.
✓ Branch 3 taken 17889 times.
✓ Branch 4 taken 4252 times.
✓ Branch 5 taken 2445 times.
|
24586 | outBucket := list(if i > removedIndex then i-1 else i for i in bucket); |
| 368 | end update_indices; | ||
| 369 | algorithm | ||
| 370 | 2077 | (index, hash) := find(key, map); | |
| 371 | 2077 | removed := index > 0; | |
| 372 | |||
| 373 | // Key didn't exist in the map, do nothing. | ||
| 374 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2077 times.
|
2077 | if not removed then |
| 375 | ✗ | return; | |
| 376 | end if; | ||
| 377 | |||
| 378 | // Remove the index from the bucket. | ||
| 379 |
1/2✓ Branch 1 taken 2077 times.
✗ Branch 2 not taken.
|
2077 | hash := intMod(hash, Vector.size(map.buckets)) + 1; |
| 380 | 2077 | bucket := Vector.get(map.buckets, hash); | |
| 381 | 2077 | bucket := List.deleteMemberOnTrue(index, bucket, intEq); | |
| 382 | 2077 | Vector.updateNoBounds(map.buckets, hash, bucket); | |
| 383 | |||
| 384 | // Remove the key/value from the arrays. | ||
| 385 | 2077 | Vector.remove(map.keys, index); | |
| 386 | 2077 | Vector.remove(map.hashes, index); | |
| 387 | 2077 | Vector.remove(map.values, index); | |
| 388 | |||
| 389 | // Update the indices in the buckets. | ||
| 390 | 2077 | Vector.apply(map.buckets, function update_indices(removedIndex = index)); | |
| 391 | end remove; | ||
| 392 | |||
| 393 | function clear | ||
| 394 | input UnorderedMap<K, V> map; | ||
| 395 | algorithm | ||
| 396 | 2548 | Vector.clear(map.buckets); | |
| 397 | 2548 | Vector.push(map.buckets, {}); | |
| 398 | 2548 | Vector.clear(map.keys); | |
| 399 | 2548 | Vector.clear(map.hashes); | |
| 400 | 2548 | Vector.clear(map.values); | |
| 401 | end clear; | ||
| 402 | |||
| 403 | function get | ||
| 404 | "Returns SOME(value) if the given key has an associated value in the map, | ||
| 405 | otherwise NONE()." | ||
| 406 | input K key; | ||
| 407 | input UnorderedMap<K, V> map; | ||
| 408 | output Option<V> value; | ||
| 409 | protected | ||
| 410 | Integer index = find(key, map); | ||
| 411 | algorithm | ||
| 412 |
2/2✓ Branch 0 taken 1162260 times.
✓ Branch 1 taken 1406799 times.
|
2569059 | value := if index > 0 then SOME(Vector.getNoBounds(map.values, index)) else NONE(); |
| 413 | end get; | ||
| 414 | |||
| 415 | function getSafe | ||
| 416 | "Returns value if the given key has an associated value in the map, | ||
| 417 | otherwise fails." | ||
| 418 | input K key; | ||
| 419 | input UnorderedMap<K, V> map; | ||
| 420 | input SourceInfo info; | ||
| 421 | output V value; | ||
| 422 | protected | ||
| 423 | Integer index = find(key, map); | ||
| 424 | algorithm | ||
| 425 |
1/2✓ Branch 0 taken 112641 times.
✗ Branch 1 not taken.
|
112641 | if index > 0 then |
| 426 | 112641 | value := Vector.getNoBounds(map.values, index); | |
| 427 | else | ||
| 428 | ✗ | Error.addInternalError(getInstanceName() + " failed because the key did not exist.", info); | |
| 429 | ✗ | fail(); | |
| 430 | end if; | ||
| 431 | end getSafe; | ||
| 432 | |||
| 433 | function getOrFail | ||
| 434 | "Returns the value associated with the given key, or fails if no such value exists." | ||
| 435 | input K key; | ||
| 436 | input UnorderedMap<K, V> map; | ||
| 437 | output V value = Vector.get(map.values, find(key, map)); | ||
| 438 | end getOrFail; | ||
| 439 | |||
| 440 | function getOrDefault | ||
| 441 | input K key; | ||
| 442 | input UnorderedMap<K, V> map; | ||
| 443 | input V default; | ||
| 444 | output V value; | ||
| 445 | protected | ||
| 446 | Integer index = find(key, map); | ||
| 447 | algorithm | ||
| 448 |
2/2✓ Branch 0 taken 352651 times.
✓ Branch 1 taken 1183818 times.
|
1536469 | value := if index > 0 then Vector.getNoBounds(map.values, index) else default; |
| 449 | end getOrDefault; | ||
| 450 | |||
| 451 | function getList | ||
| 452 | "Returns all values associated with the given keys, omits values for missing keys." | ||
| 453 | input list<K> keys; | ||
| 454 | input UnorderedMap<K, V> map; | ||
| 455 | output list<V> values = {}; | ||
| 456 | protected | ||
| 457 | Integer index; | ||
| 458 | algorithm | ||
| 459 | ✗ | for key in keys loop | |
| 460 | ✗ | index := find(key, map); | |
| 461 | ✗ | if index > 0 then | |
| 462 | ✗ | values := Vector.getNoBounds(map.values, index) :: values; | |
| 463 | end if; | ||
| 464 | end for; | ||
| 465 | ✗ | values := listReverseInPlace(values); | |
| 466 | end getList; | ||
| 467 | |||
| 468 | function getKey | ||
| 469 | "Returns SOME(key) if the key exists in the map, otherwise NONE()." | ||
| 470 | input K key; | ||
| 471 | input UnorderedMap<K, V> map; | ||
| 472 | output Option<K> outKey; | ||
| 473 | protected | ||
| 474 | Integer index = find(key, map); | ||
| 475 | algorithm | ||
| 476 |
2/2✓ Branch 0 taken 57 times.
✓ Branch 1 taken 100 times.
|
157 | outKey := if index > 0 then SOME(Vector.getNoBounds(map.keys, index)) else NONE(); |
| 477 | end getKey; | ||
| 478 | |||
| 479 | function updateKey | ||
| 480 | "Updates an existing key by replacing it with a new value. | ||
| 481 | This can be used to update properties of the key that does not affect its | ||
| 482 | hash or equivalence relation. Will fail if the key doesn't exist in the map." | ||
| 483 | input K key; | ||
| 484 | input UnorderedMap<K, V> map; | ||
| 485 | algorithm | ||
| 486 | 27 | Vector.update(map.keys, find(key, map), key); | |
| 487 | end updateKey; | ||
| 488 | |||
| 489 | function contains | ||
| 490 | "Returns whether the given key exists in the map or not." | ||
| 491 | input K key; | ||
| 492 | input UnorderedMap<K, V> map; | ||
| 493 | output Boolean res = find(key, map) > 0; | ||
| 494 | end contains; | ||
| 495 | |||
| 496 | function first | ||
| 497 | "Returns the 'first' element in the map, or fails if the map is empty." | ||
| 498 | input UnorderedMap<K, V> map; | ||
| 499 | output V value = Vector.get(map.values, 1); | ||
| 500 | end first; | ||
| 501 | |||
| 502 | function firstKey | ||
| 503 | "Returns the 'first' key in the map, or fails if the map is empty." | ||
| 504 | input UnorderedMap<K, V> map; | ||
| 505 | output K key = Vector.get(map.keys, 1); | ||
| 506 | end firstKey; | ||
| 507 | |||
| 508 | function keyAt | ||
| 509 | input UnorderedMap<K, V> map; | ||
| 510 | input Integer index; | ||
| 511 | output K key = Vector.get(map.keys, index); | ||
| 512 | end keyAt; | ||
| 513 | |||
| 514 | function valueAt | ||
| 515 | input UnorderedMap<K, V> map; | ||
| 516 | input Integer index; | ||
| 517 | output V value = Vector.get(map.values, index); | ||
| 518 | end valueAt; | ||
| 519 | |||
| 520 | function toList | ||
| 521 | "Returns a list with the (key, value) pairs." | ||
| 522 | input UnorderedMap<K, V> map; | ||
| 523 | output list<tuple<K, V>> lst = List.zip(keyList(map), valueList(map)); | ||
| 524 | end toList; | ||
| 525 | |||
| 526 | function keyList | ||
| 527 | "Returns the keys as a list." | ||
| 528 | input UnorderedMap<K, V> map; | ||
| 529 | output list<K> keys = Vector.toList(map.keys); | ||
| 530 | end keyList; | ||
| 531 | |||
| 532 | function valueList | ||
| 533 | "Returns the values as a list." | ||
| 534 | input UnorderedMap<K, V> map; | ||
| 535 | output list<V> values = Vector.toList(map.values); | ||
| 536 | end valueList; | ||
| 537 | |||
| 538 | function toArray | ||
| 539 | "Returns an array with the (key, value) pairs." | ||
| 540 | input UnorderedMap<K, V> map; | ||
| 541 | output array<tuple<K, V>> entries; | ||
| 542 | protected | ||
| 543 | Vector<K> keys = map.keys; | ||
| 544 | Vector<V> values = map.values; | ||
| 545 | tuple<K, V> t = t; | ||
| 546 | Integer sz = Vector.size(keys); | ||
| 547 | algorithm | ||
| 548 | 1590 | entries := arrayCreateNoInit(sz, t); | |
| 549 | |||
| 550 |
2/2✓ Branch 0 taken 1204 times.
✓ Branch 1 taken 386 times.
|
119780 | for i in 1:sz loop |
| 551 | 118190 | arrayUpdateNoBoundsChecking(entries, i, | |
| 552 | (Vector.getNoBounds(keys, i), Vector.getNoBounds(values, i))); | ||
| 553 | end for; | ||
| 554 | end toArray; | ||
| 555 | |||
| 556 | function keyArray | ||
| 557 | "Returns the keys as an array." | ||
| 558 | input UnorderedMap<K, V> map; | ||
| 559 | output array<K> keys = Vector.toArray(map.keys); | ||
| 560 | end keyArray; | ||
| 561 | |||
| 562 | function valueArray | ||
| 563 | "Returns the values as an array." | ||
| 564 | input UnorderedMap<K, V> map; | ||
| 565 | output array<V> values = Vector.toArray(map.values); | ||
| 566 | end valueArray; | ||
| 567 | |||
| 568 | function toVector | ||
| 569 | "Returns a Vector with the (key, value) pairs." | ||
| 570 | input UnorderedMap<K, V> map; | ||
| 571 | output Vector<tuple<K, V>> entries; | ||
| 572 | protected | ||
| 573 | Vector<K> keys = map.keys; | ||
| 574 | Vector<V> values = map.values; | ||
| 575 | Integer sz = Vector.size(keys); | ||
| 576 | type EntryT = tuple<K, V>; | ||
| 577 | algorithm | ||
| 578 | ✗ | entries := Vector.new<EntryT>(sz); | |
| 579 | |||
| 580 | ✗ | for i in 1:sz loop | |
| 581 | ✗ | Vector.updateNoBounds(entries, i, | |
| 582 | (Vector.getNoBounds(keys, i), Vector.getNoBounds(values, i))); | ||
| 583 | end for; | ||
| 584 | end toVector; | ||
| 585 | |||
| 586 | function keyVector | ||
| 587 | "Returns the keys as a Vector." | ||
| 588 | input UnorderedMap<K, V> map; | ||
| 589 | output Vector<K> keys = Vector.copy(map.keys); | ||
| 590 | end keyVector; | ||
| 591 | |||
| 592 | function valueVector | ||
| 593 | "Returns the values as a Vector." | ||
| 594 | input UnorderedMap<K, V> map; | ||
| 595 | output Vector<V> values = Vector.copy(map.values); | ||
| 596 | end valueVector; | ||
| 597 | |||
| 598 | function keySet | ||
| 599 | "Returns the keys as an UnorderedSet" | ||
| 600 | input UnorderedMap<K, V> map; | ||
| 601 | output UnorderedSet<K> set; | ||
| 602 | protected | ||
| 603 | Integer bucket_count = Vector.size(map.buckets); | ||
| 604 | array<list<K>> buckets; | ||
| 605 | algorithm | ||
| 606 | 19 | buckets := arrayCreate(bucket_count, {}); | |
| 607 |
1/2✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
|
4902 | for h in 1:bucket_count loop |
| 608 |
4/4✓ Branch 1 taken 24 times.
✓ Branch 2 taken 4883 times.
✓ Branch 3 taken 24 times.
✓ Branch 4 taken 4883 times.
|
4907 | arrayUpdateNoBoundsChecking(buckets, h, |
| 609 | list(Vector.getNoBounds(map.keys, i) for i in Vector.get(map.buckets, h))); | ||
| 610 | end for; | ||
| 611 | |||
| 612 | 19 | set := UnorderedSet.UNORDERED_SET( | |
| 613 | Mutable.create(buckets), | ||
| 614 | Mutable.create(Vector.size(map.keys)), | ||
| 615 | map.hashFn, | ||
| 616 | map.eqFn); | ||
| 617 | end keySet; | ||
| 618 | |||
| 619 | function fold<FT> | ||
| 620 | "Folds over the values in the map." | ||
| 621 | input UnorderedMap<K, V> map; | ||
| 622 | input FoldFn fn; | ||
| 623 | input output FT arg; | ||
| 624 | |||
| 625 | partial function FoldFn | ||
| 626 | input V value; | ||
| 627 | input output FT arg; | ||
| 628 | end FoldFn; | ||
| 629 | algorithm | ||
| 630 | ✗ | arg := Vector.fold(map.values, fn, arg); | |
| 631 | end fold; | ||
| 632 | |||
| 633 | function map<OT> | ||
| 634 | "Applies a function to each value in the given map and returns a copy of the | ||
| 635 | map with the new values." | ||
| 636 | input UnorderedMap<K, V> map; | ||
| 637 | input MapFn fn; | ||
| 638 | output UnorderedMap<K, OT> outMap; | ||
| 639 | |||
| 640 | partial function MapFn | ||
| 641 | input V value; | ||
| 642 | output OT outValue; | ||
| 643 | end MapFn; | ||
| 644 | protected | ||
| 645 | Vector<OT> new_values; | ||
| 646 | algorithm | ||
| 647 | ✗ | new_values := Vector.map(map.values, fn); | |
| 648 | ✗ | outMap := UNORDERED_MAP( | |
| 649 | Vector.copy(map.buckets), | ||
| 650 | Vector.copy(map.keys), | ||
| 651 | Vector.copy(map.hashes), | ||
| 652 | new_values, | ||
| 653 | map.hashFn, | ||
| 654 | map.eqFn | ||
| 655 | ); | ||
| 656 | end map; | ||
| 657 | |||
| 658 | function apply | ||
| 659 | "Replaces each value in the given map with the result of the given function | ||
| 660 | when applied to each value." | ||
| 661 | input UnorderedMap<K, V> map; | ||
| 662 | input ApplyFn fn; | ||
| 663 | |||
| 664 | partial function ApplyFn | ||
| 665 | input output V value; | ||
| 666 | end ApplyFn; | ||
| 667 | algorithm | ||
| 668 | 54609 | Vector.apply(map.values, fn); | |
| 669 | end apply; | ||
| 670 | |||
| 671 | function merge | ||
| 672 | input UnorderedMap<K, V> map1; | ||
| 673 | input UnorderedMap<K, V> map2; | ||
| 674 | input SourceInfo info; | ||
| 675 | output UnorderedMap<K, V> result; | ||
| 676 | protected | ||
| 677 | UnorderedMap<K, V> tmp; | ||
| 678 | K k; | ||
| 679 | V v; | ||
| 680 | algorithm | ||
| 681 |
2/2✓ Branch 2 taken 182 times.
✓ Branch 3 taken 229 times.
|
411 | if Vector.size(map1.keys) > Vector.size(map2.keys) then |
| 682 | 182 | result := copy(map1); | |
| 683 | tmp := map2; | ||
| 684 | else | ||
| 685 | 229 | result := copy(map2); | |
| 686 | tmp := map1; | ||
| 687 | end if; | ||
| 688 |
2/2✓ Branch 1 taken 310 times.
✓ Branch 2 taken 101 times.
|
1131 | for i in 1:Vector.size(tmp.keys) loop |
| 689 | 720 | k := Vector.getNoBounds(tmp.keys, i); | |
| 690 | 720 | v := Vector.getNoBounds(tmp.values, i); | |
| 691 | try | ||
| 692 | 720 | addUnique(k, v, result); | |
| 693 | else | ||
| 694 | ✗ | Error.addInternalError(getInstanceName() + " failed because both maps contain the same key.", info); | |
| 695 | end try; | ||
| 696 | end for; | ||
| 697 | end merge; | ||
| 698 | |||
| 699 | function subMap | ||
| 700 | input UnorderedMap<K, V> map; | ||
| 701 | input list<K> lst; | ||
| 702 | output UnorderedMap<K, V> sub_map; | ||
| 703 | protected | ||
| 704 | Integer len; | ||
| 705 | algorithm | ||
| 706 | 980 | len := listLength(lst); | |
| 707 | 980 | sub_map := UNORDERED_MAP( | |
| 708 | Vector.newFill(Util.nextPrime(len), {}), | ||
| 709 | Vector.new<K>(len), | ||
| 710 | Vector.new<Integer>(len), | ||
| 711 | Vector.new<V>(len), | ||
| 712 | map.hashFn, | ||
| 713 | map.eqFn | ||
| 714 | ); | ||
| 715 |
2/2✓ Branch 0 taken 10590 times.
✓ Branch 1 taken 980 times.
|
11570 | for k in lst loop |
| 716 | 10590 | add(k, getSafe(k, map, sourceInfo()), sub_map); | |
| 717 | end for; | ||
| 718 | end subMap; | ||
| 719 | |||
| 720 | function all | ||
| 721 | "Returns true if the given function returns true for all values in the map, | ||
| 722 | otherwise false." | ||
| 723 | input UnorderedMap<K, V> map; | ||
| 724 | input PredFn fn; | ||
| 725 | output Boolean res; | ||
| 726 | |||
| 727 | partial function PredFn | ||
| 728 | input V value; | ||
| 729 | output Boolean res; | ||
| 730 | end PredFn; | ||
| 731 | algorithm | ||
| 732 | 96 | res := Vector.all(map.values, fn); | |
| 733 | end all; | ||
| 734 | |||
| 735 | function any | ||
| 736 | "Returns true if the given function returns true for any value in the map, | ||
| 737 | otherwise false." | ||
| 738 | input UnorderedMap<K, V> map; | ||
| 739 | input PredFn fn; | ||
| 740 | output Boolean res; | ||
| 741 | |||
| 742 | partial function PredFn | ||
| 743 | input V value; | ||
| 744 | output Boolean res; | ||
| 745 | end PredFn; | ||
| 746 | algorithm | ||
| 747 | ✗ | res := Vector.any(map.values, fn); | |
| 748 | end any; | ||
| 749 | |||
| 750 | function none | ||
| 751 | "Returns true if the given function returns true for none of the values in | ||
| 752 | the map, otherwise false." | ||
| 753 | input UnorderedMap<K, V> map; | ||
| 754 | input PredFn fn; | ||
| 755 | output Boolean res; | ||
| 756 | |||
| 757 | partial function PredFn | ||
| 758 | input V value; | ||
| 759 | output Boolean res; | ||
| 760 | end PredFn; | ||
| 761 | algorithm | ||
| 762 | 203047 | res := Vector.none(map.values, fn); | |
| 763 | end none; | ||
| 764 | |||
| 765 | function size | ||
| 766 | "Returns the number of elements the map contains." | ||
| 767 | input UnorderedMap<K, V> map; | ||
| 768 | output Integer s = Vector.size(map.keys); | ||
| 769 | end size; | ||
| 770 | |||
| 771 | function isEmpty | ||
| 772 | "Returns whether the map is empty or not." | ||
| 773 | input UnorderedMap<K, V> map; | ||
| 774 | output Boolean empty = Vector.isEmpty(map.keys); | ||
| 775 | end isEmpty; | ||
| 776 | |||
| 777 | function bucketCount | ||
| 778 | "Returns the number of buckets in the map." | ||
| 779 | input UnorderedMap<K, V> map; | ||
| 780 | output Integer count = Vector.size(map.buckets); | ||
| 781 | end bucketCount; | ||
| 782 | |||
| 783 | function loadFactor | ||
| 784 | "Returns the load factor, defined as the number of entries divided by the | ||
| 785 | number of buckets." | ||
| 786 | input UnorderedMap<K, V> map; | ||
| 787 | output Real load = intReal(Vector.size(map.keys)) / Vector.size(map.buckets); | ||
| 788 | end loadFactor; | ||
| 789 | |||
| 790 | function rehash | ||
| 791 | "Changes the number of buckets to an appropriate number based on the number | ||
| 792 | of elements in the map and redistributes the keys by their stored hashes." | ||
| 793 | input UnorderedMap<K, V> map; | ||
| 794 | protected | ||
| 795 | Vector<Integer> hashes = map.hashes; | ||
| 796 | Vector<list<Integer>> buckets = map.buckets; | ||
| 797 | Integer bucket_count, bucket_id; | ||
| 798 | algorithm | ||
| 799 | // Clear the buckets. | ||
| 800 | 199355 | Vector.clear(buckets); | |
| 801 | |||
| 802 | // Change the number of buckets for a load factor of about 0.5. | ||
| 803 | 199355 | bucket_count := Util.nextPrime(Vector.size(hashes) * 2); | |
| 804 | 199355 | Vector.resize(buckets, bucket_count, {}); | |
| 805 | |||
| 806 | // Refill the buckets. | ||
| 807 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 199355 times.
|
1561829 | for i in 1:Vector.size(hashes) loop |
| 808 |
1/2✓ Branch 1 taken 1362474 times.
✗ Branch 2 not taken.
|
1362474 | bucket_id := intMod(Vector.getNoBounds(hashes, i), bucket_count) + 1; |
| 809 | 2724948 | Vector.updateNoBounds(buckets, bucket_id, i :: Vector.getNoBounds(buckets, bucket_id)); | |
| 810 | end for; | ||
| 811 | end rehash; | ||
| 812 | |||
| 813 | function toString | ||
| 814 | "Returns a string representation of the map." | ||
| 815 | input UnorderedMap<K, V> map; | ||
| 816 | input KeyStringFn keyStringFn; | ||
| 817 | input ValueStringFn valueStringFn; | ||
| 818 | input String delimiter = "\n"; | ||
| 819 | input String concatinator = ", "; | ||
| 820 | output String str; | ||
| 821 | protected | ||
| 822 | list<String> strl = {}; | ||
| 823 | Vector<K> keys = map.keys; | ||
| 824 | Vector<V> values = map.values; | ||
| 825 | algorithm | ||
| 826 |
1/2✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
|
74 | for i in Vector.size(keys):-1:1 loop |
| 827 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 56 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 56 times.
|
56 | strl := "(" + keyStringFn(Vector.get(keys, i)) + concatinator + |
| 828 | valueStringFn(Vector.get(values, i)) + ")" :: strl; | ||
| 829 | end for; | ||
| 830 | |||
| 831 | 18 | str := stringDelimitList(strl, delimiter); | |
| 832 | end toString; | ||
| 833 | |||
| 834 | function toJSON | ||
| 835 | input UnorderedMap<K, V> map; | ||
| 836 | input KeyStringFn keyStringFn; | ||
| 837 | input ValueStringFn valueStringFn; | ||
| 838 | output String str; | ||
| 839 | protected | ||
| 840 | IOStream.IOStream io; | ||
| 841 | Vector<K> keys = map.keys; | ||
| 842 | Vector<V> values = map.values; | ||
| 843 | Integer sz = Vector.size(keys); | ||
| 844 | algorithm | ||
| 845 | 5 | io := IOStream.create("UnorderedMap.toJSON", IOStream.IOStreamType.LIST()); | |
| 846 | 5 | io := IOStream.append(io, "{\n"); | |
| 847 | |||
| 848 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | if sz > 0 then |
| 849 | 5 | io := IOStream.append(io, " \""); | |
| 850 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | io := IOStream.append(io, keyStringFn(Vector.getNoBounds(keys, 1))); |
| 851 | 5 | io := IOStream.append(io, "\": \""); | |
| 852 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | io := IOStream.append(io, valueStringFn(Vector.getNoBounds(values, 1))); |
| 853 | 5 | io := IOStream.append(io, "\""); | |
| 854 | |||
| 855 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
16 | for i in 2:sz loop |
| 856 | 11 | io := IOStream.append(io, ",\n \""); | |
| 857 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | io := IOStream.append(io, keyStringFn(Vector.getNoBounds(keys, i))); |
| 858 | 11 | io := IOStream.append(io, "\": \""); | |
| 859 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
|
11 | io := IOStream.append(io, valueStringFn(Vector.getNoBounds(values, i))); |
| 860 | 11 | io := IOStream.append(io, "\""); | |
| 861 | end for; | ||
| 862 | end if; | ||
| 863 | |||
| 864 | 5 | io := IOStream.append(io, "\n}"); | |
| 865 | 5 | str := IOStream.string(io); | |
| 866 | end toJSON; | ||
| 867 | |||
| 868 | protected | ||
| 869 | function find | ||
| 870 | "Returns the array index of the given key (or -1 if the key isn't in the map) | ||
| 871 | and the key's hash." | ||
| 872 | input K key; | ||
| 873 | input UnorderedMap<K, V> map; | ||
| 874 | output Integer index = -1; | ||
| 875 | output Integer hash; | ||
| 876 | protected | ||
| 877 | Hash hashfn = map.hashFn; | ||
| 878 | KeyEq eqfn = map.eqFn; | ||
| 879 | list<Integer> bucket; | ||
| 880 | algorithm | ||
| 881 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14262958 times.
|
14262958 | hash := hashfn(key); |
| 882 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 14262958 times.
|
14262958 | if Vector.size(map.buckets) > 0 then |
| 883 | 28525916 | bucket := Vector.get(map.buckets, intMod(hash, Vector.size(map.buckets)) + 1); | |
| 884 |
2/2✓ Branch 0 taken 7579792 times.
✓ Branch 1 taken 11434316 times.
|
19014108 | for i in bucket loop |
| 885 |
6/6✓ Branch 1 taken 2876171 times.
✓ Branch 2 taken 4703621 times.
✓ Branch 3 taken 44 times.
✓ Branch 4 taken 2876127 times.
✓ Branch 9 taken 47529 times.
✓ Branch 10 taken 2828642 times.
|
7579792 | if Vector.getNoBounds(map.hashes, i) == hash and eqfn(key, Vector.getNoBounds(map.keys, i)) then |
| 886 | index := i; | ||
| 887 | break; | ||
| 888 | end if; | ||
| 889 | end for; | ||
| 890 | end if; | ||
| 891 | end find; | ||
| 892 | |||
| 893 | function addEntry | ||
| 894 | "Adds a key and value to the map given the key's hash." | ||
| 895 | input K key; | ||
| 896 | input V value; | ||
| 897 | input Integer hash; | ||
| 898 | input UnorderedMap<K, V> map; | ||
| 899 | protected | ||
| 900 | Vector<list<Integer>> buckets = map.buckets; | ||
| 901 | Integer bucket_id; | ||
| 902 | algorithm | ||
| 903 | // Add the key/value to the key/value arrays. | ||
| 904 | 2983962 | Vector.push(map.keys, key); | |
| 905 | 2983962 | Vector.push(map.hashes, hash); | |
| 906 | 2983962 | Vector.push(map.values, value); | |
| 907 | |||
| 908 |
2/2✓ Branch 1 taken 199355 times.
✓ Branch 2 taken 2784607 times.
|
2983962 | if loadFactor(map) > 1 then |
| 909 | // Rehash if the load factor is too high to keep performance up. This | ||
| 910 | // rehashes all the keys, including the one we added above. | ||
| 911 | 199355 | rehash(map); | |
| 912 | else | ||
| 913 | // Otherwise add the index of the key/value to the correct bucket. | ||
| 914 | 2784607 | bucket_id := intMod(hash, Vector.size(buckets)) + 1; | |
| 915 | 5569214 | Vector.update(buckets, bucket_id, Vector.size(map.keys) :: Vector.get(buckets, bucket_id)); | |
| 916 | end if; | ||
| 917 | end addEntry; | ||
| 918 | |||
| 919 | annotation(__OpenModelica_Interface="util"); | ||
| 920 | end UnorderedMap; | ||
| 921 |