Commit | Line | Data |
---|---|---|
51c08015 | 1 | /******************************************************************************* |
ed902a2b | 2 | * Copyright (c) 2013, 2014 École Polytechnique de Montréal |
51c08015 GB |
3 | * |
4 | * All rights reserved. This program and the accompanying materials are made | |
5 | * available under the terms of the Eclipse Public License v1.0 which | |
6 | * accompanies this distribution, and is available at | |
7 | * http://www.eclipse.org/legal/epl-v10.html | |
8 | * | |
9 | * Contributors: | |
10 | * Geneviève Bastien - Initial implementation and API | |
11 | * Francis Giraldeau - Transform computation using synchronization graph | |
12 | *******************************************************************************/ | |
13 | ||
2bdf0193 | 14 | package org.eclipse.tracecompass.internal.tmf.core.synchronization; |
51c08015 GB |
15 | |
16 | import java.io.IOException; | |
a1ff9910 | 17 | import java.io.ObjectInputStream; |
51c08015 GB |
18 | import java.io.Serializable; |
19 | import java.math.BigDecimal; | |
20 | import java.math.MathContext; | |
21 | import java.util.Collection; | |
22 | import java.util.LinkedHashMap; | |
23 | import java.util.LinkedList; | |
24 | import java.util.List; | |
25 | import java.util.Map; | |
26 | ||
2bdf0193 AM |
27 | import org.eclipse.tracecompass.internal.tmf.core.synchronization.graph.SyncSpanningTree; |
28 | import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; | |
29 | import org.eclipse.tracecompass.tmf.core.event.matching.TmfEventDependency; | |
30 | import org.eclipse.tracecompass.tmf.core.synchronization.ITmfTimestampTransform; | |
31 | import org.eclipse.tracecompass.tmf.core.synchronization.Messages; | |
32 | import org.eclipse.tracecompass.tmf.core.synchronization.SynchronizationAlgorithm; | |
33 | import org.eclipse.tracecompass.tmf.core.synchronization.TimestampTransformFactory; | |
34 | import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; | |
35 | import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; | |
51c08015 GB |
36 | |
37 | /** | |
38 | * Class implementing fully incremental trace synchronization approach as | |
39 | * described in | |
40 | * | |
41 | * Masoume Jabbarifar, Michel Dagenais and Alireza Shameli-Sendi, | |
42 | * "Streaming Mode Incremental Clock Synchronization" | |
43 | * | |
44 | * Since the algorithm itself applies to two traces, it is implemented in a | |
45 | * private class, while this public class manages the synchronization between | |
46 | * all traces. | |
47 | * | |
48 | * @author Geneviève Bastien | |
49 | */ | |
50 | public class SyncAlgorithmFullyIncremental extends SynchronizationAlgorithm { | |
51 | ||
52 | /** | |
53 | * Auto-generated serial UID | |
54 | */ | |
55 | private static final long serialVersionUID = -1782788842774838830L; | |
56 | ||
57 | private static final MathContext fMc = MathContext.DECIMAL128; | |
58 | ||
59 | /** @Serial */ | |
60 | private final List<ConvexHull> fSyncs; | |
61 | ||
a1ff9910 | 62 | private transient SyncSpanningTree fTree = null; |
51c08015 GB |
63 | |
64 | /** | |
65 | * Initialization of the attributes | |
66 | */ | |
67 | public SyncAlgorithmFullyIncremental() { | |
68 | fSyncs = new LinkedList<>(); | |
69 | } | |
70 | ||
71 | /** | |
72 | * Function called after all matching has been done, to do any post-match | |
73 | * treatment. For this class, it calculates stats, while the data is | |
74 | * available | |
75 | */ | |
76 | @Override | |
77 | public void matchingEnded() { | |
78 | getStats(); | |
79 | } | |
80 | ||
81 | @Override | |
82 | public void init(Collection<ITmfTrace> traces) { | |
83 | ITmfTrace[] traceArr = traces.toArray(new ITmfTrace[traces.size()]); | |
84 | fSyncs.clear(); | |
85 | /* Create a convex hull for all trace pairs */ | |
86 | // FIXME: is it necessary to make ConvexHull for every pairs up-front? | |
87 | // The ConvexHull seems to be created on the fly in processMatch(). | |
88 | for (int i = 0; i < traceArr.length; i++) { | |
89 | for (int j = i + 1; j < traceArr.length; j++) { | |
2c2fcd6b GB |
90 | if (!traceArr[i].getHostId().equals(traceArr[j].getHostId())) { |
91 | ConvexHull algo = new ConvexHull(traceArr[i].getHostId(), traceArr[j].getHostId()); | |
92 | fSyncs.add(algo); | |
93 | } | |
51c08015 GB |
94 | } |
95 | } | |
96 | } | |
97 | ||
98 | @Override | |
99 | protected void processMatch(TmfEventDependency match) { | |
100 | String host1 = match.getSourceEvent().getTrace().getHostId(); | |
101 | String host2 = match.getDestinationEvent().getTrace().getHostId(); | |
102 | ||
103 | /* Process only if source and destination are different */ | |
104 | if (host1.equals(host2)) { | |
105 | return; | |
106 | } | |
107 | ||
108 | /* Check if a convex hull algorithm already exists for these 2 hosts */ | |
109 | ConvexHull algo = null; | |
110 | for (ConvexHull traceSync : fSyncs) { | |
111 | if (traceSync.isForHosts(host1, host2)) { | |
112 | algo = traceSync; | |
113 | } | |
114 | } | |
115 | if (algo == null) { | |
116 | algo = new ConvexHull(host1, host2); | |
117 | fSyncs.add(algo); | |
118 | } | |
119 | algo.processMatch(match); | |
120 | invalidateSyncGraph(); | |
121 | } | |
122 | ||
123 | private void invalidateSyncGraph() { | |
124 | fTree = null; | |
125 | } | |
126 | ||
127 | @Override | |
128 | public ITmfTimestampTransform getTimestampTransform(ITmfTrace trace) { | |
129 | return getTimestampTransform(trace.getHostId()); | |
130 | } | |
131 | ||
132 | @Override | |
133 | public ITmfTimestampTransform getTimestampTransform(String hostId) { | |
134 | SyncSpanningTree tree = getSyncTree(); | |
135 | return tree.getTimestampTransform(hostId); | |
136 | } | |
137 | ||
138 | /** | |
139 | * Each convex hull computes the synchronization between 2 given hosts. A | |
140 | * synchronization can be done on multiple hosts that may not all | |
141 | * communicate with each other. We must use another algorithm to determine | |
142 | * which host will be the reference node and what synchronization formula | |
143 | * will be used between each host and this reference node. | |
144 | * | |
145 | * For example, take traces a, b and c where a and c talk to b but do not | |
146 | * know each other ({@literal a <-> b <-> c}). The convex hulls will contain | |
147 | * the formulae between their 2 traces, but if a is the reference node, then | |
148 | * the resulting formula of c would be the composition of {@literal a <-> b} | |
149 | * and {@literal b <-> c} | |
150 | * | |
151 | * @return The synchronization spanning tree for this synchronization | |
152 | */ | |
153 | private SyncSpanningTree getSyncTree() { | |
154 | if (fTree == null) { | |
155 | fTree = new SyncSpanningTree(); | |
156 | for (ConvexHull traceSync : fSyncs) { | |
157 | SyncQuality q = traceSync.getQuality(); | |
158 | if (q == SyncQuality.ACCURATE || q == SyncQuality.APPROXIMATE) { | |
159 | String from = traceSync.getReferenceHost(); | |
160 | String to = traceSync.getOtherHost(); | |
161 | fTree.addSynchronization(from, to, traceSync.getTimestampTransform(to), traceSync.getAccuracy()); | |
162 | } | |
163 | } | |
164 | } | |
165 | return fTree; | |
166 | } | |
167 | ||
168 | @Override | |
169 | public SyncQuality getSynchronizationQuality(ITmfTrace trace1, ITmfTrace trace2) { | |
170 | for (ConvexHull traceSync : fSyncs) { | |
171 | if (traceSync.isForHosts(trace1.getHostId(), trace2.getHostId())) { | |
172 | return traceSync.getQuality(); | |
173 | } | |
174 | } | |
175 | return SyncQuality.ABSENT; | |
176 | } | |
177 | ||
178 | @Override | |
179 | public boolean isTraceSynced(String hostId) { | |
180 | ITmfTimestampTransform t = getTimestampTransform(hostId); | |
181 | return !t.equals(TimestampTransformFactory.getDefaultTransform()); | |
182 | } | |
183 | ||
184 | @Override | |
185 | public Map<String, Map<String, Object>> getStats() { | |
186 | /* | |
187 | * TODO: Stats, while still accurate, may be misleading now that the | |
188 | * sync tree changes synchronization formula. The stats should use the | |
189 | * tree instead | |
190 | */ | |
191 | Map<String, Map<String, Object>> statmap = new LinkedHashMap<>(); | |
192 | for (ConvexHull traceSync : fSyncs) { | |
193 | statmap.put(traceSync.getReferenceHost() + " <==> " + traceSync.getOtherHost(), traceSync.getStats()); //$NON-NLS-1$ | |
194 | } | |
195 | return statmap; | |
196 | } | |
197 | ||
198 | @Override | |
199 | public String toString() { | |
200 | StringBuilder b = new StringBuilder(); | |
201 | b.append(getClass().getSimpleName() + " "); //$NON-NLS-1$ | |
202 | b.append(fSyncs); | |
203 | return b.toString(); | |
204 | } | |
205 | ||
206 | /** | |
207 | * This is the actual synchronization algorithm between two traces using | |
208 | * convex hull | |
209 | */ | |
210 | private class ConvexHull implements Serializable { | |
211 | ||
212 | private static final long serialVersionUID = 8309351175030935291L; | |
213 | ||
a1ff9910 GB |
214 | private final String fReferenceHost; |
215 | private final String fOtherHost; | |
216 | ||
217 | /** | |
218 | * Slopes and ordinate at origin of respectively fLmin, fLmax and the | |
219 | * bisector | |
220 | */ | |
221 | private BigDecimal fAlphamin, fBetamax, fAlphamax, fBetamin, fAlpha, fBeta; | |
222 | private int fNbMatches, fNbAccurateMatches; | |
223 | private SyncQuality fQuality; | |
224 | ||
51c08015 GB |
225 | /** |
226 | * The list of meaningful points on the upper hull (received by the | |
227 | * reference trace, below in a graph) | |
228 | */ | |
a1ff9910 | 229 | private transient LinkedList<SyncPoint> fUpperBoundList = new LinkedList<>(); |
51c08015 GB |
230 | /** |
231 | * The list of meaninful points on the lower hull (sent by the reference | |
232 | * trace, above in a graph) | |
233 | */ | |
a1ff9910 | 234 | private transient LinkedList<SyncPoint> fLowerBoundList = new LinkedList<>(); |
51c08015 GB |
235 | |
236 | /** Points forming the line with maximum slope */ | |
a1ff9910 | 237 | private transient SyncPoint[] fLmax = new SyncPoint[2]; |
51c08015 | 238 | /** Points forming the line with minimum slope */ |
a1ff9910 | 239 | private transient SyncPoint[] fLmin = new SyncPoint[2]; |
51c08015 | 240 | |
a1ff9910 | 241 | private transient Map<String, Object> fStats = new LinkedHashMap<>(); |
51c08015 GB |
242 | |
243 | /** | |
244 | * Initialization of the attributes | |
245 | * | |
246 | * @param host1 | |
247 | * ID of the first host | |
248 | * @param host2 | |
249 | * ID of the second host | |
250 | */ | |
251 | public ConvexHull(String host1, String host2) { | |
252 | if (host1.compareTo(host2) > 0) { | |
253 | fReferenceHost = host2; | |
254 | fOtherHost = host1; | |
255 | } else { | |
256 | fReferenceHost = host1; | |
257 | fOtherHost = host2; | |
258 | } | |
51c08015 GB |
259 | fAlpha = BigDecimal.ONE; |
260 | fAlphamax = BigDecimal.ONE; | |
261 | fAlphamin = BigDecimal.ONE; | |
262 | fBeta = BigDecimal.ZERO; | |
263 | fBetamax = BigDecimal.ZERO; | |
264 | fBetamin = BigDecimal.ZERO; | |
265 | fNbMatches = 0; | |
266 | fNbAccurateMatches = 0; | |
267 | fQuality = SyncQuality.ABSENT; // default quality | |
268 | } | |
269 | ||
270 | protected void processMatch(TmfEventDependency match) { | |
271 | ||
272 | LinkedList<SyncPoint> boundList, otherBoundList; | |
273 | ||
274 | SyncPoint[] line, otherLine; | |
275 | SyncPoint p; | |
276 | int inversionFactor = 1; | |
277 | boolean qualify = false; | |
278 | fNbMatches++; | |
279 | ||
280 | /* Initialize data depending on the which hull the match is part of */ | |
281 | if (match.getSourceEvent().getTrace().getHostId().compareTo(match.getDestinationEvent().getTrace().getHostId()) > 0) { | |
282 | boundList = fUpperBoundList; | |
283 | otherBoundList = fLowerBoundList; | |
284 | line = fLmin; | |
285 | otherLine = fLmax; | |
286 | p = new SyncPoint(match.getDestinationEvent(), match.getSourceEvent()); | |
287 | inversionFactor = 1; | |
288 | } else { | |
289 | boundList = fLowerBoundList; | |
290 | otherBoundList = fUpperBoundList; | |
291 | line = fLmax; | |
292 | otherLine = fLmin; | |
293 | p = new SyncPoint(match.getSourceEvent(), match.getDestinationEvent()); | |
294 | inversionFactor = -1; | |
295 | } | |
296 | ||
297 | /* | |
298 | * Does the message qualify for the hull, or is in on the wrong side | |
299 | * of the reference line | |
300 | */ | |
301 | if ((line[0] == null) || (line[1] == null) || (p.crossProduct(line[0], line[1]) * inversionFactor > 0)) { | |
302 | /* | |
303 | * If message qualifies, verify if points need to be removed | |
304 | * from the hull and add the new point as the maximum reference | |
305 | * point for the line. Also clear the stats that are not good | |
306 | * anymore | |
307 | */ | |
308 | fNbAccurateMatches++; | |
309 | qualify = true; | |
310 | removeUselessPoints(p, boundList, inversionFactor); | |
311 | line[1] = p; | |
312 | fStats.clear(); | |
313 | } | |
314 | ||
315 | /* | |
316 | * Adjust the boundary of the reference line and if one of the | |
317 | * reference point of the other line was removed from the hull, also | |
318 | * adjust the other line | |
319 | */ | |
320 | adjustBound(line, otherBoundList, inversionFactor); | |
321 | if ((otherLine[1] != null) && !boundList.contains(otherLine[0])) { | |
322 | adjustBound(otherLine, boundList, inversionFactor * -1); | |
323 | } | |
324 | ||
325 | if (qualify) { | |
326 | approximateSync(); | |
327 | } | |
328 | ||
329 | } | |
330 | ||
331 | /** | |
332 | * Calculates slopes and ordinate at origin of fLmax and fLmin to obtain | |
333 | * and approximation of the synchronization at this time | |
334 | */ | |
335 | private void approximateSync() { | |
336 | ||
337 | /** | |
338 | * Line slopes functions | |
339 | * | |
340 | * Lmax = alpha_max T + beta_min | |
341 | * | |
342 | * Lmin = alpha_min T + beta_max | |
343 | */ | |
344 | if ((fLmax[0] != null) || (fLmin[0] != null)) { | |
345 | /** | |
346 | * Do not recalculate synchronization after it is failed. We | |
347 | * keep the last not failed result. | |
348 | */ | |
349 | if (getQuality() != SyncQuality.FAIL) { | |
350 | BigDecimal alphamax = fLmax[1].getAlpha(fLmax[0]); | |
351 | BigDecimal alphamin = fLmin[1].getAlpha(fLmin[0]); | |
352 | SyncQuality quality = null; | |
353 | ||
354 | if ((fLmax[0] == null) || (fLmin[0] == null)) { | |
355 | quality = SyncQuality.APPROXIMATE; | |
356 | } | |
357 | else if (alphamax.compareTo(alphamin) > 0) { | |
358 | quality = SyncQuality.ACCURATE; | |
359 | } else { | |
360 | /* Lines intersect, not good */ | |
361 | quality = SyncQuality.FAIL; | |
362 | } | |
363 | /* | |
364 | * Only calculate sync if this match does not cause failure | |
365 | * of synchronization | |
366 | */ | |
367 | if (quality != SyncQuality.FAIL) { | |
368 | fAlphamax = alphamax; | |
369 | fBetamin = fLmax[1].getBeta(fAlphamax); | |
370 | fAlphamin = alphamin; | |
371 | fBetamax = fLmin[1].getBeta(fAlphamin); | |
372 | fAlpha = fAlphamax.add(fAlphamin).divide(BigDecimal.valueOf(2), fMc); | |
373 | fBeta = fBetamin.add(fBetamax).divide(BigDecimal.valueOf(2), fMc); | |
374 | } | |
375 | setQuality(quality); | |
376 | } | |
377 | } else if (((fLmax[0] == null) && (fLmin[1] == null)) | |
378 | || ((fLmax[1] == null) && (fLmin[0] == null))) { | |
379 | /* Either there is no upper hull point or no lower hull */ | |
380 | setQuality(SyncQuality.INCOMPLETE); | |
381 | } | |
382 | } | |
383 | ||
384 | /* | |
385 | * Verify if the line should be adjusted to be more accurate give the | |
386 | * hull | |
387 | */ | |
388 | private void adjustBound(SyncPoint[] line, LinkedList<SyncPoint> otherBoundList, int inversionFactor) { | |
389 | SyncPoint minPoint = null, nextPoint; | |
390 | boolean finishedSearch = false; | |
391 | ||
392 | /* | |
393 | * Find in the other bound, the origin point of the line, start from | |
394 | * the beginning if the point was lost | |
395 | */ | |
396 | int i = Math.max(0, otherBoundList.indexOf(line[0])); | |
397 | ||
398 | while ((i < otherBoundList.size() - 1) && !finishedSearch) { | |
399 | minPoint = otherBoundList.get(i); | |
400 | nextPoint = otherBoundList.get(i + 1); | |
401 | ||
402 | /* | |
403 | * If the rotation (cross-product) is not optimal, move to next | |
404 | * point as reference for the line (if available) | |
405 | * | |
406 | * Otherwise, the current minPoint is the minPoint of the line | |
407 | */ | |
408 | if (minPoint.crossProduct(nextPoint, line[1]) * inversionFactor > 0) { | |
409 | if (nextPoint.getTimeX() < line[1].getTimeX()) { | |
410 | i++; | |
411 | } else { | |
412 | line[0] = null; | |
413 | finishedSearch = true; | |
414 | } | |
415 | } else { | |
416 | line[0] = minPoint; | |
417 | finishedSearch = true; | |
418 | } | |
419 | } | |
420 | ||
421 | if (line[0] == null) { | |
422 | line[0] = minPoint; | |
423 | } | |
424 | ||
425 | /* Make sure point 0 is before point 1 */ | |
426 | if ((line[0] != null) && (line[0].getTimeX() > line[1].getTimeX())) { | |
427 | line[0] = null; | |
428 | } | |
429 | } | |
430 | ||
431 | /* | |
432 | * When a point qualifies to be in a hull, we verify if any of the | |
433 | * existing points need to be removed from the hull | |
434 | */ | |
435 | private void removeUselessPoints(final SyncPoint p, final LinkedList<SyncPoint> boundList, final int inversionFactor) { | |
436 | ||
437 | boolean checkRemove = true; | |
438 | ||
439 | while (checkRemove && boundList.size() >= 2) { | |
440 | if (p.crossProduct(boundList.get(boundList.size() - 2), boundList.getLast()) * inversionFactor > 0) { | |
441 | boundList.removeLast(); | |
442 | } else { | |
443 | checkRemove = false; | |
444 | } | |
445 | } | |
446 | boundList.addLast(p); | |
447 | } | |
448 | ||
449 | public ITmfTimestampTransform getTimestampTransform(String hostId) { | |
450 | if (hostId.equals(fOtherHost) && (getQuality() == SyncQuality.ACCURATE || getQuality() == SyncQuality.APPROXIMATE || getQuality() == SyncQuality.FAIL)) { | |
451 | /* alpha: beta => 1 / fAlpha, -1 * fBeta / fAlpha); */ | |
452 | return TimestampTransformFactory.createLinear(BigDecimal.ONE.divide(fAlpha, fMc), BigDecimal.valueOf(-1).multiply(fBeta).divide(fAlpha, fMc)); | |
453 | } | |
454 | return TimestampTransformFactory.getDefaultTransform(); | |
455 | } | |
456 | ||
457 | public SyncQuality getQuality() { | |
458 | return fQuality; | |
459 | } | |
460 | ||
461 | public BigDecimal getAccuracy() { | |
462 | return fAlphamax.subtract(fAlphamin); | |
463 | } | |
464 | ||
465 | public Map<String, Object> getStats() { | |
466 | if (fStats.size() == 0) { | |
467 | String syncQuality; | |
468 | switch (getQuality()) { | |
469 | case ABSENT: | |
470 | syncQuality = Messages.SyncAlgorithmFullyIncremental_absent; | |
471 | break; | |
472 | case ACCURATE: | |
473 | syncQuality = Messages.SyncAlgorithmFullyIncremental_accurate; | |
474 | break; | |
475 | case APPROXIMATE: | |
476 | syncQuality = Messages.SyncAlgorithmFullyIncremental_approx; | |
477 | break; | |
478 | case INCOMPLETE: | |
479 | syncQuality = Messages.SyncAlgorithmFullyIncremental_incomplete; | |
480 | break; | |
481 | case FAIL: | |
482 | default: | |
483 | syncQuality = Messages.SyncAlgorithmFullyIncremental_fail; | |
484 | break; | |
485 | } | |
486 | ||
487 | fStats.put(Messages.SyncAlgorithmFullyIncremental_refhost, fReferenceHost); | |
488 | fStats.put(Messages.SyncAlgorithmFullyIncremental_otherhost, fOtherHost); | |
489 | fStats.put(Messages.SyncAlgorithmFullyIncremental_quality, syncQuality); | |
490 | fStats.put(Messages.SyncAlgorithmFullyIncremental_alpha, fAlpha); | |
491 | fStats.put(Messages.SyncAlgorithmFullyIncremental_beta, fBeta); | |
492 | fStats.put(Messages.SyncAlgorithmFullyIncremental_ub, (fUpperBoundList.size() == 0) ? Messages.SyncAlgorithmFullyIncremental_NA : fUpperBoundList.size()); | |
493 | fStats.put(Messages.SyncAlgorithmFullyIncremental_lb, (fLowerBoundList.size() == 0) ? Messages.SyncAlgorithmFullyIncremental_NA : fLowerBoundList.size()); | |
494 | fStats.put(Messages.SyncAlgorithmFullyIncremental_accuracy, getAccuracy().doubleValue()); | |
495 | fStats.put(Messages.SyncAlgorithmFullyIncremental_nbmatch, (fNbMatches == 0) ? Messages.SyncAlgorithmFullyIncremental_NA : fNbMatches); | |
496 | fStats.put(Messages.SyncAlgorithmFullyIncremental_nbacc, (fNbAccurateMatches == 0) ? Messages.SyncAlgorithmFullyIncremental_NA : fNbAccurateMatches); | |
497 | fStats.put(Messages.SyncAlgorithmFullyIncremental_refformula, Messages.SyncAlgorithmFullyIncremental_T_ + fReferenceHost); | |
498 | fStats.put(Messages.SyncAlgorithmFullyIncremental_otherformula, fAlpha + Messages.SyncAlgorithmFullyIncremental_mult + Messages.SyncAlgorithmFullyIncremental_T_ + fReferenceHost + Messages.SyncAlgorithmFullyIncremental_add + fBeta); | |
499 | } | |
500 | return fStats; | |
501 | ||
502 | } | |
503 | ||
504 | public String getReferenceHost() { | |
505 | return fReferenceHost; | |
506 | } | |
507 | ||
508 | public String getOtherHost() { | |
509 | return fOtherHost; | |
510 | } | |
511 | ||
512 | public boolean isForHosts(String hostId1, String hostId2) { | |
513 | return ((fReferenceHost.equals(hostId1) && fOtherHost.equals(hostId2)) || (fReferenceHost.equals(hostId2) && fOtherHost.equals(hostId1))); | |
514 | } | |
515 | ||
a1ff9910 GB |
516 | private void readObject(ObjectInputStream stream) |
517 | throws IOException, ClassNotFoundException { | |
518 | stream.defaultReadObject(); | |
519 | ||
520 | /* Initialize transient fields */ | |
521 | fUpperBoundList = new LinkedList<>(); | |
522 | fLowerBoundList = new LinkedList<>(); | |
523 | fLmax = new SyncPoint[2]; | |
524 | fLmin = new SyncPoint[2]; | |
525 | fStats = new LinkedHashMap<>(); | |
51c08015 GB |
526 | } |
527 | ||
528 | @SuppressWarnings("nls") | |
529 | @Override | |
530 | public String toString() { | |
531 | StringBuilder b = new StringBuilder(); | |
532 | b.append("Between " + fReferenceHost + " and " + fOtherHost + " ["); | |
533 | b.append(" alpha " + fAlpha + " beta " + fBeta + " ]"); | |
534 | return b.toString(); | |
535 | } | |
536 | ||
537 | private void setQuality(SyncQuality fQuality) { | |
538 | this.fQuality = fQuality; | |
539 | } | |
540 | ||
541 | } | |
542 | ||
543 | /** | |
544 | * Private class representing a point to synchronize on a graph. The x axis | |
545 | * is the timestamp of the event from the reference trace while the y axis | |
546 | * is the timestamp of the event on the other trace | |
547 | */ | |
548 | private class SyncPoint { | |
549 | private final ITmfTimestamp x, y; | |
550 | ||
551 | public SyncPoint(ITmfEvent ex, ITmfEvent ey) { | |
552 | x = ex.getTimestamp(); | |
553 | y = ey.getTimestamp(); | |
554 | } | |
555 | ||
556 | public long getTimeX() { | |
557 | return x.getValue(); | |
558 | } | |
559 | ||
560 | /** | |
561 | * Calculate a cross product of 3 points: | |
562 | * | |
563 | * If the cross-product < 0, then p, pa, pb are clockwise | |
564 | * | |
565 | * If the cross-product > 0, then p, pa, pb are counter-clockwise | |
566 | * | |
567 | * If cross-product == 0, then they are in a line | |
568 | * | |
569 | * @param pa | |
570 | * First point | |
571 | * @param pb | |
572 | * Second point | |
573 | * @return The cross product | |
574 | */ | |
575 | public long crossProduct(SyncPoint pa, SyncPoint pb) { | |
576 | long cp = ((pa.x.getValue() - x.getValue()) * (pb.y.getValue() - y.getValue()) - (pa.y.getValue() - y.getValue()) * (pb.x.getValue() - x.getValue())); | |
577 | return cp; | |
578 | } | |
579 | ||
580 | /* | |
581 | * Gets the alpha (slope) between two points | |
582 | */ | |
583 | public BigDecimal getAlpha(SyncPoint p1) { | |
584 | if (p1 == null) { | |
585 | return BigDecimal.ONE; | |
586 | } | |
587 | BigDecimal deltay = BigDecimal.valueOf(y.getValue() - p1.y.getValue()); | |
588 | BigDecimal deltax = BigDecimal.valueOf(x.getValue() - p1.x.getValue()); | |
589 | if (deltax.equals(BigDecimal.ZERO)) { | |
590 | return BigDecimal.ONE; | |
591 | } | |
592 | return deltay.divide(deltax, fMc); | |
593 | } | |
594 | ||
595 | /* | |
596 | * Get the beta value (when x = 0) of the line given alpha | |
597 | */ | |
598 | public BigDecimal getBeta(BigDecimal alpha) { | |
599 | return BigDecimal.valueOf(y.getValue()).subtract(alpha.multiply(BigDecimal.valueOf(x.getValue()), fMc)); | |
600 | } | |
601 | ||
602 | @Override | |
603 | public String toString() { | |
604 | return String.format("%s (%s, %s)", this.getClass().getCanonicalName(), x, y); //$NON-NLS-1$ | |
605 | } | |
606 | } | |
607 | ||
51c08015 | 608 | } |