public class Parameters {
	static class CustomConstructor extends Constructor {
		@Override
		protected List<Object> createDefaultList(int initSize) {
			return new Vector<Object>();
		}
		
		@Override
		protected Map<Object, Object> createDefaultMap() {
			return new TreeMap<Object, Object>();
		}

		@Override
		protected Set<Object> createDefaultSet() {
			return new TreeSet<Object>();
		}
    } 
	public static String getGoldAnnotationDesc() {
		return GOLD_ANNOTATION_DESC;
	}
	public static String getLearningMethods(int i) {
		assert 0 <= i && i < LEARNING_METHODS.length;
		return LEARNING_METHODS[i];
	}
	public static String getNegativeClassLabel(){
		return NEGATIVE_CLASS_LABEL;
	}
	public static String getSearchMethods(int i) {
		assert 0 <= i && i < SEARCH_METHODS.length;
		return SEARCH_METHODS[i];
	}
	
	public static Parameters load(String yamlFile) {
		try {
			Parameters params = (Parameters) new Yaml(new CustomConstructor()).loadAs(Files.newReader(new File(yamlFile), Charset.forName("UTF-8")), Parameters.class);
			assert params != null;
			params.check();
			if(params.getVerbosity() > 2){
				System.out.print(LEARNING_METHODS[params.getLearningMethod()]);
				System.out.print(", "+UPDATE_METHODS[params.getUpdateMethod()]);
				System.out.print(", "+SEARCH_METHODS[params.getSearchMethod()]);
				if(params.useCloseFirst() || params.useSort()){
					System.out.print(" ("+params.getCloseAnnotation()+")");
				}
				
				System.out.println(" options are selected.");
			}
			return params;
		} catch (FileNotFoundException e) {
			System.err.println("Cannot find " + yamlFile);
			e.printStackTrace();
			System.exit(0);
		} catch (Exception e){
			System.err.println("Cannot interpret " + yamlFile);
			e.printStackTrace();
			System.exit(0);
		}
		assert false;
		return null;
	}
	private static final String GOLD_ANNOTATION_DESC = "GOLD";
	private static final String NEGATIVE_CLASS_LABEL = "NEGATIVE";
	private static final String[] LEARNING_METHODS = {"Perceptron", "SGDSVM", "AdaGrad", "AROW", "SCW", "DCDSSVM"};

	private static final String[] SEARCH_METHODS = {"LtoR", "RtoL", "CloseFirst", "EasyFirst", "DifficultFirst", "RandomFirst", "EntityLtoR", "EntityRtoL", "CloseFirst(RtoL)", "EasyEntityFirst", "EasyCloseFirst"};
	private static final String[] UPDATE_METHODS = {"MaxViolation", "Early", "Standard"};
	private int fvSize;
	private int fvBitSize;
	private int verbosity;
	private int beamSize;
	private int iteration;
	private int localIteration;
	private int outputInterval;	
	private int miniBatch;
	private int processors;
	
	private boolean useByte;
	private boolean useSelfRelation;
	private boolean useBestFirst;
	private boolean useParallel;
	private boolean useFullFeatures;
	private boolean useSimpleFeatures;
	private boolean useTermSurface;
	private boolean useDirectedRelation;
	private boolean useEntityTypedLabel;
	private boolean useEntityType;
	private boolean useDynamicSort;
	private boolean useGoldEntitySpan;
	private boolean useRelationTypeFilter;
	
	private boolean useLocalInit;
	private boolean useWeighting;
	private boolean useAveraging;
	private boolean useGlobalFeatures;
	private boolean useGlobalEntityFeatures;
	private boolean useGlobalRelationFeatures;
	private boolean useWeightedMargin;
	
	private double margin;
	private double averageNumWords;
	private double relWeight;
	private double globalWeight;
	private double epsilon;
	
	private double lambda;
	private int learningMethod;
	private int searchMethod;
	private int updateMethod;
	private String trainFolder;
	private String devFolder;
	private String testFolder;
	private String trainVectorFile;
	private String devVectorFile;
	
	private String modelFile;
	private String nictSynonymFile;
	private String nictVerbEntailmentFile;
	private String logBilinearFile;
	private String textExtension;
	private String annotationExtension;
	private String predAnnotationExtension;
	private String sentenceAnnotation;
	private String closeAnnotation;
	
	private Map<String, ParseParameters> parseParameters;
	
	private Multimap<String, LabelUnit> possibleLabels;
	
	private Multimap<String, String> nictVerbEntailmentMap;

	public Parameters(){
		//initialize
		setFvBitSize(20);
		setVerbosity(3);
		setBeamSize(10);
		setIteration(50);
		setOutputInterval(1);
		setMiniBatch(5);
		setProcessors(1);
		setUseByte(false);
		setUseSelfRelation(false);
		setLearningMethod(1);
		setUpdateMethod(0);
		setUseAveraging(true);
		setUseBestFirst(false);
		setUseDirectedRelation(true);
		setUseEntityType(true);
		setUseEntityTypedLabel(true);
		setUseGlobalFeatures(true);
		setUseGlobalEntityFeatures(true);
		setUseGlobalRelationFeatures(true);
		setUseGoldEntitySpan(false);
		setUseWeightedMargin(false);
		setUseFullFeatures(true);
		setUseSimpleFeatures(false);
		setUseTermSurface(true);
		setUseParallel(false);
		setUseLocalInit(false);
		setUseRelationTypeFilter(true);
		setLocalIteration(5);
		setUseWeighting(false);
		setUseDynamicSort(false);
		setMargin(1.0);
		setLambda(1.0e-4);
		setEpsilon(0.);
		setTrainFolder("train/");
		setDevFolder("dev/");
		setTestFolder("test/");
		setModelFile("model.txt");
		setTrainVectorFile(null);
		setDevVectorFile(null);
		setNictSynonymFile(null);
		setNictVerbEntailmentFile(null);
		setLogBilinearFile(null);
		setTextExtension(".split.txt");
		setAnnotationExtension(".split.ann");
		setPredAnnotationExtension(".split.pred.ann");
		setSearchMethod(2);
		setSentenceAnnotation("KNP");
		setCloseAnnotation(null);
		Map<String, ParseParameters> parseParameters = new TreeMap<String, ParseParameters>();
		parseParameters.put("KNP", ParseParameters.KNP);
		parseParameters.put("SYNCHA", ParseParameters.SYNCHA);
		setParseParameters(parseParameters);
		setAverageNumWords(0.);
		setRelWeight(1.);
		setGlobalWeight(1.);
	}
	
	public void check(){
		//TODO: check parameters
		if(this.getUseDynamicSort() && !this.useSort()){
			System.err.println("Dynamic sort is not available for the search method.");
			System.exit(-1);
		}
		if(this.getUseDynamicSort() && !this.getUseGlobalFeatures()){
			System.err.println("Dynamic sort is not available for the local features.");
			System.exit(-1);
		}
		if(this.useSGDSVM()){
			if(this.getLambda() == 1.){
				System.err.println("lambda should not be 1 for SGDSVM.");
				System.exit(-1);
			}
		}
	}
	
	public int fvSize() {
		return fvSize;
	}
	public String getAnnotationExtension() {
		return annotationExtension;
	}
	public double getAverageNumWords() {
		return this.averageNumWords;
	}
	
	public int getBeamSize() {
		return beamSize;
	}
	public String getCloseAnnotation() {
		return closeAnnotation;
	}
	public String getDevFolder() {
		return devFolder;
	}
	public String getDevVectorFile() {
		return devVectorFile;
	}

	public Set<String> getDirectNictVerbEntailments(Node node) {
		Set<String> entailments = new TreeSet<String>();
		String phrase = node.getText().replaceAll("する$", "");
		if(nictVerbEntailmentMap.containsKey(phrase)){
			entailments.add(phrase);
			for(String entailment:nictVerbEntailmentMap.get(phrase)){
				entailments.add(entailment);
			}
		}else{
			entailments.add(phrase);
		}
		return entailments;
	}
	public double getEpsilon() {
		return epsilon;
	}

	public int getFvBitSize() {
		return fvBitSize;
	}
	public double getGlobalWeight() {
		return globalWeight;
	}
	public List<String> getIgnoredAttributeTypes(String sourceDesc) {
		assert parseParameters.containsKey(sourceDesc);
		return parseParameters.get(sourceDesc).getIgnoredAttributeTypes();
	}
	public int getIteration() {
		return iteration;
	}
	
	public double getLambda() {
		return lambda;
	}
	private int getLearningMethod(){
		return learningMethod;
	}
	public int getLocalIteration() {
		return localIteration;
	}
	public double getMargin() {
		return margin;
	}
	public int getMiniBatch() {
		return miniBatch;
	}
	public String getModelFile() {
		return modelFile;
	}
	public String getNictSynonymFile() {
		return nictSynonymFile;
	}
	public String getNictVerbEntailmentFile() {
		return nictVerbEntailmentFile;
	}
	public Set<String> getNictVerbEntailments(Node word) {
		Set<String> entailments = new TreeSet<String>();
    	// longest match
		if(word.getPOS().equals("動詞")|| word.getPOS().equals("名詞")){
			List<Node> words = word.getNodes().getSentence().getWords(word.getSourceDesc());
			int last = words.indexOf(word);
			int current = last;
			while(current > 0){
				current--;
				String ppos = words.get(current).getPOS();
				if(ppos.equals("助詞")){
					current++;
					break;
    			}
    		}   
			for(;current <= last;current++){
				StringBuffer sb = new StringBuffer();
				for(int i = current;i <= last;i++){
					if(words.get(i).getRealBase().matches(" +"))continue;
					sb.append(words.get(i).getRealBase());
    			}
				String phrase = sb.toString().replaceAll("する$", "");
				if(nictVerbEntailmentMap.containsKey(phrase)){
					entailments.add(phrase);
					for(String entailment:nictVerbEntailmentMap.get(phrase)){
						entailments.add(entailment);
					}
			    	break;
    			}
    		}
			if(current == last){
				entailments.add(words.get(current).getRealBase());
			}
    	}    
		return entailments;		
	}
	public int getOutputInterval() {
		return outputInterval;
	}
	public Set<String> getParseAnnotationDescs() {
		return parseParameters.keySet();
	}
	public String getParseBaseAttributeType(String sourceDesc) {
		assert parseParameters.containsKey(sourceDesc);
		return parseParameters.get(sourceDesc).getBaseAttributeType();
	}
	public String getParseExtension(String sourceDesc) {
		assert parseParameters.containsKey(sourceDesc);
		return parseParameters.get(sourceDesc).getExtension();
	}
	public String getParseHeadAttributeType(String sourceDesc) {
		assert parseParameters.containsKey(sourceDesc);
		return parseParameters.get(sourceDesc).getHeadAttributeType();
	}
	public String getParseIdAttributeType(String sourceDesc) {
		assert parseParameters.containsKey(sourceDesc);
		return parseParameters.get(sourceDesc).getIdAttributeType();
	}
	public Map<String, ParseParameters> getParseParameters() {
		return parseParameters;
	}
	public String getParsePosAttributeType(String sourceDesc) {
		assert parseParameters.containsKey(sourceDesc);
		return parseParameters.get(sourceDesc).getPosAttributeType();
	}
	public String getParsePredicateAttributeType(String sourceDesc) {
		assert parseParameters.containsKey(sourceDesc);
		return parseParameters.get(sourceDesc).getPredicateAttributeType();
	}
	
	public String getParseSentenceTag(String sourceDesc) {
		assert parseParameters.containsKey(sourceDesc);
		return parseParameters.get(sourceDesc).getSentenceTag();
	}

	public String getParseWordTag(String sourceDesc) {
		return parseParameters.get(sourceDesc).getWordTag();
	}

	public Multimap<String, LabelUnit> getPossibleLabels() {
		return possibleLabels;
	}

	public Collection<LabelUnit> getPossibleLabels(String unitType) {
		return possibleLabels.get(unitType);
	}
	
	public String getPredAnnotationExtension() {
		return predAnnotationExtension;
	}

	public int getProcessors() {
		return processors;
	}

	public double getRelWeight(){
		return relWeight;
	}

	public int getSearchMethod() {
		return searchMethod;
	}

	public String getSentenceAnnotation() {
		return sentenceAnnotation;
	}

	public String getTestFolder() {
		return testFolder;
	}

	public String getTextExtension() {
		return textExtension;
	}
	
	public List<String> getTokenFeatureAttributeTypes(String sourceDesc) {
		assert parseParameters.containsKey(sourceDesc);
		return parseParameters.get(sourceDesc).getTokenFeatureAttributeTypes();
	}
	
	public String getTrainFolder() {
		return trainFolder;
	}
	
	public String getTrainVectorFile() {
		return trainVectorFile;
	}

	public int getUpdateMethod() {
		return updateMethod;
	}

	public boolean getUseAveraging() {
		return useAveraging;
	}

	public boolean getUseBestFirst() {
		return useBestFirst;
	}

	public boolean getUseByte() {
		return useByte;
	}	
	
	public boolean getUseDirectedRelation() {
		return useDirectedRelation;
	}
	
	public boolean getUseDynamicSort(){
		return useDynamicSort;
	}

	public boolean getUseEntityType() {
		return useEntityType;
	}
	public boolean getUseEntityTypedLabel() {
		return useEntityTypedLabel;
	}

	public boolean getUseFullFeatures() {
		return useFullFeatures;
	}

	public boolean getUseGlobalEntityFeatures() {
		return useGlobalEntityFeatures;
	}	
	
	public boolean getUseGlobalFeatures() {
		return useGlobalFeatures;
	}
	
	public boolean getUseGlobalRelationFeatures() {
		return useGlobalRelationFeatures;
	}

	public boolean getUseGoldEntitySpan(){
		return useGoldEntitySpan;
	}	

	public boolean getUseLocalInit() {
		return useLocalInit;
	}
	
	public boolean getUseParallel() {
		return useParallel;
	}

	public boolean getUseSelfRelation() {
		return useSelfRelation;
	}
	
	public boolean getUseSimpleFeatures() {
		return useSimpleFeatures;
	}
	
	public boolean getUseTermSurface() {
		return useTermSurface;
	}

	public boolean getUseWeightedMargin() {
		return useWeightedMargin;
	}
		
	public boolean getUseWeighting() {
		return useWeighting;
	}
	
	public int getVerbosity() {
		return verbosity;
	}

	public void loadModelParameters(BufferedReader reader) {
		try{
			this.setAverageNumWords(Double.valueOf(reader.readLine().trim()));
			possibleLabels = TreeMultimap.create();
			String line = reader.readLine().trim();
			String[] entries = line.split("\t");
			String type = entries[0];
			if(type.equals("RelationLabelUnit")){
				for(int i = 1;i < entries.length;i+=2){
					String[] labelUnit = entries[i+1].split("\\$");
					assert labelUnit.length > 1: entries[i+1];
					possibleLabels.put(entries[i], new RelationLabelUnit(labelUnit[0], Boolean.valueOf(labelUnit[1])));
				}
			}else if(type.equals("JointLabelUnit")){
				for(int i = 1;i < entries.length;i+=3){
					if(entries[i+1].equals("W")){
						possibleLabels.put(entries[i], new WordLabelUnit(entries[i+2]));
					}else if(entries[i+1].equals("P")){
						possibleLabels.put(entries[i], new PairLabelUnit(entries[i+2]));
					}else{
						assert false;
					}
				}
			}else{
				assert false;
			}
		
		}catch(IOException ex){
			ex.printStackTrace();
		}
	}	
	

	public boolean getUseRelationTypeFilter() {
		return useRelationTypeFilter;
	}
	public void setUseRelationTypeFilter(boolean useRelationTypeFilter) {
		this.useRelationTypeFilter = useRelationTypeFilter;
	}
	
	public void saveModelParameters(BufferedWriter writer) {
		try {
			LabelUnit firstLabelUnit = possibleLabels.values().iterator().next();
			writer.write(String.valueOf(this.getAverageNumWords()));
			writer.write("\n");
			if(firstLabelUnit instanceof RelationLabelUnit){
				writer.write(possibleLabels.values().iterator().next().getClass().getSimpleName());
				writer.write("\t");
				for(Entry<String, LabelUnit> possibleLabel:possibleLabels.entries()){
					writer.write(possibleLabel.getKey());
					writer.write("\t");
					writer.write(possibleLabel.getValue().toString());
					writer.write("\t");
				}
				writer.write("\n");
			}else if(firstLabelUnit instanceof WordLabelUnit || firstLabelUnit instanceof PairLabelUnit){
				writer.write("JointLabelUnit");
				writer.write("\t");
				for(Entry<String, LabelUnit> possibleLabel:possibleLabels.entries()){
					writer.write(possibleLabel.getKey());
					writer.write("\t");
					if(possibleLabel.getValue() instanceof WordLabelUnit){
						writer.write("W\t");
						writer.write(((WordLabelUnit)possibleLabel.getValue()).getLabel());
					}else if(possibleLabel.getValue() instanceof PairLabelUnit){
						writer.write("P\t");
						writer.write(((PairLabelUnit)possibleLabel.getValue()).getLabel());
					}else{
						assert false;
					}
					writer.write("\t");
				}
				writer.write("\n");					
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}	
	
	public void saveProperties(String propertyFile){
		try {
			new Yaml().dump(this, Files.newWriter(new File(propertyFile), Charset.forName("UTF-8")));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	public void setAnnotationExtension(String annotationExtension) {
		this.annotationExtension = annotationExtension;
	}

//	public Collection<String> getEntityTypes() {
//		return entityTypes;
//	}
//	public void setEntityTypes(Collection<String> entityTypes) {
//		this.entityTypes = entityTypes;
//	}
//	
	public void setAverageNumWords(double averageNumWords) {
		this.averageNumWords = averageNumWords;		
	}
	

	public void setBeamSize(int beamSize) {
		this.beamSize = beamSize;
	}

	public void setCloseAnnotation(String closeAnnotation) {
		this.closeAnnotation = closeAnnotation;
	}


	
	public void setDevFolder(String devFolder) {
		this.devFolder = devFolder;
	}

	public void setDevVectorFile(String devVectorFile) {
		this.devVectorFile = devVectorFile;
	}

	public void setEpsilon(double epsilon) {
		assert epsilon >= 0. && epsilon <= 1.;
		this.epsilon = epsilon;
	}
	
	public void setFvBitSize(int fvBitSize) {
		this.fvBitSize = fvBitSize;
		this.fvSize = 1 << fvBitSize;
		assert fvSize > 0 && (fvSize & (fvSize - 1)) == 0;
	}
	public void setGlobalWeight(double globalWeight) {
		this.globalWeight = globalWeight;
	}
	public void setIteration(int iteration) {
		assert iteration >= 0;
		this.iteration = iteration;
	}

	public void setLambda(double lambda) {
		assert lambda >= 0.;
		this.lambda = lambda;
	}

	public void setLearningMethod(int learningMethod) {
		assert learningMethod < LEARNING_METHODS.length;
		this.learningMethod = learningMethod;
	}
	
	public void setLocalIteration(int localIteration) {
		this.localIteration = localIteration;
	}
	public void setMargin(double margin) {
		assert margin >= 0.;
		this.margin = margin;
	}
	
	public void setMiniBatch(int miniBatch) {
		this.miniBatch = miniBatch;
	}

	public void setModelFile(String modelFile) {
		this.modelFile = modelFile;
	}

	public void setNictSynonymFile(String nictSynonymFile) {
		assert nictSynonymFile == null || (new File(nictSynonymFile)).exists();
		this.nictSynonymFile = nictSynonymFile;
	}
	
	public void setNictVerbEntailmentFile(String nictVerbEntailmentFile) {
		this.nictVerbEntailmentFile = nictVerbEntailmentFile;
		if(this.nictVerbEntailmentFile != null){
			this.nictVerbEntailmentMap = TreeMultimap.create();
			try {
				BufferedReader reader = Files.newReader(new File(this.nictVerbEntailmentFile), Charset.forName("UTF-8"));
				for(String line = reader.readLine();line != null;line = reader.readLine()){
					String[] entailmentRules = line.trim().split(" ");
					if(entailmentRules.length != 2){
						continue;
					}
	                if(entailmentRules[0].startsWith("N")){
	                	entailmentRules[0] = entailmentRules[0].substring(1);
	                }
	                if(entailmentRules[1].startsWith("N")){
	                	entailmentRules[1] = entailmentRules[1].substring(1);
	                }      
	                entailmentRules[0] = entailmentRules[0].replaceAll("する$", "");
	                entailmentRules[1] = entailmentRules[1].replaceAll("する$", "");
	                if(entailmentRules[0].isEmpty() || entailmentRules[1].isEmpty())continue;
	                if(entailmentRules[0].equals(entailmentRules[1]))continue;
	                nictVerbEntailmentMap.put(entailmentRules[0], entailmentRules[1]);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public void setOutputInterval(int outputInterval) {
		this.outputInterval = outputInterval;
	}
	

	public void setParseParameters(Map<String, ParseParameters> parseParameters) {
		this.parseParameters = parseParameters;
	}

	public void setPossibleLabels(Multimap<String, LabelUnit> possibleLabels) {
		this.possibleLabels = possibleLabels;		
	}

	public void setPredAnnotationExtension(String predAnnotationExtension) {
		this.predAnnotationExtension = predAnnotationExtension;
	}

	public void setProcessors(int processors) {
		this.processors = processors;
	}

	public void setRelWeight(double relWeight){
		this.relWeight = relWeight;
	}
	
	public void setSearchMethod(int searchMethod){
		assert 0 <= searchMethod && searchMethod < SEARCH_METHODS.length;
		this.searchMethod = searchMethod;
	}
	
	public void setSentenceAnnotation(String sentenceAnnotation) {
		this.sentenceAnnotation = sentenceAnnotation;
	}
	
	public void setTestFolder(String testFolder) {
		this.testFolder = testFolder;
	}
	
	public void setTextExtension(String textExtension) {
		this.textExtension = textExtension;
	}
	public void setTrainFolder(String trainFolder) {
		this.trainFolder = trainFolder;
	}
	public void setTrainVectorFile(String trainVectorFile) {
		this.trainVectorFile = trainVectorFile;
	}
	public void setUpdateMethod(int updateMethod) {
		assert 0 <= updateMethod && updateMethod < UPDATE_METHODS.length;
		this.updateMethod = updateMethod;
	}
	public void setUseAveraging(boolean useAveraging) {
		this.useAveraging = useAveraging;
	}
	public void setUseBestFirst(boolean useBestFirst){
		this.useBestFirst = useBestFirst;
	}
	public void setUseByte(boolean useByte) {
		this.useByte = useByte;
	}
	public void setUseDirectedRelation(boolean useDirectedRelation) {
		this.useDirectedRelation = useDirectedRelation;
	}	
	public void setUseDynamicSort(boolean useDynamicSort){
		this.useDynamicSort = useDynamicSort;
	}
	public void setUseEntityType(boolean useEntityType) {
		this.useEntityType = useEntityType;
	}
	public void setUseEntityTypedLabel(boolean useEntityTypedLabel) {
		this.useEntityTypedLabel = useEntityTypedLabel;
	}
	public void setUseFullFeatures(boolean useFullFeatures) {
		this.useFullFeatures = useFullFeatures;
	}
	public void setUseGlobalEntityFeatures(boolean useGlobalEntityFeatures) {
		this.useGlobalEntityFeatures = useGlobalEntityFeatures;
	}
	public void setUseGlobalFeatures(boolean useGlobalFeatures) {
		this.useGlobalFeatures = useGlobalFeatures;
	}
	public void setUseGlobalRelationFeatures(boolean useGlobalRelationFeatures) {
		this.useGlobalRelationFeatures = useGlobalRelationFeatures;
	}
	public void setUseGoldEntitySpan(boolean useGoldEntitySpan){
		this.useGoldEntitySpan = useGoldEntitySpan;
	}
	public void setUseLocalInit(boolean useLocalInit) {
		this.useLocalInit = useLocalInit;
	}
	public void setUseParallel(boolean useParallel) {
		this.useParallel = useParallel;
	}
	public void setUseSelfRelation(boolean useSelfRelation) {
		this.useSelfRelation = useSelfRelation;
	}
	public void setUseSimpleFeatures(boolean useSimpleFeatures) {
		this.useSimpleFeatures = useSimpleFeatures;
	}
	public void setUseTermSurface(boolean useTermSurface) {
		this.useTermSurface = useTermSurface;
	}	
	public void setUseWeightedMargin(boolean useWeightedMargin) {
		this.useWeightedMargin = useWeightedMargin;
	}
	public void setUseWeighting(boolean useWeighting) {
		this.useWeighting = useWeighting;
	}
	public void setVerbosity(int verbosity) {
		this.verbosity = verbosity;
	}
	public boolean useAdaGrad(){
		return getLearningMethod() == 2;
	}
	public boolean useAROW(){
		return getLearningMethod() == 3;
	}
	public boolean useCloseFirst() {
		return getSearchMethod() == 2;
	}
	public boolean useCloseFirstRtoL() {
		return getSearchMethod() == 8;
	}
	public boolean useDCDSSVM(){
		return getLearningMethod() == 5;
	}
	public boolean useDifficultFirst() {
		return getSearchMethod() == 4;
	}
	public boolean useEarlyUpdate() {
		return getUpdateMethod() == 1;
	}
	public boolean useEasyCloseFirst() {
		return getSearchMethod() == 10;
	}
	public boolean useEasyEntityFirst() {
		return getSearchMethod() == 9;
	}	
	public boolean useEasyFirst() {
		return getSearchMethod() == 3;
	}
	public boolean useEntityLtoR() {
		return getSearchMethod() == 6;
	}
	public boolean useEntityRtoL() {
		return getSearchMethod() == 7;
	}
	public boolean useLtoR() {
		return getSearchMethod() == 0;
	}
	public boolean useMaxViolationUpdate() {
		return getUpdateMethod() == 0;
	}
	public boolean usePerceptron(){
		return getLearningMethod() == 0;
	}
	public boolean useRandomFirst() {
		return getSearchMethod() == 5;
	}	
	public boolean useRtoL() {
		return getSearchMethod() == 1;
	}
	public boolean useSCW(){
		return getLearningMethod() == 4;
	}
	public boolean useSGDSVM(){
		return getLearningMethod() == 1;
	}
	public boolean useSort(){
		return useEasyFirst() || useDifficultFirst() || useRandomFirst() || useEasyEntityFirst() || useEasyCloseFirst();
	}
	public boolean useStandardUpdate() {
		return getUpdateMethod() == 2;
	}

	public String getLogBilinearFile() {
		return logBilinearFile;
	}
	public void setLogBilinearFile(String logBilinearFile) {
		this.logBilinearFile = logBilinearFile;
	}
} public abstract class Instance {
	protected Parameters params;
	protected Label goldLabel;
	protected Sequence sequence;
	protected int index; 
	protected Data data;
	protected double[] goldScores;
	public Instance(Parameters params, Sequence sequence, Label goldLabel){
		assert sequence.size() == goldLabel.size();
		this.params = params;
		this.sequence = sequence;
		this.goldLabel = goldLabel;
		this.goldScores = null;
	}	
	public Instance(Instance instance){
		this(instance.params, instance.sequence, instance.goldLabel);
	}
	public Label getGoldLabel(){
		return goldLabel;
	}
	public Sequence getSequence() {
		return sequence;
	}
	public int size() {
		return sequence.size();
	}
	public int getIndex() {
		return index;
	}
	public void setIndex(int index) {
		this.index = index;
	}
	public Collection<LabelUnit> getCandidateLabels(Label label, int index) {
		assert index < goldLabel.size();
		assert sequence.get(index).getPossibleLabels().size() > 0;
		return sequence.get(index).getPossibleLabels();
	}
	public abstract void sort(Model model, boolean test);
	public abstract void sort(Model model, Label y, int currentIndex, boolean test);
	public abstract void cacheFeatures();
	public abstract void clearCachedFeatures();
	public abstract void sort();
	public void setData(Data data) {
		this.data = data;
	}
	public Data getData(){
		return data;
	}
	public void setGoldScores(double[] goldScores) {
		this.goldScores = goldScores;
	}
	public void setGoldScore(int index, double value) {
		goldScores[index] = value;
	}

	public double getGoldScore(int index) {
		return goldScores[index];
	}
} public class Label {
	private Parameters params;
	private List<LabelUnit> labels;
	
	public Label(Parameters params){
		this.params = params;
		this.labels = new Vector<LabelUnit>();
	}
	
	public Label(Label label) {
		this.params = label.params;
		this.labels = new Vector<LabelUnit>(label.labels);
	}

	public void add(LabelUnit label) {
		labels.add(label);		
	}
	
	public int size(){
		return labels.size();
	}
	public LabelUnit getLabel(int index) {
		assert index >= 0 && index < labels.size(); 
		return labels.get(index);
	}
	
	public List<LabelUnit> getLabels() {
		return labels;
	}

	@Override
	public boolean equals(Object obj) {
		if(!(obj instanceof Label))return false;
		Label label = (Label)obj;
		if(this.size() != label.size()){
			return false;
		}
		Iterator<LabelUnit> thisIt = this.labels.iterator();
		Iterator<LabelUnit> labelIt = label.labels.iterator();
		while(thisIt.hasNext() && labelIt.hasNext()){
			if(!thisIt.next().equals(labelIt.next())){
				return false;
			}
		}
		return true;		
	}

} public interface LabelUnit extends Comparable<LabelUnit> {	
	public boolean equals(Object unit);
	public String toString();
	public boolean isNegative();
} public interface Sequence {
	int size();
	SequenceUnit get(int index);
	void add(SequenceUnit unit);
} public class Document extends Node {
	protected byte[] byteText;
	protected String utfText;
	protected List<Sentence> sentences;
	protected Multimap<Offset, Node> nodes;
	protected List<Instance> instances;
	
	public Document(Parameters params, String id) {
		this(params, null, null, id, "", "");
	}
	
	public Document(Parameters params, Document document, Offset offset, String id, String type, String source) {
		super(params, document, offset, id, type, source);
		assert document == null && offset == null;
		this.sentences = new Vector<Sentence>();
		this.nodes = TreeMultimap.create();
		this.instances = new Vector<Instance>();
	}
	
	public void setText(byte[] text){
		if(params.getUseByte()){
			byteText = text;
			this.offset = new Offset(0, text.length);
		}else{
			try {
				utfText = new String(text, "UTF-8");
				this.offset = new Offset(0, utfText.length());
			} catch (UnsupportedEncodingException e) {
				if(params.getVerbosity() > 0){
					e.printStackTrace();
				}
				System.exit(-1);
			}
		}
	}
	
	public String getText(){
		return getText(this.offset);
	}
	
	public String getText(Offset offset) {
		if(params.getUseByte()){
			return new String(byteText, offset.getStart(), offset.getSize());
		}else{
			return utfText.substring(offset.getStart(), offset.getEnd());
		}
	}

	public void addNode(Node node) {
		nodes.put(node.getOffset(), node);
	}


	public void buildTree() {
		Map<Offset, Nodes> children = new TreeMap<Offset, Nodes>();
		// merge same offset nodes
		for(Offset offset:nodes.keySet()){
			Collection<Node> sharedNodes = nodes.get(offset);
			Multimap<String, Node> nodeMap = TreeMultimap.create();
			for(Node node:sharedNodes){
				nodeMap.put(node.getSourceDesc(), node);
			}
			boolean sentence = false;
			if(nodeMap.containsKey(params.getSentenceAnnotation())){
				for(Node node:nodeMap.get(params.getSentenceAnnotation())){
					if(node.getType().equals(params.getParseSentenceTag(params.getSentenceAnnotation()))){
						sentence = true;
					}
				}
			}
			if(sentence){
				Sentence s = new Sentence(params, nodeMap);
				sentences.add(s);
			}else{
				Nodes nodes = new Nodes(params, nodeMap);
				assert !children.containsKey(offset): offset.getStart()+":"+offset.getEnd()+":"+nodes.getNode().getId()+":"+children.get(offset).getNode().getId();
        if(nodes.getNode() != null){
          children.put(offset, nodes);
        }
			}
		}
		assert children.size() + sentences.size() == nodes.keySet().size();
		assert sentences.size() > 0: "no sentences in documents";
		Collections.sort(sentences);
		// add sentence nodes
		int sentenceId = 0;
		Sentence currentSentence = sentences.get(sentenceId);
		for(Nodes nodes:children.values()){
			if(currentSentence.getOffset().includes(nodes.getOffset())){
				nodes.setSentence(currentSentence);
				currentSentence.addNodes(nodes);
			}else{
				while(currentSentence.getOffset().getEnd() <= nodes.getOffset().getStart() && sentenceId < sentences.size() - 1){
					sentenceId++;
					currentSentence = sentences.get(sentenceId);
				}
				if(currentSentence.getOffset().includes(nodes.getOffset())){
					nodes.setSentence(currentSentence);
					currentSentence.addNodes(nodes);
				}else{
					if(params.getVerbosity() > 3){	
						System.err.println("Document " + id+": a node " + nodes.getNode().id + " from " + nodes.getNode().sourceDesc + " is ignored.");	
					}
				}
			}
		}
		for(Sentence sentence:sentences){
			sentence.removeNestedEntities();
		}
	}

	public List<Instance> getInstances() {
		return instances;
	}

	
} public class Node implements Comparable<Node> {
	public final static String REVERSE_ARGUMENT_HEADER = "<=";
	protected Parameters params;
	protected Document document;
	protected Offset offset;
	protected String id;
	protected String type;
	protected String base;
	protected String pos;
	protected String sourceDesc;
	protected Nodes nodes;
	protected Node headNode;
	protected Map<String, Set<Node>> headWords;
	protected Multimap<String, String> attributes;
	protected Multimap<Node, String> relations;
	protected Multimap<Node, String> arguments;
	protected Multimap<Node, String> wordArguments;
	protected FeatureCache cache;
	public Node(Parameters params, Document document, Offset offset, String id, String type, String sourceDesc){
		this(params, document, offset, id, type, sourceDesc, null);
	}
	public Node(Parameters params, Document document, Offset offset, String id, String type, String sourceDesc, Multimap<String, String> attributes){
		this.params = params;
		this.document = document;
		this.offset = offset;
		this.id = id;
		this.type = type;
		this.sourceDesc = sourceDesc;
		this.headWords = new TreeMap<String, Set<Node>>();
		this.attributes = attributes;
		this.relations = TreeMultimap.create();
		this.arguments = TreeMultimap.create();
		if(params.getParseParameters().containsKey(sourceDesc) && 
				params.getParseBaseAttributeType(sourceDesc) != null &&
				this.attributes.get(params.getParseBaseAttributeType(sourceDesc)).size() > 0){
			this.base = this.attributes.get(params.getParseBaseAttributeType(sourceDesc)).iterator().next();
		}else{
			this.base = "";
		}
		if(params.getParseParameters().containsKey(sourceDesc) && 
				params.getParsePosAttributeType(sourceDesc) != null &&
			this.attributes.get(params.getParsePosAttributeType(sourceDesc)).size() > 0){
			this.pos = this.attributes.get(params.getParsePosAttributeType(sourceDesc)).iterator().next();
		}else{
			this.pos = "";
		}
	}
	public String getId(){
		return id;
	}
	public String getType(){
		return type;
	}
	public String getText(){
		if(!params.getUseTermSurface()){
			if(this.getNodes().isGoldEntity()){
				Node firstEntity = getNodes().getNodes(Parameters.getGoldAnnotationDesc()).iterator().next();
				return firstEntity.getType();
			}else if(this.getNodes().hasGoldParent()){
				Nodes firstParent = this.getNodes().getGoldParents().get(0);
				Node firstEntity = firstParent.getNodes(Parameters.getGoldAnnotationDesc()).iterator().next();
				return firstEntity.getType();
			}else if(this.getNodes().hasGoldChildren()){
				Nodes firstChild = this.getNodes().getGoldChildren().get(0);
				Node firstEntity = firstChild.getNodes(Parameters.getGoldAnnotationDesc()).iterator().next();
				return firstEntity.getType();
			}
		}
		return this.document.getText(offset);
	}	
	public String getRealBase() {
		if(params.getUseTermSurface()){
			if(this.base.isEmpty()){
				base = getText();
			}		
			return base;
		}else{
			return getBase();
		}
	}
	public String getBase() {
		if(this.getNodes().isGoldEntity()){
			Node firstEntity = getNodes().getNodes(Parameters.getGoldAnnotationDesc()).iterator().next();
			return firstEntity.getType();
		}else if(this.getNodes().hasGoldParent()){
			Nodes firstParent = this.getNodes().getGoldParents().get(0);
			Node firstEntity = firstParent.getNodes(Parameters.getGoldAnnotationDesc()).iterator().next();
			return firstEntity.getType();
		}else if(this.getNodes().hasGoldChildren()){
			Nodes firstChild = this.getNodes().getGoldChildren().get(0);
			Node firstEntity = firstChild.getNodes(Parameters.getGoldAnnotationDesc()).iterator().next();
			return firstEntity.getType();
		}else{
			if(this.base.isEmpty()){
				base = getText();
			}		
			return base;
		}
	}
	public String getPOS() {
		return pos;
	}
	public String getSourceDesc() {
		return sourceDesc;
	}
	public FeatureCache getCache() {
		return cache;
	}
	public void setCache(FeatureCache cache) {
		this.cache = cache;
	}
	@Override
	public int compareTo(Node rhs) {
		int comp = offset.compareTo(rhs.offset);
		if(comp != 0)return comp;
		comp = id.compareTo(rhs.id);
		if(comp != 0)return comp;
		comp = type.compareTo(rhs.type);
		if(comp != 0)return comp;
		return sourceDesc.compareTo(rhs.sourceDesc);
	}
	public boolean isInsideOf(Node node) {
		return node.getOffset().includes(this.getOffset());
	}
	public Offset getOffset() {
		return offset;
	}
	public void setNodes(Nodes nodes) {
		this.nodes = nodes;
	}
	public void addGoldRelation(Node node, String rel) {
		relations.put(node, rel);		
		assert relations.get(node).contains(rel);
	}
	public Multimap<String, String> getAttributes() {
		return attributes;
	}
	public void setHead(Node headNode) {
		this.headNode = headNode;
	}
	public void addArgument(Node argument, String key) {
		arguments.put(argument, key);
	}
	public void addWordArgument(Node argument, String key) {
		if(wordArguments == null){
			wordArguments = TreeMultimap.create();	
		}
		wordArguments.put(argument, key);
	}
	public void calcWordArguments(){
		//assert getNodes().getSentence().getWords(this.getSourceDesc()).contains(this);
		if(wordArguments == null){
			wordArguments = TreeMultimap.create();	
		}
		for(Entry<Node, String> argument:arguments.entries()){
			Node headWord = argument.getKey().getHeadWord();
			assert getNodes().getSentence().getWords(this.getSourceDesc()).contains(headWord);
			addWordArgument(headWord, argument.getValue());
			headWord.addWordArgument(this, REVERSE_ARGUMENT_HEADER.concat(argument.getValue()));
		}		
	}
	
	public Nodes getNodes() {
		return nodes;
	}
	
	public Document getDocument(){
		return document;
	}
	
	private Set<Node> getHeadWords(Set<Nodes> targets, String sourceDesc){
		Set<Node> targetHeadWords = getParentHeadWords(targets, sourceDesc);
		if(targetHeadWords.size() != 0){
			return targetHeadWords;
		}
		targetHeadWords = getChildHeadWords(targets, sourceDesc);
		if(targetHeadWords.size() != 0){
			return targetHeadWords;
		}
		//TODO: No directly related words 
		return getOverlappingWords(sourceDesc);
	}
	
	private Set<Node> getOverlappingWords(String sourceDesc){
		// get head from target node;
		Set<Node> headWords = new TreeSet<Node>();
		for(Node word:this.getNodes().getSentence().getWords(sourceDesc)){
			if(word.getOffset().overlaps(this.getOffset())){
				headWords.add(word);
			}
		}
		return headWords;
	}
	
	private Set<Node> getCurrentHeadWords(Set<Nodes> targets, String sourceDesc){
		// get head from target node;
		Set<Node> parseNodes = new TreeSet<Node>();
		List<Node> words = this.getNodes().getSentence().getWords(sourceDesc);
		for(Nodes target:targets){			
			if(target.hasNodes(sourceDesc)){
				parseNodes.addAll(target.getNodes(sourceDesc));
			}
		}
		Set<Node> headWords = new TreeSet<Node>();
		for(Node parseNode:parseNodes){
			for(Node headWord:parseNode.getHeadWords(sourceDesc)){
				if(words.contains(headWord)){
					if(headWord.isInsideOf(this) || this.isInsideOf(headWord)){
						headWords.add(headWord);
					}
				}
			}
		}
		return headWords;
	}
	
	private Set<Node> getParentHeadWords(Set<Nodes> targets, String sourceDesc){
		// get head from parent nodes;
		Set<Node> parentHeadWords = getCurrentHeadWords(targets, sourceDesc);
		if(parentHeadWords.size() != 0){
			return parentHeadWords;
		}
		Set<Nodes> parents = new TreeSet<Nodes>();
		for(Nodes target:targets){			
			parents.addAll(target.getParents());
		}
		if(parents.size() != 0){		
			return getParentHeadWords(parents, sourceDesc);
		}
		return parentHeadWords;		
	}
	
	private Set<Node> getChildHeadWords(Set<Nodes> targets, String sourceDesc){
		// get head from child nodes;
		Set<Node> childHeadWords = getCurrentHeadWords(targets, sourceDesc);
		if(childHeadWords.size() != 0){
			return childHeadWords;
		}
		Set<Nodes> children = new TreeSet<Nodes>();
		for(Nodes target:targets){			
			children.addAll(target.getChildren());
		}
		if(children.size() != 0){		
			return getChildHeadWords(children, sourceDesc);
		}
		return childHeadWords;		
	}
	
	private Node getHeadWord(){
		// get head for parse node;
		if(this.headNode != null){
			return this.headNode.getHeadWord();
		}else{
			return this;
		}
	}
	
	public Node getFirstHeadWord(String sourceDesc){
		return getHeadWords(sourceDesc).iterator().next();
	}
		
	public Set<Node> getHeadWords(String sourceDesc) {
		if(headWords.containsKey(sourceDesc)){
			return headWords.get(sourceDesc);
		}
		Set<Node> heads = new TreeSet<Node>();
		if(this.sourceDesc.equals(sourceDesc)){
			Node headWord = getHeadWord();
			assert headWord.isInsideOf(this): headWord.getText()+":"+this.getText();
			heads.add(headWord);
		}else{
			Set<Nodes> nodes = new TreeSet<Nodes>();
			nodes.add(this.getNodes());
			heads = getHeadWords(nodes, sourceDesc);
		}
		assert heads.size() > 0: sourceDesc+":"+this.getText()+":"+this.sourceDesc;
		headWords.put(sourceDesc, heads);
		return heads;
	}

	public String getFirstDependency(Node node){
		if(!wordArguments.containsKey(node)){
			return null;
		}
		for(String dep:wordArguments.get(node)){
			if(!dep.startsWith(REVERSE_ARGUMENT_HEADER)){
				return dep;
			}
		}
		for(String dep:wordArguments.get(node)){
			return dep;
		}
		return null;
	}
	
	public Multimap<Node, String> getWordArguments() {
		return wordArguments;
	}
	public Multimap<Node, String> getRelations() {
		return relations;
	}

} public class Offset implements Comparable<Offset>{
	private int start;
	private int end;

	public Offset(int start, int end) {
		assert start <= end;
		assert start >= 0;
		assert end >= 0;
		this.start = start;
		this.end = end;
	}

	public int getStart() {
		return start;
	}

	public int getEnd() {
		return end;
	}

	public int getSize() {
		assert start <= end;
		return end - start;
	}

	@Override
	public int compareTo(Offset rhs) {
		// depends on task ...
		if(getEnd() < rhs.getStart()){
			return -1;
		}else if(getStart() > rhs.getEnd()){
			return 1;
		}else if(includes(rhs)){
			// equals depending on the definition of "includes"
			if(getSize() == rhs.getSize()){
				assert this.getStart() == rhs.getStart() && this.getEnd() == rhs.getEnd();
				return 0;
			}
			assert getSize() > rhs.getSize();
			return -1;
		}else if(rhs.includes(this)){
			assert this.getStart() != rhs.getStart() || this.getEnd() != rhs.getEnd();
			assert getSize() < rhs.getSize();
			return 1;
		}else if(getStart() < rhs.getStart()){
			assert getEnd() < rhs.getEnd();
			return -1;
		}else if(getStart() > rhs.getStart()){
			assert getEnd() > rhs.getEnd();
			return 1;
		}
		assert false;
		return 0;
	}

	public boolean includes(Offset rhs) {
		return start <= rhs.start && rhs.end <= end;
	}

	@Override
	public String toString() {
		return "(" + getStart() + ", " + getEnd() + ")";
	}

	public boolean overlaps(Offset offset) {
		return start <= offset.start && offset.start <= end || offset.start <= start && start <= offset.end;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + end;
		result = prime * result + start;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Offset other = (Offset) obj;
		if (end != other.end)
			return false;
		if (start != other.start)
			return false;
		return true;
	}

	

} public class Sentence extends Nodes {
	private Map<String, Node> goldEntities;
	private Map<String, Map<String, Node>> parseNodes;
	private Map<String, List<Node>> words; 

	public Sentence(Parameters params, Multimap<String, Node> nodes) {
		super(params, nodes);
		this.goldEntities = new TreeMap<String, Node>(); 	
		this.parseNodes = new TreeMap<String, Map<String, Node>>(); 	
		this.words = new TreeMap<String, List<Node>>();
		this.setSentence(this);
		if(isGoldEntity()){
			for(Node goldEntity:this.getNodes(Parameters.getGoldAnnotationDesc())){
				goldEntities.put(goldEntity.getId(), goldEntity);
			}
		}
		for(String parseAnnotationDesc:params.getParseAnnotationDescs()){
			parseNodes.put(parseAnnotationDesc, new TreeMap<String, Node>());
			for(Node node:this.getNodes(parseAnnotationDesc)){
				parseNodes.get(parseAnnotationDesc).put(node.getId(), node);
			}
		}
	}

	public void addNodes(Nodes nodes) {
		super.addNodes(nodes);
		if(nodes.isGoldEntity()){
			for(Node goldEntity:nodes.getNodes(Parameters.getGoldAnnotationDesc())){
				goldEntities.put(goldEntity.getId(), goldEntity);
			}
		}
		for(String parseAnnotationDesc:params.getParseAnnotationDescs()){
			for(Node node:nodes.getNodes(parseAnnotationDesc)){
				parseNodes.get(parseAnnotationDesc).put(node.getId(), node);
			}
		}
	}

	public void processParse(){
		for(String parseAnnotationDesc:params.getParseAnnotationDescs()){
			Map<String, Node> currentParseNodes = parseNodes.get(parseAnnotationDesc);
			words.put(parseAnnotationDesc, new Vector<Node>());
			List<Node> currentWords = words.get(parseAnnotationDesc);
			for(Node parseNode:currentParseNodes.values()){
				String pred = "";
				if(params.getParsePredicateAttributeType(parseAnnotationDesc) != null && 
						parseNode.getAttributes().containsKey(params.getParsePredicateAttributeType(parseAnnotationDesc))){
					pred = parseNode.getAttributes().get(params.getParsePredicateAttributeType(parseAnnotationDesc)).iterator().next();
				}
				if(params.getParseHeadAttributeType(parseAnnotationDesc) == null){
					parseNode.setHead(null);
				}
				for(Entry<String, String> attribute:parseNode.getAttributes().entries()){
					if(currentParseNodes.containsKey(attribute.getValue())){
						assert !attribute.getValue().isEmpty();
						Node argument = currentParseNodes.get(attribute.getValue());
						if(params.getIgnoredAttributeTypes(parseAnnotationDesc) != null && 
								params.getIgnoredAttributeTypes(parseAnnotationDesc).contains(attribute.getKey())){
							continue;
						}else if(attribute.getKey().equals(params.getParseHeadAttributeType(parseAnnotationDesc))){
							parseNode.setHead(argument);
						}else{
							parseNode.addArgument(argument, pred+attribute.getKey());
						}
					}
				}
				if(parseNode.getType().equals(params.getParseWordTag(parseAnnotationDesc))){
					if(!currentWords.contains(parseNode)){
						currentWords.add(parseNode);
					}
				}
			}
			Collections.sort(currentWords);
			for(Node word:currentWords){
				word.calcWordArguments();
			}
			assert currentWords.size() < 2 || currentWords.get(0).getOffset().compareTo(currentWords.get(1).getOffset()) < 0;
		}
	}
	
	public Map<String, Node> getGoldEntities() {
		return goldEntities;
	}

	public List<Node> getWords(String parseAnnotationDesc) {
		return words.get(parseAnnotationDesc);
	}

	public void removeNestedEntities() {
		Map<String, Node> oldGoldEntities = new TreeMap<String, Node>(goldEntities);
		for(Map.Entry<String, Node> e1:oldGoldEntities.entrySet()){
			for(Map.Entry<String, Node> e2:oldGoldEntities.entrySet()){
				if(!e1.getKey().equals(e2.getKey()) && e1.getValue().isInsideOf(e2.getValue())){
					if(params.getVerbosity() > 3){
						System.out.println("Nested entity:" + e1.getKey() + " is ignored!!");
					}
					goldEntities.remove(e1.getKey());
				}
			}			
		}
	}
	
	
} public class Relation {
	private Parameters params;
	private Document document;
	private String id;
	private String type;
	private Multimap<String, String> arguments;
	private String sourceDesc;
	public Relation(Parameters params, Document document, String id, String type, Multimap<String, String> arguments, String sourceDesc) {
		this.params = params;
		this.document = document;
		this.id = id;
		this.type = type;
		this.arguments = arguments;
		this.sourceDesc = sourceDesc;
	}
	public String getSourceDesc() {
		return sourceDesc;
	}
	public String getType() {
		return type;
	}
	public Parameters getParams() {
		return params;
	}
	public Document getDocument() {
		return document;
	}
	public String getId() {
		return id;
	}
	public Multimap<String, String> getArguments() {
		return arguments;
	}
	
}
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;
import java.util.Map.Entry;
import com.google.common.collect.Lists;
import config.Parameters;
import data.Instance;
import data.Label;
import data.LabelUnit;
import data.Sequence;
import data.nlp.Document;
import data.nlp.Node;
import data.nlp.Offset;
import data.nlp.Sentence;
import data.nlp.relation.Relation;
package data.nlp.joint;




public class JointDocument extends Document {
	protected List<Relation> relations;

	public JointDocument(Parameters params, String id) {