public class Match implements Cloneable
{
	/**
	 * Array of variables.
	 */
	private BioPAXElement[] variables;

	/**
	 * Constructor with size.
	 * @param size array size
	 */
	public Match(int size)
	{
		this.variables = new BioPAXElement[size];
	}

	/**
	 * Getter for the element array.
	 * @return element array
	 */
	public BioPAXElement[] getVariables()
	{
		return variables;
	}

	/**
	 * Gets element at the index.
	 * @param index index of the element to get
	 * @return element at the index
	 */
	public BioPAXElement get(int index)
	{
		return variables[index];
	}

	/**
	 * Gets element corresponding to the given label in the pattern.
	 * @param label label of the element in the pattern
	 * @param p related pattern
	 * @return element of the given label
	 * @throws IllegalArgumentException if the label not in the pattern
	 */
	public BioPAXElement get(String label, Pattern p)
	{
		return variables[p.indexOf(label)];
	}

	/**
	 * Gets elements corresponding to the given labels in the pattern.
	 * @param label labels of the element in the pattern
	 * @param p related pattern
	 * @return elements of the given label
	 * @throws IllegalArgumentException if one of the labels not in the pattern
	 */
	public List<BioPAXElement> get(String[] label, Pattern p)
	{
		if (label == null) return Collections.emptyList();

		List<BioPAXElement> list = new ArrayList<BioPAXElement>(label.length);
		for (String lab : label)
		{
			list.add(variables[p.indexOf(lab)]);
		}
		return list;
	}

	/**
	 * Gets first element of the match
	 * @return first element
	 */
	public BioPAXElement getFirst()
	{
		return variables[0];
	}

	/**
	 * Gets last element of the match.
	 * @return last element
	 */
	public BioPAXElement getLast()
	{
		return variables[variables.length - 1];
	}

	/**
	 * Gets the array size.
	 * @return array size
	 */
	public int varSize()
	{
		return variables.length;
	}

	/**
	 * Sets the given element to the given index.
	 * @param ele element to set
	 * @param index index to set
	 */
	public void set(BioPAXElement ele, int index)
	{
		variables[index] = ele;
	}

	/**
	 * Checks if all given indices are assigned.
	 * @param ind indices to check
	 * @return true if none of them are null
	 */
	public boolean varsPresent(int ... ind)
	{
		for (int i : ind)
		{
			if (variables[i] == null) return false;
		}
		return true;
	}

	/**
	 * Clones a match.
	 * @return clone of the match
	 */
	@Override
	public Object clone()
	{
		Match m = null;
		try
		{
			m = (Match) super.clone();
			m.variables = new BioPAXElement[variables.length];
			System.arraycopy(variables, 0, m.variables, 0, variables.length);
			return m;
		}
		catch (CloneNotSupportedException e)
		{
			throw new RuntimeException("super.clone() not supported.");
		}
	}

	/**
	 * Gets name of variables.
	 * @return names of variables
	 */
	@Override
	public String toString()
	{
		String s = "";

		int  i = 0;
		for (BioPAXElement ele : variables)
		{
			if (ele != null) s += i + " - " + getAName(ele) + "\n";
			i++;
		}
		return s;
	}

	/**
	 * Finds a name for the variable.
	 * @param ele element to check
	 * @return a name
	 */
	public String getAName(BioPAXElement ele)
	{
		String name = null;
		
		if (ele instanceof Named)
		{
			Named n = (Named) ele;
			if (n.getDisplayName() != null && n.getDisplayName().length() > 0) 
				name = n.getDisplayName();
			else if (n.getStandardName() != null && n.getStandardName().length() > 0) 
				name = n.getStandardName();
			else if (!n.getName().isEmpty() && n.getName().iterator().next().length() > 0)
				name = n.getName().iterator().next();
		}
		if (name == null ) name = ele.getRDFId();
		
		return name + " (" + ele.getModelInterface().getName().substring(
			ele.getModelInterface().getName().lastIndexOf(".") + 1) + ")";
	}
} public class Pattern
{
	/**
	 * How many elements are there in a pattern match.
	 */
	protected int lastIndex;

	/**
	 * Class of the first elements to match with this pattern.
	 */
	protected Class<? extends BioPAXElement> startingClass;

	/**
	 * List of mapped constraints.
	 */
	protected List<MappedConst> constraints;

	/**
	 * Indexes in a pattern can be labeled using this map.
	 */
	protected Map<String, Integer> labelMap;

	/**
	 * Constructor with the starting class.
	 * @param startingClass class of first element in the pattern
	 */
	private Pattern(Class<? extends BioPAXElement> startingClass)
	{
		this.startingClass = startingClass;
		this.labelMap = new HashMap<String, Integer>();
		this.constraints = new ArrayList<MappedConst>();
		this.lastIndex = 0;
	}

	/**
	 * Constructor with the first constraint and labels it uses. This constructor is good for
	 * patterns with a single constraint.
	 * @param startingClass type of initial element
	 * @param firstConstraint first constraint
	 * @param label labels for the constraint
	 */
	public Pattern(Class<? extends BioPAXElement> startingClass, Constraint firstConstraint,
		String... label)
	{
		this(startingClass, label[0]);
		add(firstConstraint, label);
	}

	/**
	 * Constructor with a label for the element at index 0.
	 * @param startingClass type of the initial element
	 * @param label a label for the initial element
	 */
	public Pattern(Class<? extends BioPAXElement> startingClass, String label)
	{
		this(startingClass);
		label(label, 0);
	}

	/**
	 * Creates a mapped constraint with the given constraint and the indexes it applies.
	 * @param constr constraint to add
	 * @param ind indices to map the constraint to the element in the pattern
	 */
	private void add(Constraint constr, int... ind)
	{
		assert ind.length > 0;
		assert constr.getVariableSize() == ind.length;

		for (int i = 0; i < (constr.canGenerate() ? ind.length - 1 : ind.length); i++)
		{
			assert ind[i] <= lastIndex;
		}

		constraints.add(new MappedConst(constr, ind));

		if (constr.canGenerate() && ind[ind.length - 1] > lastIndex)
		{
			if (ind[ind.length - 1] - lastIndex > 1) throw new IllegalArgumentException(
				"Generated index too large. Attempting to generate index " + ind[ind.length - 1] +
					" while last index is " + lastIndex);

			else lastIndex++;
		}
	}

	/**
	 * Removes the last constraint added to the pattern.
	 */
	public void removeLastConstraint()
	{
		if (constraints.isEmpty()) return;

		MappedConst mc = constraints.get(constraints.size() - 1);
		constraints.remove(mc);
		if (mc.canGenerate() && mc.getInds()[mc.getInds().length - 1] == lastIndex)
		{
			setLastIndexToMaxFound();
		}
	}

	/**
	 * This method is used after removal of an generative constraint. Searching the remaining
	 * constraints, we decide the value of lastIndex.
	 */
	private void setLastIndexToMaxFound()
	{
		int max = -1;
		for (MappedConst mc : constraints)
		{
			int[] ind = mc.getInds();
			int last = ind[ind.length - 1];
			if (last > max) max = last;
		}
		lastIndex = max;
	}

	public void optimizeConstraintOrder()
	{
		// determine what should come after what

		Map<MappedConst, Set<MappedConst>> preMap = new HashMap<MappedConst, Set<MappedConst>>();

		for (MappedConst con1 : constraints)
		{
			preMap.put(con1, new HashSet<MappedConst>());

			int m1 = con1.getMaxInd();
			for (MappedConst con2 : constraints)
			{
				if (con1 == con2) continue;

				int m2 = con2.getMaxInd();

				if (m1 > m2 || (m1 == m2 && con2.canGenerate() && !con1.canGenerate()))
				{
					preMap.get(con1).add(con2);
				}
			}
		}

		// determine ordered levels

		List<Set<MappedConst>> levels = new ArrayList<Set<MappedConst>>();

		do
		{
			Set<MappedConst> level = new HashSet<MappedConst>();

			for (MappedConst con : preMap.keySet())
			{
				if (preMap.get(con).isEmpty()) level.add(con);
			}

			for (MappedConst con : level)
			{
				preMap.remove(con);
			}

			for (MappedConst con : preMap.keySet())
			{
				preMap.get(con).removeAll(level);
			}

			levels.add(level);
		}
		while (!preMap.isEmpty());

		// build new constraints list

		List<MappedConst> newList = new ArrayList<MappedConst>(constraints.size());

		for (Set<MappedConst> level : levels)
		{
			List<MappedConst> temp = new ArrayList<MappedConst>(constraints);
			temp.retainAll(level);
			newList.addAll(temp);
		}

		this.constraints = newList;
	}

	/**
	 * Creates a mapped constraint with the given generative constraint and the indexes it applies.
	 * Also labels the last given index.
	 * @param constr constraint to add
	 * @param label a label for the last of the given indices
	 */
	public void add(Constraint constr, String... label)
	{
		checkLabels(constr.canGenerate(), label);

		int[] ind = convertLabelsToInds(label);

		if (ind.length != constr.getVariableSize())
		{
			throw new IllegalArgumentException("Mapped elements do not match the constraint size.");
		}

		// This will also increment lastIndex if necessary
		add(constr, ind);

		if (!hasLabel(label[label.length - 1]) && constr.canGenerate())
		{
			label(label[label.length - 1], lastIndex);
		}
	}

	/**
	 * Converts the labels to the indexes. Assumes the sanity of the labels already checked and if
	 * any new label exists, it is only one and it is the last one.
	 * @param label labels
	 * @return indexes for labels
	 */
	private int[] convertLabelsToInds(String... label)
	{
		int[] ind = new int[label.length];
		for (int i = 0; i < label.length; i++)
		{
			if (hasLabel(label[i]))
			{
				ind[i] = indexOf(label[i]);
			}
			else ind[i] = lastIndex + 1;
		}
		return ind;
	}

	/**
	 * Converts the indices to the labels. All indices must have an existing label.
	 * @param ind indices
	 * @return labels for indices
	 */
	private String[] convertIndsToLabels(int... ind)
	{
		String[] label = new String[ind.length];
		for (int i = 0; i < ind.length; i++)
		{
			if (!hasLabel(ind[i])) throw new IllegalArgumentException(
				"The index " + ind[i] + " does not have a label.");

			label[i] = getLabel(ind[i]);
		}
		return label;
	}

	/**
	 * Checks if all labels (other than the last if generative) exists.
	 * @param forGenerative whether the check is performed for a generative constraint
	 * @param label labels to check
	 * @throws IllegalArgumentException when a necessary label is not found
	 */
	private void checkLabels(boolean forGenerative, String... label)
	{
		for (int i = 0; i < (forGenerative ? label.length - 1 : label.length); i++)
		{
			if (!hasLabel(label[i])) throw new IllegalArgumentException(
				"Label neither found, nor generated: " + label[i]);
		}
	}

	/**
	 * Appends the constraints in the parameter pattern to the desired location. Indexes in the
	 * constraint mappings are translated so that 0 is translated to ind0, and others are translated
	 * to orig + indAppend - 1. All slots of this pattern should already be full before calling this
	 * method. This method makes room for the new variables. Labels in the parameter pattern is
	 * transferred to this pattern. If there are equivalent labels, then these slots are mapped.
	 * 
	 * @param p the parameter pattern
	 */
	public void add(Pattern p)
	{
		if (!hasLabel(p.getLabel(0))) throw new IllegalArgumentException("The label of first " +
			"element of parameter index \"" + p.getLabel(0) + "\" not found in this pattern.");

		for (MappedConst mc : p.getConstraints())
		{
			add(mc.getConstr(), p.convertIndsToLabels(mc.getInds()));
		}
	}

	/**
	 * Gets the label for the element at the specified index.
	 * @param i index
	 * @return label for the element at the specified index
	 */
	private String getLabel(int i)
	{
		for (String label : labelMap.keySet())
		{
			if (labelMap.get(label) == i) return label;
		}
		return null;
	}

	/**
	 * A point constraint deals with only one element in a match, checks its validity. This method
	 * injects the parameter constraint multiple times among the list of mapped constraints, to the
	 * specified indexes.
	 *
	 * @param con constraint to add
	 * @param ind indices to add this point constraint
	 */
	public void insertPointConstraint(Constraint con, int ... ind)
	{
		assert con.getVariableSize() == 1;

		for (int i : ind)
		{
			for (int j = 0; j < constraints.size(); j++)
			{
				int[] index = constraints.get(j).getInds();
				if (index[index.length-1] == i)
				{
					constraints.add(j + 1, new MappedConst(con, i));
					break;
				}
			}
		}
	}

	/**
	 * Getter for the constraint list.
	 * @return constraints
	 */
	public List<MappedConst> getConstraints()
	{
		return constraints;
	}

	/**
	 * Gets the element size of the pattern.
	 * @return size inferred from lastIndex
	 */
	public int size()
	{
		return lastIndex + 1;
	}

	/**
	 * Gets the type of the initial element.
	 * @return type of first element in a match
	 */
	public Class<? extends BioPAXElement> getStartingClass()
	{
		return startingClass;
	}

	/**
	 * Puts the given label for the given index.
	 * @param labelText the label
	 * @param index index to label
	 */
	public void label(String labelText, int index)
	{
		if (labelMap.containsKey(labelText)) throw new IllegalArgumentException(
			"Label \"" + labelText + "\" already exists.");

		if (labelMap.containsValue(index)) throw new IllegalArgumentException(
			"Index \"" + index + "\" already has a label.");

		labelMap.put(labelText, index);
	}

	/**
	 * Checks if the label is already in use.
	 * @param labelText label to check
	 * @return true if label exists
	 */
	public boolean hasLabel(String labelText)
	{
		return labelMap.containsKey(labelText);
	}

	/**
	 * Checks if the given location has a label.
	 * @param index index to check
	 * @return true if a label exists for the given index
	 */
	public boolean hasLabel(int index)
	{
		return labelMap.containsValue(index);
	}

	/**
	 * Gets the index of the given label. The label must exist, otherwise a runtime exception is
	 * thrown.
	 * @param labelText label to check
	 * @return index of the label
	 */
	public int indexOf(String labelText)
	{
		if (!labelMap.containsKey(labelText))
			throw new IllegalArgumentException("The label \"" + labelText +
				"\" is absent in pattern.");
		
		return labelMap.get(labelText);
	}

	/**
	 * Changes a label. The oldLabel has to be an existing label and new label has to be a new
	 * label.
	 * @param oldLabel label to update
	 * @param newLabel updated label
	 */
	public void updateLabel(String oldLabel, String newLabel)
	{
		if (hasLabel(newLabel)) throw new IllegalArgumentException(
			"The label \"" + newLabel + "\" already exists.");

		int i = indexOf(oldLabel);
		labelMap.remove(oldLabel);
		labelMap.put(newLabel, i);
	}
} public class Searcher
{
	/**
	 * Searches the pattern starting from the given match. The first element of the match should be
	 * assigned. Others are optional.
	 * @param m match to start from
	 * @param pattern pattern to search
	 * @return result matches
	 */
	public static List<Match> search(Match m, Pattern pattern)
	{
		assert pattern.getStartingClass().isAssignableFrom(m.get(0).getModelInterface());

		return searchRecursive(m, pattern.getConstraints(), 0);
	}

	/**
	 * Searches the pattern starting from the given element.
	 * @param ele element to start from
	 * @param pattern pattern to search
	 * @return matching results
	 */
	public static List<Match> search(BioPAXElement ele, Pattern pattern)
	{
		assert pattern.getStartingClass().isAssignableFrom(ele.getModelInterface());

		Match m = new Match(pattern.size());
		m.set(ele, 0);
		return search(m, pattern);
	}

	/**
	 * Continues searching with the mapped constraint at the given index.
	 * @param match match to start from
	 * @param mc mapped constraints of the pattern
	 * @param index index of the current mapped constraint
	 * @return matching results
	 */
	public static List<Match> searchRecursive(Match match, List<MappedConst> mc, int index) 
	{
		List<Match> result = new ArrayList<Match>();

		// debug code
//		if (match.get(2) != null && match.get(2).getRDFId().equals("http://pid.nci.nih.gov/biopaxpid_39918") &&
//			match.get(6) != null && match.get(6).getRDFId().equals("http://pid.nci.nih.gov/biopaxpid_12411"))
//		{
//			System.out.println();
//		}
		// debug code

		Constraint con = mc.get(index).getConstr();
		int[] ind = mc.get(index).getInds();
		int lastInd = ind[ind.length-1];

		if (con.canGenerate() && match.get(lastInd) == null)
		{
			Collection<BioPAXElement> elements = con.generate(match, ind);

			for (BioPAXElement ele : elements)
			{
				match.set(ele, lastInd);
				
				if (mc.size() == index + 1)
				{
					result.add((Match) match.clone());
				}
				else
				{
					result.addAll(searchRecursive(match, mc, index + 1));
				}
				
				match.set(null, lastInd);
			}
		}
		else
		{
			if (con.satisfies(match, ind))
			{
				if (mc.size() == index + 1)
				{
					result.add((Match) match.clone());
				}
				else
				{
					result.addAll(searchRecursive(match, mc, index + 1));
				}
			}
		}
		return result;
	}

	/**
	 * Searches the pattern in a given model, but instead of a match map, returns all matches in a
	 * list.
	 * @param model model to search in
	 * @param pattern pattern to search for
	 * @return matching results
	 */
	public static List<Match> searchPlain(Model model, Pattern pattern)
	{
		List<Match> list = new LinkedList<Match>();

		Map<BioPAXElement, List<Match>> map = search(model, pattern);
		for (List<Match> matches : map.values())
		{
			list.addAll(matches);
		}
		return list;
	}

	/**
	 * Searches the pattern starting from given elements, but instead of a match map, returns all
	 * matches in a list.
	 * @param eles elements to start from
	 * @param pattern pattern to search for
	 * @return matching results
	 */
	public static List<Match> searchPlain(Collection<? extends BioPAXElement> eles, Pattern pattern)
	{
		List<Match> list = new LinkedList<Match>();

		Map<BioPAXElement, List<Match>> map = search(eles, pattern);
		for (List<Match> matches : map.values())
		{
			list.addAll(matches);
		}
		return list;
	}

	/**
	 * Searches the given pattern in the given model.
	 * @param model model to search in
	 * @param pattern pattern to search for
	 * @return map from starting elements to the list matching results
	 */
	public static Map<BioPAXElement, List<Match>> search(Model model, Pattern pattern)
	{
		return search(model, pattern, null);
	}

	/**
	 * Searches the given pattern in the given model.
	 * @param model model to search in
	 * @param pattern pattern to search for
	 * @param prg progress watcher to keep track of the progress
	 * @return map from starting elements to the list matching results
	 */
	public static Map<BioPAXElement, List<Match>> search(Model model, Pattern pattern,
		ProgressWatcher prg)
	{
		Map<BioPAXElement, List<Match>> map = new HashMap<BioPAXElement, List<Match>>();

		Set<? extends BioPAXElement> eles = model.getObjects(pattern.getStartingClass());

		if (prg != null) prg.setTotalTicks(eles.size());

		for (BioPAXElement ele : eles)
		{
			List<Match> matches = search(ele, pattern);
			
			if (!matches.isEmpty())
			{
				map.put(ele, matches);
			}

			if (prg != null) prg.tick(1);
		}
		return map;
	}

	/**
	 * Searches the given pattern starting from the given elements.
	 * @param eles elements to start from
	 * @param pattern pattern to search for
	 * @return map from starting element to the matching results
	 */
	public static Map<BioPAXElement, List<Match>> search(Collection<? extends BioPAXElement> eles,
		Pattern pattern)
	{
		Map<BioPAXElement, List<Match>> map = new HashMap<BioPAXElement, List<Match>>();

		for (BioPAXElement ele : eles)
		{
			if (!pattern.getStartingClass().isAssignableFrom(ele.getModelInterface())) continue;
			
			List<Match> matches = search(ele, pattern);
			
			if (!matches.isEmpty())
			{
				map.put(ele, matches);
			}
		}
		return map;
	}

	/**
	 * Searches a model for the given pattern, then collects the specified elements of the matches
	 * and returns.
	 * @param model model to search in
	 * @param pattern pattern to search for
	 * @param index index of the element in the match to collect
	 * @param c type of the element to collect
	 * @return set of the elements at the specified index of the matching results
	 */
	public static <T extends BioPAXElement> Set<T> searchAndCollect(
		Model model, Pattern pattern, int index, Class<T> c)
	{
		return searchAndCollect(model.getObjects(pattern.getStartingClass()), pattern, index, c);
	}

	/**
	 * Searches the given pattern starting from the given elements, then collects the specified
	 * elements of the matches and returns.
	 * @param eles elements to start from
	 * @param pattern pattern to search for
	 * @param index index of the element in the match to collect
	 * @param c type of the element to collect
	 * @return set of the elements at the specified index of the matching results
	 */
	public static <T extends BioPAXElement> Set<T> searchAndCollect(
		Collection<? extends BioPAXElement> eles, Pattern pattern, int index, Class<T> c)
	{
		Set<T> set = new HashSet<T>();

		for (Match match : searchPlain(eles, pattern))
		{
			set.add((T) match.get(index));
		}
		return set;
	}

	/**
	 * Searches the given pattern starting from the given element, then collects the specified
	 * elements of the matches and returns.
	 * @param ele element to start from
	 * @param pattern pattern to search for
	 * @param index index of the element in the match to collect
	 * @param c type of the element to collect
	 * @return set of the elements at the specified index of the matching results
	 */
	public static <T extends BioPAXElement> Set<T> searchAndCollect(
		BioPAXElement ele, Pattern pattern, int index, Class<T> c)
	{
		Set<T> set = new HashSet<T>();

		for (Match match : search(ele, pattern))
		{
			set.add((T) match.get(index));
		}
		return set;
	}

	/**
	 * Checks if there is any match for the given pattern if search starts from the given element.
	 * @param p pattern to search for
	 * @param ele element to start from
	 * @return true if there is a match
	 */
	public boolean hasSolution(Pattern p, BioPAXElement ... ele)
	{
		Match m = new Match(p.size());
		for (int i = 0; i < ele.length; i++)
		{
			m.set(ele[i], i);
		}

		return !search(m, p).isEmpty();
	}

	/**
	 * Searches a pattern reading the model from the given file, and creates another model that is
	 * excised using the matching patterns.
	 * @param p pattern to search for
	 * @param inFile filename for the model to search in
	 * @param outFile filename for the result model
	 * @throws FileNotFoundException
	 */
	public static void searchInFile(Pattern p, String inFile, String outFile) throws FileNotFoundException
	{
		searchInFile(p, inFile, outFile, Integer.MAX_VALUE, Integer.MAX_VALUE);
	}

	/**
	 * Searches a pattern reading the model from the given file, and creates another model that is
	 * excised using the matching patterns. USers can limit the max number of starting element, and
	 * max number of matches for any starting element. These parameters is good for limiting the
	 * size of the result graph.
	 * @param p pattern to search for
	 * @param inFile filename for the model to search in
	 * @param outFile filename for the result model
	 * @param seedLimit max number of starting elements
	 * @param graphPerSeed max number of matches for a starting element
	 * @throws FileNotFoundException
	 */
	public static void searchInFile(Pattern p, String inFile, String outFile, int seedLimit,
		int graphPerSeed) throws FileNotFoundException
	{
		SimpleIOHandler h = new SimpleIOHandler();
		Model model = h.convertFromOWL(new FileInputStream(inFile));

		Map<BioPAXElement,List<Match>> matchMap = Searcher.search(model, p);

		System.out.println("matching groups size = " + matchMap.size());

		List<Set<Interaction>> inters = new LinkedList<Set<Interaction>>();
		Set<Integer> encountered = new HashSet<Integer>();

		Set<BioPAXElement> toExise = new HashSet<BioPAXElement>();

		int seedCounter = 0;
		for (BioPAXElement ele : matchMap.keySet())
		{
			if (seedCounter >= seedLimit) break;

			int matchCounter = 0;

			List<Match> matches = matchMap.get(ele);

			if (!matches.isEmpty()) seedCounter++;

			for (Match match : matches)
			{
				matchCounter++;
				
				if (matchCounter > graphPerSeed) break;

				Set<Interaction> ints = getInter(match);

				toExise.addAll(Arrays.asList(match.getVariables()));
				toExise.addAll(ints);

				Integer hash = hashSum(ints);
				if (!encountered.contains(hash))
				{
					encountered.add(hash);
					inters.add(ints);
				}
			}
		}

		System.out.println("created pathways = " + inters.size());

		Model clonedModel = excise(model, toExise);

		int i = 0;
		for (Set<Interaction> ints : inters)
		{
			Pathway pathway = clonedModel.addNew(Pathway.class,
				System.currentTimeMillis() + "PaxtoolsPatternGeneratedMatch" + (++i));

			pathway.setDisplayName("Match " + getLeadingZeros(i, inters.size()) + i);

			for (Interaction anInt : ints)
			{
				pathway.addPathwayComponent((Process) clonedModel.getByID(anInt.getRDFId()));
			}
		}

		handler.convertToOWL(clonedModel, new FileOutputStream(outFile));
	}

	/**
	 * Prepares an int for printing with leading zeros for the given size.
	 * @param i the int to prepare
	 * @param size max value for i
	 * @return printable string for i with leading zeros
	 */
	private static String getLeadingZeros(int i, int size)
	{
		assert i <= size;
		int w1 = (int) Math.floor(Math.log10(size));
		int w2 = (int) Math.floor(Math.log10(i));
		
		String s = "";

		for (int j = w2; j < w1; j++)
		{
			s += "0";
		}
		return s;
	}

	/**
	 * IO handler for reading and writing BioPAX.
	 */
	private static BioPAXIOHandler handler =  new SimpleIOHandler();

	/**
	 * Editor map to use for excising.
	 */
	private static final SimpleEditorMap EM = SimpleEditorMap.L3;

	/**
	 * Excises a model to the given elements.
	 * @param model model to excise
	 * @param result elements to excise to
	 * @return excised model
	 */
	private static Model excise(Model model, Set<BioPAXElement> result)
	{
		Completer c = new Completer(EM);

		result = c.complete(result, model);

		Cloner cln = new Cloner(EM, BioPAXLevel.L3.getDefaultFactory());

		return cln.clone(model, result);
	}

	/**
	 * Gets all interactions in a match.
	 * @param match match to search
	 * @return all interaction in the match
	 */
	private static Set<Interaction> getInter(Match match)
	{
		Set<Interaction> set = new HashSet<Interaction>();
		for (BioPAXElement ele : match.getVariables())
		{
			if (ele instanceof Interaction) 
			{
				set.add((Interaction) ele);
				addControlsRecursive((Interaction) ele, set);
			}
		}
		return set;
	}

	/**
	 * Adds controls of the given interactions recursively to the given set.
	 * @param inter interaction to add its controls
	 * @param set set to add to
	 */
	private static void addControlsRecursive(Interaction inter, Set<Interaction> set)
	{
		for (Control ctrl : inter.getControlledOf())
		{
			set.add(ctrl);
			addControlsRecursive(ctrl, set);
		}
	}

	/**
	 * Creates a hash code for a set of interactions.
	 * @param set interactions
	 * @return sum of hashes
	 */
	private static Integer hashSum(Set<Interaction> set)
	{
		int x = 0;
		for (Interaction inter : set)
		{
			x += inter.hashCode();
		}
		return x;
	}
} public class CSCOBothControllerAndParticipantMiner extends AbstractSIFMiner
{
	/**
	 * Constructor that sets name and description.
	 */
	public CSCOBothControllerAndParticipantMiner()
	{
		super(SIFEnum.CONTROLS_STATE_CHANGE_OF, "-both-ctrl-part", "The controller is also an " +
			"input. The reaction has to be a Conversion and modified Protein should be " +
			"represented with different non-generic PhysicalEntity on each side.");
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.controlsStateChangeBothControlAndPart();
	}

	@Override
	public String getSourceLabel()
	{
		return "controller PR";
	}

	@Override
	public String getTargetLabel()
	{
		return "changed ER";
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"Control", "Conversion"};
	}

	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"controller simple PE", "controller PE"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"input PE", "input simple PE", "output PE", "output simple PE"};
	}
} public class CSCOButIsParticipantMiner extends ControlsStateChangeOfMiner
{
	/**
	 * Constructor that sets name and description.
	 */
	public CSCOButIsParticipantMiner()
	{
		super("-but-a-participant", "The controller is modeled as a " +
			"participant of the Conversion. This is in fact a modeling error, but this pattern " +
			"exists in some resources. The controller PhysicalEntity appears at both left and " +
			"right of the Conversion.");
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		Pattern p = PatternBox.controlsStateChangeButIsParticipant();
		p.add(new Type(ProteinReference.class), "controller ER");
		p.add(new Type(ProteinReference.class), "changed ER");
		return p;
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"Conversion"};
	}

	@Override
	public String getSourceLabel()
	{
		return "controller ER";
	}

	@Override
	public String getTargetLabel()
	{
		return "changed ER";
	}


	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"controller simple PE", "controller PE"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"input PE", "input simple PE", "output PE", "output simple PE"};
	}
} public class CSCOThroughBindingSmallMoleculeMiner extends ControlsStateChangeOfMiner
{
	/**
	 * Constructor that sets name and description.
	 */
	public CSCOThroughBindingSmallMoleculeMiner()
	{
		super("-through-binding-small-mol", "The first protein produces a non-ubique small " +
			"molecule, and this small molecule controls state of the second protein.");
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.controlsStateChangeThroughBindingSmallMolecule(blacklist);
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"upper Control", "upper Conversion", "Conversion", "SM"};
	}

	@Override
	public String getSourceLabel()
	{
		return "upper controller PR";
	}

	@Override
	public String getTargetLabel()
	{
		return "changed ER";
	}

	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"upper controller simple PE", "upper controller PE"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"input PE", "input simple PE", "output PE", "output simple PE"};
	}
} public class CSCOThroughControllingSmallMoleculeMiner extends ControlsStateChangeOfMiner
{
	/**
	 * Constructor that sets name and description.
	 */
	public CSCOThroughControllingSmallMoleculeMiner()
	{
		super("-through-controlling-small-mol", "The first protein produces a non-ubique small " +
			"molecule, and this small molecule controls state of the second protein.");
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.controlsStateChangeThroughControllerSmallMolecule(blacklist);
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"upper Control", "upper Conversion", "Control", "Conversion",
			"controller PE"};
	}

	@Override
	public String getSourceLabel()
	{
		return "upper controller PR";
	}

	@Override
	public String getTargetLabel()
	{
		return "changed ER";
	}

	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"upper controller simple PE", "upper controller PE"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"input PE", "input simple PE", "output PE", "output simple PE"};
	}
} public class CSCOThroughDegradationMiner extends AbstractSIFMiner
{
	/**
	 * Constructor that sets name and description.
	 */
	public CSCOThroughDegradationMiner()
	{
		super(SIFEnum.CONTROLS_STATE_CHANGE_OF);
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.controlsStateChangeThroughDegradation();
	}

	@Override
	public String getSourceLabel()
	{
		return "upstream PR";
	}

	@Override
	public String getTargetLabel()
	{
		return "downstream PR";
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"Control", "Conversion"};
	}

	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"upstream SPE", "upstream PE"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"input PE", "input SPE"};
	}
} public class CatalysisPrecedesMiner extends AbstractSIFMiner
{
	/**
	 * Constructor that sets name and description.
	 */
	public CatalysisPrecedesMiner()
	{
		super(SIFEnum.CATALYSIS_PRECEDES);
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.catalysisPrecedes(blacklist);
	}

	@Override
	public String getSourceLabel()
	{
		return "first PR";
	}

	@Override
	public String getTargetLabel()
	{
		return "second PR";
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"first Control", "first Conversion", "second Control",
			"second Conversion"};
	}

	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"first simple controller PE", "first controller PE"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"second simple controller PE", "second controller PE"};
	}
} public class ChemicalAffectsThroughBindingMiner extends AbstractSIFMiner
{
	/**
	 * Constructor that sets name and description.
	 */
	public ChemicalAffectsThroughBindingMiner()
	{
		super(SIFEnum.CHEMICAL_AFFECTS, "-through-binding", "In this case the chemical appears in" +
			" the same complex with the protein.");
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.chemicalAffectsProteinThroughBinding(blacklist);
	}

	@Override
	public String getSourceLabel()
	{
		return "SMR";
	}

	@Override
	public String getTargetLabel()
	{
		return "PR";
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"Complex"};
	}

	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"SPE1", "PE1", "Complex"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"SPE2", "PE2", "Complex"};
	}
} public class ChemicalAffectsThroughControlMiner extends AbstractSIFMiner
{
	/**
	 * Constructor that sets name and description.
	 */
	public ChemicalAffectsThroughControlMiner()
	{
		super(SIFEnum.CHEMICAL_AFFECTS, "-through-control", "In this case, chemical is " +
			"controlling a reaction of which the protein is a participant.");
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.chemicalAffectsProteinThroughControl();
	}

	@Override
	public String getSourceLabel()
	{
		return "controller SMR";
	}

	@Override
	public String getTargetLabel()
	{
		return "affected PR";
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"Control", "Interaction"};
	}

	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"controller simple PE", "controller PE"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"affected PE", "affected simple PE"};
	}
} public class ConsumptionControlledByMiner extends AbstractSIFMiner
{
	/**
	 * Constructor that sets name and description.
	 */
	public ConsumptionControlledByMiner()
	{
		super(SIFEnum.CONSUMPTION_CONTROLLED_BY);
	}

	/**
	 * Constructor for extension purposes.
	 */
	protected ConsumptionControlledByMiner(SIFType type)
	{
		super(type);
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.controlsMetabolicCatalysis(blacklist, true);
	}

	@Override
	public String getSourceLabel()
	{
		return "part SMR";
	}

	@Override
	public String getTargetLabel()
	{
		return "controller PR";
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"Control", "Conversion"};
	}

	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"controller simple PE", "controller PE"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"part PE", "part SM"};
	}
} public class ControlsDegradationIndirectMiner extends AbstractSIFMiner
{
	/**
	 * Constructor that sets name and description.
	 */
	public ControlsDegradationIndirectMiner()
	{
		super(SIFEnum.CONTROLS_STATE_CHANGE_OF, "-indirectly", "The control is to a previous " +
			"reaction that produces the degraded state of the protein.");
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.controlsDegradationIndirectly();
	}

	@Override
	public String getSourceLabel()
	{
		return "controller PR";
	}

	@Override
	public String getTargetLabel()
	{
		return "changed PR";
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"Control", "Conversion", "degrading Conv"};
	}
} public class ControlsExpressionMiner extends AbstractSIFMiner
{
	/**
	 * Constructor that sets name and description.
	 */
	public ControlsExpressionMiner()
	{
		super(SIFEnum.CONTROLS_EXPRESSION_OF);
	}

	/**
	 * Constructor for extending this class.
	 * @param nameSuppl name
	 * @param descriptionSuppl description
	 */
	public ControlsExpressionMiner(String nameSuppl, String descriptionSuppl)
	{
		super(SIFEnum.CONTROLS_EXPRESSION_OF, nameSuppl, descriptionSuppl);
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
//		Pattern p = PatternBox.transcriptionWithTemplateReac();
//		p.add(new Type(ProteinReference.class), "product ER");

		Pattern p = new Pattern(ProteinReference.class, "TF PR");
		p.add(erToPE(), "TF PR", "TF simple PE");
		p.add(linkToComplex(), "TF simple PE", "TF PE");
		p.add(peToControl(), "TF PE", "Control");
		p.add(controlToTempReac(), "Control", "TempReac");
		p.add(new NOT(participantER()), "TempReac", "TF PR");
		p.add(product(), "TempReac", "product PE");
		p.add(linkToSimple(), "product PE", "product simple PE");
		p.add(new Type(Protein.class), "product simple PE");
		p.add(peToER(), "product simple PE", "product PR");

		return p;
	}

	@Override
	public String getSourceLabel()
	{
		return "TF PR";
	}

	@Override
	public String getTargetLabel()
	{
		return "product PR";
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"Control", "TempReac"};
	}

	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"TF simple PE", "TF PE"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"product PE", "product simple PE"};
	}
} public class ControlsExpressionWithConvMiner extends ControlsExpressionMiner
{
	/**
	 * Constructor that sets name and description.
	 */
	public ControlsExpressionWithConvMiner()
	{
		super("-with-conversion", "Proper way to model this relation is to use a TemplateReaction" +
			", however we see that Conversion is also used in resources. This miner find patterns" +
			" where a Conversion with a single participant at right is used instead of a " +
			"TemplateReaction.");
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		Pattern p = PatternBox.controlsExpressionWithConversion();
		p.add(new Type(ProteinReference.class), "product ER");
		return p;
	}

	@Override
	public String getTargetLabel()
	{
		return "product ER";
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"Control", "Conversion"};
	}

	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"TF SPE", "TF PE"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"right PE", "right SPE"};
	}
} public class ControlsPhosphorylationMiner extends ControlsStateChangeOfMiner
{
	/**
	 * Constructor that sets the type.
	 */
	public ControlsPhosphorylationMiner()
	{
		super(SIFEnum.CONTROLS_PHOSPHORYLATION_OF.getTag(),
			SIFEnum.CONTROLS_PHOSPHORYLATION_OF.getDescription());
		setType(SIFEnum.CONTROLS_PHOSPHORYLATION_OF);
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.controlsPhosphorylation();
	}
} public class ControlsProductionOfMiner extends ConsumptionControlledByMiner
{
	/**
	 * Constructor that sets name, description and type.
	 */
	public ControlsProductionOfMiner()
	{
		super(SIFEnum.CONTROLS_PRODUCTION_OF);
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.controlsMetabolicCatalysis(blacklist, false);
	}
} public class ControlsStateChangeDetailedMiner extends MinerAdapter
{
	/**
	 * Constructor that sets name and description.
	 */
	public ControlsStateChangeDetailedMiner()
	{
		super("controls-state-change-detailed", "Captures exactly the same pattern as " +
			"\"Controls-state-change\", but the result file is more detailed. Together with " +
			"upstream and downstream genes, it also contains modifications of the upstream " +
			"entity, as well as the gained and lost modifications of the downstream entity.");
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.controlsStateChange();
	}

	/**
	 * Writes the result as "A modifications-of-A B gains-of-B loss-of-B", where A and B are gene
	 * symbols, and whitespace is tab. Modifications are comma separated.
	 * @param matches pattern search result
	 * @param out output stream
	 */
	@Override
	public void writeResult(Map<BioPAXElement, List<Match>> matches, OutputStream out)
		throws IOException
	{
		writeResultDetailed(matches, out, 5);
	}

	/**
	 * Gets the header of the result file.
	 * @return header
	 */
	@Override
	public String getHeader()
	{
		return "Upstream\tModifications-of-upstream\tDownstream\tGain-of-downstream\tLoss-of-downstream";
	}

	/**
	 * Creates values for the result file columns.
	 * @param m current match
	 * @param col current column
	 * @return value of the given match at the given column
	 */
	@Override
	public String getValue(Match m, int col)
	{
		switch(col)
		{
			case 0:
			{
				return getGeneSymbol(m, "controller PR");
			}
			case 1:
			{
				return getModifications(m, "controller simple PE", "controller PE");
			}
			case 2:
			{
				return getGeneSymbol(m, "changed PR");
			}
			case 3:
			{
				return getDeltaModifications(m,
					"input simple PE", "input PE", "output simple PE", "output PE")[0];
			}
			case 4:
			{
				return getDeltaModifications(m,
					"input simple PE", "input PE", "output simple PE", "output PE")[1];
			}
			default: throw new RuntimeException("Invalid col number: " + col);
		}
	}
} public class ControlsStateChangeOfMiner extends AbstractSIFMiner
{
	/**
	 * Constructor for extension purposes.
	 * @param name name of the miner
	 * @param description description of the miner
	 */
	protected ControlsStateChangeOfMiner(String name, String description)
	{
		super(SIFEnum.CONTROLS_STATE_CHANGE_OF, name, description);
	}

	/**
	 * Empty constructor.
	 */
	public ControlsStateChangeOfMiner()
	{
		super(SIFEnum.CONTROLS_STATE_CHANGE_OF);
	}

	/**
	 * Constructor for extension purposes.
	 * @param type relation type
	 */
	protected ControlsStateChangeOfMiner(SIFType type)
	{
		super(type);
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.controlsStateChange();
	}

	@Override
	public String getSourceLabel()
	{
		return "controller PR";
	}

	@Override
	public String getTargetLabel()
	{
		return "changed PR";
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"Control", "Conversion"};
	}

	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"controller simple PE", "controller PE"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"input PE", "input simple PE", "output PE", "output simple PE"};
	}
} public class ControlsTransportMiner extends ControlsStateChangeOfMiner
{
	/**
	 * Constructor that sets name and description.
	 */
	public ControlsTransportMiner()
	{
		super(SIFEnum.CONTROLS_TRANSPORT_OF);
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.controlsTransport();
	}
} public class ControlsTransportOfChemicalMiner extends AbstractSIFMiner
{
	/**
	 * Constructor that sets name and description.
	 */
	public ControlsTransportOfChemicalMiner()
	{
		super(SIFEnum.CONTROLS_TRANSPORT_OF_CHEMICAL);
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.controlsTransportOfChemical(blacklist);
	}

	@Override
	public String getSourceLabel()
	{
		return "controller PR";
	}

	@Override
	public String getTargetLabel()
	{
		return "changed SMR";
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"Control", "Conversion"};
	}

	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"controller simple PE", "controller PE"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"input PE", "input simple PE", "output PE", "output simple PE"};
	}
} public class DirectedRelationMiner extends MinerAdapter
{
	/**
	 * Constructor that sets name and description.
	 */
	public DirectedRelationMiner()
	{
		super("directed-relations", "Finds relations between proteins where " +
			"the first one is controlling an interaction where the second protein is participant.");
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		Pattern p = new Pattern(ProteinReference.class, "controller PR");
		p.add(isHuman(), "controller PR");
		p.add(erToPE(), "controller PR", "controller simple PE");
		p.add(linkToComplex(), "controller simple PE", "controller PE");
		p.add(peToControl(), "controller PE", "Control");
		p.add(controlToInter(), "Control", "Interaction");
		p.add(new NOT(participantER()), "Interaction", "controller PR");
		p.add(participant(), "Interaction", "affected PE");
		p.add(linkToSimple(), "affected PE", "affected simple PE");
		p.add(new Type(Protein.class), "affected simple PE");
		p.add(peToER(), "affected simple PE", "affected PR");

		return p;
	}

	/**
	 * Writes the result as "A B", where A and B are gene symbols, and whitespace is tab.
	 * @param matches pattern search result
	 * @param out output stream
	 */
	@Override
	public void writeResult(Map<BioPAXElement, List<Match>> matches, OutputStream out)
		throws IOException{
		writeResultAsSIF(matches, out, true, getSourceLabel(), getTargetLabel());
	}

	/**
	 * Sets header of the output.
	 * @return header
	 */
	@Override
	public String getHeader()
	{
		return "Upstream\tDownstream";
	}

	public String getSourceLabel()
	{
		return "controller PR";
	}

	public String getTargetLabel()
	{
		return "affected PR";
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"Control", "Interaction"};
	}
} public interface IDFetcher
{
	/**
	 * Finds a String ID for the given element.
	 * @param ele element to fecth the ID from
	 * @return ID
	 */
	public String fetchID(BioPAXElement ele);
} public class InComplexWithMiner extends AbstractSIFMiner
{
	/**
	 * Constructor that sets edge type.
	 */
	public InComplexWithMiner()
	{
		super(SIFEnum.IN_COMPLEX_WITH);
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.inComplexWith();
	}

	@Override
	public String getSourceLabel()
	{
		return "Protein 1";
	}

	@Override
	public String getTargetLabel()
	{
		return "Protein 2";
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"Complex"};
	}

	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"SPE1", "PE1", "Complex"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"SPE2", "PE2", "Complex"};
	}
} public class InteractsWithMiner extends AbstractSIFMiner
{
	/**
	 * Constructor that sets sif type.
	 */
	public InteractsWithMiner()
	{
		super(SIFEnum.INTERACTS_WITH);
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.molecularInteraction();
	}

	@Override
	public String getSourceLabel()
	{
		return "Protein 1";
	}

	@Override
	public String getTargetLabel()
	{
		return "Protein 2";
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"MI"};
	}


	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"SPE1", "PE1"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"SPE2", "PE2"};
	}
} public interface Miner
{
	/**
	 * Gets name of the miner.
	 * @return name
	 */
	public String getName();

	/**
	 * Gets description of the miner.
	 * @return description
	 */
	public String getDescription();

	/**
	 * Gets the pattern to use for mining the graph.
	 * @return the pattern
	 */
	public Pattern getPattern();

	/**
	 * Writes the text output to the given stream.
	 * @param matches pattern search result
	 * @param out output stream
	 */
	public void writeResult(Map<BioPAXElement, List<Match>> matches, OutputStream out) throws IOException;
} public class NeighborOfMiner extends AbstractSIFMiner
{
	/**
	 * Constructor that sets sif type.
	 */
	public NeighborOfMiner()
	{
		super(SIFEnum.NEIGHBOR_OF);
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.neighborOf();
	}

	@Override
	public String getSourceLabel()
	{
		return "Protein 1";
	}

	@Override
	public String getTargetLabel()
	{
		return "Protein 2";
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"Inter"};
	}

	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"SPE1", "PE1"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"SPE2", "PE2"};
	}
} public class ReactsWithMiner extends AbstractSIFMiner
{
	/**
	 * Constructor that sets sif type.
	 */
	public ReactsWithMiner()
	{
		super(SIFEnum.REACTS_WITH);
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.reactsWith(blacklist);
	}

	@Override
	public String getSourceLabel()
	{
		return "SMR1";
	}

	@Override
	public String getTargetLabel()
	{
		return "SMR2";
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"Conv"};
	}

	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"SPE1", "PE1"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"SPE2", "PE2"};
	}
} public class RelatedGenesOfInteractionsMiner extends MinerAdapter
{
	/**
	 * Constructor that sets name and description.
	 */
	public RelatedGenesOfInteractionsMiner()
	{
		super("related-genes-of-interactions", "This miner finds any related gene that is a " +
			"participant or a controller of an Interaction (Conversion or TemplateReaction). " +
			"The output lists the ID of the interaction and the related gene symbols in a row.");
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.relatedProteinRefOfInter(
			Conversion.class,
			TemplateReaction.class);
	}

	private static final PathAccessor controlAcc = new PathAccessor("Interaction/controlledOf*");

	
	
	
	/**
	 * Writes the IDs of interaction, then gene symbols of related proteins in a line.
	 * @param matches pattern search result
	 * @param out output stream
	 */
	@Override
	public void writeResult(Map<BioPAXElement, List<Match>> matches, OutputStream out)
		throws IOException{
		
		System.out.println();
		System.out.println("RelatedGenesOfInteractionsMiner="+main.PathwayMatrix_1_1.processingMiner+
				"	"+this.getName()+"	BiochemicalReaction -> NOT USED right now!");
		
		
		OutputStreamWriter writer = new OutputStreamWriter(out);

		for (BioPAXElement ele : matches.keySet())
		{
			Set<String> syms = new HashSet<String>();

			for (Match m : matches.get(ele))
			{
				ProteinReference pr = (ProteinReference) m.get("PR", getPattern());

				String sym = getGeneSymbol(pr);
				if (sym != null) syms.add(sym);
			}

			if (syms.size() > 1)
			{
				writer.write("\n" + ele.getRDFId());
				//System.out.print(ele.getRDFId());
				
				for (Object o : controlAcc.getValueFromBean(ele))
				{
					Control ctrl = (Control) o;
					writer.write(" " + ctrl.getRDFId());
				//	System.out.print(" *" + ctrl.getRDFId());
				}

				for (String sym : syms)
				{
					writer.write("\t" + sym);
					System.out.print("\t" + sym);
					
				}
			//	System.out.println();
				
			}
		}
		System.out.println();
		
		writer.flush();
	}

} public class SIFInteraction implements Comparable
{
	public Set<BioPAXElement> sourceERs;
	public Set<BioPAXElement> targetERs;
	public Set<BioPAXElement> sourcePEs;
	public Set<BioPAXElement> targetPEs;
	public String sourceID;
	public String targetID;
	public SIFType type;
	public Set<BioPAXElement> mediators;

	public SIFInteraction(BioPAXElement sourceER, BioPAXElement targetER, SIFType type,
		Set<BioPAXElement> mediators, Set<BioPAXElement> sourcePEs, Set<BioPAXElement> targetPEs,
		IDFetcher fetcher)
	{
		sourceID = fetcher.fetchID(sourceER);
		targetID = fetcher.fetchID(targetER);

		this.sourceERs = new HashSet<BioPAXElement>();
		this.targetERs = new HashSet<BioPAXElement>();

		if (!type.isDirected())
		{
			if (sourceID != null && targetID != null && sourceID.compareTo(targetID) > 0)
			{
				String tempID = sourceID;
				sourceID = targetID;
				targetID = tempID;

				BioPAXElement temp = sourceER;
				sourceER = targetER;
				targetER = temp;
			}
		}

		this.sourceERs.add(sourceER);
		this.targetERs.add(targetER);
		this.type = type;

		this.mediators = mediators;
		this.sourcePEs = sourcePEs;
		this.targetPEs = targetPEs;
	}

	public boolean hasIDs()
	{
		return sourceID != null && targetID != null;
	}

	@Override
	public int hashCode()
	{
		return sourceID.hashCode() + targetID.hashCode() + type.hashCode();
	}

	@Override
	public boolean equals(Object obj)
	{
		if (obj instanceof SIFInteraction)
		{
			SIFInteraction i = (SIFInteraction) obj;

			return i.type.equals(type) && i.sourceID.equals(sourceID) && i.targetID.equals(targetID);
		}

		return false;
	}

	@Override
	public int compareTo(Object o)
	{
		if (o instanceof SIFInteraction)
		{
			SIFInteraction i = (SIFInteraction) o;

			return (sourceID + targetID + type.getTag()).compareTo(
				i.sourceID + i.targetID + i.type.getTag());
		}

		return 0;
	}

	/**
	 * Merges publications of the parameter equivalent sif with this one.
	 * @param equivalent the equivalent sif interaction to get its publications.
	 */
	public void mergeWith(SIFInteraction equivalent)
	{
		if (!this.equals(equivalent))
			throw new IllegalArgumentException("SIF interactions are not equivalent.");

		sourceERs.addAll(equivalent.sourceERs);
		targetERs.addAll(equivalent.targetERs);

		sourcePEs.addAll(equivalent.sourcePEs);
		targetPEs.addAll(equivalent.targetPEs);

		if (mediators == null) mediators = equivalent.mediators;
		else if (equivalent.mediators != null)
		{
			mediators.addAll(equivalent.mediators);
		}
	}

	@Override
	public String toString()
	{
		return toString(false);
	}

	public String toString(boolean withMediators)
	{
		String s = sourceID + "\t" + type.getTag() + "\t" + targetID;

		if (withMediators)
		{
			String m = getMediatorsInString();
			if (!m.isEmpty()) s += "\t" + m;
		}

		return s;
	}

	/**
	 * Collects IDs of mediators.
	 * @return mediator IDs
	 */
	public List<String> getMediatorIDs()
	{
		List<String> ids = new ArrayList<String>(mediators.size());

		for (BioPAXElement ele : mediators)
		{
			ids.add(ele.getRDFId());
		}
		return ids;
	}

	/**
	 * Gets the mediator IDs in a String with a space between each ID.
	 */
	public String getMediatorsInString()
	{
		String m = "";
		for (String mid : getMediatorIDs())
		{
			m+= " " + mid;
		}
		return m.trim();
	}

	/**
	 * Collects PMIDs from mediators.
	 * @return PMIDs
	 */
	public List<String> getPubmedIDs()
	{
		if (mediators == null) return Collections.emptyList();

		Set<String> set = harvestPMIDs(harvestPublicationXrefs(
			mediators.toArray(new BioPAXElement[mediators.size()])));

		List<String> list = new ArrayList<String>(set);
		Collections.sort(list);

		return list;
	}

	private static final PathAccessor xrefAcc = new PathAccessor("XReferrable/xref:PublicationXref");
	private static final PathAccessor evidAcc = new PathAccessor("Observable/evidence/xref:PublicationXref");

	/**
	 * Collects publication xrefs of the given elements.
	 * @param ele element array
	 * @return publication xrefs
	 */
	private Set<PublicationXref> harvestPublicationXrefs(BioPAXElement... ele)
	{
		Set<PublicationXref> set = new HashSet<PublicationXref>();

		for (Object o : xrefAcc.getValueFromBeans(Arrays.asList(ele)))
		{
			set.add((PublicationXref) o);
		}
		for (Object o : evidAcc.getValueFromBeans(Arrays.asList(ele)))
		{
			set.add((PublicationXref) o);
		}
		return set;
	}

	/**
	 * Collects PubMed IDs fromt the given publication xrefs.
	 * @param xrefs publication xrefs
	 * @return PMIDs
	 */
	private Set<String> harvestPMIDs(Set<PublicationXref> xrefs)
	{
		Set<String> set = new HashSet<String>();

		for (PublicationXref xref : xrefs)
		{
			if (xref.getDb() != null && xref.getDb().equalsIgnoreCase("pubmed"))
				if (xref.getId() != null && !xref.getId().isEmpty())
					set.add(xref.getId());
		}
		return set;
	}

	private static final PathAccessor pathwayAcc1 = new PathAccessor("Interaction/pathwayComponentOf*");
	private static final PathAccessor pathwayAcc2 = new PathAccessor("Interaction/stepProcessOf/pathwayOrderOf");

	/**
	 * Collects Pathway objects that the Interactions among the mediators are members.
	 * @return related pathways
	 */
	public Set<Pathway> getPathways()
	{
		Set<Pathway> set = new HashSet<Pathway>();

		for (Object o : pathwayAcc1.getValueFromBeans(mediators))
		{
			set.add((Pathway) o);
		}
		for (Object o : pathwayAcc2.getValueFromBeans(mediators))
		{
			set.add((Pathway) o);
		}

		return set;
	}

	/**
	 * Collects the names of the related pathways.
	 * @return pathway names
	 */
	public List<String> getPathwayNames()
	{
		Set<Pathway> set = getPathways();
		List<String> names = new ArrayList<String>();

		for (Pathway p : set)
		{
			String name = p.getDisplayName();
			if (!names.contains(name)) names.add(name);
		}

		Collections.sort(names);
		return names;
	}

	private static final PathAccessor dataSourceAcc = new PathAccessor("Entity/dataSource/displayName");

	/**
	 * Collects data source names (Provenance display names) of mediators.
	 * @return related data sources
	 */
	public Set<String> getDataSources()
	{
		Set<String> set = new HashSet<String>();

		for (Object o : dataSourceAcc.getValueFromBeans(mediators))
		{
			set.add((String) o);
		}

		return set;
	}

	private static final PathAccessor locAcc = new PathAccessor("PhysicalEntity/cellularLocation/term");

	/**
	 * Collects cellular location terms of target objects.
	 * @return cellular locations
	 */
	public Set<String> getCellularLocationsOfTarget()
	{
		return getCellularLocations(targetPEs);
	}

	/**
	 * Collects cellular location terms of source objects.
	 * @return cellular locations
	 */
	public Set<String> getCellularLocationsOfSource()
	{
		return getCellularLocations(sourcePEs);
	}

	/**
	 * Collects cellular location terms of related objects.
	 * @return cellular locations
	 */
	private Set<String> getCellularLocations(Set<BioPAXElement> eles)
	{
		Set<String> set = new HashSet<String>();

		for (Object o : locAcc.getValueFromBeans(eles))
		{
			set.add((String) o);
		}

		return set;
	}



	//---- Section: Static methods ----------------------------------------------------------------|

	/**
	 * Collects and sorts sourceID and targetID of the given collection of sif interactions.
	 * @param sifInts interactions to consider
	 * @param type types of interest, all types accepted if empty
	 * @return sorted source and target IDS
	 */
	public static List<String> getSortedGeneNames(Collection<SIFInteraction> sifInts,
		SIFType... type)
	{
		Set<String> genes = new HashSet<String>();
		Set<SIFType> types = new HashSet<SIFType>(Arrays.asList(type));

		// collect gene names

		for (SIFInteraction sifInt : sifInts)
		{
			if (!types.isEmpty() && !types.contains(sifInt.type)) continue;

			genes.add(sifInt.sourceID);
			genes.add(sifInt.targetID);
		}

		// sort all gene names

		List<String> names = new ArrayList<String>(genes);
		Collections.sort(names);

		return names;
	}

	/**
	 * Converts the given collection of interactions into an adjacency matrix.
	 * @param sifInts interactions to consider
	 * @param type types of interest, all types accepted if empty
	 * @return the sif network as adjacency matrix
	 */
	public static boolean[][] convertToAdjacencyMatrix(Collection<SIFInteraction> sifInts,
		SIFType... type)
	{
		Set<SIFType> types = new HashSet<SIFType>(Arrays.asList(type));

		// collect gene names
		List<String> names = getSortedGeneNames(sifInts, type);

		// record name indexes for efficient lookup

		Map<String, Integer> name2ind = new HashMap<String, Integer>();
		int i = 0;
		for (String name : names)
		{
			name2ind.put(name, i++);
		}

		// generate adjacency matrix

		boolean[][] matrix = new boolean[names.size()][names.size()];
		for (boolean[] m : matrix) Arrays.fill(m, false);

		for (SIFInteraction sifInt : sifInts)
		{
			if (!types.isEmpty() && !types.contains(sifInt.type)) continue;

			matrix[name2ind.get(sifInt.sourceID)][name2ind.get(sifInt.targetID)] = true;

			if (!sifInt.type.isDirected())
			{
				matrix[name2ind.get(sifInt.targetID)][name2ind.get(sifInt.sourceID)] = true;
			}
		}

		return matrix;
	}
} public class UbiquitousIDMiner extends MinerAdapter
{
	/**
	 * Constructor that sets name and description.
	 */
	public UbiquitousIDMiner()
	{
		super("ubiquitous-molecule-lister", "Finds small molecules that participate in at least " +
			"50 Conversions. Writes down IDs of these molecules to the output file, one ID per " +
			"line.");
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		Pattern p = new Pattern(SmallMoleculeReference.class, "SMR");
		p.add(new Size(
			new PathConstraint("SmallMoleculeReference/entityReferenceOf/participantOf:Conversion"),
			50, Size.Type.GREATER_OR_EQUAL), "SMR");
		return p;
	}

	/**
	 * Writes the result as "A B", where A and B are gene symbols, and whitespace is tab.
	 * @param matches pattern search result
	 * @param out output stream
	 */
	@Override
	public void writeResult(Map<BioPAXElement, List<Match>> matches, OutputStream out)
		throws IOException
	{
		writeResultDetailed(matches, out, 1);
	}

	/**
	 * Gets header of the result file.
	 * @return header
	 */
	@Override
	public String getHeader()
	{
		return "IDs of ubiquitous elements";
	}

	/**
	 * Gets the ids of the small molecule reference and its physical entities.
	 * @param m current match
	 * @param col current column
	 * @return ubique IDs
	 */
	@Override
	public String getValue(Match m, int col)
	{
		assert col == 0;

		return getRelatedIDs((SmallMoleculeReference) m.get("SMR", getPattern()));
	}

	/**
	 * Gets IDs of the small molecule reference and its physical entities.
	 * @param smr small molecule reference
	 * @return related IDs
	 */
	private String getRelatedIDs(SmallMoleculeReference smr)
	{
		String ids = smr.getRDFId();

		for (Object o : new PathAccessor(
			"SmallMoleculeReference/entityReferenceOf").getValueFromBean(smr))
		{
			SimplePhysicalEntity spe = (SimplePhysicalEntity) o;
			ids += "\n" + spe.getRDFId();
		}
		return ids;
	}
} public class UsedToProduceMiner extends AbstractSIFMiner
{
	/**
	 * Constructor that sets sif type.
	 */
	public UsedToProduceMiner()
	{
		super(SIFEnum.USED_TO_PRODUCE);
	}

	/**
	 * Constructs the pattern.
	 * @return pattern
	 */
	@Override
	public Pattern constructPattern()
	{
		return PatternBox.usedToProduce(blacklist);
	}

	@Override
	public String getSourceLabel()
	{
		return "SMR1";
	}

	@Override
	public String getTargetLabel()
	{
		return "SMR2";
	}

	@Override
	public String[] getMediatorLabels()
	{
		return new String[]{"Conv"};
	}

	@Override
	public String[] getSourcePELabels()
	{
		return new String[]{"SPE1", "PE1"};
	}

	@Override
	public String[] getTargetPELabels()
	{
		return new String[]{"SPE2", "PE2"};
	}
} public class Blacklist
{
	/**
	 * Holds ID of blacklisted small molecule's reference. Maps them to the context of ubiquity.
	 * When the context is both INPUT and OUTPUT, it is represented with a null value.
	 */
	private Map<String, RelType> context;

	/**
	 * Maps IDs of blacklisted small molecule references to their ubiquity scores.
	 */
	private Map<String, Integer> score;

	/**
	 * The deliminator string in the data.
	 */
	private static final String DELIM = "\t";

	/**
	 * Constructor for a blank blacklist.
	 */
	public Blacklist()
	{
		context = new HashMap<String, RelType>();
		score = new HashMap<String, Integer>();
	}

	/**
	 * Constructor with resource file name.
	 */
	public Blacklist(String filename)
	{
		this();
		load(filename);
	}

	/**
	 * Constructor with resource input stream.
	 */
	public Blacklist(InputStream is)
	{
		this();
		load(is);
	}

	//----- Section: Input / Output ---------------------------------------------------------------|

	/**
	 * Reads data from the given file.
	 */
	private void load(String filename)
	{
		try
		{
			load(new FileInputStream(filename));
		}
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
		}
	}
	/**
	 * Reads data from the input stream and loads itself.
	 */
	private void load(InputStream is)
	{
		try
		{
			BufferedReader reader = new BufferedReader(new InputStreamReader(is));

			for (String line = reader.readLine(); line != null; line = reader.readLine())
			{
				String[] tok = line.split(DELIM);
				if (tok.length >= 3)
				{
					addEntry(tok[0], Integer.parseInt(tok[1]), convertContext(tok[2]));
				}
			}

			reader.close();
		}
		catch (Exception e)
		{
			e.printStackTrace();
			context = null;
			score = null;
		}
	}

	/**
	 * Adds a new blacklisted ID.
	 * @param id ID of the blacklisted molecule
	 * @param score the ubiquity score
	 * @param context context of ubiquity
	 */
	public void addEntry(String id, int score, RelType context)
	{
		this.score.put(id, score);
		this.context.put(id, context);
	}

	/**
	 * Gets the IDs of the blacklisted molecules.
	 */
	public Set<String> getListed()
	{
		return score.keySet();
	}

	/**
	 * Dumps data to the given file.
	 */
	public void write(String filename)
	{
		try
		{
			write(new FileOutputStream(filename));
		}
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * Dumps data to the given output stream.
	 */
	public void write(OutputStream os)
	{
		List<String> ids = new ArrayList<String>(score.keySet());
		final Map<String, Integer> score = this.score;
		Collections.sort(ids, new Comparator<String>()
		{
			@Override
			public int compare(String o1, String o2)
			{
				return score.get(o2).compareTo(score.get(o1));
			}
		});

		try
		{
			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os));

			boolean notFirst = false;

			for (String id : ids)
			{
				if (notFirst) writer.write("\n");
				else notFirst = true;

				writer.write(id + DELIM + score.get(id) + DELIM + convertContext(context.get(id)));
			}

			writer.close();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * Converts enum context to text.
	 * @param type context
	 * @return text value
	 */
	private String convertContext(RelType type)
	{
		if (type == null) return "B";

		switch (type)
		{
			case INPUT: return "I";
			case OUTPUT: return "O";
			default: return "B";
		}
	}

	/**
	 * Converts text context to enum.
	 * @param type context
	 * @return enum value
	 */
	private RelType convertContext(String type)
	{
		if (type.equals("I")) return RelType.INPUT;
		if (type.equals("O")) return RelType.OUTPUT;
		if (type.equals("B")) return null;
		throw new IllegalArgumentException("Unknown context: " + type);
	}

	// --------- Section: Accessory methods -------------------------------------------------------|

	/**
	 * Gets the subset with the least score.
	 */
	private Set<String> getLeastUbique(Collection<String> ids)
	{
		Set<String> select = new HashSet<String>();

		int s = getLeastScore(ids);

		for (String id : ids)
		{
			if (score.get(id) == s) select.add(id);
		}

		return select;
	}

	/**
	 * Gets the least score of the given ids.
	 */
	private int getLeastScore(Collection<String> ids)
	{
		int s = Integer.MAX_VALUE;

		for (String id : ids)
		{
			if (score.get(id) < s) s = score.get(id);
		}

		return s;
	}

	/**
	 * Gets the context of the ubiquity of the ID. Be careful with the result. If the result is
	 * null, then either the ID may not be ubique, or the ID may be ubique without a context (which
	 * means in both contexts).
	 * @param id ID to check
	 * @return context of ubiquity
	 */
	public RelType getContext(String id)
	{
		return context.get(id);
	}

	/**
	 * Checks if the given ID is blacklisted in at least one context.
	 */
	private boolean isUbique(String id)
	{
		return isUbique(id, null);
	}

	/**
	 * Checks if the given ID is blacklisted in both contexts together.
	 */
	private boolean isUbiqueInBothContexts(String id)
	{
		return context.containsKey(id) && context.get(id) == null;
	}

	/**
	 * Checks if the given ID is blacklisted in the given context.
	 */
	private boolean isUbique(String id, RelType context)
	{
		if (context == null) return this.context.containsKey(id);

		if (!isUbique(id)) return false;

		RelType ctx = this.context.get(id);
		return ctx == null || ctx.equals(context);
	}

	/**
	 * Checks if the given entity is blacklisted in at least one context.
	 */
	public boolean isUbique(PhysicalEntity pe)
	{
		String id = getSMRID(pe);
		return id != null && isUbique(id);
	}

	/**
	 * Checks if the given entity is blacklisted in both context together.
	 */
	public boolean isUbiqueInBothContexts(PhysicalEntity pe)
	{
		String id = getSMRID(pe);
		return id != null && isUbiqueInBothContexts(id);
	}

	/**
	 * Checks if the given entity is blacklisted for the given Conversion assuming the Conversion
	 * flows towards the given direction, and the entity is in given context.
	 */
	public boolean isUbique(PhysicalEntity pe, Conversion conv, ConversionDirectionType dir,
		RelType context)
	{
		String id = getSMRID(pe);
		if (id == null) return false;

		if (dir == null)
			throw new IllegalArgumentException("The conversion direction has to be specified.");

		if (context == null)
			throw new IllegalArgumentException("The context has to be only one type.");

		Set<PhysicalEntity> parts;

		if (dir == ConversionDirectionType.REVERSIBLE)
		{
			if (conv.getLeft().contains(pe)) parts = conv.getLeft();
			else if (conv.getRight().contains(pe)) parts = conv.getRight();
			else throw new IllegalArgumentException("The PhysicalEntity has to be at least one " +
					"side of the Conversion");
		}
		else
		{
			parts = dir == ConversionDirectionType.LEFT_TO_RIGHT ?
				context == RelType.INPUT ? conv.getLeft() : conv.getRight() :
				context == RelType.OUTPUT ? conv.getLeft() : conv.getRight();
		}

		// if the Conversion direction is reversible, then don't mind the current context
		if (dir == ConversionDirectionType.REVERSIBLE)
			return getUbiques(parts, null).contains(pe);
		else return getUbiques(parts, context).contains(pe);
	}

	/**
	 * Gets the ID of the reference of the given entity if it is a small molecule.
	 */
	private String getSMRID(PhysicalEntity pe)
	{
		if (pe instanceof SmallMolecule)
		{
			EntityReference er = ((SmallMolecule) pe).getEntityReference();
			if (er != null) return er.getRDFId();
		}
		return null;
	}

	/**
	 * Gets the ubiquitous small molecules among the given set and in the given context. It is
	 * assumed that the given set is either left or right of a Conversion. If there is no
	 * non-ubiquitous element in the set, then the least ubique(s) are removed from the result.
	 * @param entities left or right of a conversion
	 * @param context are these entities input or output
	 * @return ubiquitous small molecules in the given context
	 */
	public Collection<SmallMolecule> getUbiques(Set<PhysicalEntity> entities, RelType context)
	{
		Map<String, SmallMolecule> ubiques = new HashMap<String, SmallMolecule>();
		boolean allUbiques = true;

		for (PhysicalEntity pe : entities)
		{
			if (pe instanceof SmallMolecule)
			{
				EntityReference er = ((SmallMolecule) pe).getEntityReference();

				if (er != null && isUbique(er.getRDFId(), context))
				{
					ubiques.put(er.getRDFId(), (SmallMolecule) pe);
				}
				else
				{
					allUbiques = false;
				}
			}
			else allUbiques = false;
		}

		if (allUbiques && !ubiques.isEmpty())
		{
			Set<String> least = getLeastUbique(ubiques.keySet());
			for (String id : least)
			{
				ubiques.remove(id);
			}
		}

		return ubiques.values();
	}

	/**
	 * Gets the non-ubiquitous physical entities in the given set and in the given context. It is
	 * assumed that the given set is either left or right of a Conversion. If there is no
	 * non-ubiquitous element in the set, then the least ubique(s) are added to the result.
	 * @param entities left or right of a conversion
	 * @param ctx are these entities input or output
	 * @return non-ubiquitous physical entities in the given context
	 */
	public Set<PhysicalEntity> getNonUbiques(Set<PhysicalEntity> entities, RelType ctx)
	{
		Collection<SmallMolecule> ubiques = getUbiques(entities, ctx);
		if (ubiques.isEmpty()) return entities;

		Set<PhysicalEntity> result = new HashSet<PhysicalEntity>(entities);
		result.removeAll(ubiques);
		return result;
	}

	public Set getNonUbiqueObjects(Set objects)
	{
		Set result = new HashSet();
		for (Object o : objects)
		{
			if (o instanceof SmallMolecule && !isUbique((SmallMolecule) o)) result.add(o);
		}
		return result;
	}
} public class HGNC
{
	private static Map<String, String> sym2id;
	private static Map<String, String> id2sym;
	private static Map<String, String> old2new;

	public static void main(String[] args)
	{
		System.out.println(getSymbol("PKCA"));
	}

	public static String getSymbol(String idOrSymbol)
	{
		if (id2sym.containsKey(idOrSymbol)) return id2sym.get(idOrSymbol);
		else if (sym2id.containsKey(idOrSymbol)) return idOrSymbol;
		else if (old2new.containsKey(idOrSymbol)) return old2new.get(idOrSymbol);
		else if (!idOrSymbol.toUpperCase().equals(idOrSymbol))
			return getSymbol(idOrSymbol.toUpperCase());
		else return null;
	}

	static
	{
		try
		{
			sym2id = new HashMap<String, String>();
			id2sym = new HashMap<String, String>();
			old2new = new HashMap<String, String>();
			BufferedReader reader = new BufferedReader(new InputStreamReader(
				HGNC.class.getResourceAsStream("hgnc.txt")));

			reader.readLine(); //skip header
			for (String line = reader.readLine(); line != null; line = reader.readLine())
			{
				String[] token = line.split("\t");
				String sym = token[1];
				String id = token[0];
				sym2id.put(sym, id);
				id2sym.put(id, sym);

				if (token.length > 2)
				{
					String olds = token[2];
					for (String old : olds.split(","))
					{
						old = old.trim();
						old2new.put(old, sym);
					}
				}
			}
			reader.close();
		}
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}
} public class Venn_Overview{
	int count = 0;
	public String[][] data;
    public double[] areas;
    
    public double[][] centers;
    public double[] diameters;
    public String[] labels;
    private int size;
    private double mins, maxs;
    public PApplet parent;
    public int brushing=-1;
    public float bx=0;
    public float by=0;
    public float br=0;
    
    public ArrayList<String>[] pair2;
	public boolean[] deactive;
   
	public static int numMinerContainData=-1;
	public static int[] minerGlobalIDof;
	public static float currentSize;
	
	
	public Venn_Overview(PApplet pa) {
		parent = pa;
		centers = new double[0][0];	
	}  
    
	public void initialize() {
		// Select the list of miner
		numMinerContainData = 0;
		for (int i=0;i<minerList.size();i++){
			if (main.PathwayMatrix_1_1.pairs[i].size()>0)
				numMinerContainData++;
		}	
		if (numMinerContainData==0) return;
			
		//System.out.println(minerList.size()+" numMiner="+numMiner);
		
		pair2 = new ArrayList[numMinerContainData];
		minerGlobalIDof = new int[numMinerContainData];
		minerNames = new String[numMinerContainData];
		int count =0;
		for (int i=0;i<minerList.size();i++){
			if (main.PathwayMatrix_1_1.pairs[i].size()>0){
				minerGlobalIDof[count] = i;
				pair2[count] = main.PathwayMatrix_1_1.pairs[i];
				minerNames[count] = (" "+minerList.get(i));
				minerNames[count] = minerNames[count].replace("controls-state-change-ofcontrols-phosphorylation-of", "controls-phosphorylation-of");
				count++;
			}	           
		}
		deactive = new boolean[numMinerContainData];
		
	}
		
	public void compute() {
		
		// Obtain relation of intersections
		ArrayList<String> aData = new ArrayList<String>();
		ArrayList<Integer> aAreas = new ArrayList<Integer>();
		for (int i =0; i< numMinerContainData; i++){
			for (int j =i+1; j< numMinerContainData; j++){
				int countRelations=0;
				// Pair 2 is a new array
				for (int i2 =0; i2< pair2[i].size(); i2++){
					for (int j2 =0; j2< pair2[j].size(); j2++){
						if (pair2[i].get(i2).equals(pair2[j].get(j2))){
							countRelations++;		
						}
					}
				}	
				if (countRelations>0){
					aData.add(minerNames[i]+"&"+minerNames[j]);
					aAreas.add(countRelations);
				}
			}
		}
		
		data = new String[numMinerContainData+aData.size()][1];
		areas =  new double[numMinerContainData+aData.size()];		
		for (int i =0; i< numMinerContainData; i++){
			data[i][0] = minerNames[i];
			areas[i]=pair2[i].size();
		}
		for (int i =0; i< aData.size(); i++){
			data[i+numMinerContainData][0] = aData.get(i);
			areas[i+numMinerContainData] = aAreas.get(i);
		}
			
		
		
		/*
		System.out.println();
		System.out.println("****** PRINT Venn Overview   numMiner="+numMiner);
		for (int i =0; i< areas.length; i++){
			System.out.println("\t"+data[i][0]+"	"+areas[i]);
		}
		System.out.println();
		*/
			
       VennData dv = new VennData(data, areas);
	   VennAnalytic va = new VennAnalytic();
       VennDiagram venn = va.compute(dv);
     	
       centers = venn.centers;
       diameters = venn.diameters;
       labels = venn.circleLabels;
       mins = Double.POSITIVE_INFINITY;
       maxs = Double.NEGATIVE_INFINITY;
       for (int i = 0; i < centers.length; i++) {
    	   double margin = diameters[i] / 2;
           mins = Math.min(centers[i][0] - margin, mins);
           mins = Math.min(centers[i][1] - margin, mins);
           maxs = Math.max(centers[i][0] + margin, maxs);
           maxs = Math.max(centers[i][1] + margin, maxs);
       }
	}
	
	public static int globalToLocal(int id) {
		if (minerGlobalIDof==null) return -99;
		for (int i=0;i<minerGlobalIDof.length;i++){
			if (Venn_Overview.minerGlobalIDof[i]==id){
				return i;
			}	
		}
		return -100;
	}
	
	/*
	public static Color getMinerColor(int miner) {
		Color color = Color.WHITE;
		if (minerNames==null || miner>=minerNames.length || miner<0)
			return Color.GRAY;
		String name = minerNames[miner];
		if (name==null)
			return Color.WHITE;
		if (name.equals("in-complex-with"))
			color = Color.CYAN;
		else if (name.equals("neighbor-of"))
			color = Color.BLUE;
		else if (name.equals("controls-state-change-of"))
			color = Color.RED;
		else if (name.equals("directed-relations"))
			color = Color.GREEN;
		else if (name.equals("chemical-affects-through-binding"))
			color = Color.YELLOW;	
		else if (name.equals("consumption-controlled-by"))
			color = Color.MAGENTA;	
		else if (name.equals("controls-transport-of"))
			color = Color.PINK;	
		return color;
	}*/
	
	/*
	public static Color getMinerColor(int miner) {
		Color color = Color.ORANGE;
		if (minerNames==null || miner>=minerNames.length || miner<0)
			return Color.GRAY;
		String name = minerNames[miner];
		if (name==null)
			return Color.BLACK;
		if (name.equals("in-complex-with"))
			color = new Color(0,200,200);
		else if (name.equals("neighbor-of"))
			color = Color.BLUE;
		else if (name.equals("controls-state-change-of"))
			color = new Color(220,0,0);
		else if (name.equals("directed-relations"))
			color = new Color(50,180,0); //color = Color.GREEN;
		else if (name.equals("chemical-affects-through-binding"))
			color = new Color(200,200,0); //color = Color.YELLOW;	
		else if (name.equals("consumption-controlled-by"))
			color = Color.MAGENTA.darker();	
		else if (name.equals("controls-transport-of"))
			color = Color.PINK.darker();	
		else if (name.equals("use-to-produce"))
			color = Color.PINK.darker();	
		return color;
	}
	*/
	
	public void draw(float xPanelRight, float yy4, int numSongs) {
		parent.noStroke();
		parent.textAlign(PApplet.CENTER);
		parent.textSize(12);
		
		brushing=-1;
		size = 400;
        for (int i = 0; i < centers.length; i++) {
            double xi = (centers[i][0] - mins) / (maxs - mins);
            double yi = (centers[i][1] - mins) / (maxs - mins);
            double pi = diameters[i] / (maxs - mins);
            int radius = (int) (pi * size);
            float x = xPanelRight+10+(int) (xi * size);
            float y = yy4 + (int) (yi * size);
            Color color = new Color(main.PathwayMatrix_1_1.colorRelations[minerGlobalIDof[i]]);  
            
             // if (deactive[i])
           // 	color = new Color(255,255,255,10);
            
            if (minerGlobalIDof[i]==main.PathwayMatrix_1_1.currentRelation){
            	currentSize = radius;
            }
            
            /*
            if (labels[i].contains("controls-production-of")){
            	x=x-50;
            }
            else if (labels[i].contains("consumption-con")){
            	x=x-60;
            	y=y+60;
            }
            else if (labels[i].contains("used-to-produce")){
            	x=x-20;
            	y=y-50;
            	
            }
           */
            
           if (radius>0){
        	   	parent.fill(color.getRed(), color.getGreen(), color.getBlue(),180);
        	   	if (PApplet.dist(x, y, parent.mouseX, parent.mouseY)<radius/2 && brushing<0){
        	   		parent.fill(color.getRed(), color.getGreen(), color.getBlue(),255);
            	   	brushing=i;
                }
              
        	   	parent.ellipse(x , y , radius, radius);
        	   	parent.fill(0);
        	   	//if (deactive[i])
	            //	parent.fill(50,50,50,50);
	            parent.text(labels[i], x , y+4);
	          //  System.out.println(i+"	labels[i] = "+labels[i]);
           }
        }
        if (minerGlobalIDof!=null && brushing>=0)
        	main.PathwayMatrix_1_1.currentRelation = minerGlobalIDof[brushing];
        else
        	main.PathwayMatrix_1_1.currentRelation = -1;
        
        parent.fill(Color.GRAY.getRGB());
		parent.textSize(14);
		parent.textAlign(PApplet.LEFT);
	}

	
	 public static Color rainbow(double value, float transparency) {
	        /* blue to red, approximately by wavelength */
	        float v = (float) value * 255.f;
	        float vmin = 0;
	        float vmax = 255;
	        float range = vmax - vmin;

	        if (v < vmin + 0.25f * range)
	            return new Color(0.f, 4.f * (v - vmin) / range, 1.f, transparency);
	        else if (v < vmin + 0.5 * range)
	            return new Color(0.f, 1.f, 1.f + 4.f * (vmin + 0.25f * range - v) / range, transparency);
	        else if (v < vmin + 0.75 * range)
	            return new Color(4.f * (v - vmin - 0.5f * range) / range, 1.f, 0, transparency);
	        else
	            return new Color(1.f, 1.f + 4.f * (vmin + 0.75f * range - v) / range, 0, transparency);
	    }
	 
	public boolean mouseClicked() {
		if (brushing>=0){
			main.PathwayMatrix_1_1.currentRelation = minerGlobalIDof[brushing];
			deactive[brushing] =! deactive[brushing];
			return true;
		}
		else{
			main.PathwayMatrix_1_1.currentRelation = -2;
			return false;
		}	
	}

	
	
	// zoom in or out:
	void mouseWheel(int delta) {
		if (delta > 0) {
		} else if (delta < 0) {
		}
	}

} public static ArrayList<Gene> ggg = new ArrayList<Gene>();
 public class Venn_Overview{
	int count = 0;
	public String[][] data;
    public double[] areas;
    
    public double[][] centers;
    public double[] diameters;
    public String[] labels;
    private int size;
    private double mins, maxs;
    public PApplet parent;
    public int brushing=-1;
    public float bx=0;
    public float by=0;
    public float br=0;
    
    public ArrayList<String>[] pair2;
	public boolean[] deactive;
   
	public static int numMinerContainData=-1;
	public static int[] minerGlobalIDof;
	public static float currentSize;
	
	
	public Venn_Overview(PApplet pa) {
		parent = pa;
		centers = new double[0][0];	
	}  
    
	public void initialize() {
		// Select the list of miner
		numMinerContainData = 0;
		for (int i=0;i<minerList.size();i++){
			if (main.PathwayMatrix_1_1.pairs[i].size()>0)
				numMinerContainData++;
		}	
		if (numMinerContainData==0) return;
			
		//System.out.println(minerList.size()+" numMiner="+numMiner);
		
		pair2 = new ArrayList[numMinerContainData];
		minerGlobalIDof = new int[numMinerContainData];
		minerNames = new String[numMinerContainData];
		int count =0;
		for (int i=0;i<minerList.size();i++){
			if (main.PathwayMatrix_1_1.pairs[i].size()>0){
				minerGlobalIDof[count] = i;
				pair2[count] = main.PathwayMatrix_1_1.pairs[i];
				minerNames[count] = (" "+minerList.get(i));
				minerNames[count] = minerNames[count].replace("controls-state-change-ofcontrols-phosphorylation-of", "controls-phosphorylation-of");
				count++;
			}	           
		}
		deactive = new boolean[numMinerContainData];
		
	}
		
	public void compute() {
		
		// Obtain relation of intersections
		ArrayList<String> aData = new ArrayList<String>();
		ArrayList<Integer> aAreas = new ArrayList<Integer>();
		for (int i =0; i< numMinerContainData; i++){
			for (int j =i+1; j< numMinerContainData; j++){
				int countRelations=0;
				// Pair 2 is a new array
				for (int i2 =0; i2< pair2[i].size(); i2++){
					for (int j2 =0; j2< pair2[j].size(); j2++){
						if (pair2[i].get(i2).equals(pair2[j].get(j2))){
							countRelations++;		
						}
					}
				}	
				if (countRelations>0){
					aData.add(minerNames[i]+"&"+minerNames[j]);
					aAreas.add(countRelations);
				}
			}
		}
		
		data = new String[numMinerContainData+aData.size()][1];
		areas =  new double[numMinerContainData+aData.size()];		
		for (int i =0; i< numMinerContainData; i++){
			data[i][0] = minerNames[i];
			areas[i]=pair2[i].size();
		}
		for (int i =0; i< aData.size(); i++){
			data[i+numMinerContainData][0] = aData.get(i);
			areas[i+numMinerContainData] = aAreas.get(i);
		}
			
		
		
		/*
		System.out.println();
		System.out.println("****** PRINT Venn Overview   numMiner="+numMiner);
		for (int i =0; i< areas.length; i++){
			System.out.println("\t"+data[i][0]+"	"+areas[i]);
		}
		System.out.println();
		*/
			
       VennData dv = new VennData(data, areas);
	   VennAnalytic va = new VennAnalytic();
       VennDiagram venn = va.compute(dv);
     	
       centers = venn.centers;
       diameters = venn.diameters;
       labels = venn.circleLabels;
       mins = Double.POSITIVE_INFINITY;
       maxs = Double.NEGATIVE_INFINITY;
       for (int i = 0; i < centers.length; i++) {
    	   double margin = diameters[i] / 2;
           mins = Math.min(centers[i][0] - margin, mins);
           mins = Math.min(centers[i][1] - margin, mins);
           maxs = Math.max(centers[i][0] + margin, maxs);
           maxs = Math.max(centers[i][1] + margin, maxs);
       }
	}
	
	public static int globalToLocal(int id) {
		if (minerGlobalIDof==null) return -99;
		for (int i=0;i<minerGlobalIDof.length;i++){
			if (Venn_Overview.minerGlobalIDof[i]==id){
				return i;
			}	
		}
		return -100;
	}
	
	/*
	public static Color getMinerColor(int miner) {
		Color color = Color.WHITE;
		if (minerNames==null || miner>=minerNames.length || miner<0)
			return Color.GRAY;
		String name = minerNames[miner];
		if (name==null)
			return Color.WHITE;
		if (name.equals("in-complex-with"))
			color = Color.CYAN;
		else if (name.equals("neighbor-of"))
			color = Color.BLUE;
		else if (name.equals("controls-state-change-of"))
			color = Color.RED;
		else if (name.equals("directed-relations"))
			color = Color.GREEN;
		else if (name.equals("chemical-affects-through-binding"))
			color = Color.YELLOW;	
		else if (name.equals("consumption-controlled-by"))
			color = Color.MAGENTA;	
		else if (name.equals("controls-transport-of"))
			color = Color.PINK;	
		return color;
	}*/
	
	/*
	public static Color getMinerColor(int miner) {
		Color color = Color.ORANGE;
		if (minerNames==null || miner>=minerNames.length || miner<0)
			return Color.GRAY;
		String name = minerNames[miner];
		if (name==null)
			return Color.BLACK;
		if (name.equals("in-complex-with"))
			color = new Color(0,200,200);
		else if (name.equals("neighbor-of"))
			color = Color.BLUE;
		else if (name.equals("controls-state-change-of"))
			color = new Color(220,0,0);
		else if (name.equals("directed-relations"))
			color = new Color(50,180,0); //color = Color.GREEN;
		else if (name.equals("chemical-affects-through-binding"))
			color = new Color(200,200,0); //color = Color.YELLOW;	
		else if (name.equals("consumption-controlled-by"))
			color = Color.MAGENTA.darker();	
		else if (name.equals("controls-transport-of"))
			color = Color.PINK.darker();	
		else if (name.equals("use-to-produce"))
			color = Color.PINK.darker();	
		return color;
	}
	*/
	
	public void draw(float xPanelRight, float yy4, int numSongs) {
		parent.noStroke();
		parent.textAlign(PApplet.CENTER);
		parent.textSize(12);
		
		brushing=-1;
		size = 400;
        for (int i = 0; i < centers.length; i++) {
            double xi = (centers[i][0] - mins) / (maxs - mins);
            double yi = (centers[i][1] - mins) / (maxs - mins);
            double pi = diameters[i] / (maxs - mins);
            int radius = (int) (pi * size);
            float x = xPanelRight+10+(int) (xi * size);
            float y = yy4 + (int) (yi * size);
            Color color = new Color(main.PathwayMatrix_1_1.colorRelations[minerGlobalIDof[i]]);  
            
             // if (deactive[i])
           // 	color = new Color(255,255,255,10);
            
            if (minerGlobalIDof[i]==main.PathwayMatrix_1_1.currentRelation){
            	currentSize = radius;
            }
            
            /*
            if (labels[i].contains("controls-production-of")){
            	x=x-50;
            }
            else if (labels[i].contains("consumption-con")){
            	x=x-60;
            	y=y+60;
            }
            else if (labels[i].contains("used-to-produce")){
            	x=x-20;
            	y=y-50;
            	
            }
           */
            
           if (radius>0){
        	   	parent.fill(color.getRed(), color.getGreen(), color.getBlue(),180);
        	   	if (PApplet.dist(x, y, parent.mouseX, parent.mouseY)<radius/2 && brushing<0){
        	   		parent.fill(color.getRed(), color.getGreen(), color.getBlue(),255);
            	   	brushing=i;
                }
              
        	   	parent.ellipse(x , y , radius, radius);
        	   	parent.fill(0);
        	   	//if (deactive[i])
	            //	parent.fill(50,50,50,50);
	            parent.text(labels[i], x , y+4);
	          //  System.out.println(i+"	labels[i] = "+labels[i]);
           }
        }
        if (minerGlobalIDof!=null && brushing>=0)
        	main.PathwayMatrix_1_1.currentRelation = minerGlobalIDof[brushing];
        else
        	main.PathwayMatrix_1_1.currentRelation = -1;
        
        parent.fill(Color.GRAY.getRGB());
		parent.textSize(14);
		parent.textAlign(PApplet.LEFT);
	}

	
	 public static Color rainbow(double value, float transparency) {
	        /* blue to red, approximately by wavelength */
	        float v = (float) value * 255.f;
	        float vmin = 0;
	        float vmax = 255;
	        float range = vmax - vmin;

	        if (v < vmin + 0.25f * range)
	            return new Color(0.f, 4.f * (v - vmin) / range, 1.f, transparency);
	        else if (v < vmin + 0.5 * range)
	            return new Color(0.f, 1.f, 1.f + 4.f * (vmin + 0.25f * range - v) / range, transparency);
	        else if (v < vmin + 0.75 * range)
	            return new Color(4.f * (v - vmin - 0.5f * range) / range, 1.f, 0, transparency);
	        else
	            return new Color(1.f, 1.f + 4.f * (vmin + 0.75f * range - v) / range, 0, transparency);
	    }
	 
	public boolean mouseClicked() {
		if (brushing>=0){
			main.PathwayMatrix_1_1.currentRelation = minerGlobalIDof[brushing];
			deactive[brushing] =! deactive[brushing];
			return true;
		}
		else{
			main.PathwayMatrix_1_1.currentRelation = -2;
			return false;
		}	
	}

	
	
	// zoom in or out:
	void mouseWheel(int delta) {
		if (delta > 0) {
		} else if (delta < 0) {
		}
	}

}
import java.awt.Color;
import java.awt.FileDialog;
import java.awt.Frame;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.biopax.paxtools.io.SimpleIOHandler;
import org.biopax.paxtools.io.sbgn.L3ToSBGNPDConverter;
import org.biopax.paxtools.io.sif.SimpleInteractionConverter;
import org.biopax.paxtools.io.sif.level3.ControlRule;
import org.biopax.paxtools.model.BioPAXElement;
import org.biopax.paxtools.model.Model;
import org.biopax.paxtools.model.level3.*;
import org.biopax.paxtools.model.level3.Process;
import org.biopax.paxtools.pattern.Match;
import org.biopax.paxtools.pattern.Pattern;
import org.biopax.paxtools.pattern.Searcher;
import org.biopax.paxtools.pattern.miner.CSCOBothControllerAndParticipantMiner;
import org.biopax.paxtools.pattern.miner.CSCOButIsParticipantMiner;
import org.biopax.paxtools.pattern.miner.CSCOThroughBindingSmallMoleculeMiner;
import org.biopax.paxtools.pattern.miner.CSCOThroughControllingSmallMoleculeMiner;
import org.biopax.paxtools.pattern.miner.CSCOThroughDegradationMiner;
import org.biopax.paxtools.pattern.miner.CatalysisPrecedesMiner;
import org.biopax.paxtools.pattern.miner.ChemicalAffectsThroughBindingMiner;
import org.biopax.paxtools.pattern.miner.ChemicalAffectsThroughControlMiner;
import org.biopax.paxtools.pattern.miner.ConsumptionControlledByMiner;
import org.biopax.paxtools.pattern.miner.ControlsDegradationIndirectMiner;
import org.biopax.paxtools.pattern.miner.ControlsExpressionMiner;
import org.biopax.paxtools.pattern.miner.ControlsExpressionWithConvMiner;
import org.biopax.paxtools.pattern.miner.ControlsPhosphorylationMiner;
import org.biopax.paxtools.pattern.miner.ControlsProductionOfMiner;
import org.biopax.paxtools.pattern.miner.ControlsStateChangeDetailedMiner;
import org.biopax.paxtools.pattern.miner.ControlsStateChangeOfMiner;
import org.biopax.paxtools.pattern.miner.ControlsTransportMiner;
import org.biopax.paxtools.pattern.miner.ControlsTransportOfChemicalMiner;
import org.biopax.paxtools.pattern.miner.DirectedRelationMiner;
import org.biopax.paxtools.pattern.miner.IDFetcher;
import org.biopax.paxtools.pattern.miner.InComplexWithMiner;
import org.biopax.paxtools.pattern.miner.InteractsWithMiner;
import org.biopax.paxtools.pattern.miner.Miner;
import org.biopax.paxtools.pattern.miner.NeighborOfMiner;
import org.biopax.paxtools.pattern.miner.ReactsWithMiner;
import org.biopax.paxtools.pattern.miner.RelatedGenesOfInteractionsMiner;
import org.biopax.paxtools.pattern.miner.SIFInteraction;
import org.biopax.paxtools.pattern.miner.UbiquitousIDMiner;
import org.biopax.paxtools.pattern.miner.UsedToProduceMiner;
import org.biopax.paxtools.pattern.util.Blacklist;
import org.biopax.paxtools.pattern.util.HGNC;
import static edu.uic.ncdm.venn.Venn_Overview.*;
import static main.PathwayMatrix_1_1.ggg;
import edu.uic.ncdm.venn.Venn_Overview;
import processing.core.*;

package main;
/*
 * DARPA project
 *
 * Copyright 2014 by Tuan Dang.
 *
 * The contents of this file are subject to the Mozilla Public License Version 2.0 (the "License")
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the License.
 */





public class PathwayMatrix_1_1 extends PApplet {
	private static final long serialVersionUID = 1L;
	public int count = 0;
	
	public static List<Miner> minerList = new ArrayList<Miner>();
	public static int currentRelation = -1;
	public static int processingMiner = 0;
	public String currentFile = "./level3/RAF-Cascade.owl";
	//public String currentFile = "./level3/RAF-MAP Kinase Cascade.owl";
	//public String currentFile = "./level3_2015/HIV_life_cycle.owl";

	
	public static ButtonBrowse buttonBrowse;
	
	// Store the genes results
	public static ArrayList<String>[] pairs;
	public static ArrayList<Integer>[][] geneRelationList;
	public static int[][] gene_gene_InComplex; 
	public static int maxGeneInComplex; 
	
	// Global data
	public static String[] minerNames;
	public static ArrayList<Gene> ggg = new ArrayList<Gene>();
	
	public static Map<Integer, Integer> leaderSortedMap;
	public static ArrayList<Integer>[] locals = null;
	
	//public static ArrayList<Integrator> iW;
	// Contains the location and size of each gene to display
	public float size=0;
	public static float marginX = 160;
	public static float marginY = 120;
	public static String message="";
	
	public ThreadLoader1 loader1=new ThreadLoader1(this);
	public Thread thread1=new Thread(loader1);
	
	public ThreadLoader3 loader3=new ThreadLoader3();
	public Thread thread3=new Thread(loader3);
	
	public ThreadLoader4 loader4=new ThreadLoader4(this);
	public Thread thread4=new Thread(loader4);
	
	// Venn
	public Venn_Overview vennOverview; 
	public int bX,bY;
	
	// Order genes
	public static PopupComplex popupComplex;
	public static PopupRelation popupRelation;
	public static PopupOrder popupOrder;
	public static CheckBox check1;
	public static CheckBox check2;
	public static CheckBox check3;
	
	// Grouping animation
	public static int stateAnimation =0;
	public static int bg =0;
	
	
	// Color of miner
	public static int[] colorRelations;
	
	
	// Allow to draw 
	public static boolean isAllowedDrawing = false;
	public static int  ccc = 0; // count to draw progessing bar
	
	public PFont metaBold = loadFont("Arial-BoldMT-18.vlw");
	
	
	// New to read data 
	public static  Map<String,String> mapProteinRDFId;
	public static  Map<String,String> mapProteinRef;
	public static  Map<String,String> mapSmallMoleculeRDFId;
	public static  Map<String,String> mapPhysicalEntity;
	public static ArrayList<Complex> complexList; 
	public static  Map<String,Integer> mapComplexRDFId_index;
	public static Set<SmallMolecule> smallMoleculeSet;
	public static ArrayList<String>[] proteinsInComplex; 
	
	
	public static void main(String args[]){
	  PApplet.main(new String[] { PathwayMatrix_1_1.class.getName() });
    }

	public void setup() {
		textFont(metaBold,14);
		size(1440, 900);
		//size(2000, 1200);
		if (frame != null) {
		    frame.setResizable(true);
		  }
		background(0);
		frameRate(12);
		curveTightness(0.7f); 
		smooth();
		
		// Get the output file
		//minerList.add(new DirectedRelationMiner());
		//minerList.add(new CSCOButIsParticipantMiner());
		//minerList.add(new CSCOBothControllerAndParticipantMiner());
		//minerList.add(new CSCOThroughControllingSmallMoleculeMiner());
		//minerList.add(new CSCOThroughBindingSmallMoleculeMiner());
		//minerList.add(new CSCOThroughDegradationMiner());
		//minerList.add(new ControlsStateChangeDetailedMiner());
		//minerList.add(new ControlsExpressionWithConvMiner());
		//minerList.add(new ControlsDegradationIndirectMiner());
		//minerList.add(new ConsumptionControlledByMiner());
		//minerList.add(new ControlsProductionOfMiner());
		//minerList.add(new ChemicalAffectsThroughBindingMiner());
		//minerList.add(new ChemicalAffectsThroughControlMiner());
		//minerList.add(new ControlsTransportOfChemicalMiner());

		minerList.add(new NeighborOfMiner());
