// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.JunctionChecker.junctionchecking;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import org.openstreetmap.josm.gui.progress.ProgressMonitor;
import org.openstreetmap.josm.plugins.JunctionChecker.datastructure.Channel;
import org.openstreetmap.josm.plugins.JunctionChecker.datastructure.ChannelDiGraph;
/**
* Testet, dass ein subgraph keine kleinere Junction enth�lt
* @author Jörg Possin, Simon Scheider
*/
public class JMinimality {
private boolean CheckMinimal = true;
private final ArrayList<Channel> E;
private final int[][] Grid;
private final ArrayList<Channel> OrEn;
private final ArrayList<Channel> OrEx;
private final int n;
private final List<List<Object>> L = new ArrayList<>(); //The list of columns to be sorted
private final HashSet<Channel> subgraph = new HashSet<>(); //The candidate subgraph to be tested
private ProgressMonitor pm;
private final boolean pmenabled;
private final ArrayList<HashSet<Channel>> junctions = new ArrayList<>();
private final boolean searchFirstJunction;
private final ArrayList<Channel> subJunction = new ArrayList<>();
private final JPrepare jprepare;
private boolean Check = false;
private Iterator<int[]> it;
public JMinimality(int[][] Grid, int n,
ArrayList<Channel> E,
ArrayList<Channel> entries,
ArrayList<Channel> exits,
ChannelDiGraph channeldigraph,
boolean junctionsearch) {
this.E = E;
this.n = n;
this.Grid = Grid;
this.OrEn = entries;
this.OrEx = exits;
this.pmenabled = false;
this.searchFirstJunction = junctionsearch;
this.jprepare = new JPrepare(channeldigraph);
}
public JMinimality(int[][] Grid, int n,
ArrayList<Channel> E,
ArrayList<Channel> entries,
ArrayList<Channel> exits,
ChannelDiGraph channeldigraph,
ProgressMonitor pm,
boolean junctionsearch) {
this.E = E;
this.n = n;
this.Grid = Grid;
this.OrEn = entries;
this.OrEx = exits;
this.pm = pm;
this.pmenabled = true;
this.searchFirstJunction = junctionsearch;
this.jprepare = new JPrepare(channeldigraph);
//this.jCheck= new JCheck();
}
public void GenerateSubcolumns() { //Generates all combinations of subcolumns in the grid
if (pmenabled) {
pm.setCustomText(tr("generate all combinations from entrie/exit candidates"));
}
Combination c = new Combination(Grid.length, n);
//EEovern = (int) Combination.Choose(Grid.length*Grid.length, n*n);
long ans = c.Choose(); //This is the number of subcolumns to be generated
int[][] v; // this is a column variable containing n y-index entries plus true false values (0/1)
List<Object> C; //The column is packed together with 2 indices into this variable
for (int i = 0; i < Grid.length; i++) {
int h = 0; //this is the index denoting the "n out of Grid.length"- combination, indicating a subcolumn of length n
do {
int missing = 0;
C = new ArrayList<>(3);
v = new int[n][2];
C.add(i); //the first position of column variable C is the column index
C.add(h); //the second is the entry-subset index
for (int t = 0; t < c.data.length; t++) {
if (Grid[(int) c.data[t]][i] == 0) {
missing++;
v[t][1] = 0; //false
} else {
v[t][1] = 1;
} //true
v[t][0] = (int) c.data[t]; //Write a y index of the combination into column
}
if (missing <= 1) { //If column has at most one missing entry
C.add(v); //insert column as the third position of column variable C
L.add(C); //Insert C in list to be ordered
}
h++; //Iterate through all subcolumns
if (h < ans) {
c = c.Successor(); //generate the next combination
}
} while (h < ans);
c = new Combination(Grid.length, n); //For each original column in the grid, generate new subcolumns
}
Collections.sort(L, new Comparator<List<Object>>() {
@Override
public int compare(List<Object> o1, List<Object> o2) {
return (Integer) o1.get(1) - (Integer) o2.get(1); //sort according to h index in each column
} });
}
public boolean IterateThroughKn() { //Iterates through all K_{n-1} subgrids of the Grid and tests them
if (L.size() == 0) {
return true;
}
if (pmenabled) {
pm.setTicksCount(L.size());
pm.setCustomText("Iterates through all K_{n-1} subgrids of the Grid and tests them");
}
Combination c;
Iterator<List<Object>> l = L.listIterator();
List<Object> C;
ArrayList<int[]> CandidateK = new ArrayList<>(n*n); //saves the candidate K_{n-1} in entry-exit pairs
long lindex = 0;
int h = 0;
int m = 0;
int[][] v;
int x_i;
int y_j;
int progressmonitorcounter = 1;
boolean mchanged = false;
boolean hchanged = false;
C = l.next();
do { //Loop over list of columns L
if (mchanged) {
C = l.next(); //Iterator in L
lindex++; //Index in L
if (hchanged) {
m = 1;
hchanged = false;
}
}
if ((Integer) C.get(1) == h && l.hasNext()) { //m counts the set of columns with index h
m++;
mchanged = true;
} else {
if (l.hasNext() == false) {
m++;
lindex++;
} //At the end of L, counter are set one up
c = new Combination(m, n);
long ans = c.Choose();
int missing = 0;
boolean smallerjunction = false;
for (int k = 0; k < ans; k++) { //Makes sure that subset of m columns contains an n*n subgrid, because ans = m over n would be 0 otherwise
for (int y = 0; y < n; y++) { //Iterates over all rows of subgrid k
missing = 0; //test = "";
for (int x = 0; x < c.data.length; x++) { //Iterates over all columns of subgrid k
x_i = ((Integer) L.get((int) (lindex-m+c.data[x])).get(0)); //columnindex in grid
v = ((int[][]) (L.get((int) (lindex-m+c.data[x])).get(2))); //subcolumn of grid
y_j = v[y][0]; //rowindex in grid
if (v[y][1] == 0) {
missing++;
} else {
CandidateK.add(new int[]{y_j, x_i});
} //save entry/exit tuple
if (!smallerjunction && ((!OrEn.contains(E.get(v[y][0])))
&& (!OrEx.contains(E.get(x_i))))) { // Tests, whether y or x is not an original entry/exit
smallerjunction = true; //Then k identifies a different junction than the original one
}
//test = test+" ("+y_j+", "+x_i+", "+v[y][1]+")";
}
if (missing > 1) {
break;
} //If a row has more than one missing value, break
}
if (missing <= 1 && smallerjunction) { //The k-subgrid is a different junction candidate satisfying total reachability
CheckMinimal = !CheckSmallJunction(CandidateK); // If the candidate is a smaller junction, then minimality is false
//log.info("durchlauf: " + durchlauf + " Wert von CheckMinimal: " + CheckMinimal);
if (!CheckMinimal) {
break;
}
}
CandidateK.clear();
if (k+1 < ans) {
c = c.Successor();
} //Produces the m over n combinations
}
m = 1; //Sets m to the first column with next index h+1
h++;
mchanged = false;
hchanged = true;
}
if (pmenabled) {
progressmonitorcounter++;
pm.setTicks(progressmonitorcounter);
}
}
while (l.hasNext() && CheckMinimal);
return CheckMinimal;
}
/**
* gibt true zurück, wenn Kandidat eine Kreuzung ist, aber nicht, wenn junctionsearch auf true gesetzt ist
*/
public boolean CheckSmallJunction(ArrayList<int[]> CandidateK) {
Check = false;
subgraph.clear(); //Zu konstruierender Subgraph
it = CandidateK.iterator();
//Reconstruct small Junction from paths
while (it.hasNext()) {
int[]point = it.next();
for (int j = 0; j < E.get(point[0]).getReachableNodes().size(); j++) {
if (E.get(point[0]).getReachableNodeAt(j).equals(E.get(point[1]))) {
subgraph.addAll(E.get(point[0]).getPathsAt(E.get(point[0]).getReachableNodeAt(j)));
subgraph.add(E.get(point[0]));
}
}
}
jprepare.jPrepare(new ArrayList<>(subgraph));
JCheck jCheck = new JCheck();
Check = jCheck.jCheck(jprepare.getEntries(), jprepare.getExits(), n);
jprepare.resetSubgraph();
if (Check) {
subJunction.clear();
subJunction.addAll(subgraph);
//soll mehr als ein Kandidat gesucht werden? Dann Kandidaten speichern und Check wieder auf false stellen, damit die Hauptschleife weitergeht
if (!searchFirstJunction) {
boolean isin = false;
for (int i = 0; i < junctions.size(); i++) {
//log.debug("Kreuzung " + i +" hat Channels: " + junctions.get(i).size() + " subgraph: " + subgraph.size());
if (junctions.get(i).size() == subgraph.size()) {
Iterator<Channel> it = subgraph.iterator();
isin = true;
while (it.hasNext()) {
if (!junctions.get(i).contains(it.next())) {
isin = false;
//log.info("nicht drin");
}
}
}
}
if (isin == false) {
junctions.add(new HashSet<>(subgraph));
//log.info("Kreuzungskandidat der Liste zugefügt" + junctions.size());
}
Check = false;
}
}
return Check;
}
/**
* enthält alle Channels des zuletzt durchlaufenden Kreuzungskandidaten (muß keine gültige Kreuzung sein)
*/
public ArrayList<Channel> getSubJunctionCandidate() {
return new ArrayList<>(subgraph);
}
/**
* gibt alle gefundenen Kreuzungskandidaten zurück (ist leer, wenn junctionsearch auf true gesetzt wurde)
*/
public ArrayList<HashSet<Channel>> getJunctionCandidates() {
return junctions;
}
}