/**
 **
 ** PairsGraph - Process graph of the pairwise connected scenes for
 ** the global map generation. 
 **
 ** Copyright (C) 2024 Elphel, Inc.
 **
 ** -----------------------------------------------------------------------------**
 **
 **  PairsGraph.java is free software: you can redistribute it and/or modify
 **  it under the terms of the GNU General Public License as published by
 **  the Free Software Foundation, either version 3 of the License, or
 **  (at your option) any later version.
 **
 **  This program is distributed in the hope that it will be useful,
 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 **  GNU General Public License for more details.
 **
 **  You should have received a copy of the GNU General Public License
 **  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ** -----------------------------------------------------------------------------**
 **
 */

package com.elphel.imagej.orthomosaic;

import java.util.HashSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import com.elphel.imagej.common.ShowDoubleFloatArrays;
import com.elphel.imagej.tileprocessor.ImageDtt;

public class PairsGraph {
	public static final int PAIR_NONE =      0;
	public static final int PAIR_DEFINED =   1;
	public static final int PAIR_UNDEFINED = 2;
	public static final int PAIR_FAILED =   -1;
	
	public static final int HEUR_LAST_SEQ =  1; // use last of connected following this  
	public static final int HEUR_DIV_LONG =  2; // divide long connected series  
	public static final int HEUR_MIN_DIA =   4; // minimal diameter increase (or no increase)  
	public static final int HEUR_SAME_DIA =  8; // minimal diameter increase (or no increase)
	
	private final OrthoMapsCollection orthoMapsCollection; 
	private final int    []   indices;
	private final int    [][] pair_state;
	private final double [][] overlaps;
	private final double []   timestamps;
	private final int    []   groups;
	private final int    [][] dist;
	private int          [][]  pairs;
	private final int         debugLevel;
	private boolean           multi; // use single-threaded in debug mode?

	
	public PairsGraph (
			OrthoMapsCollection orthoMapsCollection,
			int []  indices,
			double  min_overlap_frac,
			double  max_rmse_reuse,
			boolean multi,
			int     debugLevel) {
		this.orthoMapsCollection = orthoMapsCollection;
		this.indices =             indices;
		this.debugLevel =          debugLevel;
		this.multi =               multi;
		pair_state =               new int [indices.length][indices.length];
		overlaps =                 new double [indices.length][indices.length];
		timestamps =               new double [indices.length];
		groups =                   new int [indices.length];
		dist =                     new int [indices.length][indices.length];
		OrthoMap []                ortho_maps=orthoMapsCollection.getMaps();
		for (int i = 0; i < indices.length; i++) {
			groups[i] = i;
			timestamps[i] = ortho_maps[indices[i]].getTimeStamp();
		}
		int num_defined = 0;
		int num_undefined = 0;
		int num_bad = 0;
		int num_low_overlap = 0;
		for (int i = 0; i < indices.length-1; i++) {
			for (int j = i+1; j < indices.length; j++){
				String name2 = ortho_maps[indices[j]].getName();
				PairwiseOrthoMatch match = ortho_maps[indices[i]].getMatch(name2,true);
				if (match != null) {
					if (match.getOverlap() < min_overlap_frac){
						num_low_overlap++;			
					} else {
						if (match.isDefined()) {
							if ((match.getOverlap() < min_overlap_frac) || ((max_rmse_reuse > 0) && !(match.getRMS() <= max_rmse_reuse))) {
								pair_state[i][j] = PAIR_UNDEFINED;
								num_undefined++;
								num_bad++;
							} else {
								pair_state[i][j] = PAIR_DEFINED;
								num_defined++;
								recordPair(
										new int [] {i,j}, // int []   pair,
										true,             // boolean  success,
										multi);
							}

						} else {
							pair_state[i][j] = PAIR_UNDEFINED;
							num_undefined++;
						}
						overlaps[i][j] = match.getOverlap();
					}
				}
			}
		}
		pairs = multi? getPairsIndicesMulti():getPairsIndicesSingle();
		if (debugLevel > -1) {
			System.out.println("Number of scenes - "+indices.length);
			System.out.println("Number of defined pairs - "+num_defined);
			System.out.println("Number of undefined pairs - "+num_undefined);
			System.out.println("Number of low overlap pairs ("+min_overlap_frac+") - "+num_low_overlap);
			System.out.println("Number of bad rms pairs ("+max_rmse_reuse+") - "+num_bad);
			System.out.println("Number of disconnected groups - "+getNumberOfGroups());
		}
	}
	
	
	public PairsGraph (
			OrthoMapsCollection orthoMapsCollection,
			int [][] undefined_pairs_all, // indices in orthoMapsCollection.ortho_maps[] (all scenes, not just selected)
			int [][] defined_pairs_all,   // indices in orthoMapsCollection.ortho_maps[] (all scenes, not just selected)
			boolean multi,
			int     debugLevel) {
		this.orthoMapsCollection = orthoMapsCollection;
		int [] indices0 =          orthoMapsCollection.getScenesFromPairs(undefined_pairs_all, null);
		this.indices =             orthoMapsCollection.getScenesFromPairs(defined_pairs_all, indices0);
		int [][] undefined_pairs = orthoMapsCollection.condensePairs (undefined_pairs_all,indices);
		int [][] defined_pairs =   orthoMapsCollection.condensePairs (defined_pairs_all,indices);
		this.debugLevel =          debugLevel;
		this.multi =               multi;
		pair_state =               new int [indices.length][indices.length];
		overlaps =                 new double [indices.length][indices.length];
		timestamps =               new double [indices.length];
		groups =                   new int [indices.length];
		dist =                     new int [indices.length][indices.length];
		OrthoMap []                ortho_maps=orthoMapsCollection.getMaps();
		for (int i = 0; i < indices.length; i++) {
			groups[i] = i;
			timestamps[i] = ortho_maps[indices[i]].getTimeStamp();
		}
		
		for (int [] pair: undefined_pairs) {
			pair_state[pair[0]][pair[1]] = PAIR_UNDEFINED;
			PairwiseOrthoMatch match = ortho_maps[indices[pair[0]]].getMatch(ortho_maps[indices[pair[1]]].getName(),true);
			overlaps[pair[0]][pair[1]] = match.getOverlap();

		}
		for (int [] pair: defined_pairs) {
			pair_state[pair[0]][pair[1]] = PAIR_DEFINED;
			PairwiseOrthoMatch match = ortho_maps[indices[pair[0]]].getMatch(ortho_maps[indices[pair[1]]].getName(),true);
			overlaps[pair[0]][pair[1]] = match.getOverlap();
			recordPair(
					pair, // int []   pair,
					true,             // boolean  success,
					multi);
		}
		pairs = multi? getPairsIndicesMulti():getPairsIndicesSingle();
		if (debugLevel > -1) {
			System.out.println("Number of scenes - "+indices.length);
			System.out.println("Number of defined pairs - "+defined_pairs.length);
			System.out.println("Number of undefined pairs - "+undefined_pairs.length);
			System.out.println("Number of disconnected groups - "+getNumberOfGroups());
		}
	}
	
	public int getNumberOfGroups() {
		HashSet<Integer> hs = new HashSet<Integer>();
		for (int i:groups) {
			hs.add(i);
		}
		return hs.size();
	}
	
	public int [] getIndices() {
		return indices;
	}

	public boolean useMultiThreaded() {
		return multi;
	}
	
	public double getOverlap(
			int [] pair) {
		return overlaps[Math.min(pair[0],pair[1])][Math.max(pair[0],pair[1])];
	}
	public boolean dryRun(
			int heur,
			int debugLevel) {
		int num_pairs = 0;
		int ok_dist = 1;
		int [] start_ij = {0,0};
		int [] next_pair;
		while (!isSingleConnected()) {
			next_pair = suggestPairSingle( // single-connected, no cycles
					// add some parameters
					heur, // int         heur,
					ok_dist, // int         ok_dist)
					start_ij, // int []      start_ij) //  = new int [indices.length];
					multi,
					debugLevel);
			if (next_pair == null) {
				break;
			}
			if (next_pair[2] > HEUR_LAST_SEQ) {
				heur &= ~HEUR_LAST_SEQ;
			}
			if (next_pair[2] > HEUR_DIV_LONG) {
				heur &= ~HEUR_DIV_LONG;
			}
			if (next_pair.length > 3) {
				ok_dist = next_pair[3];
				start_ij[0] = next_pair[0];
				start_ij[1] = next_pair[1];
				System.out.println(String.format("%3d: %3d-%3d %2d (%3d)", num_pairs,next_pair[0], next_pair[1],next_pair[2],next_pair[3]));
				
			} else {
				System.out.println(String.format("%3d: %3d-%3d %2d", num_pairs,next_pair[0], next_pair[1],next_pair[2]));
			}
			recordPair(
					next_pair, // int []   pair,
					true,      // boolean  success,
					multi);
			num_pairs++;	
		}
		String [] titles = {"overlap", "state", "dist"};
		double [][] dbg_img = new double [titles.length][indices.length*indices.length];
		for (int i = 0; i < indices.length; i++) {
			for (int j = 0; j < indices.length; j++) {
				int indx = i * indices.length+ j;
				dbg_img[0][indx] = overlaps[i][j];
				dbg_img[1][indx] = pair_state[i][j];
				dbg_img[2][indx] = dist[i][j];
			}
		}
		ShowDoubleFloatArrays.showArrays(
				dbg_img,
				indices.length,
				indices.length,
				true,
				"dry_run.tiff",
				titles);
		
		return true;
	}
	
	public int [] suggestPairSingle( // single-connected, no cycles
			// add some parameters
			int     heur,
			int     ok_dist,
			int []  start_ij,
			boolean multi,
			int     debugLevel) {
		int [] rslt = null;
		if (isSingleConnected()) {
			return null;
		}
		if ((heur & HEUR_LAST_SEQ) != 0) {// try last in connected sequence - it should not yet be the same group
			rslt = coverLongSeq();
			if (rslt != null) {
				return rslt;
			}
		}
		if ((heur & HEUR_DIV_LONG) != 0) {// Try middle of the long connected series
			rslt = getMidSeq();
			if (rslt != null) {
				return rslt;
			}
		}		
		if ((heur & (HEUR_MIN_DIA | HEUR_SAME_DIA)) != 0) { // minimal diameter increase (or no increase)
			boolean use_min_dia = (heur & HEUR_MIN_DIA) != 0;
			boolean use_same_dia = (heur & HEUR_SAME_DIA) != 0;
			if (multi) {
				rslt = getByDiameterMulti(
						use_min_dia,  // final boolean use_min_dia,
						use_same_dia, // final boolean use_same_dia,
						start_ij,     // final int []  start_ij,
						ok_dist,      // final int     ok_dist,
						debugLevel);  // final int     debugLevel) {
			} else {
				rslt = getByDiameterSingle(
						use_min_dia,  // final boolean use_min_dia,
						use_same_dia, // final boolean use_same_dia,
						start_ij,     // final int []  start_ij,
						ok_dist,      // final int     ok_dist,
						debugLevel);  // final int     debugLevel) {
			}
			if (rslt != null) {
				return rslt;
			}
		}		
		return rslt;
	}	
	
	
	public boolean isSingleConnected() {
		int i0 = 1;
		int g0 = groups[0];
		for (; i0 < groups.length; i0++) if (groups[i0] !=g0){
			return false;
		}
		return true; // already single-connected
	}
	
	public int [] coverLongSeq() {
		for (int i = 0; i < (groups.length-1); i++) {
			if (groups[i+1] != groups[i]) {
				if ((pair_state[i][i+1] != 0) && ((i == 0) || (pair_state[i-1][i] == 0))) { // next is somehow connected, but previous is not
					int j = i+1;
					for (; (j < groups.length) && (pair_state[i][j] != 0); j++);
					j--; // now j - last connected. Go backward if last connected failed
					for (; j > i; j--) {
						if (pair_state[i][j] != PAIR_FAILED) {
							if (groups[j] != groups[i]) {
								return new int [] {i,j,HEUR_LAST_SEQ};
							}
							break; // for (; j > i; j--) { : already connected 
						}
					}
				}
			}
		}
		return null;
	}
	
	public int [] getMidSeq() { // asymmetrical - center, then center of the first half, first quarter, ...
		for (int i = 0; i < (groups.length-1); i++) {
			if (groups[i+1] != groups[i]) {
				if ((pair_state[i][i+1] != 0) && ((i == 0) || (pair_state[i-1][i] == 0))) { // next is somehow connected, but previous is not
					
					int j = i+1;
					int num_cand=0;
					for (; (j < groups.length) && (pair_state[i][j] != 0) && (groups[j] != groups[i]); j++) {
						if (pair_state[i][j] == PAIR_UNDEFINED) num_cand++;
					}
					if (num_cand > 0) {
						j--; // now j - last connected. Go backward if last connected failed
						// find the middle
						int kbest = -1;
						double best = -1;
						for (int k = i+1; k <= j; k++) if (pair_state[i][k] == PAIR_UNDEFINED){
							double d = Math.min(timestamps[k]-timestamps[i], timestamps[j]-timestamps[k]);
							if (d > best) {
								kbest = k;
								best = d;
							}
						}
						return new int [] {i,kbest,HEUR_DIV_LONG};
					}
				}
			}
		}
		return null;
	}
	
	
	public int [] getByDiameterMulti(
			final boolean use_min_dia,
			final boolean use_same_dia,
			final int []  start_ij,
			final int     ok_dist,
			final int     debugLevel) {
		final boolean [] tried = new boolean [pairs.length];
		final Thread[] threads = ImageDtt.newThreadArray();
		final AtomicInteger ai = new AtomicInteger(0);
		final AtomicInteger abest_dia= new AtomicInteger(pair_state.length);
		final AtomicBoolean ago = new AtomicBoolean(true);
		final int [][] best_row_col= new int [pair_state.length+1][];
//		final boolean use_min_dia = (heur & HEUR_MIN_DIA) != 0;
//		final boolean use_same_dia = (heur & HEUR_SAME_DIA) != 0;
		final int [] same_row_col = {-1,-1};
		// find minimal diameter or first that does not increase it
		for (int pass = 0; pass <2; pass++) {
			if (pass == 0) {
				for (int n = 0; n < pairs.length; n++) if ((pairs[n] != null) && (pairs[n][0]>=start_ij[0])){
					if ((pairs[n][0] >start_ij[0]) || (pairs[n][1] > start_ij[1])) {
						ai.set(n);
						break;
					}
				}
			} else {
				ai.set(0); // start from the very beginning on pass 1
			}
			for (int ithread = 0; ithread < threads.length; ithread++) {
				threads[ithread] = new Thread() {
					public void run() {
						for (int n = ai.getAndIncrement(); n < pairs.length; n = ai.getAndIncrement()) if (ago.get() && (pairs[n] != null) && !tried[n]){
							tried[n] = true; // for the second pass
							int row = pairs[n][0];
							int col = pairs[n][1];
							if ((pair_state[row][col] == PAIR_UNDEFINED) && (groups[row] != groups[col])) {
								int [] dias = newDiameterSingle(pairs[n]);
								int d = dias[0];
								if (use_min_dia && (d <= ok_dist)) {
									ago.set(false); // got it, other threads may finish
								}
								int was_min = abest_dia.getAndAccumulate(dias[0], Math::min);
								if (was_min > d) { // I set it
									best_row_col[d] = pairs[n];
								}
								if (use_same_dia && (dias[0] <= dias[1])) {
									boolean i_stopped = ago.getAndSet(false);
									if (i_stopped) {
										same_row_col[0] = pairs[n][0];
										same_row_col[1] = pairs[n][1];
									}
								}
							}
						}
					}
				};
			}		      
			ImageDtt.startAndJoin(threads);
			if (!ago.get()) { // was stopped after condition met (on any pass)
				if (same_row_col[0] >= 0) {
					return new int [] {same_row_col[0],same_row_col[1], HEUR_SAME_DIA};
				} else {
					int [] row_col = best_row_col[abest_dia.get()];
					return new int [] {row_col[0],row_col[1], HEUR_MIN_DIA, ok_dist}; // keep same distance 
				}
			}
		} // for (int pass = 0; pass <2; pass++)
		if (use_min_dia) {
			int [] row_col = best_row_col[abest_dia.get()];
			return new int [] {row_col[0],row_col[1], HEUR_MIN_DIA, abest_dia.get()}; // update (increase) best distance 
		}
		return null;
	}
	

	public int [] getByDiameterSingle(
			boolean use_min_dia,
			boolean use_same_dia,
			int []  start_ij,
			int     ok_dist,
			int     debugLevel) {
		boolean [] tried = new boolean [pairs.length];
		int [] best_row_col= null;
		int [] same_row_col = null;
		// find minimal diameter or first that does not increase it
		int start_indx=0;
		boolean ago= true;
		int best_dia = pair_state.length;
		for (int pass = 0; pass <2; pass++) {
			if (pass == 0) {
				for (int n = 0; n < pairs.length; n++) if ((pairs[n] != null) && (pairs[n][0]>=start_ij[0])){
					if ((pairs[n][0] >start_ij[0]) || (pairs[n][1] > start_ij[1])) {
						start_indx = n;
						break;
					}
				}
			} else {
				start_indx = 0;
			}

			for (int n = start_indx; n < pairs.length; n++) if ((pairs[n] != null) && !tried[n]){
				tried[n] = true; // for the second pass
				int row = pairs[n][0];
				int col = pairs[n][1];
				if ((pair_state[row][col] == PAIR_UNDEFINED) && (groups[row] != groups[col])) {
					int [] dias = newDiameterSingle(pairs[n]);
					int d = dias[0];
					if (d < best_dia) {
						best_dia = d;
						best_row_col = pairs[n];
					}
					if (use_min_dia && (d <= ok_dist)) {
						ago = false; // got it, other threads may finish
						break;
					}
					if (use_same_dia && (dias[0] <= dias[1])) {
						same_row_col = pairs[n];
					}
				}
			}
			if (!ago) { // was stopped after condition met (on any pass)
				if (same_row_col != null) {
					return new int [] {same_row_col[0],same_row_col[1], HEUR_SAME_DIA};
				} else {
					return new int [] {best_row_col[0],best_row_col[1], HEUR_MIN_DIA, ok_dist}; // keep same distance 
				}
			}
		} // for (int pass = 0; pass <2; pass++)
		if (use_min_dia) {
			return new int [] {best_row_col[0],best_row_col[1], HEUR_MIN_DIA, best_dia}; // update (increase) best distance 
		}
		return null;
	}
	
	public int [] newDiameterMulti( // or return a pair - new/old?
			final int [] pair) { // symmetrical
		final int i = pair[0], j = pair[1];
		final Thread[] threads = ImageDtt.newThreadArray();
		final AtomicInteger ai = new AtomicInteger(0);
		final AtomicInteger amax_new = new AtomicInteger(0);
		final AtomicInteger amax_old = new AtomicInteger(0);
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				public void run() {
					for (int n = ai.getAndIncrement(); n < dist.length; n = ai.getAndIncrement()) if ((dist[i][n] > 0) || (n==i)){
						int d1 = dist[i][n]+1;
						amax_new.getAndAccumulate(d1, Math::max);
						int mx = d1-1;
						for (int m = 0; m < dist.length; m++) if (dist[j][m] > 0){
							int d = d1+ dist[j][m];
							amax_new.getAndAccumulate(d, Math::max);
							mx = Math.max(mx, dist[j][m]);
						}
						amax_old.getAndAccumulate(mx, Math::max);
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		return new int [] {amax_new.get(),amax_old.get()};
	}
	
	public int [] newDiameterSingle( // or return a pair - new/old?
			final int [] pair) {
		final int i = pair[0], j = pair[1];
		int max_new = 0, max_old = 0;
		for (int n = 0; n < dist.length; n++) if ((dist[i][n] > 0) || (n==i)){
			int d1 = dist[i][n]+1;
			max_new = Math.max(max_new, d1);
			max_old = Math.max(max_old, d1-1);
			for (int m = 0; m < dist.length; m++) if (dist[j][m] > 0){
				int d = d1+ dist[j][m];
				max_new = Math.max(max_new, d);
				max_old = Math.max(max_old, dist[j][m]);
			}			
		}
		return new int [] {max_new,max_old};
	}
	
	
	
	public int [][] getPairsIndicesSingle(){
		int n = pair_state.length;
		int n2 = n * n;
		int [][] pairs0 = new int[n * (n-1) / 2][];
		int indx = 0;
		for (int i = 0; i < n2; i++){
			int row = i / n;
			int col = i % n;
			if ((col > row ) && (groups[col] != groups[row]) && (pair_state[row][col] == PAIR_UNDEFINED)) {
				pairs0[indx++] = new int[] {row,col};
			}
		}
		final int [][] pairs = new int [indx][2];
		for (int i = 0; i < pairs.length; i++){
			pairs[i][0] = pairs0[i][0]; 
			pairs[i][1] = pairs0[i][1]; 
		}
		return pairs;
	}

	public int [][] getPairsIndicesMulti(){
		final int n = pair_state.length;
		final int n2 = n * n;
		final int [][] pairs0 = new int[n * (n-1) / 2][];
		final Thread[] threads = ImageDtt.newThreadArray();
		final AtomicInteger ai = new AtomicInteger(0);
		final AtomicInteger aindx = new AtomicInteger(0);
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				public void run() {
					for (int i = ai.getAndIncrement(); i < n2; i = ai.getAndIncrement()){
						int row = i / n;
						int col = i % n;
						if ((col > row ) && (groups[col] != groups[row]) && (pair_state[row][col] == PAIR_UNDEFINED)) {
							int indx = aindx.getAndIncrement();
							pairs0[indx] = new int[] {row,col};
						}
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		ai.set(0);
		final int [][] pairs = new int [aindx.get()][2];
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				public void run() {
					for (int i = ai.getAndIncrement(); i < pairs.length; i = ai.getAndIncrement()){
						pairs[i][0] = pairs0[i][0]; 
						pairs[i][1] = pairs0[i][1]; 
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		return pairs;
	}

	
	public void recordPair(
			int []   pair,
			boolean  success,
			boolean  multi) { // multithreaded mode

		int i = pair[0], j = pair[1];
		if (success) {
			pair_state[i][j] = PAIR_DEFINED;
			// merge groups
			if (groups[i] != groups[j]) {
				int g0 = Math.min(groups[i], groups[j]);
				int g1 = Math.max(groups[i], groups[j]);
				for (int k = 0; k < groups.length; k++) {
					if (groups[k] == g1) {
						groups[k] = g0;
					}
				}
			}
			if (multi) {
				updateDistanceMulti(pair);
			} else {
				updateDistanceSingle(pair);
			}
		} else {
			pair_state[i][j] = PAIR_FAILED;
		}
	}	
	
	
	
	public void updateDistanceMulti(
			final int [] pair) { // symmetrical
		final int i = pair[0], j = pair[1];
		final int [][] dist2 = {dist[i].clone(),dist[j].clone()};
		final Thread[] threads = ImageDtt.newThreadArray();
		final AtomicInteger ai = new AtomicInteger(0);
		for (int ithread = 0; ithread < threads.length; ithread++) {
			threads[ithread] = new Thread() {
				public void run() {
					// pair should be disconnected, so n from one thread != m from the other
					for (int n = ai.getAndIncrement(); n < dist.length; n = ai.getAndIncrement()) if ((dist2[0][n] > 0) || (n == i)){
						int d1 = dist2[0][n]+1;
						for (int m = 0; m < dist.length; m++) if ((dist2[1][m] > 0) || ((m == j) && (n != i))){
							int d = d1+ dist2[1][m];
							dist[n][m] = d;
							dist[m][n] = d;
						}
					}
				}
			};
		}		      
		ImageDtt.startAndJoin(threads);
		dist[i][j] = 1;
		dist[j][i] = 1;
		return;
	}

	public void updateDistanceSingle(
			final int [] pair) { // symmetrical
		final int i = pair[0], j = pair[1];
		final int [][] dist2 = {dist[i].clone(),dist[j].clone()};
		for (int n = 0; n < dist.length; n++) if ((dist2[0][n] > 0) || (n == i)){
			int d1 = dist2[0][n]+1;
			for (int m = 0; m < dist.length; m++) if ((dist2[1][m] > 0) || ((m == j) && (n != i))){
				int d = d1+ dist2[1][m];
				dist[n][m] = d;
				dist[m][n] = d;
			}
		}
		dist[i][j] = 1;
		dist[j][i] = 1;
		return;
	}

	
	
}
