ExpressionTreeNode CustomHbondForceImpl::replaceFunctions(const ExpressionTreeNode& node, map<string, int> atoms,
        map<string, vector<int> >& distances, map<string, vector<int> >& angles, map<string, vector<int> >& dihedrals, set<string>& variables) {
    const Operation& op = node.getOperation();
    if (op.getId() == Operation::VARIABLE && variables.find(op.getName()) == variables.end())
        throw OpenMMException("CustomHBondForce: Unknown variable '"+op.getName()+"'");
    if (op.getId() != Operation::CUSTOM || op.getNumArguments() < 2)
    {
        // This is not an angle or dihedral, so process its children.

        vector<ExpressionTreeNode> children;
        for (int i = 0; i < (int) node.getChildren().size(); i++)
            children.push_back(replaceFunctions(node.getChildren()[i], atoms, distances, angles, dihedrals, variables));
        return ExpressionTreeNode(op.clone(), children);
    }
    const Operation::Custom& custom = static_cast<const Operation::Custom&>(op);

    // Identify the atoms this term is based on.

    int numArgs = custom.getNumArguments();
    vector<int> indices(numArgs);
    for (int i = 0; i < numArgs; i++) {
        map<string, int>::const_iterator iter = atoms.find(node.getChildren()[i].getOperation().getName());
        if (iter == atoms.end())
            throw OpenMMException("CustomHBondForce: Unknown particle '"+node.getChildren()[i].getOperation().getName()+"'");
        indices[i] = iter->second;
    }

    // Select a name for the variable and add it to the appropriate map.

    stringstream variable;
    if (numArgs == 2)
        variable << "distance";
    else if (numArgs == 3)
        variable << "angle";
    else
        variable << "dihedral";
    for (int i = 0; i < numArgs; i++)
        variable << indices[i];
    string name = variable.str();
    if (numArgs == 2)
        distances[name] = indices;
    else if (numArgs == 3)
        angles[name] = indices;
    else
        dihedrals[name] = indices;

    // Return a new node that represents it as a simple variable.

    return ExpressionTreeNode(new Operation::Variable(name));
}
ParsedExpression CustomHbondForceImpl::prepareExpression(const CustomHbondForce& force, const map<string, CustomFunction*>& customFunctions, map<string, vector<int> >& distances,
        map<string, vector<int> >& angles, map<string, vector<int> >& dihedrals) {
    CustomHbondForceImpl::FunctionPlaceholder custom(1);
    CustomHbondForceImpl::FunctionPlaceholder distance(2);
    CustomHbondForceImpl::FunctionPlaceholder angle(3);
    CustomHbondForceImpl::FunctionPlaceholder dihedral(4);
    map<string, CustomFunction*> functions = customFunctions;
    functions["distance"] = &distance;
    functions["angle"] = &angle;
    functions["dihedral"] = &dihedral;
    ParsedExpression expression = Lepton::Parser::parse(force.getEnergyFunction(), functions);
    map<string, int> atoms;
    atoms["a1"] = 0;
    atoms["a2"] = 1;
    atoms["a3"] = 2;
    atoms["d1"] = 3;
    atoms["d2"] = 4;
    atoms["d3"] = 5;
    set<string> variables;
    for (int i = 0; i < force.getNumPerDonorParameters(); i++)
        variables.insert(force.getPerDonorParameterName(i));
    for (int i = 0; i < force.getNumPerAcceptorParameters(); i++)
        variables.insert(force.getPerAcceptorParameterName(i));
    for (int i = 0; i < force.getNumGlobalParameters(); i++)
        variables.insert(force.getGlobalParameterName(i));
    return ParsedExpression(replaceFunctions(expression.getRootNode(), atoms, distances, angles, dihedrals, variables)).optimize();
}
std::vector<Doublet> WLeptonic::ReconstructNeutrino(Lepton const &lepton, Vector2<Momentum> const &missing_et) const
{
    INFO0;
    auto linear_term = (sqr(MassOf(Id::W)) - lepton.MassSquare()) / 2. + missing_et * lepton.Transverse();
    auto lepton_pz_square = sqr(lepton.Pz());
    auto lepton_square = sqr(lepton.Energy()) - lepton_pz_square;
    auto radicant = lepton_pz_square * (sqr(linear_term) -  lepton_square * sqr(missing_et));
    if (radicant < 0_eV * eV * eV2 * eV2) {
        INFO("Imaginary root", "move missing et towards lepton");
        auto reco = missing_et + 0.1 * (lepton.Transverse() - missing_et);
        return ReconstructNeutrino(lepton, reco);
    }
    CHECK(radicant != 0_eV * eV * eV2 * eV2, "Radicant exactly zero", "implement this case!");
    auto sqrt = boost::units::sqrt(radicant);
    auto neutrino_1_e = (lepton.Energy() * linear_term - sqrt) / lepton_square;
    auto neutrino_1_pz = (lepton_pz_square * linear_term - lepton.Energy() * sqrt) / lepton.Pz() / lepton_square;
    auto neutrino_1 = Lepton {missing_et, neutrino_1_pz, neutrino_1_e};
    auto neutrino_2_e = (lepton.Energy() * linear_term + sqrt) / lepton_square;
    auto neutrino_2_pz = (lepton_pz_square * linear_term + lepton.Energy() * sqrt) / lepton.Pz() / lepton_square;
    auto neutrino_2 = Lepton {missing_et, neutrino_2_pz, neutrino_2_e};
    return {Doublet(lepton, neutrino_1), Doublet(lepton, neutrino_2)};
}
void ClosestLepton::AddLepton(const Lepton& lepton)
{
    INFO0;
    if (Close<Lepton>(lepton)(jet_) && lepton.Pt() > Pt()) lepton_ = lepton;
}
void ChargedHiggsTauNu::analyzeGen(Event*e, string systname)
{
    // no systematics shifts for this
    if (systname !="" and systname != "NONE") return;

    string label = GetLabel(e);

    Weight* w = e->GetWeight();
    // make sure we don't book sf or corrections here
    double mcWeight=w->GetBareMCWeight() * w->GetBarePUWeight()* w->GetBareMCXsec() * w->GetBareLumi() / w->GetBareNevents();
    Fill("ChargedHiggsTauNu/Gen/CutFlow_"+label,systname,0,mcWeight);

    // find the H+
    int iHpm=-1;
    int iTau=-1;
    int iLFromTau=-1; // find leptonic taus
    int iIsoL = -1; // is there an isolated lepton ?
    for(unsigned i = 0 ; ;++i)
    {
        GenParticle *g = e->GetGenParticle(i);
        if (g==NULL) break; //exit condition

        int apdg = abs(g->GetPdgId());
        if (iHpm <0  and apdg == 37 ) { iHpm = i; }
        if (iTau <0 and apdg == 15 ) { iTau = i; }
        if (iLFromTau <0 and (apdg == 11 or apdg == 13) and e->GenParticleDecayedFrom(i,15) ) { iLFromTau = i; }
        if (iIsoL <0  and (apdg == 11 or apdg == 13) and g->IsPromptFinalState() and g->Pt() >10 and abs(g->Eta())<2.4){
            float iso = 0.0;
            for(unsigned j = 0 ; ;++j)
            {
                if (i == j ) continue; // no double counting;
                GenParticle *g2 = e->GetGenParticle(j);
                if (g2 == NULL) break;
                if (not g2->IsPromptFinalState()) continue;
                if (g2->IsDressed() ) continue; // no dressed leptons
                if (g2->DeltaR(g) >0.1)  continue;
                iso += g2->Pt();
            }
            if (iso<10) iIsoL = i;
        }

    }

    bool lepVeto=false;
    if (e->Nleps() ==0 ) lepVeto=true;
    else if( e->GetMuon(0) == NULL){ // no 10 GeV muon
        
        if (e->GetElectron(0) !=NULL and e->GetElectron(0)->Pt() <15) lepVeto=true; // pt ordered
    }

    vector<Lepton*> miniIsoLeptons;
    bool lepVetoMiniIso=true;
    for(unsigned il=0 ; ;++il)
    {
        Lepton *l = e->GetBareLepton(il);
        if (l == NULL) break;  // exit strategy
        
        // selection
        if (l->Pt() <10 ) continue;
        //l->SetIsoRelCut(0.25);
        if (abs(l->Eta()) >2.4) continue;
        //medium id
        if( not l->GetMediumId() ) continue;

        //MINI-ISO
        if( l->MiniIsolation() >0.4 ) continue;//loose

        // for muons require tracker and global
        if (l->IsMuonDirty() and not l->GetTrackerMuon())  continue;
        if (l->IsMuonDirty() and not l->GetGlobalMuon())  continue;

        // selected leptons
        miniIsoLeptons.push_back(l);
        lepVetoMiniIso=false;
    }
    // sort miniIsoLeptons by pt
    std::sort(miniIsoLeptons.begin(),miniIsoLeptons.end(),[](Lepton const *a, Lepton const *b ){return a->Pt() > b->Pt();});


    if (iHpm >=0 ) Fill("ChargedHiggsTauNu/Gen/CutFlow_"+label,systname,1,mcWeight);
    if (iHpm >=0 and iTau>=0 ) Fill("ChargedHiggsTauNu/Gen/CutFlow_"+label,systname,2,mcWeight);
    if (iHpm >=0 and iTau>=0 and iLFromTau<0){

        Fill("ChargedHiggsTauNu/Gen/CutFlow_"+label,systname,3,mcWeight);
        if (iIsoL <0) Fill("ChargedHiggsTauNu/Gen/CutFlow_"+label,systname,4,mcWeight);
        if (lepVeto) Fill("ChargedHiggsTauNu/Gen/CutFlow_"+label,systname,5,mcWeight);
        if (lepVetoMiniIso ) Fill("ChargedHiggsTauNu/Gen/CutFlow_"+label,systname,6,mcWeight);
    }

}