package de.wwwu.awolf.presenter.util; import com.google.common.collect.Lists; import de.wwwu.awolf.model.Line; import de.wwwu.awolf.model.Point; import java.util.*; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.concurrent.RecursiveTask; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; /** * Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden. * * @Author: Armin Wolf * @Email: a_wolf28@uni-muenster.de * @Date: 18.09.2017. */ public class IntersectionComputer { private static IntersectionComputer instance; /** * Konstruktor */ private IntersectionComputer() { } public static IntersectionComputer getInstance() { if (instance == null) { instance = new IntersectionComputer(); Logging.logInfo("Created instance of IntersectionComputer"); } return instance; } /** * Berechnet zu einer gegebenen Menge von dualen Geraden die Schnittpunkte. Dafür wird ein modifizierter Merge-Sort * Algorithmus verwendett. Um die Performance zu steigern wird die Berechnung ab einer passenden Größe auf vier * Threads ausgelagert. * * @param lower untere Schranke * @param higher obere Schranke * @return Liste der Schnittpunkte */ public Set compute(final Collection lines, final double lower, final double higher) { final Set fullInput = new HashSet<>(lines); final Set copyInput = new HashSet<>(lines); if (lower == higher) { return Collections.emptySet(); } else { Logging.logDebug("Open ForkJoinPool: lines: " + lines.size() + " I(" + lower + ", " + higher + "]"); ForkJoinPool pool = ForkJoinPool.commonPool(); RecursiveComputationTask recursiveComputationTask = new RecursiveComputationTask(fullInput, copyInput, lower, higher); pool.execute(recursiveComputationTask); Set join = recursiveComputationTask.join(); return join; } } /** * Berechnet die Schnittpunkte zwischen einer gegebenen Gerade und einer Menge an Geraden. * * @param set Menge an Geraden * @param sampledLine eine spezielle Gerade * @return Liste mit x Koordinaten der Schnittpunkte */ public List calculateIntersectionAbscissas(Collection set, Line sampledLine, double lower, double upper) { List lines = new LinkedList<>(set); Set intersections = new HashSet<>(); for (Line line : lines) { if (line != sampledLine) { if (sampledLine.doIntersect(line, lower, upper)) { intersections.add(sampledLine.intersect(line).getX()); } } } return new ArrayList<>(intersections); } private class RecursiveComputationTask extends RecursiveTask> { private static final int THRESHOLD = 200; private final Set lines; private final Set fullList; private final double lower; private final double upper; public RecursiveComputationTask(final Set fullList, final Set lines, final double lower, final double upper) { this.lines = lines; this.fullList = fullList; this.lower = lower; this.upper = upper; } @Override protected Set compute() { if (this.lines.isEmpty()) { return Collections.emptySet(); } else if (this.lines.size() > THRESHOLD) { return ForkJoinTask.invokeAll(createSubTask()).stream().map(ForkJoinTask::join).flatMap(Collection::stream).collect(Collectors.toSet()); } else { return work(this.fullList, this.lines, this.lower, this.upper); } } private Collection createSubTask() { List dividedTasks = new ArrayList<>(); long midpoint = Math.round(this.lines.size() * 0.5); Set firstSubSet = new HashSet<>(); Set secondSubSet = new HashSet<>(); AtomicInteger count = new AtomicInteger(); this.lines.forEach(next -> { int index = count.getAndIncrement(); if (index < midpoint) { firstSubSet.add(next); } else { secondSubSet.add(next); } }); dividedTasks.add(new RecursiveComputationTask(this.fullList, firstSubSet, this.lower, this.upper)); dividedTasks.add(new RecursiveComputationTask(this.fullList, secondSubSet, this.lower, this.upper)); return dividedTasks; } private Set work(Set fullList, Set lines, double lower, double higher) { Set points = new HashSet<>(); List> lists = Lists.cartesianProduct(new ArrayList<>(fullList), new ArrayList<>(lines)); lists.forEach(entry -> { if (entry.get(0).doIntersect(entry.get(1), lower, upper)) { Point intersect = entry.get(0).intersect(entry.get(1)); if (intersect.getX() > lower && intersect.getX() <= higher) { points.add(intersect); } } }); return new HashSet<>(points); } } }