();
46 |
47 | }
48 |
49 | static {
50 |
51 | try {
52 |
53 | Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
54 |
55 | field.setAccessible(true);
56 |
57 | UNSAFE = (sun.misc.Unsafe) field.get(null);
58 |
59 | } catch (Throwable ex) {
60 |
61 | throw new ExceptionInInitializerError(ex);
62 |
63 | }
64 |
65 | }
66 |
67 | static long estimatedAvailableForUnsafe = Runtime.getRuntime().maxMemory(); // NB: this is what Java by default also allows for direct buffer allocations but
68 | // it tends to be an underestimate for the off-heap space available. TODO: more accurate estimation wanted (without
69 | // asking the OS or using Native Memory Tracking (NMT))
70 |
71 | static final HotSpotDiagnosticMXBean vmOptions = ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class);
72 |
73 | static {
74 |
75 | long maxDirectMemorySize = vmOptions == null ? 0l : Long.valueOf(vmOptions.getVMOption("MaxDirectMemorySize").getValue());
76 |
77 | // Observe that sun.misc.VM.maxDirectMemory() isn't available in recent JVMs.
78 |
79 | if (maxDirectMemorySize > 0l)
80 | estimatedAvailableForUnsafe = maxDirectMemorySize;
81 |
82 | if (debugMode)
83 | System.out.println("Off-heap memory estimate: " + approxSizeOfCurrentFreeUnsafeMemory() + " (" + Double.valueOf(approxSizeOfCurrentFreeUnsafeMemory()) / 1073741824d + " GB)");
84 |
85 | }
86 |
87 | static String formatCallerInfo(StackTraceElement[] stackStrace) {
88 |
89 | String callTreeStr = Arrays.toString(stackStrace);
90 |
91 | String callerInfo = callTreeStr;
92 |
93 | return callerInfo;
94 |
95 | }
96 |
97 | public static long allocateOffHeapMem(long size) { // calling this method alone does NOT make it garbage collectable!
98 |
99 | allocatedOffHeapDiffSATGlobal.getAndAdd(size);
100 |
101 | if (debugMode) {
102 |
103 | long a = UNSAFE.allocateMemory(size);
104 |
105 | allocs.put(a, formatCallerInfo(new Throwable().fillInStackTrace().getStackTrace()));
106 |
107 | return a;
108 |
109 | } else
110 | return UNSAFE.allocateMemory(size);
111 |
112 | }
113 |
114 | public static long resizeOffHeapMem(long oldAddress, long oldSize, long newSize) { // calling this method alone does NOT make it garbage collectable!
115 |
116 | allocatedOffHeapDiffSATGlobal.getAndAdd(-oldSize + newSize);
117 |
118 | if (debugMode) {
119 |
120 | long a = UNSAFE.reallocateMemory(oldAddress, newSize);
121 |
122 | allocs.remove(oldAddress);
123 |
124 | allocs.put(a, formatCallerInfo(new Throwable().fillInStackTrace().getStackTrace()));
125 |
126 | // System.out.println("FREED (by resize) " + oldAddress + ", bytes: " + oldSize + ", caller: " + callerInfo);
127 |
128 | // System.out.println("ALLOCATED (by resize) " + a + ", bytes: " + newSize + ", caller: " + callerInfo);
129 |
130 | return a;
131 |
132 | } else
133 | return UNSAFE.reallocateMemory(oldAddress, newSize);
134 |
135 | }
136 |
137 | public static void freeOffHeapMem(long address, long size) {
138 |
139 | allocatedOffHeapDiffSATGlobal.getAndAdd(-size);
140 |
141 | UNSAFE.freeMemory(address);
142 |
143 | if (debugMode) {
144 |
145 | allocs.remove(address);
146 |
147 | /* String callerInfo = formatCallerInfo(new Throwable().fillInStackTrace().getStackTrace())
148 |
149 | System.out.println("FREED " + address + ", bytes: " + size + ", caller: " + callerInfo);
150 | */
151 |
152 | }
153 |
154 | }
155 |
156 | public static void addAllocOffHeapMemToGarbage(long address, long size) {
157 |
158 | synchronized(garbage) {
159 |
160 | garbage.add(new long[]{address, size});
161 |
162 | }
163 | }
164 |
165 | public static void freeGarbageOffHeapMem(long targetMinBytesToFree) {
166 |
167 | synchronized(garbage) {
168 |
169 | long freed = 0l;
170 |
171 | if (targetMinBytesToFree > 0l && !garbage.isEmpty()) {
172 |
173 | if(debugMode) System.out.println("\nOff-heap garbage collection...");
174 |
175 | long f = 0l;
176 |
177 | do {
178 |
179 | long[] entry = garbage.pop/*poll*/();
180 |
181 | if(entry != null) {
182 |
183 | freeOffHeapMem(entry[0], entry[1]);
184 |
185 | f += entry[1];
186 |
187 | }
188 |
189 | } while (freed < targetMinBytesToFree && !garbage.isEmpty());
190 |
191 | System.out.println("\n\u001B[35mOff-heap garbage collection complete. Freed " + f + " bytes (" + round(Float.valueOf(f) / 1073741824, 4) + " G)\u001B[0m");
192 |
193 | if (debugMode)
194 | diffSAT.stats().writeEntry("offheapGarbageFreedBytes", f, 0, false);
195 |
196 | }
197 |
198 | }
199 |
200 | }
201 |
202 | public static long allocatedUnsafeMemory() {
203 |
204 | return allocatedOffHeapDiffSATGlobal.get();
205 |
206 | }
207 |
208 | /**
209 | * Accuracy depends on estimatedAvailableForUnsafe.
210 | *
211 | * @return Rough estimation of space originally (before any unsafe or direct buffer etc allocations) available for native allocations
212 | */
213 | public static long approxSizeOfInitialFreeUnsafeMemory() {
214 |
215 | return estimatedAvailableForUnsafe;
216 |
217 | }
218 |
219 | /**
220 | * Accuracy depends on estimatedAvailableForUnsafe. If this value is too low, we might even get a negative result.
221 | *
222 | * @return Rough estimation of space currently available for new native allocations
223 | */
224 | public static long approxSizeOfCurrentFreeUnsafeMemory() {
225 |
226 | return approxSizeOfInitialFreeUnsafeMemory() - allocatedUnsafeMemory();
227 |
228 | }
229 |
230 | /**
231 | * For debugging only. Don't call in code which uses this class concurrently
232 | */
233 | public static void resetMemTracerDebug() {
234 |
235 | allocatedOffHeapDiffSATGlobal.set(0l);
236 |
237 | if (debugMode) {
238 |
239 | allocs.clear();
240 |
241 | }
242 |
243 | }
244 |
245 | /**
246 | * For debugging, to find off-heap memory leaks
247 | */
248 | public static void showRemainingAllocsDebug() {
249 |
250 | if (debugMode) {
251 |
252 | if(allocs.isEmpty())
253 | System.out.println("No remaining items in off-heap garbage list for debugging");
254 | else allocs.forEach((aLong, s) -> {
255 |
256 | if (true || !s.contains("LeanUS"))
257 |
258 | System.out.println("REMAINING address (off-heap memory not freed by garbage collection):\n" + aLong + ", caller: " + s);
259 |
260 | });
261 |
262 | }
263 |
264 | }
265 |
266 | }
267 |
--------------------------------------------------------------------------------
/src/main/scala/input/package.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * Find User API entry point classes [[input.ProbabilisticAnswerSetProgram]] (API for plain or probabilistic Answer Set Programming) and [[input.BooleanFormulaWithCosts]] (API for plain or probabilistic SAT solving) here
3 | */
4 | package object input {
5 |
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/scala/solving/NogoodReduciblesSequence.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * diff-SAT
3 | *
4 | * Copyright (c) 2018,2020 Matthias Nickles
5 | *
6 | * matthiasDOTnicklesATgmxDOTnet
7 | *
8 | * This code is licensed under MIT License (see file LICENSE for details)
9 | *
10 | */
11 |
12 | package solving
13 |
14 | import it.unimi.dsi.fastutil.longs.LongArrayList
15 |
16 |
17 | @deprecated class NogoodReduciblesSequence(capacity: Int) extends LongArrayList(capacity) { // consider using NogoodReduciblesSequenceUnsafe instead
18 |
19 | type NogoodReducible = Long
20 |
21 | @inline def clearUS(): Unit = size = 0
22 |
23 | @inline def cutoffUS(whereExclusive: Int): Unit = size = whereExclusive
24 |
25 | @inline def getUS(index: Int): NogoodReducible = a(index)
26 |
27 | @inline def removeUS(k: NogoodReducible): Unit = {
28 |
29 | this.rem(k)
30 |
31 | }
32 |
33 | @inline def removeByIndexUS(i: Int): Unit = {
34 |
35 | if(i < size - 1)
36 | set(i, getUS(size - 1))
37 |
38 | size -= 1
39 |
40 | }
41 |
42 | @inline def addUS(k: NogoodReducible): Unit = {
43 |
44 | if (size >= a.length) {
45 |
46 | val t = new Array[NogoodReducible](/*(a.length << 1) + 1*/a.length + 128)
47 |
48 | if(size > 0)
49 | System.arraycopy(a, 0, t, 0, size)
50 |
51 | a = t.asInstanceOf[Array[NogoodReducible]]
52 |
53 | }
54 |
55 | a.update(size, k)
56 |
57 | size += 1
58 |
59 | }
60 |
61 | @inline def getArray: Array[NogoodReducible] = a
62 |
63 | @inline def count(item: NogoodReducible) = {
64 |
65 | var c = 0
66 |
67 | var i = 0
68 |
69 | while(i < size) {
70 |
71 | if(getUS(i) == item)
72 | c += 1
73 |
74 | i += 1
75 |
76 | }
77 |
78 | c
79 |
80 | }
81 |
82 | }
--------------------------------------------------------------------------------
/src/main/scala/solving/NogoodReduciblesSequenceUnsafe.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * diff-SAT
3 | *
4 | * Copyright (c) 2018, 2020 by Matthias Nickles
5 | *
6 | * matthiasDOTnicklesATgmxDOTnet
7 | *
8 | * This code is licensed under MIT License (see file LICENSE for details)
9 | *
10 | */
11 |
12 | package solving
13 |
14 | import input.UNSAFEhelper.{addAllocOffHeapMemToGarbage, allocateOffHeapMem, _}
15 |
16 | import it.unimi.dsi.fastutil.longs.LongOpenHashSet
17 |
18 | import sharedDefs._
19 |
20 | import scala.collection.mutable.ArrayBuffer
21 |
22 | class NogoodReduciblesSequenceUnsafe(initialCapacity: Int) {
23 |
24 | type NogoodReducible = Long
25 |
26 | private[this] val bytesPerElement = 8 /*<-- reducible:Long*/
27 | // + 4/*<-- blocking eli:Int*/
28 | // To activate blocker elis, add 4 to slot (see above), look for other mentions of "blocker" in this class, and in BCP
29 | // omit all reducible list entries for which !isNegSetInPass(UNSAFE.getInt(addrOfItemInRedList + 8l)).
30 | // But doesn't seem to have any benefit in preliminary experiments.
31 |
32 | private[this] var noOfAvailableSlotsForItems = initialCapacity
33 |
34 | private[this] var a: Long = allocateOffHeapMem(allocatedBytes(noOfAvailableSlotsForItems)) //new LongArrayUnsafeS(sizev = initialCapacity)
35 |
36 | private[this] var sizev = 0
37 |
38 | private[this] var cachedHashSet: LongOpenHashSet = null.asInstanceOf[LongOpenHashSet]
39 |
40 | //private[this] var sortings = 0
41 | private[this] var traversalDirectionUpwards = true
42 |
43 | @inline def setTraversalDirection(direction: Boolean) = traversalDirectionUpwards = direction
44 |
45 | @inline def allocatedBytes(allocatedItems: Int = noOfAvailableSlotsForItems): Int =
46 | allocatedItems * bytesPerElement
47 |
48 | @inline def addToGarbage(): Unit = {
49 |
50 | addAllocOffHeapMemToGarbage(a, allocatedBytes(noOfAvailableSlotsForItems)) //a.addToGarbage()
51 |
52 | }
53 |
54 | @inline def clearUS(): Unit = sizev = 0
55 |
56 | @inline def cutoffUS(whereExclusive: Int): Unit = sizev = whereExclusive
57 |
58 | @inline def getAddr: Long = a
59 |
60 | @inline def getBytesPerElement: Int = bytesPerElement
61 |
62 | @inline def getAddrOfItem(index: Int): Long = a + index * bytesPerElement // TODO: make shiftable?
63 |
64 | @inline def getReducibleUS(index: Int): NogoodReducible = UNSAFE.getLong(getAddrOfItem(index))
65 |
66 | @inline def removeByIndexUS(i: Int): Unit = {
67 |
68 | sizev -= 1
69 |
70 | if (i < sizev) {
71 |
72 | UNSAFE.copyMemory(getAddrOfItem(sizev), getAddrOfItem(i), bytesPerElement)
73 |
74 | }
75 |
76 | }
77 |
78 | @inline def removeByAddrUS(iAddr: Long): Unit = {
79 |
80 | sizev -= 1
81 |
82 | UNSAFE.copyMemory(getAddrOfItem(sizev), iAddr, bytesPerElement)
83 |
84 | }
85 |
86 | @inline def addReducibleUS(reducible: NogoodReducible): Unit = {
87 |
88 | if (sizev >= noOfAvailableSlotsForItems) { // should occur only rarely if we chose the initial size appropriately, however
89 | // it also depends on the number of learned nogoods which is difficult to predict.
90 |
91 | val newAvailableNoOfSlotsForItems = noOfAvailableSlotsForItems << 1 // sizev.get + incIfOverflow
92 |
93 | a = resizeOffHeapMem(a, allocatedBytes(noOfAvailableSlotsForItems),
94 | allocatedBytes(newAvailableNoOfSlotsForItems))
95 |
96 | noOfAvailableSlotsForItems = newAvailableNoOfSlotsForItems
97 |
98 | }
99 |
100 | UNSAFE.putLong(getAddrOfItem(sizev), reducible)
101 |
102 | sizev += 1
103 |
104 | if(keepNogoodsWeaklySorted && sizev % 32 == 0) {
105 |
106 | if(traversalDirectionUpwards)
107 | sortByInplace(red => -UNSAFE.getInt(red)/*=size of nogood*//*-UNSAFE.getFloat(red + ((3 + 4 - 4) << 2))*/, sizev)
108 | else
109 | sortByInplace(red => UNSAFE.getInt(red)/*UNSAFE.getFloat(red + ((3 + 4 - 4) << 2))*/, sizev)
110 |
111 | }
112 |
113 | }
114 |
115 | def sortByInplace(by: NogoodReducible => Float, until: Int): Unit = {
116 |
117 | // insertion sort - use only if array is very small (but then it's fast):
118 |
119 | var j = 1
120 |
121 | var key = -1l
122 |
123 | var i = -1
124 |
125 | while (j < until) {
126 |
127 | key = getReducibleUS(j)
128 |
129 | i = j - 1
130 |
131 | //println(by(key))
132 |
133 | while (i > -1 && by(getReducibleUS(i)) > by(key)) {
134 |
135 | updateItemUS(i + 1, getReducibleUS(i))
136 |
137 | i -= 1
138 |
139 | }
140 |
141 | updateItemUS(i + 1, key)
142 |
143 | j += 1
144 |
145 | }
146 |
147 | }
148 |
149 |
150 | @inline def updateItemUS(index: Int, reducible: NogoodReducible) = {
151 |
152 | UNSAFE.putLong(getAddrOfItem(index), reducible)
153 |
154 | }
155 |
156 |
157 | @inline def swap(i: Int, j: Int, valueI: Long): Unit = {
158 |
159 | updateItemUS(i, getReducibleUS(j)) // if blockingElis are enabled, modify accordingly
160 |
161 | updateItemUS(j, valueI)
162 |
163 | }
164 |
165 | @inline def toArrayBufferOfReducibles: ArrayBuffer[NogoodReducible] = {
166 |
167 | val ab = ArrayBuffer[NogoodReducible]()
168 |
169 | var i = 0
170 |
171 | while (i < sizev) {
172 |
173 | ab.append(getReducibleUS(i))
174 |
175 | i += 1
176 |
177 | }
178 |
179 | ab
180 |
181 | }
182 |
183 | @inline def findReducibleUS(item: NogoodReducible): Int = {
184 |
185 | var i = 0
186 |
187 | while (i < sizev) {
188 |
189 | if (getReducibleUS(i) == item)
190 | return i
191 |
192 | i += 1
193 |
194 | }
195 |
196 | -1
197 |
198 | }
199 |
200 | @inline def containsReducible(item: NogoodReducible): Boolean = {
201 |
202 | findReducibleUS(item) != -1
203 |
204 | }
205 |
206 | @inline def toHashSetOfReducibles: LongOpenHashSet = {
207 |
208 | val hs = new LongOpenHashSet(64)
209 |
210 | var i = 0
211 |
212 | while (i < sizev) {
213 |
214 | hs.add(getReducibleUS(i))
215 |
216 | i += 1
217 |
218 | }
219 |
220 | cachedHashSet = hs
221 |
222 | hs
223 |
224 | }
225 |
226 | @inline def toHashSetOfReduciblesIncompl: LongOpenHashSet = {
227 |
228 | if (cachedHashSet == null) {
229 |
230 | toHashSetOfReducibles
231 |
232 | } else
233 | cachedHashSet
234 |
235 | }
236 |
237 | @inline def filterByReducibleUS(keepBy: NogoodReducible => Boolean): NogoodReduciblesSequenceUnsafe = {
238 |
239 | val ns = new NogoodReduciblesSequenceUnsafe(initialCapacity = sizev)
240 |
241 | var k = 0
242 |
243 | while (k < sizev) {
244 |
245 | if (keepBy(getReducibleUS(k)))
246 | ns.addReducibleUS(getReducibleUS(k))
247 |
248 | k += 1
249 |
250 | }
251 |
252 | ns
253 |
254 | }
255 |
256 | @inline def countByReducibleUS(keepBy: NogoodReducible => Boolean): Int = {
257 |
258 | var c = 0
259 |
260 | var k = 0
261 |
262 | while (k < sizev) {
263 |
264 | if (keepBy(getReducibleUS(k)))
265 | c += 1
266 |
267 | k += 1
268 |
269 | }
270 |
271 | c
272 |
273 | }
274 |
275 | @inline def size(): Eli = sizev
276 |
277 | }
--------------------------------------------------------------------------------
/src/main/scala/solving/SamplingResult.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * diff-SAT
3 | *
4 | * Copyright (c) 2018, 2020 Matthias Nickles
5 | *
6 | * matthiasDOTnicklesATgmxDOTnet
7 | *
8 | * This code is licensed under MIT License (see file LICENSE for details)
9 | *
10 | */
11 |
12 | package solving
13 |
14 | import it.unimi.dsi.fastutil.ints.IntOpenHashSet
15 |
16 | import sharedDefs.Eli
17 |
18 | import scala.collection.mutable
19 |
20 | /**
21 | * The result of a sampler call
22 | *
23 | * @param modelsSymbolic Sequence of models represented as arrays of symbolic literals
24 | * @param modelsUsingElis Sequence of models represented as arrays of elis (literals represented as pos/neg Ints) and hash sets of elis
25 | * @param samplingDurationNs Sampling duration in nanoseconds
26 | */
27 | case class SamplingResult(modelsSymbolic: mutable.Seq[Array[String]],
28 | modelsUsingElis: mutable.ArrayBuffer[(Array[Eli], IntOpenHashSet)],
29 | samplingDurationNs: Long)
30 |
--------------------------------------------------------------------------------
/src/main/scala/solving/SharedAmongSingleSolverThreads.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * diff-SAT
3 | *
4 | * Copyright (c) 2018,2022 Matthias Nickles
5 | *
6 | * matthiasDOTnicklesATgmxDOTnet
7 | *
8 | * This code is licensed under MIT License (see file LICENSE for details).
9 | *
10 | */
11 |
12 | package solving
13 |
14 | import java.util.concurrent.ConcurrentHashMap
15 | import it.unimi.dsi.fastutil.longs.LongArrayList
16 | import sharedDefs._
17 | import utils.{ByteArrayUnsafeS, FiniteQueue}
18 | /*import vmm.API
19 | import vmm.API.VMMType.PPMC
20 | import vmm.algs.PPMCPredictor */
21 |
22 | class SharedAmongSingleSolverThreads(noOfAbsElis: Int) {
23 |
24 | var refreshedBestPhasesGlobal: Int = 0
25 |
26 | val bestPhasesQueueGlobal: FiniteQueue[ByteArrayUnsafeS] = if (globalBestPhaseMemo &&
27 | (weakRephasingAtRestartEveryP.getAllAlternativeValues.find(_ > 0).isDefined || rephasePhaseMemoP.getAllAlternativeValues.contains(true))) // i.e., we are using a global array here. TODO: more tests whether thread-local is mostly better or not
28 | new FiniteQueue[ByteArrayUnsafeS](scala.collection.mutable.Queue[ByteArrayUnsafeS]())
29 | else
30 | null
31 |
32 | var greedilyBestThread: Int = 0
33 |
34 | val fmStackGlobalCapacity: Int = 500000
35 |
36 | val fmStackGlobal: LongArrayList = if (freeOrReassignDeletedNogoodMemory &&
37 | (freeDeletedNogoodMemoryApproach == 3)) new LongArrayList(fmStackGlobalCapacity)
38 | else
39 | null
40 |
41 | val nogoodReducibleExchangePoolSourceThreadsForCyclicSharingPrevention: ConcurrentHashMap[NogoodReducible, Int] = new java.util.concurrent.ConcurrentHashMap[NogoodReducible, Int /*producing thread*/ ]()
42 |
43 | val nogoodReducibleExchangePool: ConcurrentHashMap[NogoodReducible, Int] =
44 | /*TODO: use of this comes with heavy boxing*/ new java.util.concurrent.ConcurrentHashMap[NogoodReducible, Int /*producing thread*/ ]()
45 |
46 | val scoresFromSLS: ConcurrentHashMap[Eli, Float] = new java.util.concurrent.ConcurrentHashMap[Eli, Float]()
47 |
48 | //var minViolNogoodCountSum = Int.MaxValue
49 |
50 | var threadConfsOpt: Option[Array[SolverThreadSpecificSettings]] = None
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/scala/solving/SolverThreadSpecificSettings.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * diff-SAT
3 | *
4 | * Copyright (c) 2018, 2022 Matthias Nickles
5 | *
6 | * matthiasDOTnicklesATgmxDOTnet
7 | *
8 | * This code is licensed under MIT License (see file LICENSE for details)
9 | *
10 | */
11 |
12 | package solving
13 |
14 | import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
15 |
16 | import sharedDefs.Eli
17 |
18 | /** Includes only those settings which can be different across individual solver threads, including
19 | * all settings whose names end with ...P in sharedDefs.scala */
20 | case class SolverThreadSpecificSettings(var threadNo: Int /*>=1*/ ,
21 | positiveDependencyGraph: Int2ObjectOpenHashMap[List[Eli]], // TODO: move elsewhere
22 | assureProgIsTight: Boolean,
23 | var freeEliSearchApproach: Int,
24 | restartTriggerConfR: (Int, Int, Double),
25 | traverseReduciblesUpwardsInUnitProp: Boolean,
26 | initAbsElisArrangement: Int,
27 | prep: Preparation /*<-for debugging/crosschecks only*/ ,
28 | seed: Long,
29 | restartFrequencyModifierFactorR: Float,
30 | useSLSinPhaseMemoRephasingR: Boolean,
31 | nogoodRemovalThreshRatio: Double,
32 | absEliScoringApproach: Int,
33 | nogoodRemovalThreshInit: Int,
34 | noisePhaseMemoR: Float,
35 | localRestarts: Boolean,
36 | scoringForRemovalOfLearnedNogoodsR: Int,
37 | weakRephasingAtRestartEvery: Int,
38 | rephasePhaseMemo: Boolean,
39 | nogoodBCPtraversalDirection: Int,
40 | absEliScoringApproach15NoOfBins: Int,
41 | var singleSolverThreadDataOpt: Option[SingleSolverThreadData])
42 |
--------------------------------------------------------------------------------
/src/main/scala/utils/ASPIOutils.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * diff-SAT
3 | *
4 | * Copyright (c) 2018, 2020 Matthias Nickles
5 | *
6 | * matthiasDOTnicklesATgmxDOTnet
7 | *
8 | * This code is licensed under MIT License (see file LICENSE for details)
9 | *
10 | */
11 |
12 | import java.io._
13 |
14 | import sharedDefs.evalFactPrefix
15 |
16 | import scala.collection.{Set, mutable}
17 | import scala.language.postfixOps
18 |
19 | package object aspIOutils {
20 |
21 | type Pred = String
22 |
23 | type Model = Array[String]
24 |
25 | val parserInstanceCount = new java.util.concurrent.atomic.AtomicInteger(0)
26 |
27 | val auxPredPrefixBase = "__aux_" // prefixes for newly introduced atom symbols
28 |
29 | def isAuxAtom(pred: String) = pred.contains /*startsWith*/ (auxPredPrefixBase) // "contains" as pred could also be param(__aux...)
30 |
31 | /* The following kinds of auxiliary symbols are currently reserved:
32 |
33 | - auxiliary atoms used to desugar :- constraints (symbol contains _F_)
34 | - helper atoms in automatically generated spanning formulas (symbol contains _R_)
35 | - for other purposes (symbol contains _O_)
36 |
37 | */
38 |
39 | def auxPredPrefix(kind: String) = auxPredPrefixBase + kind + "_" + parserInstanceCount.get() + "_"
40 |
41 | def newFalsePredsPrefix = auxPredPrefix("F") // prefixes for newly introduced auxiliary atoms for
42 | // desugaring :- integrity constraints (see aspif parser).
43 |
44 | def isFalsAuxAtom(pred: String) = pred.startsWith(auxPredPrefixBase) && pred.contains("F")
45 |
46 | def isSpanAuxAtom(pred: String) = isAuxAtom(pred) && pred.contains("R")
47 |
48 | def newLatentSymbolAuxAtomPrefix = auxPredPrefix("L") // prefixes for newly introduced auxiliary atom symbols for (re-)introduced atoms from certain #show aspif rules (see aspif parser).
49 |
50 | def newSpanSymbolAuxAtomPrefix = auxPredPrefix("R") // prefixes for newly introduced auxiliary atom symbols for (re-)introduced atoms from certain #show aspif rules (see aspif parser).
51 |
52 | def isLatentSymbolAuxAtom(pred: String) = pred.startsWith(auxPredPrefixBase) && pred.contains("L")
53 |
54 | // TODO(?): ^ these don't consider auxiliar variables introduced by the PCNF parser (probabilistic SAT)
55 |
56 | @inline def auxAtomSymbol(prefix: String, index: Int, encloseUncertainAuxWithParam: Boolean = false) = if (encloseUncertainAuxWithParam)
57 | "param_" + prefix + index else prefix + index
58 |
59 | def slurpFromInputStream(in: InputStream): String = {
60 |
61 | val byteArray = new Array[Byte](4096)
62 |
63 | val strBuf: StringBuilder = new StringBuilder()
64 |
65 | var bytesRead = 0
66 |
67 | while ( {
68 |
69 | bytesRead = in.read(byteArray, 0, byteArray.length)
70 |
71 | bytesRead != -1
72 |
73 | }) strBuf.append(new String(byteArray, 0, bytesRead))
74 |
75 | strBuf.toString()
76 |
77 | }
78 |
79 | case class DisjRule(headPosAtoms: Set[Pred] = Set[Pred](), headNegAtoms: Set[Pred] = Set[Pred](),
80 | bodyPosAtoms: Set[Pred] = Set[Pred](), bodyNegAtoms: Set[Pred] = Set[Pred](),
81 | // ^ Which rule types are actually supported depends of course on solver and its preprocessor.
82 | // If multiple distinct head atoms are given, their semantics is disjunction.
83 | weight: (Double, Double) = (1d, 1d),
84 | var blit: Int = -1) {
85 |
86 | @deprecated override def toString = {
87 |
88 | val endDot = if (headPosAtoms.isEmpty || !headPosAtoms.head.contains('[')) "." else ""
89 | // ^ hack to allow for Clingo softconstraint weights within formulas (which have [weight ...] after the dot).
90 |
91 | val r = headPosAtoms.mkString(",") + headNegAtoms.map("not " + _).mkString(",") + (if (bodyPosAtoms.isEmpty && bodyNegAtoms.isEmpty) "" else " :- " + (bodyPosAtoms ++ bodyNegAtoms.map("not " + _)).mkString(",")) + endDot
92 |
93 | val rr = (if (weight != (1d, 1d))
94 | "[" + weight._1 + ";" + weight._2 + "] "
95 | else
96 | "") + r
97 |
98 | rr.trim
99 |
100 | }
101 |
102 | }
103 |
104 | @inline def splitByRepChar(s: String, delim1: Char = ' ', delim2: Char = '\t'): Array[String] = {
105 |
106 | val ll = new mutable.ArrayBuilder.ofRef[String]
107 |
108 | var index = 0
109 |
110 | var i = 0
111 |
112 | val sl = s.length
113 |
114 | var inStr = false
115 |
116 | while (i < sl) {
117 |
118 | if(s.charAt(i) == '\"' && (i == 0 || s.charAt(i - 1) != '\\' || (i > 1 && s.charAt(i - 2) == '\\'))) {
119 |
120 | inStr = !inStr
121 |
122 | i += 1
123 |
124 | } else if (!inStr && s.charAt(i) == delim1 || s.charAt(i) == delim2) {
125 |
126 | ll.+=(s.substring(index, i))
127 |
128 | i += 1
129 |
130 | while (s.charAt(i) == delim1 || s.charAt(i) == delim2) { // e.g., "token1 token2" gives ["token1", "token2"], not ["token1", " ", "token2"]
131 |
132 | i += 1
133 |
134 | }
135 |
136 | index = i
137 |
138 | }
139 |
140 | i += 1
141 |
142 | }
143 |
144 | if (s.last != delim1 && s.last != delim2)
145 | ll.+=(s.substring(index))
146 |
147 | val r = ll.result()
148 |
149 | r
150 |
151 | }
152 |
153 | def splitEvalSymbol(sym: Pred) = {
154 |
155 | //val isTermQuoted = sym.startsWith(evalFactPrefix + "(\"")
156 |
157 | val endOfTermIndex = sym.indexOf(",\"?\"")
158 |
159 | // note that the term can be quoted or not
160 |
161 | val term = sym.take(endOfTermIndex).stripPrefix(evalFactPrefix).dropWhile(c => c == '(' || c == '\"').stripSuffix("\"").replaceAll("\\s", "")
162 |
163 | val remainder = sym.drop(endOfTermIndex + 4)
164 |
165 | (term, remainder)
166 |
167 | }
168 |
169 | }
170 |
--------------------------------------------------------------------------------
/src/main/scala/utils/ArrayValExtensibleIntUnsafe.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * diff-SAT
3 | *
4 | * Copyright (c) 2018, 2020 Matthias Nickles
5 | *
6 | * matthiasDOTnicklesATgmxDOTnet
7 | *
8 | * This code is licensed under MIT License (see file LICENSE for details)
9 | *
10 | */
11 |
12 | package utils
13 |
14 | import sharedDefs.Eli
15 |
16 | /**
17 | * Unsafe low-level non-boxing replacement for a growable-only ArrayBuffer. Similar to ArrayBuilder, but
18 | * with random access and traversal. NB: couldn't avoid boxing in bytecode with
19 | * the generic [@specialized T (Int/Long)] variant (with Scala 2.13.1)
20 | *
21 | * @param buffer
22 | * @param initiallyOccupiedInBuffer
23 | */
24 | class ArrayValExtensibleIntUnsafe(var buffer: IntArrayUnsafeS, initiallyOccupiedInBuffer: Int = 0) {
25 |
26 | var contentLen = if (initiallyOccupiedInBuffer == -1) buffer.size else initiallyOccupiedInBuffer
27 |
28 | @inline def getContent: Array[Int] = {
29 |
30 | buffer.toArray.take(contentLen)
31 |
32 | }
33 |
34 | @inline def getContentUnsafe: IntArrayUnsafeS = {
35 |
36 | buffer.cloneToNew(padding = 0, keep = contentLen, cutOff = true)
37 |
38 | }
39 |
40 | @inline def length: Int = contentLen
41 |
42 | @inline def bufferSize: Int = buffer.size
43 |
44 | @inline def get(index: Int): Int = buffer.get(index)
45 |
46 | @inline def get(index: Long): Int = buffer.get(index)
47 |
48 | @inline def update(index: Int, value: Int): Unit = buffer.update(index, value)
49 |
50 | @inline def popLast: Int = {
51 |
52 | contentLen -= 1
53 |
54 | get(contentLen)
55 |
56 | }
57 |
58 | @inline def removeByIndex(index: Int): Unit = {
59 |
60 | if (index < contentLen - 1)
61 | update(index, popLast)
62 | else
63 | contentLen -= 1
64 |
65 | }
66 |
67 | @inline def removeByItem(item: Int): Unit = {
68 |
69 | removeByIndex(indexOf(item))
70 |
71 | }
72 |
73 | @inline def isEmpty: Boolean = contentLen == 0
74 |
75 | @inline def traverseUntil(itemOp: Int => Boolean): Unit = {
76 |
77 | var i = 0
78 |
79 | var stop = false
80 |
81 | while (!stop) {
82 |
83 | stop = i >= contentLen || itemOp(buffer.get(i))
84 |
85 | i += 1
86 |
87 | }
88 |
89 | }
90 |
91 | @inline def contains(item: Int, until: Int): Boolean = {
92 |
93 | var i = until - 1
94 |
95 | while (i >= 0) {
96 |
97 | if (buffer.get(i) == item)
98 | return true
99 |
100 | i -= 1
101 |
102 | }
103 |
104 | false
105 |
106 | }
107 |
108 | @inline def containsBy(item: Int, until: Int, by: Int => Int): Boolean = {
109 |
110 | var i = until - 1
111 |
112 | while (i >= 0) {
113 |
114 | if (by(buffer.get(i)) == by(item))
115 | return true
116 |
117 | i -= 1
118 |
119 | }
120 |
121 | false
122 |
123 | }
124 |
125 | @inline def indexOf(item: Int): Int = {
126 |
127 | var i = 0
128 |
129 | while (i < size) {
130 |
131 | if (buffer.get(i) == item)
132 | return i
133 |
134 | i += 1
135 |
136 | }
137 |
138 | -1
139 |
140 | }
141 |
142 | @inline def append(item: Int): Unit = {
143 |
144 | if (contentLen >= buffer.size) {
145 |
146 | val bufferOld = buffer
147 |
148 | val additionalSpace = contentLen >> 1
149 |
150 | buffer = buffer.cloneToNew(padding = additionalSpace, keep = contentLen, cutOff = false)
151 |
152 | if (buffer.getAddr != bufferOld.getAddr) {
153 |
154 | bufferOld.free() // (alternatively we could add the old buffer address to the UNSAFEhelper garbage)
155 |
156 | }
157 |
158 | }
159 |
160 | buffer.update(contentLen, item)
161 |
162 | contentLen += 1
163 |
164 | }
165 |
166 | @inline def appendNoExpansion(item: Int): Boolean = {
167 |
168 | (contentLen < buffer.size) && {
169 |
170 | buffer.update(contentLen, item)
171 |
172 | contentLen += 1
173 |
174 | true
175 |
176 | }
177 |
178 | }
179 |
180 | @inline def clear(): Unit = contentLen = 0
181 |
182 | @inline def size(): Eli = contentLen
183 |
184 | @inline def freeBuffer(): Unit = buffer.free()
185 |
186 | @inline def addToGarbageBuffer(): Unit = buffer.addToGarbage()
187 |
188 | }
--------------------------------------------------------------------------------
/src/main/scala/utils/ArrayValExtensibleLongUnsafe.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * diff-SAT
3 | *
4 | * Copyright (c) 2018, 2020 Matthias Nickles
5 | *
6 | * matthiasDOTnicklesATgmxDOTnet
7 | *
8 | * This code is licensed under MIT License (see file LICENSE for details)
9 | *
10 | */
11 |
12 | package utils
13 |
14 | import sharedDefs.Eli
15 |
16 | /**
17 | * Unsafe low-level non-boxing replacement for a growable-only ArrayBuffer. Similar to ArrayBuilder, but
18 | * with random access and traversal.
19 | *
20 | * @param buffer
21 | * @param initiallyOccupiedInBuffer
22 | */
23 | class ArrayValExtensibleLongUnsafe(var buffer: LongArrayUnsafeS, initiallyOccupiedInBuffer: Int = 0) {
24 |
25 | var contentLen = if (initiallyOccupiedInBuffer == -1) buffer.size else initiallyOccupiedInBuffer
26 |
27 | @inline def bufferSize: Int = buffer.size
28 |
29 | @inline def get(index: Int): Long = buffer.get(index)
30 |
31 | @inline def get(index: Long): Long = buffer.get(index)
32 |
33 | @inline def update(index: Int, value: Long): Unit = buffer.update(index, value)
34 |
35 | @inline def popLast: Long = {
36 |
37 | contentLen -= 1
38 |
39 | get(contentLen)
40 |
41 | }
42 |
43 | @inline def removeByIndex(index: Int): Unit = {
44 |
45 | if (index < contentLen - 1)
46 | update(index, popLast)
47 | else
48 | contentLen -= 1
49 |
50 | }
51 |
52 | @inline def removeByItem(item: Long): Unit = {
53 |
54 | removeByIndex(indexOf(item))
55 |
56 | }
57 |
58 | @inline def isEmpty: Boolean = contentLen == 0
59 |
60 | @inline def setSize(newSize: Int): Unit = contentLen = newSize
61 |
62 | @inline def traverseUntil(itemOp: Long => Boolean): Unit = {
63 |
64 | var i = 0
65 |
66 | var stop = false
67 |
68 | while (!stop) {
69 |
70 | stop = i >= contentLen || itemOp(buffer.get(i))
71 |
72 | i += 1
73 |
74 | }
75 |
76 | }
77 |
78 | @inline def contains(item: Long): Boolean = contains(item, contentLen)
79 |
80 | @inline def contains(item: Long, until: Int): Boolean = {
81 |
82 | var i = until - 1
83 |
84 | while (i >= 0) {
85 |
86 | if (buffer.get(i) == item)
87 | return true
88 |
89 | i -= 1
90 |
91 | }
92 |
93 | false
94 |
95 | }
96 |
97 | @inline def containsBy(item: Long, until: Int, by: Long => Int): Boolean = {
98 |
99 | var i = until - 1
100 |
101 | while (i >= 0) {
102 |
103 | if (by(buffer.get(i)) == by(item))
104 | return true
105 |
106 | i -= 1
107 |
108 | }
109 |
110 | false
111 |
112 | }
113 |
114 | @inline def indexOf(item: Long): Int = {
115 |
116 | var i = 0
117 |
118 | while (i < size) {
119 |
120 | if (buffer.get(i) == item)
121 | return i
122 |
123 | i += 1
124 |
125 | }
126 |
127 | -1
128 |
129 | }
130 |
131 | @inline def append(item: Long): Unit = {
132 |
133 | if (contentLen >= buffer.size) {
134 |
135 | val bufferOldAddr = buffer.getAddr
136 |
137 | val additionalSpace = contentLen >> 1
138 |
139 | buffer = buffer.cloneToNew(padding = additionalSpace, contentLen, cutOff = false)
140 |
141 | if (buffer.getAddr != bufferOldAddr) {
142 |
143 | input.UNSAFEhelper.UNSAFE.freeMemory(bufferOldAddr) // (alternatively we could add the old buffer address to the UNSAFEhelper garbage)
144 |
145 | }
146 |
147 | }
148 |
149 | buffer.update(contentLen, item)
150 |
151 | contentLen += 1
152 |
153 | }
154 |
155 | @inline def appendNoExpansion(item: Long): Boolean = {
156 |
157 | (contentLen < buffer.size) && {
158 |
159 | buffer.update(contentLen, item)
160 |
161 | contentLen += 1
162 |
163 | true
164 |
165 | }
166 |
167 | }
168 |
169 | @inline def clear(): Unit = contentLen = 0
170 |
171 | @inline def size(): Int = contentLen
172 |
173 | @inline def sort(by: Long => Double): Unit = {
174 |
175 | buffer.sortByInplace(by = by, until = contentLen)
176 |
177 | }
178 |
179 | @inline def freeBuffer(): Unit = buffer.free()
180 |
181 | @inline def addToGarbageBuffer(): Unit = buffer.addToGarbage()
182 |
183 | }
--------------------------------------------------------------------------------
/src/main/scala/utils/ByteArrayUnsafeS.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * diff-SAT
3 | *
4 | * Copyright (c) 2018,2020 Matthias Nickles
5 | *
6 | * matthiasDOTnicklesATgmxDOTnet
7 | *
8 | * This code is licensed under MIT License (see file LICENSE for details)
9 | *
10 | */
11 |
12 | package utils
13 |
14 | import input.UNSAFEhelper._
15 | import sun.misc.Unsafe
16 |
17 | /** This is not a general-purpose unsafe (off-heap) byte array class - designed for use in project diff-SAT only! */
18 | object ByteArrayUnsafeS {
19 |
20 | //private[this] var unsafe: Unsafe = null
21 |
22 | //var alignment = 128 //UNSAFE.pageSize
23 |
24 | //var internalPaddingFact = 1 // multiple of actual alignment (see below)
25 |
26 | var byteArrayOffset = -1l
27 |
28 | def init(us: Unsafe): Unit = {
29 |
30 | byteArrayOffset = UNSAFE.arrayBaseOffset(classOf[Array[Byte]])
31 |
32 | }
33 |
34 | @inline def getUnsafe: Unsafe = UNSAFE
35 |
36 | }
37 |
38 | /** This is not a general-purpose unsafe byte array class - designed for use in project diff-SAT only! */
39 | class ByteArrayUnsafeS(var sizev: Int /*, aligned: Boolean*/) {
40 |
41 | //var isSorted: Boolean = false
42 |
43 | private[this] var addr: Long = 0L
44 |
45 | //private[this] val unsafe = ByteArrayUnsafeS.getUnsafe
46 |
47 | private[this] val stringBuilder: java.lang.StringBuilder = new java.lang.StringBuilder(sizev)
48 |
49 | addr = allocateOffHeapMem(sizev) /*{
50 |
51 | val alignment = 64
52 |
53 | addr = /*UNSAFE.allocateMemory*/allocateOffHeapMem((sizev) + alignment + alignment * 8/*ByteArrayUnsafeS.internalPaddingFact*/)
54 |
55 | if (alignment > 0l && (addr & (alignment - 1l)) != 0)
56 | addr += (alignment - (addr & (alignment - 1)))
57 |
58 | addr += alignment * 8/*ByteArrayUnsafeS.internalPaddingFact*/
59 |
60 | }*/
61 |
62 | private[this] val addrMid: Long = addr + ((sizev >> 1))
63 |
64 |
65 | @inline def this(values: Array[Byte]) = {
66 |
67 | this(sizev = values.length)
68 |
69 | setFromArray(values)
70 |
71 | }
72 |
73 | @inline def this(s: Int, initialValue: Byte /*, aligned: Boolean*/) = {
74 |
75 | this(sizev = s /*, aligned = aligned*/)
76 |
77 | fill(initialValue)
78 |
79 | }
80 |
81 | @inline def fill(value: Byte, length: Int = sizev): Unit = {
82 |
83 | UNSAFE.setMemory(addr, length, value)
84 |
85 | }
86 |
87 | @inline def free(): Unit = freeOffHeapMem(addr, sizev) //UNSAFE.freeMemory(addr)
88 |
89 | @inline def addToGarbage(): Unit = addAllocOffHeapMemToGarbage(addr, sizev)
90 |
91 | @inline def size(): Int = sizev
92 |
93 | @inline override def hashCode(): Int = {
94 |
95 | var h = 1
96 |
97 | var i = 0
98 |
99 | while (i < sizev) {
100 |
101 | h = 31 * h + get(i)
102 |
103 | i += 1
104 |
105 | }
106 |
107 | h
108 |
109 | }
110 |
111 | @inline override def equals(obj: Any): Boolean = {
112 |
113 | if (obj.isInstanceOf[ByteArrayUnsafeS] && obj != null)
114 | this.hashCode == obj.asInstanceOf[ByteArrayUnsafeS].hashCode
115 | else
116 | super.equals(obj)
117 |
118 | }
119 |
120 | @inline def setFromArray(values: Array[Byte]): Unit = {
121 |
122 | UNSAFE.copyMemory(values, ByteArrayUnsafeS.byteArrayOffset, null, addr, values.length)
123 |
124 | }
125 |
126 | @inline def setFromUnsafeByteArray(values: ByteArrayUnsafeS): Unit = {
127 |
128 | UNSAFE.copyMemory(values, 0l, null, addr, values.sizev)
129 |
130 | }
131 |
132 | @inline def get(index: Int): Byte = {
133 |
134 | UNSAFE.getByte(addr + index)
135 |
136 | }
137 |
138 | @inline def getMid(index: Int): Byte = {
139 |
140 | UNSAFE.getByte(addrMid + index)
141 |
142 | }
143 |
144 | @inline def get(index: Long): Byte = {
145 |
146 | UNSAFE.getByte(addr + index)
147 |
148 | }
149 |
150 | @inline def getBoolean(index: Int): Boolean = {
151 |
152 | UNSAFE.getByte(addr + index) != 0x00.toByte // NB: caller specific. There is no "norm" for how to represent Booleans as bytes
153 |
154 | }
155 |
156 | @inline def first: Byte = {
157 |
158 | UNSAFE.getByte(addr)
159 |
160 | }
161 |
162 | @inline def update(index: Int, value: Byte): Unit = {
163 |
164 | UNSAFE.putByte(addr + index, value)
165 |
166 | }
167 |
168 | @inline def updateMid(index: Int, value: Byte): Unit = {
169 |
170 | UNSAFE.putByte(addrMid + index, value)
171 |
172 | }
173 |
174 | @inline def update(index: Long, value: Byte): Unit = {
175 |
176 | UNSAFE.putByte(addr + index, value)
177 |
178 | }
179 |
180 | /*
181 | @inline def update(index: Int, value: Boolean): Unit = {
182 |
183 | UNSAFE.putByte(addr + index, if (value) 0xFF.toByte else 0x00.toByte) // NB: caller specific. There is no "norm" for how to represent Booleans as bytes
184 |
185 | }
186 |
187 |
188 | @inline def updateMid(index: Int, value: Boolean): Unit = {
189 |
190 | UNSAFE.putByte(addrMid + index, if (value) 0xFF.toByte else 0x00.toByte) // NB: caller specific. There is no "norm" for how to represent Booleans as bytes
191 |
192 | }*/
193 |
194 |
195 | @inline def toArray: Array[Byte] = {
196 |
197 | val array = new Array[Byte](sizev)
198 |
199 | UNSAFE.copyMemory(null, addr, array, ByteArrayUnsafeS.byteArrayOffset, sizev)
200 |
201 | array
202 |
203 | }
204 |
205 | @inline def toArray(l: Int): Array[Byte] = {
206 |
207 | val array = new Array[Byte](l)
208 |
209 | UNSAFE.copyMemory(null, addr, array, ByteArrayUnsafeS.byteArrayOffset, l)
210 |
211 | array
212 |
213 | }
214 |
215 | @inline def toArray(from: Int, until: Int): Array[Byte] = {
216 |
217 | val array = new Array[Byte](until - from)
218 |
219 | UNSAFE.copyMemory(null, addr + (from), array, ByteArrayUnsafeS.byteArrayOffset, (until - from))
220 |
221 | array
222 |
223 | }
224 |
225 | @inline override def toString: String = {
226 |
227 | val s: StringBuilder = new StringBuilder
228 |
229 | var i = 0
230 |
231 | while (i < sizev) {
232 |
233 | s.append(if (i < sizev - 1)
234 | get(i) + ","
235 | else
236 | get(i)) {
237 |
238 | i += 1
239 |
240 | i - 1
241 |
242 | }
243 |
244 | }
245 |
246 | s.toString
247 |
248 | }
249 |
250 | @inline def toBitString: String = {
251 |
252 | stringBuilder.setLength(0)
253 |
254 | var i = 0
255 |
256 | while (i < sizev) {
257 |
258 | stringBuilder.append(
259 | if (get(i) != 0x00.toByte)
260 | '1'
261 | else
262 | '0'
263 | )
264 |
265 | i += 1
266 |
267 | }
268 |
269 | stringBuilder.toString
270 |
271 | }
272 |
273 | @inline def toBitStringLocal: String = {
274 |
275 | val stringBuilder: java.lang.StringBuilder = new java.lang.StringBuilder(sizev)
276 |
277 | var i = 0
278 |
279 | while (i < sizev) {
280 |
281 | stringBuilder.append(
282 | if (get(i) != 0x00.toByte)
283 | '1'
284 | else
285 | '0'
286 | )
287 |
288 | i += 1
289 |
290 | }
291 |
292 | stringBuilder.toString
293 |
294 | }
295 |
296 | @inline def getAddr: Long = addr
297 |
298 | @inline def clone(padding: Int = 0): ByteArrayUnsafeS = {
299 |
300 | val bau: ByteArrayUnsafeS = new ByteArrayUnsafeS(sizev + padding /*, aligned = aligned*/)
301 |
302 | UNSAFE.copyMemory(addr, bau.getAddr, sizev)
303 |
304 | bau
305 |
306 | }
307 |
308 | }
309 |
--------------------------------------------------------------------------------
/src/main/scala/utils/DoubleArrayUnsafeS.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * diff-SAT
3 | *
4 | * Copyright (c) 2018,2020 Matthias Nickles
5 | *
6 | * matthiasDOTnicklesATgmxDOTnet
7 | *
8 | * This code is licensed under MIT License (see file LICENSE for details)
9 | *
10 | */
11 |
12 | package utils
13 |
14 | import sun.misc.Unsafe
15 | import input.UNSAFEhelper._
16 |
17 | /** This is not general-purpose code for using unsafe memory - designed for use in project diff-SAT only! */
18 | object DoubleArrayUnsafeS {
19 |
20 | //private[this] var unsafe: Unsafe = null
21 |
22 | var alignment = 0
23 |
24 | var internalPaddingFact = 0 // multiple of actual alignment (see below)
25 |
26 | var doubleArrayOffs = -1
27 |
28 | @inline def init(us: Unsafe): Unit = {
29 |
30 | //unsafe = us
31 |
32 | doubleArrayOffs = UNSAFE.arrayBaseOffset(classOf[Array[Double]])
33 |
34 | }
35 |
36 | @inline def getUnsafe: Unsafe = UNSAFE
37 |
38 | }
39 |
40 | /** This is not a general-purpose unsafe array class - designed for use in project diff-SAT only! */
41 | class DoubleArrayUnsafeS(var sizev: Int, aligned: Boolean) {
42 |
43 | //private[this] val unsafe = ByteArrayUnsafeS.getUnsafe // without private[this] the field access in the bytecode would be by invokevirtual
44 |
45 | private[this] var addr: Long = 0L
46 |
47 | private[this] var allocated: Long = 0l
48 |
49 | if (DoubleArrayUnsafeS.alignment == 0) {
50 |
51 | allocated = sizev << 3
52 |
53 | addr = allocateOffHeapMem(allocated)
54 |
55 | } else {
56 |
57 | allocated = (sizev << 3) + DoubleArrayUnsafeS.alignment +
58 | DoubleArrayUnsafeS.alignment * DoubleArrayUnsafeS.internalPaddingFact
59 |
60 | addr = allocateOffHeapMem(allocated)
61 |
62 | if (DoubleArrayUnsafeS.alignment > 0l && (addr & (DoubleArrayUnsafeS.alignment - 1l)) != 0)
63 | addr += (DoubleArrayUnsafeS.alignment - (addr & (DoubleArrayUnsafeS.alignment - 1)))
64 |
65 | addr += DoubleArrayUnsafeS.alignment * DoubleArrayUnsafeS.internalPaddingFact
66 |
67 | }
68 |
69 | @inline def this(values: Array[Double], aligned: Boolean) {
70 |
71 | this(sizev = values.length, aligned = aligned)
72 |
73 | setFromArray(values)
74 |
75 | }
76 |
77 | @inline def this(s: Int, initValue: Double, aligned: Boolean) {
78 |
79 | this(sizev = s, aligned = aligned)
80 |
81 | var i = 0
82 |
83 | while(i < s) {
84 |
85 | update(i, initValue)
86 |
87 | i += 1
88 |
89 | }
90 |
91 | }
92 |
93 | @inline def free(): Unit = freeOffHeapMem(addr, allocated)
94 |
95 | @inline def size(): Int = sizev
96 |
97 | @inline def setFromArray(values: Array[Double]): Unit = {
98 |
99 | UNSAFE.copyMemory(values, DoubleArrayUnsafeS.doubleArrayOffs, null, addr, values.length << 3)
100 |
101 | }
102 |
103 | @inline def get(index: Int): Double = {
104 |
105 | UNSAFE.getDouble(addr + (index << 3))
106 |
107 | }
108 |
109 | @inline def get(index: Long): Double = {
110 |
111 | UNSAFE.getDouble(addr + (index << 3))
112 |
113 | }
114 |
115 | @inline def first: Double = {
116 |
117 | UNSAFE.getDouble(addr)
118 |
119 | }
120 |
121 | @inline def update(index: Int, value: Double): Unit = {
122 |
123 | UNSAFE.putDouble(addr + (index << 3), value)
124 |
125 | }
126 |
127 | @inline def update(index: Long, value: Double): Unit = {
128 |
129 | UNSAFE.putDouble(addr + (index << 3), value)
130 |
131 | }
132 |
133 | @inline final def incBy(index: Int, by: Double): Double = {
134 |
135 | val newValue = UNSAFE.getDouble(addr + (index << 3)) + by
136 |
137 | UNSAFE.putDouble(addr + (index << 3), newValue)
138 |
139 | newValue
140 |
141 | }
142 |
143 | @inline final def mulBy(index: Int, by: Double): Unit = {
144 |
145 | UNSAFE.putDouble(addr + (index << 3), UNSAFE.getDouble(addr + (index << 3)) * by)
146 |
147 | }
148 |
149 | @inline def toArray: Array[Double] = {
150 |
151 | val array = new Array[Double](sizev)
152 |
153 | UNSAFE.copyMemory(null, addr, array, DoubleArrayUnsafeS.doubleArrayOffs, sizev << 3)
154 |
155 | array
156 |
157 | }
158 |
159 | @inline def toArray(l: Int): Array[Double] = {
160 |
161 | val array = new Array[Double](l)
162 |
163 | UNSAFE.copyMemory(null, addr, array, DoubleArrayUnsafeS.doubleArrayOffs, l << 3)
164 |
165 | array
166 |
167 | }
168 |
169 | @inline def toArray(from: Int, until: Int): Array[Double] = {
170 |
171 | val array = new Array[Double](until - from)
172 |
173 | UNSAFE.copyMemory(null, addr + (from << 3), array, DoubleArrayUnsafeS.doubleArrayOffs, (until - from) << 3)
174 |
175 | array
176 |
177 | }
178 |
179 | override def toString: String = {
180 |
181 | val s: StringBuilder = new StringBuilder
182 |
183 | var i = 0
184 |
185 | while (i < sizev) {
186 |
187 | s.append(if (i < sizev - 1)
188 | get(i) + ","
189 | else
190 | get(i)) {
191 |
192 | i += 1
193 |
194 | i - 1
195 |
196 | }
197 |
198 | }
199 |
200 | s.toString
201 |
202 | }
203 |
204 | @inline def getAddr: Long = addr
205 |
206 | @inline def clone(padding: Int): DoubleArrayUnsafeS = {
207 |
208 | val bau: DoubleArrayUnsafeS = new DoubleArrayUnsafeS(sizev + padding, aligned = aligned)
209 |
210 | UNSAFE.copyMemory(addr, bau.getAddr, sizev << 3)
211 |
212 | bau
213 |
214 | }
215 |
216 | }
217 |
--------------------------------------------------------------------------------
/src/main/scala/utils/FiniteQueue.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * diff-SAT
3 | *
4 | * Copyright (c) 2018,2020 by Matthias Nickles
5 | *
6 | * matthiasDOTnicklesATgmxDOTnet
7 | *
8 | * This code is licensed under MIT License (see file LICENSE for details)
9 | *
10 | */
11 |
12 | package utils
13 |
14 | import sharedDefs.rngGlobal
15 |
16 | class FiniteQueue[A](q: scala.collection.mutable.Queue[A]) {
17 |
18 | @inline def enqueueFinite(elem: A, maxSize: Int) = {
19 |
20 | while (q.size >= maxSize) {
21 | q.dequeue
22 | }
23 |
24 | q.enqueue(elem)
25 |
26 | }
27 |
28 | @inline def front = q.front
29 |
30 | @inline def size = q.size
31 |
32 | @inline def isEmpty = q.isEmpty
33 |
34 | @inline def randomElement(limit: Int): A = {
35 |
36 | q.apply(rngGlobal.nextInt(limit))
37 |
38 | }
39 |
40 | @inline def dequeue: A = q.dequeue()
41 |
42 | }
--------------------------------------------------------------------------------
/src/main/scala/utils/FloatArrayUnsafeS.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * diff-SAT
3 | *
4 | * Copyright (c) 2018,2020 Matthias Nickles
5 | *
6 | * matthiasDOTnicklesATgmxDOTnet
7 | *
8 | * This code is licensed under MIT License (see file LICENSE for details)
9 | *
10 | */
11 |
12 | package utils
13 |
14 | import sun.misc.Unsafe
15 | import input.UNSAFEhelper._
16 |
17 | /** This is not a general-purpose unsafe class - designed for use in project diff-SAT only! */
18 | object FloatArrayUnsafeS {
19 |
20 | //private[this] var unsafe: Unsafe = null
21 |
22 | var alignment = 128 // UNSAFE.pageSize Observe that unsafe's allocateMemory does some alignment (to value size) by itself
23 |
24 | var internalPaddingFact = 1 // multiple of actual alignment (see below)
25 |
26 | var floatArrayOffs = -1
27 |
28 | @inline def init(us: Unsafe): Unit = {
29 |
30 | //unsafe = us
31 |
32 | floatArrayOffs = UNSAFE.arrayBaseOffset(classOf[Array[Float]])
33 |
34 | }
35 |
36 | @inline def getUnsafe: Unsafe = UNSAFE
37 |
38 | }
39 |
40 | /** This is not a general-purpose unsafe array class - designed for use in project diff-SAT only! */
41 | class FloatArrayUnsafeS(var sizev: Int, aligned: Boolean) {
42 |
43 | //private[this] val unsafe = ByteArrayUnsafeS.getUnsafe // without private[this] the field access in the bytecode would be by invokevirtual
44 |
45 | private[this] var addr: Long = 0L
46 |
47 | private[this] var allocated: Long = 0l
48 |
49 | if (!aligned) {
50 |
51 | allocated = sizev << 2
52 |
53 | addr = allocateOffHeapMem(allocated)
54 |
55 | } else {
56 |
57 | allocated = (sizev << 2) + FloatArrayUnsafeS.alignment +
58 | FloatArrayUnsafeS.alignment * FloatArrayUnsafeS.internalPaddingFact
59 |
60 | addr = allocateOffHeapMem(allocated)
61 |
62 | if (FloatArrayUnsafeS.alignment > 0l && (addr & (FloatArrayUnsafeS.alignment - 1l)) != 0)
63 | addr += (FloatArrayUnsafeS.alignment - (addr & (FloatArrayUnsafeS.alignment - 1)))
64 |
65 | addr += FloatArrayUnsafeS.alignment * FloatArrayUnsafeS.internalPaddingFact
66 |
67 | }
68 |
69 | @inline def this(values: Array[Float], aligned: Boolean) {
70 |
71 | this(sizev = values.length, aligned = aligned)
72 |
73 | setFromArray(values)
74 |
75 | }
76 |
77 | @inline def this(s: Int, initValue: Float, aligned: Boolean) {
78 |
79 | this(sizev = s, aligned = aligned)
80 |
81 | var i = 0
82 |
83 | while(i < s) {
84 |
85 | update(i, initValue)
86 |
87 | i += 1
88 |
89 | }
90 |
91 | }
92 |
93 | @inline def free(): Unit = freeOffHeapMem(addr, allocated)
94 |
95 | @inline def size(): Int = sizev
96 |
97 | @inline def setFromArray(values: Array[Float]): Unit = {
98 |
99 | UNSAFE.copyMemory(values, FloatArrayUnsafeS.floatArrayOffs, null, addr, values.length << 2)
100 |
101 | }
102 |
103 | @inline def get(index: Int): Float = {
104 |
105 | UNSAFE.getFloat(addr + (index << 2))
106 |
107 | }
108 |
109 | @inline def get(index: Long): Float = {
110 |
111 | UNSAFE.getFloat(addr + (index << 2))
112 |
113 | }
114 |
115 | @inline def first: Float = {
116 |
117 | UNSAFE.getFloat(addr)
118 |
119 | }
120 |
121 | @inline def update(index: Int, value: Float): Unit = {
122 |
123 | UNSAFE.putFloat(addr + (index << 2), value)
124 |
125 | }
126 |
127 | @inline def update(index: Long, value: Float): Unit = {
128 |
129 | UNSAFE.putFloat(addr + (index << 2), value)
130 |
131 | }
132 |
133 | @inline final def incBy(index: Int, by: Float): Float = {
134 |
135 | val newValue = UNSAFE.getFloat(addr + (index << 2)) + by
136 |
137 | UNSAFE.putFloat(addr + (index << 2), newValue)
138 |
139 | newValue
140 |
141 | }
142 |
143 | @inline final def mulBy(index: Int, by: Float): Unit = {
144 |
145 | UNSAFE.putFloat(addr + (index << 2), UNSAFE.getFloat(addr + (index << 2)) * by)
146 |
147 | }
148 |
149 | @inline def toArray: Array[Float] = {
150 |
151 | val array = new Array[Float](sizev)
152 |
153 | UNSAFE.copyMemory(null, addr, array, FloatArrayUnsafeS.floatArrayOffs, sizev << 2)
154 |
155 | array
156 |
157 | }
158 |
159 | @inline def toArray(l: Int): Array[Float] = {
160 |
161 | val array = new Array[Float](l)
162 |
163 | UNSAFE.copyMemory(null, addr, array, FloatArrayUnsafeS.floatArrayOffs, l << 2)
164 |
165 | array
166 |
167 | }
168 |
169 | @inline def toArray(from: Int, until: Int): Array[Float] = {
170 |
171 | val array = new Array[Float](until - from)
172 |
173 | UNSAFE.copyMemory(null, addr + (from << 2), array, FloatArrayUnsafeS.floatArrayOffs, (until - from) << 2)
174 |
175 | array
176 |
177 | }
178 |
179 | override def toString: String = {
180 |
181 | val s: StringBuilder = new StringBuilder
182 |
183 | var i = 0
184 |
185 | while (i < sizev) {
186 |
187 | s.append(if (i < sizev - 1)
188 | get(i) + ","
189 | else
190 | get(i)) {
191 |
192 | i += 1
193 |
194 | i - 1
195 |
196 | }
197 |
198 | }
199 |
200 | s.toString
201 |
202 | }
203 |
204 | @inline def getAddr: Long = addr
205 |
206 | @inline def clone(padding: Int): FloatArrayUnsafeS = {
207 |
208 | val bau: FloatArrayUnsafeS = new FloatArrayUnsafeS(sizev + padding, aligned = aligned)
209 |
210 | UNSAFE.copyMemory(addr, bau.getAddr, sizev << 2)
211 |
212 | bau
213 |
214 | }
215 |
216 | }
217 |
--------------------------------------------------------------------------------
/src/main/scala/utils/IntOrLongArrayUnsafe.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * diff-SAT
3 | *
4 | * Copyright (c) 2018-2020 Matthias Nickles
5 | *
6 | * matthiasDOTnicklesATgmxDOTnet
7 | *
8 | * This code is licensed under MIT License (see file LICENSE for details)
9 | *
10 | */
11 |
12 | package utils
13 |
14 | @deprecated trait IntOrLongArrayUnsafe[@specialized T <: AnyVal] { // nope (cannot avoid boxing)
15 |
16 | @inline def size(): Int
17 |
18 | @inline def getAddr: Long
19 |
20 | @inline def setAddr(newAddr: Long): Unit
21 |
22 | @inline def toArray: Array[T]
23 |
24 | @inline def get(index: Int): T
25 |
26 | @inline def get(index: Long): T
27 |
28 | @inline def popLast: T
29 |
30 | @inline def update(index: Int, value: T): Unit
31 |
32 | @inline def removeByIndex(index: Int): Unit
33 |
34 | @inline def cloneToNew(padding: Int, keep: Int, cutOff: Boolean): IntOrLongArrayUnsafe[T]
35 |
36 | @inline def free(): Unit
37 |
38 | @inline def addToGarbage(): Unit
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/scala/utils/LongArrayUnsafeS.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * diff-SAT
3 | *
4 | * Copyright (c) 2018,2020 Matthias Nickles
5 | *
6 | * matthiasDOTnicklesATgmxDOTnet
7 | *
8 | * This code is licensed under MIT License (see file LICENSE for details)
9 | *
10 | */
11 |
12 | package utils
13 |
14 | import input.UNSAFEhelper._
15 | import it.unimi.dsi.fastutil.longs.{LongArrayList, LongOpenHashSet}
16 |
17 |
18 | /** This is not a general-purpose unsafe array class - designed for use in project diff-SAT only! */
19 | class LongArrayUnsafeS(var sizev: Int) extends IntOrLongArrayUnsafe[Long] {
20 |
21 | //private[this] val unsafe: Unsafe = sharedDefs.unsafe
22 |
23 | private[this] val alignment = 0
24 |
25 | //private[this] val alignment = UNSAFE.pageSize // currently not used (see code below), no visible effect in diff-SAT
26 |
27 | //private[this] val internalPaddingFact = 0
28 |
29 | //private[this] val longArrayOffs = UNSAFE.arrayBaseOffset(classOf[Array[Long]])
30 |
31 | //private[this] val aligned = false // must be false (true not fully implemented yet)
32 |
33 | private[this] var allocated = (sizev << 3) + alignment
34 |
35 | //sharedDefs.offHeapAllocatedEstimate += allocateSize
36 |
37 | private[this] var addr: Long = if (alignment == 0) { // without private[this] the field access in the bytecode would be by invokevirtual
38 |
39 | allocateOffHeapMem(allocated)
40 |
41 | } else { // the following only makes sense where alignment is different from unsafe's alignment (sizeOf?)
42 |
43 | var addra = allocateOffHeapMem(allocated)
44 |
45 | if (alignment > 0l && (addra & (alignment - 1l)) != 0)
46 | addra += (alignment - (addra & (alignment - 1)))
47 |
48 | addra // + alignment * internalPaddingFact
49 |
50 | }
51 |
52 | private[this] val addrMid: Long = addr + ((sizev >> 1) << 3) // nope: addr + (allocateSize / 2)
53 |
54 | assert((addrMid - addr) % 8 == 0)
55 |
56 | @inline def this(values: LongArrayList) {
57 |
58 | this(sizev = values.size)
59 |
60 | var i = 0
61 |
62 | while (i < sizev) {
63 |
64 | update(i, values.getLong((i)))
65 |
66 | i += 1
67 |
68 | }
69 |
70 | }
71 |
72 | @inline def this(s: Int, initValue: Long) {
73 |
74 | this(sizev = s)
75 |
76 | var i = 0
77 |
78 | while (i < s) {
79 |
80 | update(i, initValue)
81 |
82 | i += 1
83 |
84 | }
85 |
86 | }
87 |
88 | @inline def free(): Unit = {
89 |
90 | freeOffHeapMem(addr, allocated)
91 |
92 |
93 | }
94 |
95 | @inline def addToGarbage(): Unit = {
96 |
97 | addAllocOffHeapMemToGarbage(addr, allocated)
98 |
99 | }
100 |
101 | @inline def size(): Int = sizev
102 |
103 | @inline def compareAndUpdate(index: Int, expectedValue: Long, newValue: Long): Boolean = {
104 |
105 | UNSAFE.compareAndSwapLong(null, addr + (index << 3), expectedValue, newValue)
106 |
107 | }
108 |
109 | @inline def compareWithZeroAndUpdate(index: Int, newValue: Long): Boolean = {
110 |
111 | UNSAFE.compareAndSwapLong(null, addr + (index << 3), 0l, newValue)
112 |
113 | }
114 |
115 | @inline def get(index: Int): Long = {
116 |
117 | UNSAFE.getLong(addr + (index << 3))
118 |
119 | }
120 |
121 | @inline def copyLongTo(fromIndex: Int, toIndex: Int): Unit = {
122 |
123 | UNSAFE.putLong(addr + (toIndex << 3), UNSAFE.getLong(addr + (fromIndex << 3)))
124 |
125 | }
126 |
127 | @inline def get(index: Long): Long = {
128 |
129 | UNSAFE.getLong(addr + (index << 3))
130 |
131 | }
132 |
133 | @inline def getMid(index: Long): Long = {
134 |
135 | UNSAFE.getLong(addrMid + (index << 3))
136 |
137 | }
138 |
139 | @inline def first: Long = {
140 |
141 | UNSAFE.getLong(addr)
142 |
143 | }
144 |
145 | @inline def last: Long = {
146 |
147 | UNSAFE.getLong(addr + ((sizev - 1) << 3))
148 |
149 | }
150 |
151 | @inline def popLast: Long = {
152 |
153 | sizev -= 1
154 |
155 | UNSAFE.getLong(addr + (sizev << 3))
156 |
157 | }
158 |
159 | @inline def update(index: Int, value: Long): Unit = {
160 |
161 | UNSAFE.putLong(addr + (index << 3), value)
162 |
163 | }
164 |
165 | @inline def updateVolatile(index: Int, value: Long): Unit = {
166 |
167 | UNSAFE.putLongVolatile(null, addr + (index << 3), value)
168 |
169 | }
170 |
171 | @inline def updateOrdered(index: Int, value: Long): Unit = {
172 |
173 | UNSAFE.putOrderedLong(null, addr + (index << 3), value)
174 |
175 | }
176 |
177 | @inline def updateMid(index: Int/*<- negative or positive*/, value: Long): Unit = {
178 |
179 | UNSAFE.putLong(addrMid + (index << 3), value)
180 |
181 | }
182 |
183 | @inline def update(index: Int, value: Int): Unit = {
184 |
185 | UNSAFE.putLong(addr + (index << 3), value)
186 |
187 | }
188 |
189 | @inline def update(index: Long, value: Long): Unit = {
190 |
191 | UNSAFE.putLong(addr + (index << 3), value)
192 |
193 | }
194 |
195 | @inline def inc(index: Int): Long = {
196 |
197 | UNSAFE.getAndAddLong(null, addr + (index << 3), 1)
198 |
199 | }
200 |
201 | @inline def dec(index: Int): Long = {
202 |
203 | UNSAFE.getAndAddLong(null, addr + (index << 3), -1) - 1
204 |
205 | }
206 |
207 | @inline def incBy(index: Int, x: Long): Unit = {
208 |
209 | UNSAFE.getAndAddLong(null, addr + (index << 3), x)
210 |
211 | }
212 |
213 | @inline def compareAndSwap(index: Int, valueOld: Long, valueNew: Long): Unit = {
214 |
215 | UNSAFE.compareAndSwapLong(null, addr + (index << 3), valueOld, valueNew)
216 |
217 | }
218 |
219 | @inline def removeByIndex(index: Int): Unit = {
220 |
221 | sizev -= 1
222 |
223 | if(index < sizev) {
224 |
225 | update(index, get(sizev))
226 |
227 | }
228 |
229 | }
230 |
231 | @inline def filter(keepBy: Long => Boolean): Unit = {
232 |
233 | var k = sizev - 1
234 |
235 | while(k >= 0) {
236 |
237 | if(!keepBy(get(k)))
238 | removeByIndex(k)
239 |
240 | k -= 1
241 |
242 | }
243 |
244 | }
245 |
246 |
247 | @inline def toArray(): Array[Long] = {
248 |
249 | val array = new Array[Long](sizev)
250 |
251 | UNSAFE.copyMemory(null, addr, array, UNSAFE.arrayBaseOffset(classOf[Array[Long]]), sizev << 3)
252 |
253 | array
254 |
255 | }
256 |
257 | @inline def toArray(l: Int): Array[Long] = {
258 |
259 | val array = new Array[Long](l)
260 |
261 | UNSAFE.copyMemory(null, addr, array, UNSAFE.arrayBaseOffset(classOf[Array[Long]]), l << 3)
262 |
263 | array
264 |
265 | }
266 |
267 | override def toString: String = {
268 |
269 | val s: StringBuilder = new StringBuilder
270 |
271 | var i = 0
272 |
273 | while (i < sizev) {
274 |
275 | s.append(if (i < sizev - 1)
276 | get(i) + ","
277 | else
278 | get(i)) {
279 |
280 | i += 1
281 |
282 | i - 1
283 |
284 | }
285 |
286 | }
287 |
288 | s.toString
289 |
290 | }
291 |
292 | @inline def getAddr: Long = addr
293 |
294 | @inline def setAddr(newAddr: Long): Unit = addr = newAddr
295 |
296 | @inline def getAllocated: Int = allocated
297 |
298 | @inline def clone(padding: Int): LongArrayUnsafeS = {
299 |
300 | val bau: LongArrayUnsafeS = new LongArrayUnsafeS(sizev + padding)
301 |
302 | UNSAFE.copyMemory(addr, bau.getAddr, sizev << 3)
303 |
304 | bau
305 |
306 | }
307 |
308 | @inline def cloneToNew(padding: Int, keep: Int, cutOff: Boolean): LongArrayUnsafeS = {
309 |
310 | val bau: LongArrayUnsafeS = new LongArrayUnsafeS(if(cutOff) keep + padding else sizev + padding)
311 |
312 | //assert(addr + ((sizev - 1) << 3) < bau.getAddr || bau.getAddr + ((sizev + padding - 1) << 3) < addr)
313 |
314 | UNSAFE.copyMemory(addr, bau.getAddr, /*sizev*/keep << 3)
315 |
316 | bau
317 |
318 | }
319 |
320 | @inline def resize(newSize: Int): Unit = { // see resize(newSize: Int, retain: Int) for the case where only a part of the current content needs to be retained
321 |
322 | allocated = newSize << 3
323 |
324 | addr = resizeOffHeapMem(addr, sizev << 3, allocated)
325 |
326 | sizev = newSize
327 |
328 | }
329 |
330 | @inline def resize(newSize: Int, retain: Int): Unit = {
331 |
332 | allocated = newSize << 3
333 |
334 | val newAddr = allocateOffHeapMem(allocated)
335 |
336 | if(retain > 0)
337 | UNSAFE.copyMemory(addr, newAddr, retain << 3)
338 |
339 | freeOffHeapMem(addr, sizev << 3)
340 |
341 | sizev = newSize
342 |
343 | addr = newAddr
344 |
345 | }
346 |
347 | def sortByInplace(by: Long => Double, until: Int): Unit = {
348 |
349 | // insertion sort - use only if array is very small (but then it's fast):
350 |
351 | var j = 1
352 |
353 | var key = -1l
354 |
355 | var i = -1
356 |
357 | while (j < until) {
358 |
359 | key = get(j)
360 |
361 | i = j - 1
362 |
363 | while (i > -1 && by(get(i)) > by(key)) {
364 |
365 | update(i + 1, get(i))
366 |
367 | i -= 1
368 |
369 | }
370 |
371 | update(i + 1, key)
372 |
373 | j += 1
374 |
375 | }
376 |
377 | }
378 |
379 | @inline def swap(i: Int, j: Int) = {
380 |
381 | val h = get(i)
382 |
383 | update(i, get(j))
384 |
385 | update(j, h)
386 |
387 | }
388 |
389 | // Returns index of k-th smallest item in this array
390 | def floydRivest(leftR: Int, rightR: Int, k: Int, by: Long => Double): Int = {
391 |
392 | var left = leftR
393 |
394 | var right = rightR
395 |
396 | while (right > left) {
397 |
398 | if (right - left > 600) { // sample subarray (constants 600, 0.5 largely arbitrary, from original implementation)
399 |
400 | val n = right - left + 1
401 |
402 | val i = k - left + 1
403 |
404 | val z = Math.log(n)
405 |
406 | val s = 0.5 * Math.exp(2 * z / 3)
407 |
408 | val sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * Math.signum(i - n / 2)
409 |
410 | val newLeft = Math.max(left, (k - i * s / n + sd).toInt)
411 |
412 | val newRight = Math.min(right, (k + (n - i) * s / n + sd).toInt)
413 |
414 | floydRivest(newLeft, newRight, k, by) // TODO: the Ints get boxed
415 |
416 | }
417 |
418 | val t = get(k)
419 |
420 | var i = left
421 |
422 | var j = right
423 |
424 | swap(left, k)
425 |
426 | if (by(get(right)) > by(t))
427 | swap(left, right)
428 |
429 | while (i < j) {
430 |
431 | swap(i, j)
432 |
433 | i += 1
434 |
435 | j -= 1
436 |
437 | while (by(get(i)) < by(t))
438 | i += 1
439 |
440 | while (by(get(j)) > by(t))
441 | j -= 1
442 |
443 | }
444 |
445 | if (by(get(left)) == by(t))
446 | swap(left, j)
447 | else {
448 |
449 | j += 1
450 |
451 | swap(right, j)
452 |
453 | }
454 |
455 | if (j <= k)
456 | left = j + 1
457 |
458 | if (k <= j)
459 | right = j - 1
460 |
461 | }
462 |
463 | k //get(k)
464 |
465 | }
466 |
467 | @inline def removeDuplicates(to: Int): Unit = {
468 |
469 | val hashForDuplRem = new LongOpenHashSet()
470 |
471 | var src = 0
472 |
473 | var dest = 0
474 |
475 | while(src <= to) {
476 |
477 | if(src == 0 || !hashForDuplRem.contains(get(src))) {
478 |
479 | hashForDuplRem.add(get(src))
480 |
481 | update(dest, get(src))
482 |
483 | dest += 1
484 |
485 | }
486 |
487 | src += 1
488 |
489 | }
490 |
491 | sizev = dest
492 |
493 | }
494 |
495 | @inline def establishDuplicateFreePrefixBy(by: Long => Long, to: Int): Int = {
496 |
497 | val hashForDuplRem = new LongOpenHashSet()
498 |
499 | var src = 0
500 |
501 | var dest = 0
502 |
503 | while(src <= to) {
504 |
505 | if(src == 0 || !hashForDuplRem.contains(by(get(src)))) {
506 |
507 | hashForDuplRem.add(by(get(src)))
508 |
509 | update(dest, get(src))
510 |
511 | dest += 1
512 |
513 | }
514 |
515 | src += 1
516 |
517 | }
518 |
519 | dest
520 |
521 | }
522 |
523 | }
524 |
--------------------------------------------------------------------------------
/src/main/scala/utils/RandomLongSet.scala:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import java.util.Random
4 |
5 | import it.unimi.dsi.fastutil.longs.{Long2IntOpenHashMap, LongArrayList}
6 |
7 | /**
8 | * For positive (Scala-)Longs only (in case of diff-SAT: nogood reducibles). Not suitable for cryptographic purposes.
9 | */
10 | class RandomLongSet(expectedNoOfItems: Int = 1024) {
11 |
12 | val data = new LongArrayList(expectedNoOfItems)
13 |
14 | val index = new Long2IntOpenHashMap(expectedNoOfItems)
15 | // ^ maps items to their indices in data
16 |
17 | index.defaultReturnValue(-1) // important because otherwise the "not found" value would be 0 which is a valid item in our use case
18 |
19 | @inline def clear(): Unit = {
20 |
21 | index.clear()
22 |
23 | data.clear()
24 |
25 | }
26 |
27 | @inline def contains(item: Long): Boolean = {
28 |
29 | index.containsKey(item)
30 |
31 | }
32 |
33 | @inline def add(item: Long): Unit = { // NB: in contrast to similar standard lib methods, we avoid returning (boxed) Boolean success indicators
34 |
35 | if (!index.containsKey(item)) {
36 |
37 | index.put(item, data.size)
38 |
39 | data.add(item)
40 |
41 | }
42 |
43 | }
44 |
45 | @inline def removeAt(id: Int): Unit = {
46 |
47 | if (id < data.size) {
48 |
49 | index.remove(data.getLong(id))
50 |
51 | val last: Long = data.removeLong(data.size - 1)
52 |
53 | if (id < data.size) {
54 |
55 | index.put(last, id)
56 |
57 | data.set(id, last)
58 |
59 | }
60 |
61 | }
62 |
63 | }
64 |
65 | @inline def size: Int = data.size
66 |
67 | @inline def remove(item: Long): Unit = {
68 |
69 | val id = index.get(item)
70 |
71 | if (id >= 0)
72 | removeAt(id)
73 |
74 | }
75 |
76 | @inline def get(id: Int): Long = data.getLong(id)
77 |
78 | @inline def getRandomItem(rnd: Random): Long = {
79 |
80 | val id = rnd.nextInt(data.size)
81 |
82 | data.getLong(id)
83 |
84 | }
85 |
86 | @inline def pollRandomItem(rnd: Random): Long = {
87 |
88 | val id: Int = rnd.nextInt(data.size)
89 |
90 | val res: Long = data.getLong(id)
91 |
92 | removeAt(id)
93 |
94 | res
95 |
96 | }
97 |
98 |
99 | }
--------------------------------------------------------------------------------
/src/main/scala/utils/SatalyzerUse.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * diff-SAT
3 | *
4 | * Copyright (c) 2018, 2020 Matthias Nickles
5 | *
6 | * matthiasDOTnicklesATgmxDOTnet
7 | *
8 | * This code is licensed under MIT License (see file LICENSE for details)
9 | *
10 | */
11 |
12 | package utils
13 |
14 | import java.io.{BufferedOutputStream, File, FileOutputStream, OutputStream}
15 | import java.time.Instant
16 | import java.util
17 | import java.util.UUID
18 |
19 | import com.jsoniter.output.JsonStream
20 |
21 | import input.diffSAT
22 | import input.diffSAT.stats
23 |
24 | import sharedDefs._
25 | import utils.Various._
26 |
27 |
28 | // For visualization of the serialized runtime statistics events, use Satalyzer (separate project)
29 |
30 | final case class StatsEntry( // Any modification of this class (e.g., new fields or field names) need to be reflected in the
31 | // deserialization method further below (can't be don't automatically).
32 | // Also, of course, class StatsEntry needs to be identical with StatsEntry in project RuntimeStats/StatsVisualizer.
33 |
34 | messageTimeStamp: Long = -1l, // in nano secs from program start (not to be used as a unique entry or message key)
35 |
36 | threadNo: Int = 0,
37 |
38 | key: String = null,
39 |
40 | valueStr: String = null,
41 |
42 | @transient runIDTransient: String = null
43 |
44 | ) {
45 |
46 | }
47 |
48 | final case class Stats( // Any modification of this class (e.g., new fields or field names) need to be reflected in the
49 | // deserialization method further below (can't be don't automatically).
50 | // Also, of course, class Stats needs to be identical with Stats in project RuntimeStats/StatsVisualizer.
51 |
52 | problemFile: String,
53 |
54 | runID: String = UUID.randomUUID().toString, // a type-4 UUID represented as a string
55 |
56 | runStartTimeMs: Long = Instant.now.toEpochMilli(),
57 |
58 | // any other information, including solving duration, should be written as StatsEntry entries
59 |
60 | entries: util.List[StatsEntry] /*doesn't serialize properly: ArrayBuffer[StatsEntry]*/ =
61 | new util.ArrayList[StatsEntry](),
62 |
63 | @transient var outFile: File = null,
64 |
65 | @transient var statsFileStream: OutputStream = null,
66 |
67 | @transient var lastFlush: Long = 0l
68 |
69 | ) {
70 |
71 | def initializeStatsFile(): Unit = {
72 |
73 | val outFileName = "stats_" + toString.replaceAll("[^\\w.-]", "_") + "(UTC).json"
74 |
75 | val dir = new File(writeStatsDirectory)
76 |
77 | outFile = new File(dir, outFileName)
78 |
79 | }
80 |
81 | @inline def initializeStatsFileStream(): Unit = {
82 |
83 | assert(outFile != null)
84 |
85 | statsFileStream = new BufferedOutputStream(
86 | new FileOutputStream(outFile))
87 |
88 | }
89 |
90 | /** This writes the entire stats to file, replacing any existing file with the respective name */
91 | @inline def writeToFile(): Unit = {
92 |
93 | if (writeRuntimeStatsToFile) { // for performance reasons, this condition should ideally be checked already
94 | // before calling this method
95 |
96 | initializeStatsFileStream()
97 |
98 | try {
99 |
100 | JsonStream.serialize(stats, statsFileStream) // closes stream!
101 |
102 | } catch { // TODO: probable reason: Graal 20 native image not working with Jsoniter
103 |
104 | case e: Exception => diffSAT.stomp(-10000, "Cannot serialize runtime stats: " + e + "\nIf you are using a native image executable, it is recommended to try writeRuntimeStatsToFile() with a JVM instead")
105 |
106 | }
107 |
108 | }
109 |
110 | }
111 |
112 | /** Don't use this for frequent writing such as logging. For long period statistics gathering only.
113 | */
114 | @inline def writeEntry(key: String, value: scala.Any,
115 | solverThreadNo: Int = 0 /*0: outside SAT solver thread*/ ,
116 | replace: Boolean = false): Unit = {
117 |
118 | if (writeRuntimeStatsToFile) { // for performance reasons, this condition should ideally be checked already
119 | // before calling writeEntry
120 |
121 | val messageTimeStampRelativeToProgStartNs = (Instant.now.toEpochMilli() - runStartTimeMs) * 1000000l // TODO
122 |
123 | val valueStr = value.toString()
124 |
125 | val newStatsEntry = new StatsEntry(messageTimeStamp = messageTimeStampRelativeToProgStartNs,
126 | threadNo = solverThreadNo, key = key, valueStr = valueStr,
127 | runIDTransient = runID)
128 |
129 | //println(statsEntry)
130 |
131 | this.synchronized {
132 |
133 | assert(newStatsEntry != null)
134 |
135 | if (replace) { // (costly, but rarely used and for deserialization-related reasons we shouldn't use a map here anyway)
136 |
137 | var i = stats.entries.size() - 1
138 |
139 | while (i >= 0) {
140 |
141 | val existingEntry = stats.entries.get(i)
142 |
143 | if (existingEntry.key == key) {
144 |
145 | stats.entries.set(i, newStatsEntry)
146 |
147 | i = -2
148 |
149 | } else
150 | i -= 1
151 |
152 | }
153 |
154 | if (i == -2)
155 | writeToFile() // since otherwise any existing serialization would be "outdated" (containing the old value of the unique key)
156 | else
157 | entries.add(newStatsEntry)
158 |
159 | } else
160 | entries.add(newStatsEntry)
161 |
162 | val currentTimerNs = System.nanoTime()
163 |
164 | if (currentTimerNs - lastFlush > flushRuntimeStatsEverySec /*sec*/ * 1000000000l) {
165 |
166 | lastFlush = currentTimerNs
167 |
168 | writeToFile()
169 |
170 | }
171 |
172 | }
173 |
174 | }
175 |
176 | }
177 |
178 | def getFirstOpt(key: String): Option[StatsEntry] = {
179 |
180 | var i = 0
181 |
182 | while (i < entries.size()) {
183 |
184 | val existingEntry = entries.get(i)
185 |
186 | if (existingEntry.key == key)
187 | return Some(existingEntry)
188 |
189 | i += 1
190 |
191 | }
192 |
193 | None
194 |
195 | }
196 |
197 | def countEntriesWithKey(key: String, maxCount: Int = Int.MaxValue): Int = {
198 |
199 | var count = 0
200 |
201 | var i = 0
202 |
203 | while (i < entries.size()) {
204 |
205 | if (entries.get(i).key == key) {
206 |
207 | count += 1
208 |
209 | if (count >= maxCount)
210 | return count
211 |
212 | }
213 |
214 | i += 1
215 |
216 | }
217 |
218 | count
219 |
220 | }
221 |
222 | override def toString(): String = {
223 |
224 | val runStartTimeMsStr = Instant.ofEpochMilli(runStartTimeMs).toString
225 |
226 | val problemName = fileNameFromFullPath(problemFile)
227 |
228 | problemName + " [" + runStartTimeMsStr + "]"
229 |
230 | }
231 |
232 | }
233 |
234 |
--------------------------------------------------------------------------------
/src/main/scala/utils/Tarjan.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * diff-SAT
3 | *
4 | * Copyright (c) 2018,2020 Matthias Nickles
5 | *
6 | * matthiasDOTnicklesATgmxDOTnet
7 | *
8 | * This code is licensed under MIT License (see file LICENSE for details)
9 | *
10 | */
11 |
12 | package utils
13 |
14 | import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
15 |
16 | import scala.collection.mutable
17 |
18 | /**
19 | * Not a general-purpose Tarjan implementation. Not well-tested yet.
20 | */
21 | object Tarjan {
22 |
23 | /** Based on code from https://stackoverflow.com/a/18290478, somewhat amended */
24 | def trajanRec(g: Int2ObjectOpenHashMap[List[Int]]): mutable.ArrayBuffer[mutable.ArrayBuffer[Int]] = {
25 |
26 | // TODO: create iterative variant
27 |
28 | val s = mutable.Buffer.empty[Int]
29 |
30 | val sSet = mutable.Set.empty[Int]
31 |
32 | val index = new java.util.HashMap[Int, Int]()
33 |
34 | val lowLink = new java.util.HashMap[Int, Int]()
35 |
36 | val ret = mutable.ArrayBuffer.empty[mutable.ArrayBuffer[Int]]
37 |
38 | def visit(v: Int): Unit = {
39 |
40 | index.put(v, index.size)
41 |
42 | lowLink.put(v, index.get(v))
43 |
44 | s += v
45 |
46 | sSet += v
47 |
48 | for(w <- g.get(v)) {
49 |
50 | if(!index.keySet.contains(w)) {
51 |
52 | visit(w)
53 |
54 | lowLink.put(v, math.min(lowLink.get(w), lowLink.get(v)))
55 |
56 | } else if(sSet(w))
57 | lowLink.put(v, math.min(lowLink.get(v), index.get(w)))
58 | }
59 |
60 | if(lowLink.get(v) == index.get(v)) {
61 |
62 | val scc = mutable.ArrayBuffer.empty[Int]
63 |
64 | var w = Int.MinValue // -1
65 |
66 | while(v != w) {
67 |
68 | w = s.remove(s.size - 1)
69 |
70 | scc += w
71 |
72 | sSet -= w
73 |
74 | }
75 |
76 | ret += scc
77 |
78 | }
79 | }
80 |
81 | val gKeysIterator = g.keySet().iterator()
82 |
83 | while(gKeysIterator.hasNext()) {
84 |
85 | val v = gKeysIterator.nextInt()
86 |
87 | if (/*v >= 0 &&*/ !index.keySet.contains(v))
88 | visit(v)
89 |
90 | }
91 |
92 | ret
93 |
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/scala/utils/Various.scala:
--------------------------------------------------------------------------------
1 | /**
2 | * diff-SAT
3 | *
4 | * Copyright (c) 2018, 2020 Matthias Nickles
5 | *
6 | * matthiasDOTnicklesATgmxDOTnet
7 | *
8 | * This code is licensed under MIT License (see file LICENSE for details)
9 | *
10 | */
11 |
12 | package utils
13 |
14 | import input.diffSAT
15 | import input.diffSAT.changeConsoleWidthUnderWin
16 | import sharedDefs.RandomGenSuperclass
17 |
18 | import scala.collection.mutable
19 |
20 | object Various {
21 |
22 | /** Plain Fisher-Yates shuffle on init of array */
23 | @inline def shuffleArray[A](array: mutable.Seq[A], rg: RandomGenSuperclass, to: Int = -1): Unit = {
24 |
25 | if (to < 0 && array.length >= 16384)
26 | shuffleArrayBlocked[A](array, rg)
27 | else {
28 |
29 | val l = if (to < 0) array.length - 1 else to
30 |
31 | var i = l
32 |
33 | while (i > 0) {
34 |
35 | val j = rg.nextInt(i + 1)
36 |
37 | val temp = array(j)
38 |
39 | array(j) = array(i)
40 |
41 | array(i) = temp
42 |
43 | i -= 1
44 |
45 | }
46 |
47 | }
48 |
49 | }
50 |
51 | /** Fisher-Yates-Durstenfeld shuffle */
52 | @inline def shuffleArrayUnsafe(array: IntArrayUnsafeS, rg: RandomGenSuperclass, from /*inclusive*/ : Long = 0l, to /*inclusive*/ : Long = -1l): Unit = {
53 |
54 | val l = if (to < 0l) array.sizev - 1 else to
55 |
56 | var i: Long = l
57 |
58 | while (i > from) {
59 |
60 | val j = rg.nextInt(i.toInt + 1)
61 |
62 | val temp = array.get(j)
63 |
64 | array.update(j, array.get(i))
65 |
66 | array.update(i, temp)
67 |
68 | i -= 1
69 |
70 | }
71 |
72 | }
73 |
74 | /** Fisher-Yates-Durstenfeld shuffle */
75 | @inline def shuffleLongArrayUnsafe(array: LongArrayUnsafeS, rg: RandomGenSuperclass, from /*inclusive*/ : Long = 0l, to /*inclusive*/ : Long = -1l): Unit = {
76 |
77 | val l = if (to < 0l) array.sizev - 1 else to
78 |
79 | var i: Long = l
80 |
81 | while (i > from) {
82 |
83 | val j = rg.nextInt(i.toInt + 1)
84 |
85 | val temp = array.get(j)
86 |
87 | array.update(j, array.get(i))
88 |
89 | array.update(i, temp)
90 |
91 | i -= 1
92 |
93 | }
94 |
95 | }
96 |
97 | /** Blocked Fisher-Yates shuffle */
98 | @inline def shuffleArrayBlocked[A](arr: mutable.Seq[A], rg: RandomGenSuperclass): Unit = {
99 | // (method code based on public domain code from https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog)
100 |
101 | def swap(i: Int, j: Int) = {
102 |
103 | val tmp = arr(i)
104 |
105 | arr(i) = arr(j)
106 |
107 | arr(j) = tmp
108 |
109 | }
110 |
111 | val size = arr.length
112 |
113 | val block = 1024
114 |
115 | val buffer = Array.ofDim[Int](block)
116 |
117 | var i = size
118 |
119 | while (i > block + 1) {
120 |
121 | var k = 0
122 |
123 | while (k < block) {
124 |
125 | buffer(k) = rg.nextInt(i - k)
126 |
127 | k += 1
128 |
129 | }
130 |
131 | k = 0
132 |
133 | while (k < block) {
134 |
135 | swap(i - 1 - k, buffer(k))
136 |
137 | k += 1
138 |
139 | }
140 |
141 | i -= block
142 |
143 | }
144 |
145 | while (i > 1) {
146 |
147 | swap(i - 1, rg.nextInt(i))
148 |
149 | i -= 1
150 |
151 | }
152 |
153 | }
154 |
155 | @inline def round(n: Double, digits: Int): Double = {
156 |
157 | val p = Math.pow(10d, digits)
158 |
159 | Math.round(n * p) / p
160 |
161 | }
162 |
163 |
164 | @inline def powFloat(a: Float, b: Float): Float = {
165 |
166 | Math.pow(a, b).toFloat //org.apache.commons.math3.util.FastMath.pow(a, b).toFloat
167 |
168 | }
169 |
170 | @inline def powInt(a: Double, b: Int): Double = org.apache.commons.math3.util.FastMath.pow(a, b)
171 |
172 | @inline def toK(n: Long): String = n / 1000L + "K"
173 |
174 | @inline def toM(n: Long): String = round(n.toDouble / 1000000d, 1) + "M"
175 |
176 | @inline def toB(n: Long): String = (n / 1000000000) + "B"
177 |
178 | @inline def toKorM(n: Long): String = if (n < 1000) n.toString else if (n < 1000000) toK(n) else if(n < 1000000000) toM(n) else toB(n)
179 |
180 | @inline def printStatusLine(pStrR: String, cutOffAt: Int = 0) = {
181 |
182 | /* println("sharedDefs.maxAssumedConsoleWidth = " + sharedDefs.maxAssumedConsoleWidth)
183 |
184 | println("diffSAT.osWin = " + diffSAT.osWin)
185 |
186 | println("changeConsoleWidthUnderWin = " + changeConsoleWidthUnderWin)
187 | */
188 | val pStr = if(sharedDefs.debug) pStrR else pStrR.take(sharedDefs.maxAssumedConsoleWidth.max(cutOffAt))+(" " * (sharedDefs.maxAssumedConsoleWidth.max(cutOffAt) - pStrR.length).max(0)) // progress line scrolls if larger than console width. No reliable way to determine console width in Java
189 |
190 | if(diffSAT.osWin /*&& changeConsoleWidthUnderWin*/) { // unclear if this needs to be done by all versions of Windows and all builds of Windows 10:
191 |
192 | // System.out.write(pStr.getBytes()) // if more than 4 lines, IntelliJ console flickers
193 |
194 | // \r moves to start of current console row (not supported with all OS)
195 |
196 | System.out.print(("\b" * 1100) + "\r" + pStr + " ")
197 | // with Win, \b's seem to required in addition to '\r'. But still doesn't work if console line buffer width to small.
198 |
199 | // print("\u001b[s" + pStr + "\u001b[u") // nope in Win
200 |
201 | } else {
202 |
203 | // System.out.print("\u001b[1A\u001b[2K")
204 |
205 | System.out.write(("\r" + pStr).getBytes())
206 |
207 | }
208 |
209 | System.out.flush()
210 |
211 | }
212 |
213 | @inline def timerToElapsedMs(startNano: Long): Long = (System.nanoTime() - startNano) / 1000000
214 |
215 | @inline def next2Pow(x: Int): Int = if (x <= 1) 1 else 1 << (32 - Integer.numberOfLeadingZeros(x - 1))
216 |
217 | /** Divisor must be a power of 2 */
218 | @inline def fastModByPow2(dividend: Int, divisor: Int): Int = dividend & (divisor - 1)
219 |
220 | @inline def binLog(x: Int): Int = {
221 |
222 | //assert(x != 0)
223 |
224 | 31 - Integer.numberOfLeadingZeros(x)
225 |
226 | }
227 |
228 |
229 | def fileNameFromFullPath(fileStr: String): String = {
230 |
231 | java.nio.file.Paths.get(fileStr).getFileName().toString()
232 |
233 | }
234 |
235 | /**
236 | * Pretty prints a Scala value similar to its source represention.
237 | * Particularly useful for case classes.
238 | * Derived from code at https://gist.github.com/carymrobbins/7b8ed52cd6ea186dbdf8
239 | * with enhancements: @see https://gist.github.com/myDisconnect/1f7046b23e18b4b43dd3c5932d0db7dc
240 | *
241 | * @param a - The value to pretty print.
242 | * @param indentSize - Number of spaces for each indent.
243 | * @param maxElementWidth - Largest element size before wrapping.
244 | * @param depth - Initial depth to pretty print indents.
245 | * @return
246 | */
247 | def prettyPrint(a: Any, indentSize: Int = 3, maxElementWidth: Int = 30, depth: Int = 0, omit: List[String]): String = {
248 |
249 | val indent = " " * depth * indentSize
250 |
251 | val fieldIndent = indent + (" " * indentSize)
252 |
253 | val nextDepth = prettyPrint(_: Any, indentSize, maxElementWidth, depth + 1, omit = omit)
254 |
255 | a match {
256 |
257 | case s: String =>
258 | val replaceMap = Seq(
259 | "\n" -> "\\n",
260 | "\r" -> "\\r",
261 | "\t" -> "\\t",
262 | "\"" -> "\\\""
263 | )
264 | '"' + replaceMap.foldLeft(s) { case (acc, (c, r)) => acc.replace(c, r) } + '"'
265 |
266 | case opt: Some[_] =>
267 | val resultOneLine = s"Some(${nextDepth(opt.get)})"
268 | if (resultOneLine.length <= maxElementWidth) return resultOneLine
269 | s"Some(\n$fieldIndent${nextDepth(opt.get)}\n$indent)"
270 |
271 | case xs: Seq[_] if xs.isEmpty =>
272 | xs.toString()
273 |
274 | case map: Map[_, _] if map.isEmpty =>
275 | map.toString()
276 |
277 | case xs: Map[_, _] =>
278 | val result = xs.map { case (key, value) => s"\n$fieldIndent${nextDepth(key)} -> ${nextDepth(value)}" }.toString
279 | "Map" + s"${result.substring(0, result.length - 1)}\n$indent)".substring(4)
280 |
281 | // Make Strings look similar to their literal form.
282 | // For an empty Seq just use its normal String representation.
283 | case xs: Seq[_] =>
284 | // If the Seq is not too long, pretty print on one line.
285 | val resultOneLine = xs.map(nextDepth).toString()
286 | if (resultOneLine.length <= maxElementWidth) return resultOneLine
287 | // Otherwise, build it with newlines and proper field indents.
288 | val result = xs.map(x => s"\n$fieldIndent${nextDepth(x)}").toString()
289 | result.substring(0, result.length - 1) + "\n" + indent + ")"
290 |
291 | // Product should cover case classes.
292 | case p: Product =>
293 | val prefix = p.productPrefix
294 | // We'll use reflection to get the constructor arg names and values.
295 | val cls = p.getClass
296 | val fields = cls.getDeclaredFields.filterNot(_.isSynthetic).map(_.getName)
297 | val values = p.productIterator.toSeq
298 | // If we weren't able to match up fields/values, fall back to toString.
299 | if (fields.length != values.length) return p.toString
300 | fields.zip(values).toList match {
301 | // If there are no fields, just use the normal String representation.
302 | case Nil => p.toString
303 | // If there is more than one field, build up the field names and values.
304 | case kvps =>
305 | val prettyFields = kvps.map {
306 |
307 | case (k, v) => {
308 |
309 | if (omit.contains(k))
310 | s"$fieldIndent$k = (omitted)"
311 | else
312 | s"$k = ${nextDepth(v)}"
313 |
314 | }
315 |
316 |
317 | }
318 | // If the result is not too long, pretty print on one line.
319 | val resultOneLine = s"$prefix(${prettyFields.mkString(", ")})"
320 | if (resultOneLine.length <= maxElementWidth) return resultOneLine
321 | // Otherwise, build it with newlines and proper field indents.
322 | s"$prefix(\n${
323 | kvps.map {
324 |
325 | // case ("dependencyGraph", _) => fieldIndent + "dependencyGraph = (omitted)"
326 |
327 | case (k, v) => {
328 |
329 | if (omit.contains(k))
330 | s"$fieldIndent$k = (omitted)"
331 | else
332 | s"$fieldIndent$k = ${nextDepth(v)}"
333 |
334 |
335 | }
336 |
337 |
338 | }.mkString(",\n")
339 | }\n$indent)"
340 | }
341 |
342 | // if we haven't specialized this type, just use its toString.
343 | case _ => a.toString
344 |
345 | }
346 |
347 | }
348 |
349 | }
350 |
--------------------------------------------------------------------------------
/src/main/scala/utils/XORShift32.java:
--------------------------------------------------------------------------------
1 | package utils;
2 |
3 | import java.util.Optional;
4 |
5 | /**
6 | * Medium-quality random numbers using a basic XOR-shift algorithm. For underlying basic algorithm see, e.g., http://www.javamex.com/tutorials/random_numbers/xorshift.shtml
7 | * Not suitable as a source of randomness for cryptography. Not thread-safe.
8 | *
9 | * For algo with better quality (longer period) but also not cryptographically secure, consider using a >=128 XOR-Shift,
10 | * see, e.g., https://www.codeproject.com/Articles/9187/A-fast-equivalent-for-System-Random
11 | */
12 |
13 | public class XORShift32 extends java.util.Random {
14 |
15 | private long x;
16 |
17 | public XORShift32() {
18 |
19 | this(Optional.empty());
20 |
21 | }
22 |
23 | public XORShift32(Optional seedOpt) {
24 |
25 | x = seedOpt.orElse(System.nanoTime());
26 |
27 | }
28 |
29 | @Override
30 | public long nextLong() {
31 |
32 | x ^= (x << 21);
33 |
34 | x ^= (x >>> 35);
35 |
36 | x ^= (x << 4);
37 |
38 | return x;
39 |
40 | }
41 |
42 | public long nextPosLong() {
43 |
44 | return nextLong() & 0x7fffffffffffffffL;
45 |
46 | }
47 |
48 | @Override
49 | public int nextInt() {
50 |
51 | return (int) nextLong();
52 |
53 | }
54 |
55 | public int nextPosInt() {
56 |
57 | return nextInt() & 0x7fffffff;
58 |
59 | }
60 |
61 | @Override
62 | public int nextInt(int max) {
63 |
64 | return (int) (nextPosLong() / (0x7fffffffffffffffL / max + 1l));
65 |
66 | /* somewhat better quality? (better at avoiding bias in lower bits):
67 |
68 | int threshold = (0x7fffffff - max + 1) % max;
69 |
70 | for (;;) {
71 |
72 | int bits = (int) (nextLong() & 0x7fffffff);
73 |
74 | if (bits >= threshold)
75 | return bits % max;
76 |
77 | } */
78 |
79 | }
80 |
81 | @Override
82 | public float nextFloat() {
83 |
84 | return Math.scalb((float) (nextLong() & 0x7fffffffL), -31);
85 |
86 | }
87 |
88 | @Override
89 | public double nextDouble() {
90 |
91 | //return Math.scalb(nextLong() >>> 1, -63);
92 | return (nextLong() >>> (64 - 53)) * 0x1.0p-53;
93 |
94 | }
95 |
96 | @Override
97 | public boolean nextBoolean() {
98 |
99 | return nextLong() >= 0l;
100 |
101 | }
102 |
103 | /*public double nextTriangular(double a, double b, double c) {
104 |
105 | double d = (c - a) / (b - a);
106 |
107 | double rand = nextDouble();
108 |
109 | if (rand < d)
110 | return a + Math.sqrt(rand * (b - a) * (c - a));
111 | else
112 | return b - Math.sqrt((1 - rand) * (b - a) * (b - c));
113 |
114 | }*/
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/src/main/scala/utils/XoRoRNGd.java:
--------------------------------------------------------------------------------
1 | package utils;
2 |
3 | /* Based on code written in 2016 by David Blackman and Sebastiano Vigna (vigna@acm.org)
4 | http://squidpony.github.io/SquidLib/squidlib-util/apidocs/src-html/squidpony/squidmath/XoRoRNG.html
5 | */
6 |
7 | /**
8 | * A port of Blackman and Vigna's xoroshiro128+ generator; should be very fast and produce medium-quality output.
9 | * Testing shows it is within 5% the speed of LightRNG, sometimes faster and sometimes slower, and has a larger period.
10 | * It's called XoRo because it involves Xor as well as Rotate operations on the 128-bit pseudo-random state. Note that
11 | * xoroshiro128+ fails some statistical quality tests systematically, and fails others often; if this could be a concern
12 | * for you, {@link DiverRNG}, which is the default for {@link RNG}, will be faster and won't fail tests, and
13 | * though its period is shorter, it would still take years to exhaust on one core generating only random numbers.
14 | *
15 | * {@link LightRNG} is also very fast, but relative to XoRoRNG it has a significantly shorter period (the amount of
16 | * random numbers it will go through before repeating), at {@code pow(2, 64)} as opposed to XorRNG and XoRoRNG's
17 | * {@code pow(2, 128) - 1}, but LightRNG also allows the current RNG state to be retrieved and altered with
18 | * {@code getState()} and {@code setState()}. For most cases, you should decide between DiverRNG, LightRNG, XoRoRNG,
19 | * and other RandomnessSource implementations based on your needs for period length and state manipulation (DiverRNG
20 | * is also used internally by almost all StatefulRNG objects). You might want significantly less predictable random
21 | * results, which {@link IsaacRNG} can provide, along with a large period. You may want a very long period of random
22 | * numbers, which would suggest {@link LongPeriodRNG} as a good choice or {@link MersenneTwister} as a potential
23 | * alternative. You may want better performance on 32-bit machines or on GWT, where {@link Starfish32RNG} is currently
24 | * the best choice most of the time, and {@link Lathe32RNG} can be faster but has slightly worse quality (both of these
25 | * generators use a 32-bit variant on the xoroshiro algorithm but change the output scrambler). These all can generate
26 | * pseudo-random numbers in a handful of nanoseconds (with the key exception of 64-bit generators being used on GWT,
27 | * where they may take more than 100 nanoseconds per number), so unless you need a LOT of random numbers in a hurry,
28 | * they'll probably all be fine on performance. You may want to decide on the special features of a generator, indicated
29 | * by implementing {@link StatefulRandomness} if their state can be read and written to, and/or
30 | * {@link SkippingRandomness} if sections in the generator's sequence can be skipped in long forward or backward leaps.
31 | *
32 | * Original version here.
33 | *
34 | * Written in 2016 by David Blackman and Sebastiano Vigna (vigna@acm.org)
35 | *
36 | * @author Sebastiano Vigna
37 | * @author David Blackman
38 | * @author Tommy Ettinger (if there's a flaw, use SquidLib's issues and don't bother Vigna or Blackman, it's probably a mistake in SquidLib's implementation)
39 | * @author Matthias Nickles (cosmetic changes)
40 | */
41 | public /*final*/ class XoRoRNGd extends java.util.Random {
42 |
43 | private static final long DOUBLE_MASK = (1L << 53) - 1;
44 | private static final double NORM_53 = 1. / (1L << 53);
45 | private static final long FLOAT_MASK = (1L << 24) - 1;
46 | private static final double NORM_24 = 1. / (1L << 24);
47 |
48 | private static final long serialVersionUID = 1018744536171610262L;
49 |
50 | private long state0, state1;
51 |
52 | /**
53 | * Creates a new generator seeded using four calls to Math.random().
54 | */
55 | public XoRoRNGd() {
56 | this((long) ((Math.random() - 0.5) * 0x10000000000000L)
57 | ^ (long) (((Math.random() - 0.5) * 2.0) * 0x8000000000000000L),
58 | (long) ((Math.random() - 0.5) * 0x10000000000000L)
59 | ^ (long) (((Math.random() - 0.5) * 2.0) * 0x8000000000000000L));
60 | }
61 | /**
62 | * Constructs this XoRoRNG by dispersing the bits of seed using {@link #setSeed(long)} across the two parts of state
63 | * this has.
64 | * @param seed a long that won't be used exactly, but will affect both components of state
65 | */
66 | public XoRoRNGd(final long seed) {
67 | setSeed(seed);
68 | }
69 | /**
70 | * Constructs this XoRoRNG by calling {@link #setSeed(long, long)} on the arguments as given; see that method for
71 | * the specific details (stateA and stateB are kept as-is unless they are both 0).
72 | * @param stateA the number to use as the first part of the state; this will be 1 instead if both seeds are 0
73 | * @param stateB the number to use as the second part of the state
74 | */
75 | public XoRoRNGd(final long stateA, final long stateB) {
76 | setSeed(stateA, stateB);
77 | }
78 |
79 | public final int next(int bits) {
80 | final long s0 = state0;
81 | long s1 = state1;
82 | final int result = (int)(s0 + s1) >>> (32 - bits);
83 | s1 ^= s0;
84 | state0 = (s0 << 55 | s0 >>> 9) ^ s1 ^ (s1 << 14); // a, b
85 | state1 = (s1 << 36 | s1 >>> 28); // c
86 | return result;
87 | }
88 |
89 | @Override
90 | public final long nextLong() {
91 | final long s0 = state0;
92 | long s1 = state1;
93 | final long result = s0 + s1;
94 |
95 | s1 ^= s0;
96 | state0 = (s0 << 55 | s0 >>> 9) ^ s1 ^ (s1 << 14); // a, b
97 | state1 = (s1 << 36 | s1 >>> 28); // c
98 | /*
99 | state0 = Long.rotateLeft(s0, 55) ^ s1 ^ (s1 << 14); // a, b
100 | state1 = Long.rotateLeft(s1, 36); // c
101 | */
102 | return result;
103 | }
104 |
105 | /**
106 | * Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the
107 | * copy, both will generate the same sequence of random numbers from the point copy() was called. This just needs to
108 | * copy the state so it isn't shared, usually, and produce a new value with the same exact state.
109 | *
110 | * @return a copy of this RandomnessSource
111 | */
112 |
113 | public XoRoRNGd copy() {
114 | XoRoRNGd next = new XoRoRNGd(state0);
115 | next.state0 = state0;
116 | next.state1 = state1;
117 | return next;
118 | }
119 |
120 |
121 | /**
122 | * Can return any int, positive or negative, of any size permissible in a 32-bit signed integer.
123 | * @return any int, all 32 bits are random
124 | */
125 | @Override
126 | public int nextInt() {
127 | return (int)nextLong();
128 | }
129 |
130 | /**
131 | * Exclusive on the outer bound; the inner bound is 0. The bound may be negative, which will produce a non-positive
132 | * result.
133 | * @param bound the outer exclusive bound; may be positive or negative
134 | * @return a random int between 0 (inclusive) and bound (exclusive)
135 | */
136 | @Override
137 | public int nextInt(final int bound) {
138 | return (int) ((bound * (nextLong() >>> 33)) >> 31);
139 | }
140 | /**
141 | * Inclusive lower, exclusive upper.
142 | * @param inner the inner bound, inclusive, can be positive or negative
143 | * @param outer the outer bound, exclusive, should be positive, should usually be greater than inner
144 | * @return a random int that may be equal to inner and will otherwise be between inner and outer
145 | */
146 | public int nextInt(final int inner, final int outer) {
147 | return inner + nextInt(outer - inner);
148 | }
149 |
150 | /**
151 | * Exclusive on the outer bound; the inner bound is 0. The bound may be negative, which will produce a non-positive
152 | * result.
153 | * @param bound the outer exclusive bound; may be positive or negative
154 | * @return a random long between 0 (inclusive) and bound (exclusive)
155 | */
156 | public long nextLong(long bound) {
157 | long rand = nextLong();
158 | final long randLow = rand & 0xFFFFFFFFL;
159 | final long boundLow = bound & 0xFFFFFFFFL;
160 | rand >>>= 32;
161 | bound >>= 32;
162 | final long z = (randLow * boundLow >> 32);
163 | long t = rand * boundLow + z;
164 | final long tLow = t & 0xFFFFFFFFL;
165 | t >>>= 32;
166 | return rand * bound + t + (tLow + randLow * bound >> 32) - (z >> 63) - (bound >> 63);
167 | }
168 | /**
169 | * Inclusive inner, exclusive outer; both inner and outer can be positive or negative.
170 | * @param inner the inner bound, inclusive, can be positive or negative
171 | * @param outer the outer bound, exclusive, can be positive or negative and may be greater than or less than inner
172 | * @return a random long that may be equal to inner and will otherwise be between inner and outer
173 | */
174 | public long nextLong(final long inner, final long outer) {
175 | return inner + nextLong(outer - inner);
176 | }
177 |
178 | @Override
179 | public double nextDouble() {
180 | return (nextLong() & DOUBLE_MASK) * NORM_53;
181 | }
182 |
183 | @Override
184 | public float nextFloat() {
185 | return (float) ((nextLong() & FLOAT_MASK) * NORM_24);
186 | }
187 |
188 | @Override
189 | public boolean nextBoolean() {
190 | return nextLong() < 0L;
191 | }
192 |
193 | @Override
194 | public void nextBytes(final byte[] bytes) {
195 | int i = bytes.length, n = 0;
196 | while (i != 0) {
197 | n = Math.min(i, 8);
198 | for (long bits = nextLong(); n-- != 0; bits >>>= 8) {
199 | bytes[--i] = (byte) bits;
200 | }
201 | }
202 | }
203 |
204 |
205 | /**
206 | * Sets the seed of this generator using one long, running that through LightRNG's algorithm twice to get the state.
207 | * @param seed the number to use as the seed
208 | */
209 | @Override
210 | public void setSeed(final long seed) {
211 |
212 | long state = seed + 0x9E3779B97F4A7C15L,
213 | z = state;
214 | z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L;
215 | z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL;
216 | state0 = z ^ (z >>> 31);
217 | state += 0x9E3779B97F4A7C15L;
218 | z = state;
219 | z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L;
220 | z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL;
221 | state1 = z ^ (z >>> 31);
222 | }
223 |
224 | /**
225 | * Sets the seed of this generator using two longs, using them without changes unless both are 0 (then it makes the
226 | * state variable corresponding to stateA 1 instead).
227 | * @param stateA the number to use as the first part of the state; this will be 1 instead if both seeds are 0
228 | * @param stateB the number to use as the second part of the state
229 | */
230 | public void setSeed(final long stateA, final long stateB) {
231 |
232 | state0 = stateA;
233 | state1 = stateB;
234 | if((stateA | stateB) == 0L)
235 | state0 = 1L;
236 | }
237 |
238 | /**
239 | * Gets the first component of this generator's two-part state, as a long. This can be 0 on its own, but will never
240 | * be 0 at the same time as the other component of state, {@link #getStateB()}. You can set the state with two exact
241 | * values using {@link #setSeed(long, long)}, but the alternative overload {@link #setSeed(long)} won't use the
242 | * state without changing it (it needs to cover 128 bits with a 64-bit value).
243 | * @return the first component of this generator's state
244 | */
245 | public long getStateA()
246 | {
247 | return state0;
248 | }
249 | /**
250 | * Gets the second component of this generator's two-part state, as a long. This can be 0 on its own, but will never
251 | * be 0 at the same time as the other component of state, {@link #getStateA()}. You can set the state with two exact
252 | * values using {@link #setSeed(long, long)}, but the alternative overload {@link #setSeed(long)} won't use the
253 | * state without changing it (it needs to cover 128 bits with a 64-bit value).
254 | * @return the second component of this generator's state
255 | */
256 | public long getStateB()
257 | {
258 | return state1;
259 | }
260 |
261 | @Override
262 | public String toString() {
263 | return "XoRoRNGd";
264 | }
265 |
266 | @Override
267 | public boolean equals(Object o) {
268 | if (this == o) return true;
269 | if (o == null || getClass() != o.getClass()) return false;
270 |
271 | XoRoRNGd xoRoRNG = (XoRoRNGd) o;
272 |
273 | if (state0 != xoRoRNG.state0) return false;
274 | return state1 == xoRoRNG.state1;
275 | }
276 |
277 | @Override
278 | public int hashCode() {
279 | return (int) (31L * (state0 ^ (state0 >>> 32)) + (state1 ^ (state1 >>> 32)));
280 | }
281 | }
282 |
--------------------------------------------------------------------------------