content: /*
   * Copyright 2010 Aalto University, ComNet
   * Released under GPLv3. See LICENSE.txt for details.
   */
  package test;
  
  import input.WKTMapReader;
  
  import java.io.File;
  import java.io.FileWriter;
  import java.io.IOException;
  import java.io.PrintWriter;
  import java.io.StringReader;
  import java.util.Random;
  
  import junit.framework.TestCase;
  import movement.map.MapNode;
  import movement.map.PointsOfInterest;
  import movement.map.SimMap;
  import core.Coord;
  import core.Settings;
  
  /**
   * Test for selecting Points Of Interest from different node groups.
   * Tests that the probability of getting a node from a specific group is
   * approximately same as the defined probability.
   */
  public class PointsOfInterestTest extends TestCase {
  
  	/* Topology:  n7--n5
  	 *            |   |
  	 *        n1--n2--n6--n3
  	 *         |
  	 *        n4
  	 */
  	private static final String MAP_DATA =
  	//				   n1       n2       n6        n3
  		"LINESTRING (1.0 1.0, 2.0 1.0, 3.0 1.0, 4.0 1.0) \n" +
  	//              n1        n4
  	"LINESTRING (1.0 1.0, 1.0 2.0)\n";
  
  	private static final String MAP_DATA2 =
  	//              n2       n7       n5       n6
  	"LINESTRING (2.0 1.0, 2.0 0.0, 3.0 0.0, 3.0 1.0)\n";
  
  	private static final String[] POINTS_IN_MAP = {
  		"POINT (1.0 1.0)\n POINT (2.0 1.0)",
  		"POINT (4.0 1.0)\n POINT (1.0 2.0)",
  		"POINT (3.0 1.0)\n"
  		};
  
  	private static final Coord[][] COORDS_IN_MAP = {
  		{new Coord(1,1), new Coord(2,1)},
  		{new Coord(4,1), new Coord(1,2)},
  		{new Coord(3,1)}
  		};
  
  	private PointsOfInterest pois;
  	private int nrofMapNodes;
  
  	protected void setUpWith(double[] poiProbs, int rngSeed, int [] okNodes)
  			throws Exception {
  		super.setUp();
  
  		Settings.init(null);
  		StringReader input = new StringReader(MAP_DATA);
  
  		WKTMapReader reader = new WKTMapReader(true);
  		try {
  			reader.addPaths(input, 1);
  			input = new StringReader(MAP_DATA2);
  			reader.addPaths(input, 2);
  		} catch (IOException e) {
  			fail(e.toString());
  		}
  
  		SimMap map = reader.getMap();
  		this.nrofMapNodes = map.getNodes().size();
  
  		File[] poiFiles = new File[POINTS_IN_MAP.length];
  		for (int i=0; i<POINTS_IN_MAP.length; i++) {
  			File poiFile = File.createTempFile("poifile"+i, ".tmp");
  			poiFile.deleteOnExit();
  			PrintWriter out = new PrintWriter(new FileWriter(poiFile));
  			out.println(POINTS_IN_MAP[i]);
  			out.close();
  			poiFiles[i] = poiFile;
  		}
  
  		TestSettings s = new TestSettings();
  		String ns = PointsOfInterest.POI_NS;
  		String fset = PointsOfInterest.POI_FILE_S;
  
  		for (int i=0; i<poiFiles.length; i++) {
  			s.putSetting(ns+"."+fset+i, fixFile(poiFiles[i].getAbsolutePath()));
  		}
  
  		String probSet = "";
  		for (int i=0; i<poiProbs.length; i++) {
  			probSet += i + "," + poiProbs[i];
  			if (i<poiProbs.length-1) {
  				probSet += ",";
  			}
  		}
  
  		s.putSetting(PointsOfInterest.POI_SELECT_S,probSet);
  
  		Random rng = new Random(rngSeed);
  
  		pois = new PointsOfInterest(map, okNodes, s, rng);
  	}
  
  	private String fixFile(String fileName) {
  		return fileName.replace('\\', '/');
  	}
  
  	public void testPoiSelection() throws Exception {
  		runTestPoiSelection(new double[] {0.1, 0.8, 0.05}, 0);
  	}
  
  	public void testDifferentRngSeeds() throws Exception {
  		runTestPoiSelection(new double[] {0.1, 0.8, 0.05}, 1);
  		runTestPoiSelection(new double[] {0.1, 0.8, 0.05}, 2);
  		runTestPoiSelection(new double[] {0.1, 0.8, 0.05}, 3);
  		runTestPoiSelection(new double[] {0.1, 0.8, 0.05}, 10);
  		runTestPoiSelection(new double[] {0.1, 0.8, 0.05}, 54534543);
  	}
  
  	/**
  	 * Tests full probability for one node group
  	 */
  	public void testFullProb() throws Exception {
  		runTestPoiSelection(new double[] {1.0, 0.0, 0.0}, 0);
  		runTestPoiSelection(new double[] {0.0, 1.0, 0.0}, 0);
  		runTestPoiSelection(new double[] {0.0, 0.0, 1.0}, 0);
  		// all POIs nodes are random nodes
  		runTestPoiSelection(new double[] {0.0, 0.0, 0.0}, 0);
  	}
  
  
  	/**
  	 * Runs the POI selecting procedure and performs checks
  	 */
  	public void runTestPoiSelection(double[] poiProbs, int rngSeed) throws Exception {
  		setUpWith(poiProbs, rngSeed, null);
  		// how many POIs are requested in total
  		final int TOTAL = 5000;
  		// how much offset from requested probability is allowed
  		final double DELTA = 0.02;
  		double probSum = 0; // sum of all requested probs
  		double[] expectedProbs = new double[poiProbs.length];
  
  		int[] nrofHits = new int[poiProbs.length];
  
  		for (int i=0; i<poiProbs.length;i++) {
  			nrofHits[i] = 0;
  			probSum += poiProbs[i];
  		}
  
  		for (int i=0; i<poiProbs.length;i++) {
  			// expected probability per group is probability of selecting that
  			// group + probability of randomly selecting a node in that group
  			double randomProb = (1-probSum) *
  				(COORDS_IN_MAP[i].length/(nrofMapNodes*1.0));
  			expectedProbs[i] = poiProbs[i] + randomProb;
  		}
  
  		// select TOTAL nodes from POIs
  		for (int i=0; i<TOTAL; i++) {
  			MapNode n = pois.selectDestination();
  			for (int j=0; j<POINTS_IN_MAP.length; j++) {
  				// check in which POI group the selected nodes belongs to
  				if (isPartOf(n.getLocation(), COORDS_IN_MAP[j])) {
  					nrofHits[j]++;
  				}
  			}
  		}
  
  		int nrofHitsSum = 0; // nrof hits in POI groups
  		// check that probabilites are close to requested
  		for (int i=0; i<nrofHits.length;i++) {
  			nrofHitsSum += nrofHits[i];
  			double prob = nrofHits[i] / (TOTAL*1.0);
  			assertEquals("Prob too far for index " + i,
  					expectedProbs[i], prob, DELTA);
  		}
  
  		// check that other nodes got their fair share
  		double poiProbSum = 0;
  		for (int i=0; i<expectedProbs.length; i++) {
  			poiProbSum += expectedProbs[i];
  		}
  		double otherProb = (TOTAL-nrofHitsSum) / (TOTAL*1.0);
  		assertEquals(1-poiProbSum, otherProb, DELTA);
  
  	}
  
  	public void testOkNodes() throws Exception {
  		int nrof = 100;
  		setUpWith(new double[] {0.1, 0.1, 0.1}, 1, new int [] {1});
  
  		// should choose only nodes of type 1
  		for (int i=0; i< nrof; i++)
  			assertTrue(pois.selectDestination().isType(1));
  
  	}
  
  	// return true if c is part of set
  	private boolean isPartOf(Coord c, Coord[] set) {
  		for (Coord coord : set) {
  			if (c.equals(coord)) {
  				return true;
  			}
  		}
  		return false;
  	}
  }
  