gt:       Set<Gender> acGender = Generics.newHashSet();
context: package edu.stanford.nlp.hcoref.sieve;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Locale;
  import java.util.Properties;
  import java.util.Set;
  
  import edu.stanford.nlp.hcoref.CorefPrinter;
  import edu.stanford.nlp.hcoref.CorefProperties;
  import edu.stanford.nlp.hcoref.Rules;
  import edu.stanford.nlp.hcoref.data.CorefChain;
  import edu.stanford.nlp.hcoref.data.CorefCluster;
  import edu.stanford.nlp.hcoref.data.Dictionaries;
  import edu.stanford.nlp.hcoref.data.Dictionaries.Animacy;
  import edu.stanford.nlp.hcoref.data.Dictionaries.Gender;
  import edu.stanford.nlp.hcoref.data.Dictionaries.MentionType;
  import edu.stanford.nlp.hcoref.data.Dictionaries.Number;
  import edu.stanford.nlp.hcoref.data.Dictionaries.Person;
  import edu.stanford.nlp.hcoref.data.Document;
  import edu.stanford.nlp.hcoref.data.Document.DocType;
  import edu.stanford.nlp.hcoref.data.Mention;
  import edu.stanford.nlp.hcoref.md.RuleBasedCorefMentionFinder;
  import edu.stanford.nlp.hcoref.rf.RandomForest;
  import edu.stanford.nlp.ling.CoreAnnotations;
  import edu.stanford.nlp.ling.CoreAnnotations.SpeakerAnnotation;
  import edu.stanford.nlp.ling.CoreLabel;
  import edu.stanford.nlp.ling.RVFDatum;
  import edu.stanford.nlp.math.ArrayMath;
  import edu.stanford.nlp.stats.ClassicCounter;
  import edu.stanford.nlp.stats.Counter;
  import edu.stanford.nlp.stats.Counters;
  import edu.stanford.nlp.trees.Tree;
  import edu.stanford.nlp.util.Generics;
  
  public class RFSieve extends Sieve {
    
    private static final long serialVersionUID = -4090017054885920527L;
    
    // for RF sieve
    public RandomForest rf;
    
    /** the probability threshold for merging two mentions */
    public double thresMerge;
    
    // constructor for RF sieve
    public RFSieve(RandomForest rf, Properties props, String sievename) {
      super(props, sievename);
      this.rf = rf;
      this.props = props;
      this.classifierType = ClassifierType.RF;
    }
  
    public void findCoreferentAntecedent(Mention m, int mIdx, Document document, Dictionaries dict, Properties props, StringBuilder sbLog) throws Exception {
      int sentIdx = m.sentNum;
  
      Counter<Integer> probs = new ClassicCounter<>();
      
      int mentionDist = 0;
      for(int sentDist=0 ; sentDist <= Math.min(this.maxSentDist, sentIdx) ; sentDist++) {
        List<Mention> candidates = getOrderedAntecedents(m, sentIdx-sentDist, mIdx, document.predictedMentions, dict);
        
  
        for(Mention candidate : candidates) {
          if(skipForAnalysis(candidate, m, props)) continue;
          if(candidate == m) continue;
          if(!aType.contains(candidate.mentionType)) continue;
          if(m.mentionType == MentionType.PRONOMINAL) {
            if(!matchedMentionType(m, mTypeStr)) continue;
            if(!matchedMentionType(candidate, aTypeStr)) continue;
          }
          
          if(sentDist==0 && m.appearEarlierThan(candidate)) continue;   // ignore cataphora
          mentionDist++;
  
          RVFDatum<Boolean, String> datum = extractDatum(m, candidate, document, mentionDist, dict, props, sievename);
          
          double probTrue = 0;
          if(this.classifierType == ClassifierType.RF) {
            probTrue = this.rf.probabilityOfTrue(datum);
          }
          
          probs.setCount(candidate.mentionID, probTrue);
        }
      }
      
      if(CorefProperties.debug(props)) {
        sbLog.append(CorefPrinter.printErrorLog(m, document, probs, mIdx, dict, this));
      }
      
      if(probs.size() > 0 && Counters.max(probs) > this.thresMerge) {
        // merge highest prob candidate
        int antID = Counters.argmax(probs);
        
        Sieve.merge(document, m.mentionID, antID);
      }
    }
    public static RVFDatum<Boolean, String> extractDatum(Mention m, Mention candidate, 
        Document document, int mentionDist, Dictionaries dict, Properties props, String sievename) {
      try {
        
        boolean label = (document.goldMentions==null)? false : document.isCoref(m, candidate);
        Counter<String> features = new ClassicCounter<>();
        CorefCluster mC = document.corefClusters.get(m.corefClusterID);
        CorefCluster aC = document.corefClusters.get(candidate.corefClusterID);
        
        CoreLabel mFirst = m.sentenceWords.get(m.startIndex);
        CoreLabel mLast = m.sentenceWords.get(m.endIndex-1);
        CoreLabel mPreceding = (m.startIndex>0)? m.sentenceWords.get(m.startIndex-1) : null;
        CoreLabel mFollowing = (m.endIndex < m.sentenceWords.size())? m.sentenceWords.get(m.endIndex) : null;
        
        CoreLabel aFirst = candidate.sentenceWords.get(candidate.startIndex);
        CoreLabel aLast = candidate.sentenceWords.get(candidate.endIndex-1);
        CoreLabel aPreceding = (candidate.startIndex>0)? candidate.sentenceWords.get(candidate.startIndex-1) : null;
        CoreLabel aFollowing = (candidate.endIndex < candidate.sentenceWords.size())? candidate.sentenceWords.get(candidate.endIndex) : null;
        
        
        ////////////////////////////////////////////////////////////////////////////////
        ///////    basic features: distance, doctype, mention length, roles ////////////
        ////////////////////////////////////////////////////////////////////////////////
        if(CorefProperties.useBasicFeatures(props, sievename)) {
          int sentDist = m.sentNum - candidate.sentNum;
          features.incrementCount("SENTDIST", sentDist);
          features.incrementCount("MENTIONDIST", mentionDist);
          
          int minSentDist = sentDist;
          for(Mention a : aC.corefMentions) {
            minSentDist = Math.min(minSentDist, Math.abs(m.sentNum - a.sentNum));
          }
          features.incrementCount("MINSENTDIST", minSentDist);
  
          // When they are in the same sentence, divides a sentence into clauses and add such feature
          if(CorefProperties.useConstituencyTree(props)) {
            if(m.sentNum == candidate.sentNum) {
              int clauseCount = 0;
              Tree tree = m.contextParseTree;
              Tree current = m.mentionSubTree;
              
              while(true){
                current = current.ancestor(1, tree);
                if(current.label().value().startsWith("S")) {
                  clauseCount++;
                }
                if(current.dominates(candidate.mentionSubTree)) break;
                if(current.label().value().equals("ROOT") || current.ancestor(1, tree)==null) break;
              }
              features.incrementCount("CLAUSECOUNT", clauseCount);
            }
          }
          
          if(document.docType == DocType.CONVERSATION) features.incrementCount("B-DOCTYPE-"+document.docType);
          if(m.headWord.get(SpeakerAnnotation.class).equalsIgnoreCase("PER0")) {
            features.incrementCount("B-SPEAKER-PER0");
          }
          
          if(document.docInfo!=null && document.docInfo.containsKey("DOC_ID")) {
            features.incrementCount("B-DOCSOURCE-"+document.docInfo.get("DOC_ID").split("/")[1]);
          }
          
          features.incrementCount("M-LENGTH", m.originalSpan.size());
          features.incrementCount("A-LENGTH", candidate.originalSpan.size());
          if(m.originalSpan.size() < candidate.originalSpan.size()) features.incrementCount("B-A-ISLONGER");
          features.incrementCount("A-SIZE", aC.getCorefMentions().size());
          features.incrementCount("M-SIZE", mC.getCorefMentions().size());
  
          String antRole = "A-NOROLE";
          String mRole = "M-NOROLE";
          
          if(m.isSubject) mRole = "M-SUBJ";
          if(m.isDirectObject) mRole = "M-DOBJ";
          if(m.isIndirectObject) mRole = "M-IOBJ";
          if(m.isPrepositionObject) mRole = "M-POBJ";
          
          if(candidate.isSubject) antRole = "A-SUBJ";
          if(candidate.isDirectObject) antRole = "A-DOBJ";
          if(candidate.isIndirectObject) antRole = "A-IOBJ";
          if(candidate.isPrepositionObject) antRole = "A-POBJ";
          
          features.incrementCount("B-"+mRole);
          features.incrementCount("B-"+antRole);
          features.incrementCount("B-"+antRole+"-"+mRole);
  
          if(CorefProperties.combineObjectRoles(props, sievename)) {
            // combine all objects
            if(m.isDirectObject || m.isIndirectObject || m.isPrepositionObject
                || candidate.isDirectObject || candidate.isIndirectObject || candidate.isPrepositionObject) {
              if(m.isDirectObject || m.isIndirectObject || m.isPrepositionObject) {
                mRole = "M-OBJ";
                features.incrementCount("B-M-OBJ");
              }
              if(candidate.isDirectObject || candidate.isIndirectObject || candidate.isPrepositionObject) {
                antRole = "A-OBJ";
                features.incrementCount("B-A-OBJ");
              }
              features.incrementCount("B-"+antRole+"-"+mRole);
            }
          }
          
          if(mFirst.word().toLowerCase().matches("a|an")) {
            features.incrementCount("B-M-START-WITH-INDEFINITE");
          }
          if(aFirst.word().toLowerCase().matches("a|an")) {
            features.incrementCount("B-A-START-WITH-INDEFINITE");
          }
          if(mFirst.word().equalsIgnoreCase("the")) {
            features.incrementCount("B-M-START-WITH-DEFINITE");
          }
          if(aFirst.word().equalsIgnoreCase("the")) {
            features.incrementCount("B-A-START-WITH-DEFINITE");
          }
          
          if(dict.indefinitePronouns.contains(m.lowercaseNormalizedSpanString())) {
            features.incrementCount("B-M-INDEFINITE-PRONOUN");
          }
          if(dict.indefinitePronouns.contains(candidate.lowercaseNormalizedSpanString())) {
            features.incrementCount("B-A-INDEFINITE-PRONOUN");
          }
          if(dict.indefinitePronouns.contains(mFirst.word().toLowerCase())) {
            features.incrementCount("B-M-INDEFINITE-ADJ");
          }
          if(dict.indefinitePronouns.contains(aFirst.word().toLowerCase())){
            features.incrementCount("B-A-INDEFINITE-ADJ");
          }
          if(dict.reflexivePronouns.contains(m.headString)) {
            features.incrementCount("B-M-REFLEXIVE");
          }
          if(dict.reflexivePronouns.contains(candidate.headString)) {
            features.incrementCount("B-A-REFLEXIVE");
          }
          
          if(m.headIndex == m.endIndex-1) features.incrementCount("B-M-HEADEND");
          if(m.headIndex < m.endIndex-1) {
            CoreLabel headnext = m.sentenceWords.get(m.headIndex+1);
            if(headnext.word().matches("that|,") || headnext.tag().startsWith("W")) {
              features.incrementCount("B-M-HASPOSTPHRASE");
              if(mFirst.tag().equals("DT") && mFirst.word().toLowerCase().matches("the|this|these|those")) features.incrementCount("B-M-THE-HASPOSTPHRASE");
              else if(mFirst.word().toLowerCase().matches("a|an")) features.incrementCount("B-M-INDEFINITE-HASPOSTPHRASE");
            }
          }
  
          // shape feature from Bjorkelund & Kuhn
          StringBuilder sb = new StringBuilder();
          List<Mention> sortedMentions = new ArrayList<>(aC.corefMentions.size());
          sortedMentions.addAll(aC.corefMentions);
          Collections.sort(sortedMentions, new CorefChain.MentionComparator());
          for(Mention a : sortedMentions) {
            sb.append(a.mentionType).append("-");
          }
          features.incrementCount("B-A-SHAPE-"+sb.toString());
          
          sb = new StringBuilder();
          sortedMentions = new ArrayList<>(mC.corefMentions.size());
          sortedMentions.addAll(mC.corefMentions);
          Collections.sort(sortedMentions, new CorefChain.MentionComparator());
          for(Mention men : sortedMentions) {
            sb.append(men.mentionType).append("-");
          }
          features.incrementCount("B-M-SHAPE-"+sb.toString());
          
          if(CorefProperties.useConstituencyTree(props)) {
            sb = new StringBuilder();
            Tree mTree = m.contextParseTree;
            Tree mHead = mTree.getLeaves().get(m.headIndex).ancestor(1, mTree);
            for(Tree node : mTree.pathNodeToNode(mHead, mTree)){
              sb.append(node.value()).append("-");
              if(node.value().equals("S")) break;
            }
            features.incrementCount("B-M-SYNPATH-"+sb.toString());
  
            sb = new StringBuilder();
            Tree aTree = candidate.contextParseTree;
            Tree aHead = aTree.getLeaves().get(candidate.headIndex).ancestor(1, aTree);
            for(Tree node : aTree.pathNodeToNode(aHead, aTree)){
              sb.append(node.value()).append("-");
              if(node.value().equals("S")) break;
            }
            features.incrementCount("B-A-SYNPATH-"+sb.toString());
          }
          
          
          features.incrementCount("A-FIRSTAPPEAR", aC.representative.sentNum);
          features.incrementCount("M-FIRSTAPPEAR", mC.representative.sentNum);
          int docSize = document.predictedMentions.size();   // document size in # of sentences
          features.incrementCount("A-FIRSTAPPEAR-NORMALIZED", aC.representative.sentNum/docSize);
          features.incrementCount("M-FIRSTAPPEAR-NORMALIZED", mC.representative.sentNum/docSize);
        }
        
        ////////////////////////////////////////////////////////////////////////////////
        ///////    mention detection features                               ////////////
        ////////////////////////////////////////////////////////////////////////////////
        if(CorefProperties.useMentionDetectionFeatures(props, sievename)) {
          // bare plurals
          if(m.originalSpan.size()==1 && m.headWord.tag().equals("NNS")) features.incrementCount("B-M-BAREPLURAL");
          if(candidate.originalSpan.size()==1 && candidate.headWord.tag().equals("NNS")) features.incrementCount("B-A-BAREPLURAL");
          
          // pleonastic it
          if(CorefProperties.useConstituencyTree(props)) {
            if(RuleBasedCorefMentionFinder.isPleonastic(m, m.contextParseTree) 
                || RuleBasedCorefMentionFinder.isPleonastic(candidate, candidate.contextParseTree)) {
              features.incrementCount("B-PLEONASTICIT");
            }
          }
          
          // quantRule
          if(dict.quantifiers.contains(mFirst.word().toLowerCase(Locale.ENGLISH))) features.incrementCount("B-M-QUANTIFIER");
          if(dict.quantifiers.contains(aFirst.word().toLowerCase(Locale.ENGLISH))) features.incrementCount("B-A-QUANTIFIER");
          
          // starts with negation 
          if(mFirst.word().toLowerCase(Locale.ENGLISH).matches("none|no|nothing|not")
              || aFirst.word().toLowerCase(Locale.ENGLISH).matches("none|no|nothing|not")) {
            features.incrementCount("B-NEGATIVE-START");
          }
          
          // parititive rule
          if(RuleBasedCorefMentionFinder.partitiveRule(m, m.sentenceWords, dict)) features.incrementCount("B-M-PARTITIVE");
          if(RuleBasedCorefMentionFinder.partitiveRule(candidate, candidate.sentenceWords, dict)) features.incrementCount("B-A-PARTITIVE");
          
          // %
          if(m.headString.equals("%")) features.incrementCount("B-M-HEAD%");
          if(candidate.headString.equals("%")) features.incrementCount("B-A-HEAD%");
          
          // adjective form of nations
          if(dict.isAdjectivalDemonym(m.spanToString())) features.incrementCount("B-M-ADJ-DEMONYM");
          if(dict.isAdjectivalDemonym(candidate.spanToString())) features.incrementCount("B-A-ADJ-DEMONYM");
          
          // ends with "etc."
          if(m.lowercaseNormalizedSpanString().endsWith("etc.")) features.incrementCount("B-M-ETC-END");
          if(candidate.lowercaseNormalizedSpanString().endsWith("etc.")) features.incrementCount("B-A-ETC-END");
          
        }
  
        ////////////////////////////////////////////////////////////////////////////////
        ///////    attributes, attributes agree                             ////////////
        ////////////////////////////////////////////////////////////////////////////////
        features.incrementCount("B-M-NUMBER-"+m.number);
        features.incrementCount("B-A-NUMBER-"+candidate.number);
        features.incrementCount("B-M-GENDER-"+m.gender);
        features.incrementCount("B-A-GENDER-"+candidate.gender);
        features.incrementCount("B-M-ANIMACY-"+m.animacy);
        features.incrementCount("B-A-ANIMACY-"+candidate.animacy);
        features.incrementCount("B-M-PERSON-"+m.person);
        features.incrementCount("B-A-PERSON-"+candidate.person);
        features.incrementCount("B-M-NETYPE-"+m.nerString);
        features.incrementCount("B-A-NETYPE-"+candidate.nerString);
        
        features.incrementCount("B-BOTH-NUMBER-"+candidate.number+"-"+m.number);
        features.incrementCount("B-BOTH-GENDER-"+candidate.gender+"-"+m.gender);
        features.incrementCount("B-BOTH-ANIMACY-"+candidate.animacy+"-"+m.animacy);
        features.incrementCount("B-BOTH-PERSON-"+candidate.person+"-"+m.person);
        features.incrementCount("B-BOTH-NETYPE-"+candidate.nerString+"-"+m.nerString);
        
    
        Set<Number> mcNumber = Generics.newHashSet();
        for(Number n : mC.numbers) {
          features.incrementCount("B-MC-NUMBER-"+n);
          mcNumber.add(n);
        }
        if(mcNumber.size()==1) {
          features.incrementCount("B-MC-CLUSTERNUMBER-"+mcNumber.iterator().next());
        } else {
          mcNumber.remove(Number.UNKNOWN);
          if(mcNumber.size() == 1) features.incrementCount("B-MC-CLUSTERNUMBER-"+mcNumber.iterator().next());
          else features.incrementCount("B-MC-CLUSTERNUMBER-CONFLICT");
        }
        
        Set<Gender> mcGender = Generics.newHashSet();
        for(Gender g : mC.genders) {
          features.incrementCount("B-MC-GENDER-"+g);
          mcGender.add(g);
        }
        if(mcGender.size()==1) {
          features.incrementCount("B-MC-CLUSTERGENDER-"+mcGender.iterator().next());
        } else {
          mcGender.remove(Gender.UNKNOWN);
          if(mcGender.size() == 1) features.incrementCount("B-MC-CLUSTERGENDER-"+mcGender.iterator().next());
          else features.incrementCount("B-MC-CLUSTERGENDER-CONFLICT");
        }
        
        Set<Animacy> mcAnimacy = Generics.newHashSet();
        for(Animacy a : mC.animacies) {
          features.incrementCount("B-MC-ANIMACY-"+a);
          mcAnimacy.add(a);
        }
        if(mcAnimacy.size()==1) {
          features.incrementCount("B-MC-CLUSTERANIMACY-"+mcAnimacy.iterator().next());
        } else {
          mcAnimacy.remove(Animacy.UNKNOWN);
          if(mcAnimacy.size() == 1) features.incrementCount("B-MC-CLUSTERANIMACY-"+mcAnimacy.iterator().next());
          else features.incrementCount("B-MC-CLUSTERANIMACY-CONFLICT");
        }
        
        Set<String> mcNER = Generics.newHashSet();
        for(String t : mC.nerStrings) {
          features.incrementCount("B-MC-NETYPE-"+t);
          mcNER.add(t);
        }
        if(mcNER.size()==1) {
          features.incrementCount("B-MC-CLUSTERNETYPE-"+mcNER.iterator().next());
        } else {
          mcNER.remove("O");
          if(mcNER.size() == 1) features.incrementCount("B-MC-CLUSTERNETYPE-"+mcNER.iterator().next());
          else features.incrementCount("B-MC-CLUSTERNETYPE-CONFLICT");
        }
        
        Set<Number> acNumber = Generics.newHashSet();
        for(Number n : aC.numbers) {
          features.incrementCount("B-AC-NUMBER-"+n);
          acNumber.add(n);
        }
        if(acNumber.size()==1) {
          features.incrementCount("B-AC-CLUSTERNUMBER-"+acNumber.iterator().next());
        } else {
          acNumber.remove(Number.UNKNOWN);
          if(acNumber.size() == 1) features.incrementCount("B-AC-CLUSTERNUMBER-"+acNumber.iterator().next());
          else features.incrementCount("B-AC-CLUSTERNUMBER-CONFLICT");
        }
        
  