text:
#pragma once

#include "../fol/Constant.h"
#include "../fol/Function.h"
#include "../fol/Literal.h"
#include "../fol/Predicate.h"
#include "../fol/Term.h"
#include "../fol/Variable.h"
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/position_tagged.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include <iostream>
#include <string>
#include <tuple>
#include <unordered_set>

// TODO Enforce optionality wherever appropriate, based on EBNF specification.

namespace ast {
    using Name = std::string;
    namespace x3 = boost::spirit::x3;

    // Import some classes that are more generally useful
    using fol::Constant;
    using fol::Literal;
    using fol::Predicate;
    using fol::Term;
    using fol::Variable;

    using PrimitiveType = std::string;

    using EitherType = std::unordered_set<PrimitiveType>;

    // Whenever we don't have recursive variants, we use x3::variant instead of
    // boost::variant, so we get a distinct type.
    struct Type : x3::variant<PrimitiveType, EitherType> {
        using base_type::base_type;
        using base_type::operator=;
    };

    template <class T> using ImplicitlyTypedList = std::vector<T>;

    template <class T> struct ExplicitlyTypedList {
        std::vector<T> entries;
        Type type;
    };

    // TypedList EBNF Definition:
        // typed list (x)> ::= x+ - <type> [<typed list (x)>]
    template <class T> struct TypedList {
        std::vector<ExplicitlyTypedList<T>> explicitly_typed_lists;
        ImplicitlyTypedList<T> implicitly_typed_list = {};
    };

    // AtomicFormulaSkeleton EBNF Definition:
        // <atomic-formula-skeleton> ::= (<predicate> <typed list (variable)>)
        // <predicate> ::= <name>
    struct AtomicFormulaSkeleton : x3::position_tagged {
        Predicate predicate;
        TypedList<Variable> variables;
    };

    // Nil EBNF Definition: <gd> ::= ()
    struct Nil : x3::position_tagged {
        bool operator==(const Nil& nil) const;
    };

    // AtomicFormula EBNF Definition:
        // <atomic formula(t)> ::= (<predicate> t*)
    template <class T> struct AtomicFormula {
        Predicate predicate;
        std::vector<T> args;
    };

    // Forward declare classes in order to work with Boost's recursive_wrapper
    struct ConnectedSentence;
    struct NotSentence;
    struct ImplySentence;
    struct QuantifiedSentence;

    // EqualsSentence EBNF Definition:  <gd> ::= (= <term> <term>)
    struct EqualsSentence {
        Term lhs;
        Term rhs;
    };

    struct NotEqualsSentence : x3::position_tagged {
        Term lhs;
        Term rhs;
    };

    using Sentence =
        boost::variant<Nil,
                       Literal<Term>,
                       boost::recursive_wrapper<ConnectedSentence>,
                       boost::recursive_wrapper<NotSentence>,
                       boost::recursive_wrapper<ImplySentence>,
                       boost::recursive_wrapper<QuantifiedSentence>,
                       EqualsSentence>;

    // ConnectedSentence EBNF Definition: requires disjunctive-preconditions
        // <gd> ::= (and <gd>*)
        // <gd> ::= (or <gd>*)
    struct ConnectedSentence {
        std::string connector;
        std::vector<Sentence> sentences;
    };

    // NotSentence EBNF Definition: requires disjunctive-preconditions
        // <gd> ::= (not <gd>)
    struct NotSentence {
        Sentence sentence;
    };

    // ImplySentence EBNF Definition: requires disjunctive-preconditions
        // <gd> ::= (imply <gd> <gd>)
    struct ImplySentence {
        Sentence sentence1;
        Sentence sentence2;
    };

    // QuantifiedSentence EBNF Definition of exists and forall;
    // exists requires existential-preconditions
        // <gd> ::= (exists (<typed list (variable)>*) <gd>)
    // forall requires universal-preconditions
        // <gd> ::= (forall (<typed list (variable)>*) <gd>)
    struct QuantifiedSentence {
        std::string quantifier;
        TypedList<Variable> variables;
        Sentence sentence;
    };

    struct Constraint : x3::variant<Nil, EqualsSentence, NotEqualsSentence> {
        using base_type::base_type;
        using base_type::operator=;
    };

    // Constraints EBNF Definition:
        // <constraint-defs> ::= ()
        //    | <constraint-def>
        //    | (= <term> <term>)
    struct Constraints : x3::variant<Nil, Constraint, std::vector<Constraint>> {
        using base_type::base_type;
        using base_type::operator=;
    };

    // Abstract Tasks, decomposed by methods
    // Task EBNF Definition:
        // <task-def> ::= <task-symbol>
        //     :parameters (<typed list (variable)>)
        // <task-symbol> ::= <name>
    struct Task : x3::position_tagged {
        Name name;
        TypedList<Variable> parameters;
    };

    // MTask is subtask without an ID. EBNF Definiton:
        // <subtask-def> ::= (<task-symbol> <term>*)
    struct MTask : x3::position_tagged {
        Name name;
        std::vector<Term> parameters;
    };

    // SubTaskWithId EBNF Definition:
        // <subtask-def> ::= (<subtask-id> (<task-symbol> <term>*))
    struct SubTaskWithId : x3::position_tagged {
        Name id;
        MTask subtask;
    };

    // SubTask EBNF Definition:
        // <subtask-def> ::= (<task-symbol> <term>*)
        //                 | (<subtask-id> (<task-symbol> <term>*))

    struct SubTask : x3::variant<MTask, SubTaskWithId> {
        using base_type::base_type;
        using base_type::operator=;
    };

    // SubTasks EBNF Definition:
        // <subtask-defs> ::= () | <subtask-def> | (and <subtask-def>+)
    struct SubTasks : x3::variant<Nil, SubTask, std::vector<SubTask>> {
        using base_type::base_type;
        using base_type::operator=;
    };

    // Ordering EBNF Definition:
        // <ordering_def> ::= (<subtask-id> "<" <subtask-id>)
    struct Ordering : x3::position_tagged {
        Name first;
        Name second;
    };

    // Orderings EBNF Definition:
        // <ordering-defs> ::= () | <ordering-def> | (and <ordering-def>+)
    struct Orderings : x3::variant<Nil, Ordering, std::vector<Ordering>> {
        using base_type::base_type;
        using base_type::operator=;
    };

    struct MethodSubTasks : x3::position_tagged {
        std::string ordering_kw;
        SubTasks subtasks;
    };

    // TaskNetwork EBNF Definition:
        // <tasknetwork-def> ::=
        //    [:[ordered-][sub]tasks <subtask-defs>]
        //    [:order[ing] <ordering-defs>]
        //    [:constraints <constraint-defs>]
    struct TaskNetwork : x3::position_tagged {
        std::optional<MethodSubTasks> subtasks;
        std::optional<Orderings> orderings;
        std::optional<Constraints> constraints;
    };

    // Method EBNF Definition:
        // (<method-def> ::=
        //     method <name>
        //     (<typed list (variable)>)
        //     (<task-symbol> <term>*)
        //     [:precondition <gd>]
        //     <tasknetwork-def>)
    struct Method : x3::position_tagged {
        Name name;
        TypedList<Variable> parameters;
        MTask task;
        Sentence precondition;
        TaskNetwork task_network;
    };

    // Conditional effects
    using PEffect = Literal<Term>;
    using CondEffect = boost::variant<PEffect, std::vector<PEffect>>;

    struct WhenCEffect;
    struct ForallCEffect;
    struct AndCEffect;

    using CEffect = boost::variant<boost::recursive_wrapper<ForallCEffect>,
                                   boost::recursive_wrapper<WhenCEffect>,
                                   PEffect>;

    // Effect EBNF Definition:
        // <effect> ::= ()
        //            | (and <c-effect>*)
        //            | <c-effect>
    using Effect =
        boost::variant<Nil, boost::recursive_wrapper<AndCEffect>, CEffect>;

    // WhenCEffect EBNF Definition: requires conditional-effects
        // <c-effect> ::= (when <gd> <cond-effect>)
    struct WhenCEffect : x3::position_tagged {
        Sentence gd;
        CondEffect cond_effect;
    };

    // ForallCEffect EBNF Definition: requires conditional-effects
        // <c-effect> ::= (forall (<variable>*) <effect>)
    struct ForallCEffect : x3::position_tagged {
        std::vector<Variable> variables;
        Effect effect;
    };

    // AndCEffect EBNF Definition: <effect> ::= (and <c-effect>*)
    struct AndCEffect : x3::position_tagged {
        std::vector<CEffect> c_effects;
    };

    // Primitive Actions
    // Action EBNF Definition:
        // <action-def> ::= (:action
        //     <task-def>
        //     [:precondition <gd>]
        //     [:effects <effect>])
    struct Action : x3::position_tagged {
        Name name;
        TypedList<Variable> parameters;
        Sentence precondition;
        Effect effect;
    };

    // Domain EBNF Definition:
        //  <domain> ::= (define (domain <name>)
        //      [<require-def>]
        //      [<types-def] ^(:typing)
        //      [<constants-def>]
        //      [<predicates-def>]
        //      <comp-task-def>*
        //      <method-def>*
        //      <action-def>*)
    struct Domain : x3::position_tagged {
        Name name;
        std::vector<std::string> requirements;
        TypedList<Name> types;
        TypedList<Name> constants;
        std::vector<AtomicFormulaSkeleton> predicates;
        std::vector<Task> tasks;
        std::vector<Method> methods;
        std::vector<Action> actions;
    };

    // ProblemHTN EBNF Definition:
        // <p-htn> ::= (<p-class>
        //     [:parameters (<typed list (variable)>)]
        //     <tasknetwork-def>)

        // <p-class> ::= :htn
    struct ProblemHTN : x3::position_tagged {
        std::string problem_class;
        TypedList<Variable> parameters;
        TaskNetwork task_network;
    };

    using Init = std::vector<Literal<Name>>;

    // Problem EBNF Definition:
        // <problem> ::= (define (problem <name>)
        //     (:domain <name>)
        //     [<require-def>]
        //     [<p-object-declaration>]
        //     [<p-htn>]
        //     <p-int>
        //     [<p-goal>])
    struct Problem : x3::position_tagged {
        Name name;
        Name domain_name;
        std::vector<std::string> requirements;
        TypedList<Name> objects;
        ProblemHTN problem_htn;
        Init init;
        Sentence goal;
    };

    using boost::fusion::operator<<;
} // namespace ast


text:
// Copyright 2008 by BBN Technologies Corp.
// All Rights Reserved.

#include "Generic/common/leak_detection.h"

#include "Chinese/relations/ch_PotentialRelationCollector.h"
#include "Generic/relations/PotentialTrainingRelation.h"
#include "Generic/relations/PotentialRelationInstance.h"
#include "Chinese/relations/ch_RelationUtilities.h"
#include "Generic/preprocessors/RelationTypeMap.h"
#include "Chinese/relations/ch_PotentialRelationInstance.h"
#include "Generic/theories/SentenceTheory.h"
#include "Generic/theories/DocTheory.h"
#include "Generic/common/InternalInconsistencyException.h"
#include "Generic/common/UTF8OutputStream.h"
#include "Generic/common/ParamReader.h"
#include "Generic/common/UnicodeUtil.h"
#include "Generic/theories/Parse.h"
#include "Generic/theories/Mention.h"
#include "Generic/theories/SynNode.h"
#include "Generic/theories/Token.h"
#include "Generic/theories/PropositionSet.h"
#include "Generic/theories/Proposition.h"
#include "Generic/theories/Argument.h"
#include "Generic/theories/TokenSequence.h"
#include "Generic/common/limits.h"
#include "Generic/common/UTF8InputStream.h"
#include <boost/scoped_ptr.hpp>

using namespace std;

const Symbol NONE_SYM = Symbol(L"NONE");
const Symbol NULL_SYM = Symbol();
const Symbol BAD_EDT_SYM = Symbol(L"BAD EDT");
const Symbol SAME_ENTITY_SYM = Symbol(L"SAME ENTITY");
const Symbol NEG_HYPO_SYM = Symbol(L"NEG/HYPO");

ChinesePotentialRelationCollector::ChinesePotentialRelationCollector(int collectionMode, RelationTypeMap *relationTypes) :
												_docTheory(0),  _mentionSet(0)
{
	_collectionMode = collectionMode;
	_relationTypes = relationTypes;

	if (_collectionMode != EXTRACT &&
		_collectionMode != TRAIN &&
		_collectionMode != CLASSIFY &&
		_collectionMode != CORRECT_ANSWERS)
	{
		char c[200];
		sprintf(c, "%d is not a valid relation collection mode: use EXTRACT, TRAIN, CLASSIFY or CORRECT_ANSWERS", _collectionMode);
				throw InternalInconsistencyException("ChinesePotentialRelationCollector::ChinesePotentialRelationCollector()", c);
	}

	if (_collectionMode == CORRECT_ANSWERS) {
		std::string packet = ParamReader::getRequiredParam("relation_packet_file");
		std::string ca_file = ParamReader::getRequiredParam("correct_answer_file");
		_annotationSet = readPacketAnnotation(packet.c_str());
		_outputStream.open(ca_file.c_str());
		_outputStream << "(\n";
	}

	_n_relations = 0;
}

ChinesePotentialRelationCollector::~ChinesePotentialRelationCollector() {
	if (_collectionMode == CORRECT_ANSWERS) {
		_outputStream << ")\n";
		_outputStream.close();
	}
	finalize();
}

void ChinesePotentialRelationCollector::resetForNewSentence() {
	if (_collectionMode != TRAIN && _collectionMode != CORRECT_ANSWERS)
		finalize(); // get rid of old stuff
}

void ChinesePotentialRelationCollector::loadDocTheory(DocTheory* theory) {
	if (_collectionMode != TRAIN)
		finalize(); // get rid of old stuff
	_docTheory = theory;
}

void ChinesePotentialRelationCollector::produceOutput(const wchar_t *output_dir,
											   const wchar_t *doc_filename)
{
	collectPotentialDocumentRelations();

	if (_collectionMode == EXTRACT && _trainingRelations.length() > 0) {
		wstring output_file = wstring(output_dir) + LSERIF_PATH_SEP + wstring(doc_filename) + L".relsent";

		_outputStream.open(output_file.c_str());
		outputPotentialTrainingRelations();
		_outputStream.close();
		_outputFiles.add(output_file);
	}

	else if (_collectionMode == CORRECT_ANSWERS) {
		outputCorrectAnswerFile();
	}

}

void ChinesePotentialRelationCollector::collectPotentialDocumentRelations() {
	for (int i = 0; i < _docTheory->getNSentences(); i++) {
		Parse *parse = _docTheory->getSentenceTheory(i)->getPrimaryParse();
		PropositionSet *propSet = _docTheory->getSentenceTheory(i)->getPropositionSet();
		MentionSet *mentionSet = _docTheory->getSentenceTheory(i)->getMentionSet();
		collectPotentialSentenceRelations(parse, mentionSet, propSet);
	}
}

void ChinesePotentialRelationCollector::collectPotentialSentenceRelations(const Parse *parse,
																   MentionSet *mentionSet,
																   const PropositionSet *propSet)
{
	_mentionSet = mentionSet;

	std::vector<bool> is_false_or_hypothetical = RelationUtilities::get()->identifyFalseOrHypotheticalProps(propSet, _mentionSet);
	collectDefinitions(propSet);

	// Debug output
	if (RelationUtilities::get()->debugStreamIsOn()) {
		for (int i = 0; i < propSet->getNPropositions(); i++) {
			propSet->getProposition(i)->dump(RelationUtilities::get()->getDebugStream(), 0);
			if (is_false_or_hypothetical[propSet->getProposition(i)->getIndex()])
				RelationUtilities::get()->getDebugStream() << L" -- UNTRUTH";
			RelationUtilities::get()->getDebugStream() << L"\n";
		}
		RelationUtilities::get()->getDebugStream() << parse->getRoot()->toTextString() << L"\n";
		for (int j = 0; j < mentionSet->getNMentions(); j++) {
			Mention *ment = mentionSet->getMention(j);
			if (ment->getEntityType().isRecognized())
				RelationUtilities::get()->getDebugStream() << ment->getNode()->toTextString() << ": " << ment->getEntityType().getName().to_string() << "\n";
		}
		RelationUtilities::get()->getDebugStream() << parse->getRoot()->toPrettyParse(0) << L"\n";
	}

	for (int m = 0; m < propSet->getNPropositions(); m++) {
		Proposition *prop = propSet->getProposition(m);

		if (is_false_or_hypothetical[prop->getIndex()])
			continue;

		if (prop->getPredType() == Proposition::COPULA_PRED) {
			classifyCopulaProposition(prop);
		} else if (prop->getPredType() == Proposition::LOC_PRED) {
			; //classifyLocationProposition(prop); - special case: no classification
		// JCS - 03/12/04 - Don't want to classify members of set with one another
		// JCS - 04/02/04 - Okay, maybe we do - there are some examples in the 2003 ACE Eval data
		} else if (prop->getPredType() == Proposition::SET_PRED) {
			classifySetProposition(prop);
		} else if (prop->getPredType() == Proposition::COMP_PRED) {
			classifyCompProposition(prop);
		} else if (prop->getPredType() == Proposition::VERB_PRED ||
				prop->getPredType() == Proposition::POSS_PRED ||
				prop->getPredType() == Proposition::MODIFIER_PRED ||
				prop->getPredType() == Proposition::NOUN_PRED) {
			classifyProposition(prop);
		}
	}
}

void ChinesePotentialRelationCollector::classifyCopulaProposition(Proposition *prop) {

	const Mention *subject = 0;
	const Mention *object = 0;

	for (int i = 0; i < prop->getNArgs(); i++) {
		Argument *arg = prop->getArg(i);
		if (arg->getType() == Argument::MENTION_ARG) {
			if (arg->getRoleSym() == Argument::SUB_ROLE) {
				subject = arg->getMention(_mentionSet);
			} else if (arg->getRoleSym() == Argument::OBJ_ROLE) {
				object = arg->getMention(_mentionSet);
			}
		}
	}

	if (subject == 0 || object == 0)
		return;

	// Special Cases - not implemented yet in Chinese
	/*int type = RelationTypeSet::getTypeFromSymbol(SpecialRelationCases::getGroupMembers());
	if (type != RelationTypeSet::INVALID_TYPE) {
		if (subject->getMentionType() == Mention::LIST) {
			if (isGroupMention(object)) {
				const Mention *iter = subject->getChild();
				while (iter != 0) {
					if (iter->getEntityType().isRecognized())
						addRelation(iter, object, type, 1);
					iter = iter->getNext();
				}
			}
		} else if (object->getMentionType() == Mention::LIST) {
			if (isGroupMention(subject)) {
				const Mention *iter = object->getChild();
				while (iter != 0) {
					if (iter->getEntityType().isRecognized())
						addRelation(iter, subject, type, 1);
					iter = iter->getNext();
				}
			}
		}
	}*/

	classifyProposition(prop);
}

void ChinesePotentialRelationCollector::classifySetProposition(Proposition *prop) {

	Argument *whereArg = 0;

	// start at 1 instead of 0 to skip the LIST mention
	for (int i = 1; i < prop->getNArgs(); i++) {
		if (prop->getArg(i)->getType() != Argument::MENTION_ARG)
			continue;

		for (int j = i + 1; j < prop->getNArgs(); j++) {
			if (prop->getArg(j)->getType() != Argument::MENTION_ARG)
				continue;
			classifyArgumentPair(prop->getArg(i), prop->getArg(j), prop);
		}

		if (whereArg != 0) {
			classifyArgumentPair(prop->getArg(i), whereArg, prop);
		}
	}

	delete whereArg;

}

void ChinesePotentialRelationCollector::classifyCompProposition(Proposition *prop) {
	// If there are prepositional phrases or other modifiers
	// attached to the comp, we want to distribute those to
	// each member proposition.
	for (int i = 0; i < prop->getNArgs(); i++) {
		if (prop->getArg(i)->getType() == Argument::MENTION_ARG) {
			for (int j = i + 1; j < prop->getNArgs(); j++) {
				if (prop->getArg(j)->getType() == Argument::PROPOSITION_ARG)
					classifyPropArgumentPair(prop->getArg(i), prop->getArg(j), prop, false);
			}
		}
		if (prop->getArg(i)->getType() == Argument::PROPOSITION_ARG) {
			for (int j = i + 1; j < prop->getNArgs(); j++) {
				if (prop->getArg(j)->getType() == Argument::MENTION_ARG)
					classifyPropArgumentPair(prop->getArg(i), prop->getArg(j), prop, true);
			}
		}
	}
}

void ChinesePotentialRelationCollector::classifyProposition(Proposition *prop) {

	bool prop_is_definition_of_EDT_entity = false;
	if (prop->getPredType() == Proposition::NOUN_PRED &&
		prop->getArg(0)->getType() == Argument::MENTION_ARG &&
		prop->getArg(0)->getMention(_mentionSet)->getEntityType().isRecognized())
	{	prop_is_definition_of_EDT_entity = true; }

	Argument *whereArg = 0;
	/*if (_propTakesPlaceAt[prop->getIndex()] != 0) {
		whereArg = _new Argument();
		whereArg->populateWithMention(in_symbol,
			_propTakesPlaceAt[prop->getIndex()]->getMentionIndex());
	}*/

	// BASIC IDEA:
	//
	// Call classifyArgumentPair on every pair of arguments within
	// a proposition.
	//
	// For now, if we have the construction "the LOC, where X happened",
	// we consider it as if in:LOC were an argument to the proposition
	// "X happened". So, for example:
	//
	// "the place where he was killed"
	// p1 = place<noun>(<ref>:e1, where:p2)
	// p2 = killed<verb>(<obj>:e2)
	//  -->
	// argument pair: <obj>:e2 and in:e1

	for (int i = 0; i < prop->getNArgs(); i++) {
		if (prop->getArg(i)->getType() != Argument::MENTION_ARG &&
			prop->getArg(i)->getType() != Argument::PROPOSITION_ARG)
			continue;

		// we don't nest through EDT entities (e.g. _his_ house in _Limassol_)
		if (prop_is_definition_of_EDT_entity &&	i > 0) {
			delete whereArg;
			return;
		}

		if (prop->getArg(i)->getType() == Argument::MENTION_ARG) {

			for (int j = i + 1; j < prop->getNArgs(); j++) {
				if (prop->getArg(j)->getType() == Argument::MENTION_ARG)
					classifyArgumentPair(prop->getArg(i), prop->getArg(j), prop);
				if (prop->getArg(j)->getType() == Argument::PROPOSITION_ARG)
					classifyPropArgumentPair(prop->getArg(i), prop->getArg(j), prop, false);
			}

			if (whereArg != 0) {
				classifyArgumentPair(prop->getArg(i), whereArg, prop);
			}
		}

		if (prop->getArg(i)->getType() == Argument::PROPOSITION_ARG) {

			for (int j = i + 1; j < prop->getNArgs(); j++) {
				if (prop->getArg(j)->getType() == Argument::MENTION_ARG)
					classifyPropArgumentPair(prop->getArg(i), prop->getArg(j), prop, true);
			}
		}

	}

	delete whereArg;

}

void ChinesePotentialRelationCollector::classifyArgumentPair(Argument *leftArg, Argument *rightArg,
															Proposition *prop)
{
	const Mention* left = leftArg->getMention(_mentionSet);
	const Mention* right = rightArg->getMention(_mentionSet);

	// if leftArg and rightArg are both EDT entities, add to
	// _relations
	//
	// if one is and one isn't, but the non-EDT entity is a list,
	// go to FindListInstances
	//
	// if one is and one isn't, and the non-EDT entity isn't a list,
	// go to findNestedInstances
	//
	// if neither are, return.

	if (left->getEntityType().isRecognized() &&
		right->getEntityType().isRecognized())
	{
		addPotentialRelation(leftArg, rightArg, prop);
	} else if (left->getEntityType().isRecognized() &&
		       right->getMentionType() == Mention::LIST) {
		findListInstances(leftArg, rightArg, prop, false);
	} else if (right->getEntityType().isRecognized() &&
		       left->getMentionType() == Mention::LIST) {
		findListInstances(leftArg, rightArg, prop, true);
	} else if (left->getEntityType().isRecognized()) {
		//if (!findWithCoercedType(leftArg, rightArg, prop))  No coercion yet
			findNestedInstances(leftArg, rightArg, prop);
	} else if (right->getEntityType().isRecognized()) {
		// LB: we don't nest on this side (for now)
		; //findWithCoercedType(leftArg, rightArg, prop);    No coercion yet
	}
}

void ChinesePotentialRelationCollector::classifyPropArgumentPair(Argument *left, Argument *right,
														  Proposition *prop, bool leftIsProp)
{
	const Proposition *inner_term;
	if (leftIsProp) {
		inner_term = left->getProposition();
		if (!right->getMention(_mentionSet)->getEntityType().isRecognized())
			return;
	}
	else {
		inner_term = right->getProposition();
		if (!left->getMention(_mentionSet)->getEntityType().isRecognized())
			return;
	}

	for (int i = 0; i < inner_term->getNArgs(); i++) {
		if (inner_term->getArg(i)->getType() == Argument::PROPOSITION_ARG) {
			if (leftIsProp)
				classifyPropArgumentPair(inner_term->getArg(i), right, prop, true);
			else
				classifyPropArgumentPair(left, inner_term->getArg(i), prop, false);
		}
		if (inner_term->getArg(i)->getType() != Argument::MENTION_ARG)
			continue;
		if (!inner_term->getArg(i)->getMention(_mentionSet)->getEntityType().isRecognized())
			continue;
		if (leftIsProp)
			addNestedPotentialRelation(right, left, inner_term->getArg(i), prop, inner_term);
		else
			addNestedPotentialRelation(left, right, inner_term->getArg(i), prop, inner_term);
	}

}

/** this function deals specifically with lists of type OTH
 *  with children of only one EDT type
 *
 *  for example: in "a boxer and an emigre", we don't get "a boxer" as
 *  a PER, so the list is of type OTH. Same with "Bob, 52," (annoying but true)
 *
 */
void ChinesePotentialRelationCollector::findListInstances(Argument *left, Argument *right,
														 Proposition *prop, bool leftIsList)
{

	const Mention *iter;
	if (leftIsList)
		iter = left->getMention(_mentionSet)->getChild();
	else iter = right->getMention(_mentionSet)->getChild();

	while (iter != 0) {
		if (iter->getEntityType().isRecognized() && _definitions[iter->getIndex()] != 0) {
			if (leftIsList)
				addPotentialRelation(_definitions[iter->getIndex()]->getArg(0), right, prop);
			else addPotentialRelation(left, _definitions[iter->getIndex()]->getArg(0), prop);
		}
		iter = iter->getNext();
	}
}

void ChinesePotentialRelationCollector::findNestedInstances(Argument *first, Argument *intermediate,
														  Proposition *prop)
{
	if (first->getRoleSym() != Argument::SUB_ROLE &&
		first->getRoleSym() != Argument::OBJ_ROLE &&
		first->getRoleSym() != Argument::REF_ROLE)
		return;

	int inner_term_index = intermediate->getMentionIndex();
	Proposition *inner_term = _definitions[inner_term_index];
	if (inner_term == 0)
		return;

	for (int i = 1; i < inner_term->getNArgs(); i++) {
		if (inner_term->getArg(i)->getType() == Argument::PROPOSITION_ARG)
			// example: "He found the cat that was in the barn" ??
			classifyPropArgumentPair(first, inner_term->getArg(i), prop, false);
		if (inner_term->getArg(i)->getType() != Argument::MENTION_ARG)
			continue;
		if (!inner_term->getArg(i)->getMention(_mentionSet)->getEntityType().isRecognized())
			continue;
		addNestedPotentialRelation(first, intermediate, inner_term->getArg(i), prop, inner_term);
	}
}

void ChinesePotentialRelationCollector::addPotentialRelation(Argument *arg1, Argument *arg2, const Proposition *prop) {

	// return if same mention
	if (arg1->getMentionIndex() == arg2->getMentionIndex())
		return;

	// return if mentions of the same entity (we only have access to entity set in EXTRACT or TRAIN mode)
	if (_collectionMode == EXTRACT || _collectionMode == TRAIN) {
		if (_docTheory->getEntitySet()->getEntityByMention(
			arg1->getMention(_mentionSet)->getUID(), arg1->getMention(_mentionSet)->getEntityType()) ==
		_docTheory->getEntitySet()->getEntityByMention(
			arg2->getMention(_mentionSet)->getUID(), arg2->getMention(_mentionSet)->getEntityType()))
		return;
	}

	Proposition *def = _definitions[arg1->getMentionIndex()];
	if (def != NULL && def->getPredType() == Proposition::SET_PRED) {
		distributeOverSet(def, arg2, true, prop);
		return;
	}
	def = _definitions[arg2->getMentionIndex()];
	if (def != NULL && def->getPredType() == Proposition::SET_PRED) {
		distributeOverSet(def, arg1, false, prop);
		return;
	}

	if (_collectionMode == EXTRACT || _collectionMode == TRAIN || _collectionMode == CORRECT_ANSWERS) {
		PotentialTrainingRelation *tRel = _new PotentialTrainingRelation(arg1, arg2, _docTheory,
																		 _mentionSet->getSentenceNumber());
		if (_relationSet.find(*tRel) != _relationSet.end()) {
			delete tRel;
			return;
		}
		_relationSet[*tRel] = true;
		_trainingRelations.add(tRel);
	}
	if (_collectionMode == TRAIN || _collectionMode == CLASSIFY) {
		PotentialRelationInstance *iRel = _new ChinesePotentialRelationInstance();
		iRel->setStandardInstance(arg1, arg2, prop, _mentionSet);
		_relationInstances.add(iRel);
	}

	//addMetonymicRelations(arg1, arg2, prop);

	_n_relations++;
}

void ChinesePotentialRelationCollector::distributeOverSet(Proposition *set, Argument *arg,
													  bool set_is_arg1, const Proposition *prop)
{

	for (int j = 1; j < set->getNArgs(); j++) {
		Argument *memberArg = set->getArg(j);
		if (memberArg->getType() == Argument::MENTION_ARG &&
			memberArg->getMention(_mentionSet)->isOfRecognizedType())
		{
			if (set_is_arg1)
				addPotentialRelation(memberArg, arg, prop);
			else
				addPotentialRelation(arg, memberArg, prop);
		}
	}

}

void ChinesePotentialRelationCollector::distributeOverNestedSet(Proposition *set, Argument *arg,
															   Argument *intermediate,
															   bool set_is_arg1,
															   const Proposition *outer_arg,
															   const Proposition *inner_arg)
{

	for (int j = 1; j < set->getNArgs(); j++) {
		Argument *memberArg = set->getArg(j);
		if (memberArg->getType() == Argument::MENTION_ARG &&
			memberArg->getMention(_mentionSet)->isOfRecognizedType())
		{
			if (set_is_arg1)
				addNestedPotentialRelation(memberArg, intermediate, arg, outer_arg, inner_arg);
			else
				addNestedPotentialRelation(arg, intermediate, memberArg, outer_arg, inner_arg);
		}
	}

}

void ChinesePotentialRelationCollector::addNestedPotentialRelation(Argument *arg, Argument *intermediate,
																  Argument *nested, const Proposition *outer_arg,
																  const Proposition *inner_arg)
{
	if (arg->getMentionIndex() == nested->getMentionIndex())
		return;

	// return if mentions of the same entity (we only have access to entity set in EXTRACT or TRAIN mode)
	if (_collectionMode == EXTRACT || _collectionMode == TRAIN) {
		if (_docTheory->getEntitySet()->getEntityByMention(
			arg->getMention(_mentionSet)->getUID(), arg->getMention(_mentionSet)->getEntityType()) ==
			_docTheory->getEntitySet()->getEntityByMention(
			nested->getMention(_mentionSet)->getUID(), nested->getMention(_mentionSet)->getEntityType()))
			return;
	}

	Proposition *def = _definitions[arg->getMentionIndex()];
	if (def != NULL && def->getPredType() == Proposition::SET_PRED) {
		distributeOverNestedSet(def, nested, intermediate, true, outer_arg, inner_arg);
		return;
	}
	def = _definitions[nested->getMentionIndex()];
	if (def != NULL && def->getPredType() == Proposition::SET_PRED) {
		distributeOverNestedSet(def, arg, intermediate, false, outer_arg, inner_arg);
		return;
	}

	if (_collectionMode == EXTRACT || _collectionMode == TRAIN || _collectionMode == CORRECT_ANSWERS) {
		PotentialTrainingRelation *tRel = _new PotentialTrainingRelation(arg, nested, _docTheory,
																_mentionSet->getSentenceNumber());
		if (_relationSet.find(*tRel) != _relationSet.end()) {
			delete tRel;
			return;
		}
		_relationSet[*tRel] = true;
		_trainingRelations.add(tRel);
	}
	if (_collectionMode == TRAIN || _collectionMode == CLASSIFY) {
		PotentialRelationInstance *iRel = _new ChinesePotentialRelationInstance();
		iRel->setStandardNestedInstance(arg, intermediate, nested, outer_arg, inner_arg, _mentionSet);
		_relationInstances.add(iRel);
	}

	//addMetonymicNestedRelations(arg, intermediate, nested, outer_arg, inner_arg);

	_n_relations++;
}

void ChinesePotentialRelationCollector::outputPotentialTrainingRelations() {
	_outputStream << _trainingRelations.length() << L"\n";
	for (int i = 0; i < _trainingRelations.length(); i++) {
		_outputStream << *_trainingRelations[i];
	}
}

void ChinesePotentialRelationCollector::outputCorrectAnswerFile() {
	_outputStream << "(" << _docTheory->getDocument()->getName().to_string() << "\n";
	outputCorrectEntities();
	outputCorrectRelations();
	_outputStream << ")" << "\n";
}

void ChinesePotentialRelationCollector::outputCorrectEntities() {
	_outputStream << "  (Entities" << "\n";

	for (int i = 0; i < _docTheory->getNSentences(); i++) {
		MentionSet *mentions = _docTheory->getSentenceTheory(i)->getMentionSet();
		TokenSequence *tokens = _docTheory->getSentenceTheory(i)->getTokenSequence();
		for (int j = 0; j < mentions->getNMentions(); j++) {
			Mention *m = mentions->getMention(j);
			if (m->isOfRecognizedType() &&
			    (m->getMentionType() == Mention::NAME ||
				 m->getMentionType() == Mention::DESC ||
				 m->getMentionType() == Mention::PART ||
				 m->getMentionType() == Mention::PRON))
			{
				_outputStream << "    (" << m->getEntityType().getName().to_string();
				_outputStream << " " << getDefaultSubtype(m->getEntityType()).to_string();
				_outputStream << " SPC FALSE E" << m->getUID() << "\n";
				EDTOffset start = tokens->getToken(m->getNode()->getStartToken())->getStartEDTOffset();
				EDTOffset end = tokens->getToken(m->getNode()->getEndToken())->getEndEDTOffset();
				_outputStream << "      (" << convertMentionType(m) << " ";
				if (m->hasRoleType())
					_outputStream << m->getRoleType().getName().to_string() << " ";
				else
					_outputStream << "NONE ";
				_outputStream << start << " " << end;
				_outputStream << " " << start << " " << end << " " << m->getUID() << "-1)\n";
				_outputStream << "    )\n";
			}
		}
	}
	_outputStream << "  )" << "\n";
}

void ChinesePotentialRelationCollector::outputCorrectRelations() {
	_outputStream << "  (Relations" << "\n";

	for (int i = 0; i < getNRelations(); i++) {
		PotentialTrainingRelation *rel = getPotentialTrainingRelation(i);
		PotentialRelationMap::iterator it = _annotationSet->find(*rel);
		if (it != _annotationSet->end())  {
			PotentialTrainingRelation ann = ((*it).first);
			if (ann.getRelationType() != BAD_EDT_SYM &&
				ann.getRelationType() != SAME_ENTITY_SYM &&
				ann.getRelationType() != NEG_HYPO_SYM &&
				ann.getRelationType() != NONE_SYM)
			{
				Symbol type = _relationTypes->lookup(ann.getRelationType());
				_outputStream << "    (" << type.to_string() << " EXPLICIT R" << i << "\n";
				if (!ann.relationIsReversed()) {
					_outputStream << "      (" << rel->getLeftMention() << "-1 ";
					_outputStream << rel->getRightMention() << "-1 " << i << "-1)\n";
				}
				else {
					_outputStream << "      (" << rel->getRightMention() << "-1 ";
					_outputStream << rel->getLeftMention() << "-1 " << i << "-1)\n";
				}
				_outputStream << "    )\n";
			}
			else if (ann.getRelationType() == NEG_HYPO_SYM ||
					 ann.getRelationType() == NONE_SYM)
			{
				_outputStream << "    (" << NONE_SYM.to_string() << " EXPLICIT R" << i << "\n";
				if (!ann.relationIsReversed()) {
					_outputStream << "      (" << rel->getLeftMention() << "-1 ";
					_outputStream << rel->getRightMention() << "-1 " << i << "-1)\n";
				}
				else {
					_outputStream << "      (" << rel->getRightMention() << "-1 ";
					_outputStream << rel->getLeftMention() << "-1 " << i << "-1)\n";
				}
				_outputStream << "    )\n";
			}

		}

	}
	_outputStream << "  )" << "\n";
}

void ChinesePotentialRelationCollector::finalize() {
	while (_trainingRelations.length() > 0) {
		PotentialTrainingRelation *r = _trainingRelations.removeLast();
		delete r;
	}
	while (_relationInstances.length() > 0) {
		PotentialRelationInstance *r = _relationInstances.removeLast();
		delete r;
	}

	_relationSet.clear();
	_n_relations = 0;
}

void ChinesePotentialRelationCollector::outputPacketFile(const char *output_dir,
												  const char *packet_name)
{
	std::stringstream packet_file;
	packet_file << output_dir << SERIF_PATH_SEP << packet_name << ".pkt";

	ofstream packetStream;
	packetStream.open(packet_file.str().c_str());
	packetStream << _outputFiles.length() << "\n";
	for (int i = 0; i < _outputFiles.length(); i++) {
		packetStream << _outputFiles[i] << "\n";
	}
	packetStream.close();

}

PotentialTrainingRelation* ChinesePotentialRelationCollector::getPotentialTrainingRelation(int i) {
	if (i < 0 || i > _trainingRelations.length() - 1)
		throw InternalInconsistencyException("ChinesePotentialRelationCollector::getPotentialTrainingRelation",
											 "Array index out of bounds");
	else
		return _trainingRelations[i];
}

PotentialRelationInstance* ChinesePotentialRelationCollector::getPotentialRelationInstance(int i) {
	if (i < 0 || i > _relationInstances.length() - 1)
		throw InternalInconsistencyException("ChinesePotentialRelationCollector::getPotentialRelationInstance",
											 "Array index out of bounds");
	else
		return _relationInstances[i];
}

void ChinesePotentialRelationCollector::collectDefinitions(const PropositionSet *propSet) {

	_definitions.clear();
	/*for (int j = 0; j < propSet->getNPropositions(); j++) {
		_propTakesPlaceAt[j] = 0;
	}*/

	for (int k = 0; k < propSet->getNPropositions(); k++) {
		Proposition *prop = propSet->getProposition(k);
		if (prop->getPredType() == Proposition::NOUN_PRED ||
			prop->getPredType() == Proposition::NAME_PRED ||
			prop->getPredType() == Proposition::MODIFIER_PRED ||
			prop->getPredType() == Proposition::SET_PRED ||
			prop->getPredType() == Proposition::PRONOUN_PRED) {
			int index = prop->getArg(0)->getMentionIndex();
			_definitions[index] = prop;
			/*if (prop->getArg(0)->getType() == Argument::MENTION_ARG &&
				prop->getArg(0)->getMention(_mentionSet)->getEntityType().isRecognized())
			{
				for (int j = 1; j < prop->getNArgs(); j++) {
					Argument *arg = prop->getArg(j);
					if (arg->getType() == Argument::PROPOSITION_ARG &&
						arg->getRoleSym() == ChineseWordConstants::WHERE)
					{
						_propTakesPlaceAt[arg->getProposition()->getIndex()] =
							prop->getArg(0);
					}
				}
			}*/
		}
	}
}

void ChinesePotentialRelationCollector::addMetonymicRelations(Argument *arg1, Argument *arg2, const Proposition *prop) {

	if (arg1->getMention(_mentionSet)->hasIntendedType()) {
		if (_collectionMode == EXTRACT || _collectionMode == TRAIN || _collectionMode == CORRECT_ANSWERS) {
			PotentialTrainingRelation *tRel = _new PotentialTrainingRelation(arg1, arg2,
																			 _docTheory,
																			 _mentionSet->getSentenceNumber(),
																			 true, false);
			_relationSet[*tRel] = true;
			_trainingRelations.add(tRel);
		}
		if (_collectionMode == TRAIN || _collectionMode == CLASSIFY) {
			PotentialRelationInstance *iRel = _new ChinesePotentialRelationInstance();
			iRel->setStandardInstance(arg1, arg2, prop, _mentionSet);
			iRel->setLeftEntityType(arg1->getMention(_mentionSet)->getIntendedType().getName());
			_relationInstances.add(iRel);
		}
		_n_relations++;
	}
	if (arg2->getMention(_mentionSet)->hasIntendedType()) {
		if (_collectionMode == EXTRACT || _collectionMode == TRAIN || _collectionMode == CORRECT_ANSWERS) {
			PotentialTrainingRelation *tRel = _new PotentialTrainingRelation(arg1, arg2,
																			_docTheory,
																			_mentionSet->getSentenceNumber(),
																			false, true);
			_relationSet[*tRel] = true;
			_trainingRelations.add(tRel);
		}
		if (_collectionMode == TRAIN || _collectionMode == CLASSIFY) {
			PotentialRelationInstance *iRel = _new ChinesePotentialRelationInstance();
			iRel->setStandardInstance(arg1, arg2, prop, _mentionSet);
			iRel->setRightEntityType(arg2->getMention(_mentionSet)->getIntendedType().getName());
			_relationInstances.add(iRel);
		}
		_n_relations++;
	}
	if (arg1->getMention(_mentionSet)->hasIntendedType() && arg2->getMention(_mentionSet)->hasIntendedType()) {
		EntityType lIntended = arg1->getMention(_mentionSet)->getIntendedType();
		EntityType rIntended = arg2->getMention(_mentionSet)->getIntendedType();
		if (_collectionMode == EXTRACT || _collectionMode == TRAIN || _collectionMode == CORRECT_ANSWERS) {
			PotentialTrainingRelation *tRel = _new PotentialTrainingRelation(arg1, arg2,
																			_docTheory,
																			_mentionSet->getSentenceNumber(),
																			true, true);
			_relationSet[*tRel] = true;
			_trainingRelations.add(tRel);
		}
		if (_collectionMode == TRAIN || _collectionMode == CLASSIFY) {
			PotentialRelationInstance *iRel = _new ChinesePotentialRelationInstance();
			iRel->setStandardInstance(arg1, arg2, prop, _mentionSet);
			iRel->setLeftEntityType(arg1->getMention(_mentionSet)->getIntendedType().getName());
			iRel->setRightEntityType(arg2->getMention(_mentionSet)->getIntendedType().getName());
			_relationInstances.add(iRel);
		}
		_n_relations++;
	}
}

void ChinesePotentialRelationCollector::addMetonymicNestedRelations(Argument *arg, Argument *intermediate,
															 Argument *nested, const Proposition *outer_arg,
															 const Proposition *inner_arg) {
	if (arg->getMention(_mentionSet)->hasIntendedType()) {
		if (_collectionMode == EXTRACT || _collectionMode == TRAIN || _collectionMode == CORRECT_ANSWERS) {
			PotentialTrainingRelation *tRel = _new PotentialTrainingRelation(arg, nested,
																			 _docTheory,
																			 _mentionSet->getSentenceNumber(),
																			 true, false);
			_relationSet[*tRel] = true;
			_trainingRelations.add(tRel);
		}
		if (_collectionMode == TRAIN || _collectionMode == CLASSIFY) {
			PotentialRelationInstance *iRel = _new ChinesePotentialRelationInstance();
			iRel->setStandardNestedInstance(arg, intermediate, nested, outer_arg, inner_arg, _mentionSet);
			iRel->setLeftEntityType(arg->getMention(_mentionSet)->getIntendedType().getName());
			_relationInstances.add(iRel);
		}
		_n_relations++;
	}
	if (nested->getMention(_mentionSet)->hasIntendedType()) {
		if (_collectionMode == EXTRACT || _collectionMode == TRAIN || _collectionMode == CORRECT_ANSWERS) {
			PotentialTrainingRelation *tRel = _new PotentialTrainingRelation(arg, nested,
																			 _docTheory,
																			 _mentionSet->getSentenceNumber(),
																			 false, true);
			_relationSet[*tRel] = true;
			_trainingRelations.add(tRel);
		}
		if (_collectionMode == TRAIN || _collectionMode == CLASSIFY) {
			PotentialRelationInstance *iRel = _new ChinesePotentialRelationInstance();
			iRel->setStandardNestedInstance(arg, intermediate, nested, outer_arg, inner_arg, _mentionSet);
			iRel->setRightEntityType(arg->getMention(_mentionSet)->getIntendedType().getName());
			_relationInstances.add(iRel);
		}
		_n_relations++;
	}
	if (arg->getMention(_mentionSet)->hasIntendedType() && nested->getMention(_mentionSet)->hasIntendedType()) {
		if (_collectionMode == EXTRACT || _collectionMode == TRAIN || _collectionMode == CORRECT_ANSWERS) {
			PotentialTrainingRelation *tRel = _new PotentialTrainingRelation(arg, nested,
																			 _docTheory,
																			 _mentionSet->getSentenceNumber(),
																			 true, true);
			_relationSet[*tRel] = true;
			_trainingRelations.add(tRel);
		}
		if (_collectionMode == TRAIN || _collectionMode == CLASSIFY) {
			PotentialRelationInstance *iRel = _new ChinesePotentialRelationInstance();
			iRel->setStandardNestedInstance(arg, intermediate, nested, outer_arg, inner_arg, _mentionSet);
			iRel->setLeftEntityType(arg->getMention(_mentionSet)->getIntendedType().getName());
			iRel->setRightEntityType(arg->getMention(_mentionSet)->getIntendedType().getName());
			_relationInstances.add(iRel);
		}
		_n_relations++;
	}
}

const wchar_t* ChinesePotentialRelationCollector::convertMentionType(Mention* ment)
{
	Mention::Type type = ment->mentionType;
	Mention* subMent = 0;
	switch (type) {
		case Mention::NAME:
			return L"NAME";
		case Mention::DESC:
		case Mention::PART:
			return L"NOMINAL";
		case Mention::PRON:
			return L"PRONOUN";
		case Mention::APPO:
			// in ACE 2004, we shouldn't be printing these
			return L"WARNING_BAD_MENTION_TYPE";
		case Mention::LIST:
			// for list, the type is the type of the first child
			subMent = ment->getChild();
			return convertMentionType(subMent);
		default:
			return L"WARNING_BAD_MENTION_TYPE";
	}
}

Symbol ChinesePotentialRelationCollector::getDefaultSubtype(EntityType type) {
	if (type.matchesPER())
		return Symbol(L"NONE");
	else if (type.matchesLOC())
		return Symbol(L"Address");
	else
		return Symbol(L"Other");
}

ChinesePotentialRelationCollector::PotentialRelationMap *ChinesePotentialRelationCollector::readPacketAnnotation
																			(const char *packet_file)
{
	PotentialRelationMap *annotationSet = _new PotentialRelationMap(1024);
	boost::scoped_ptr<UTF8InputStream> packet_scoped_ptr(UTF8InputStream::build());
	UTF8InputStream& packet(*packet_scoped_ptr);
	wchar_t line[200];

	packet.open(packet_file);
	if (packet.fail()) {

		char message[300];
		sprintf(message, "Could not open packet file `%s'", packet_file);
		throw UnexpectedInputException("ChinesePotentialRelationCollector::readPacketAnnotation()", message);
	}
	packet.getLine(line, 200);
	int n_packet_files = _wtoi(line);
	for (int i = 0; i < n_packet_files; i++) {
		PotentialTrainingRelation rel;
		boost::scoped_ptr<UTF8InputStream> in_scoped_ptr(UTF8InputStream::build());
		UTF8InputStream& in(*in_scoped_ptr);

		if (packet.eof()) {
			char message[300];
			sprintf(message, "Reached unexpected end of `%s'", packet_file);
			throw UnexpectedInputException("ChinesePotentialRelationCollector::readPacketAnnotation()",  message);
		}
		packet.getLine(line, 200);
		in.open(line);
		if (in.fail()) {
			char message[300];
			sprintf(message, "Could not open annotation file `%s'", UnicodeUtil::toUTF8String(line));
			throw UnexpectedInputException("ChinesePotentialRelationCollector::readPacketAnnotation()", message);
		}
		in.getLine(line, 200);
		int n_relations = _wtoi(line);
		for (int j = 0; j < n_relations; j++) {
			in >> rel;
			(*annotationSet)[rel] = rel.getRelationType();
		}
		in.close();

	}
	packet.close();
	return annotationSet;
}

ext: cpp

max_stars_repo_name: BBN-E/serif

max_issues_repo_name: BBN-E/serif

max_forks_repo_name: BBN-E/serif


text:
/*
 * Copyright 2014-2019, CNRS
 * Copyright 2018-2019, INRIA
 */

#ifndef __eigenpy_stride_hpp__
#define __eigenpy_stride_hpp__

#include <Eigen/Core>

namespace eigenpy
{
  template<typename MatType, int InnerStride = Eigen::Dynamic, int OuterStride = Eigen::Dynamic, bool IsVectorAtCompileTime = MatType::IsVectorAtCompileTime>
  struct StrideType
  {
    typedef Eigen::Stride<OuterStride,InnerStride> type;
  };

  template<typename MatType, int InnerStride, int OuterStride>
  struct StrideType<MatType,InnerStride,OuterStride,true>
  {
    typedef Eigen::InnerStride<InnerStride> type;
  };

}

#endif // ifndef __eigenpy_stride_hpp__

ext: hpp

max_stars_repo_name: seanyen/eigenpy

max_issues_repo_name: seanyen/eigenpy

max_forks_repo_name: seanyen/eigenpy


text:
/*
 * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved.
 */

#include "oper/instance_manager.h"

#include <cstdlib>
#include <boost/filesystem.hpp>
#include <boost/uuid/random_generator.hpp>

#include "base/logging.h"
#include "base/os.h"
#include "base/test/task_test_util.h"
#include "cfg/cfg_init.h"
#include "db/db_graph.h"
#include "db/test/db_test_util.h"
#include "ifmap/ifmap_agent_table.h"
#include "ifmap/ifmap_node.h"
#include "ifmap/test/ifmap_test_util.h"
#include "oper/ifmap_dependency_manager.h"
#include "oper/instance_task.h"
#include "oper/loadbalancer.h"
#include "oper/operdb_init.h"
#include "oper/service_instance.h"
#include "schema/vnc_cfg_types.h"
#include "testing/gunit.h"

using namespace std;
class Agent;
void RouterIdDepInit(Agent *agent) {
}

static boost::uuids::uuid IdPermsGetUuid(const autogen::IdPermsType &id) {
    boost::uuids::uuid uuid;
    CfgUuidSet(id.uuid.uuid_mslong, id.uuid.uuid_lslong, uuid);
    return uuid;
}

class InstanceManagerTest : public ::testing::Test {
public:
    bool IsExpectedStatusType(InstanceState *state, int expected) {
        return state->status_type() == expected;
    }

    bool IsUpdateCommand(InstanceState *state) {
        return string::npos != state->cmd().find("--update");
    }

    bool WaitForAWhile(time_t target) {
        time_t now = time(NULL);
        return now >= target;
    }

protected:
    static const int kTimeoutSeconds = 15;

    InstanceManagerTest()
          : agent_(new Agent),
            agent_config_(new AgentConfig(agent_.get())) {
        agent_->set_cfg(agent_config_.get());
        oper_db_.reset(new OperDB(agent_.get()));
        agent_->set_oper_db(oper_db_.get());
        stringstream ss;
        boost::filesystem::path curr_dir(boost::filesystem::current_path());
        ss << curr_dir.string() << "/" << getpid() << "/";
        agent_->oper_db()-> instance_manager()->loadbalancer_config_path_ = ss.str();
    }

    ~InstanceManagerTest() {
    }

    virtual void TearDown() {
        task_util::WaitForIdle();

        agent_->oper_db()->Shutdown();
        agent_->cfg()->Shutdown();
        task_util::WaitForIdle();

        DB *database = agent_->db();
        db_util::Clear(database);
        task_util::WaitForIdle();

        /**
         * The factory create method for ifmap link table takes the
         * graph as a boost::bind() argument; failure to cleanup the registry
         * implies creating the table using a stale graph pointer.
         */
        DB::ClearFactoryRegistry();
    }

    virtual void SetUp() {
        DB *db = agent_->db();
        agent_config_->CreateDBTables(db);
        oper_db_->CreateDBTables(db);
        agent_config_->RegisterDBClients(db);
        oper_db_->RegisterDBClients();
        agent_config_->Init();
        // Different test cases initialize the instance_manager with
        // different parameters so we avoid called OperDB::Init().
        oper_db_->dependency_manager()->Initialize();
    }

    boost::uuids::uuid AddServiceInstance(const string &name) {
        ifmap_test_util::IFMapMsgNodeAdd(agent_->db(), "service-instance", name);
        task_util::WaitForIdle();
        IFMapTable *table = IFMapTable::FindTable(agent_->db(),
                                                  "service-instance");
        ServiceInstanceTable *si_table = agent_->service_instance_table();
        IFMapNode *node = table->FindNode(name);
        if (node == NULL) {
            return boost::uuids::nil_uuid();
        }
        DBRequest request;
        si_table->IFNodeToReq(node, request);
        si_table->Enqueue(&request);
        task_util::WaitForIdle();

        autogen::ServiceInstance *si_object =
                static_cast<autogen::ServiceInstance *>(node->GetObject());
        const autogen::IdPermsType &id = si_object->id_perms();
        boost::uuids::uuid instance_id = IdPermsGetUuid(id);
        ServiceInstanceKey key(instance_id);
        ServiceInstance *svc_instance =
                static_cast<ServiceInstance *>(si_table->Find(&key, true));
        if (svc_instance == NULL) {
            return boost::uuids::nil_uuid();
        }

        /*
         * Set non-nil uuids
         */
        UpdateProperties(svc_instance, true);
        return instance_id;
    }

    void AddLoadbalancerVip(const string &pool_name) {
        string vip_name("vip" + pool_name);
        autogen::VirtualIpType vip_attr;
        vip_attr.protocol = "HTTP";
        vip_attr.protocol_port = 80;
        vip_attr.connection_limit = 100;
        map<string, AutogenProperty *> pmap;
        pmap.insert(make_pair("virtual-ip-properties", &vip_attr));
        ifmap_test_util::IFMapMsgPropertySet(
            agent_->db(), "virtual-ip", vip_name, pmap, 0);

        ifmap_test_util::IFMapMsgLink(
            agent_->db(), "loadbalancer-pool", pool_name,
            "virtual-ip", vip_name,
            "virtual-ip-loadbalancer-pool");
    }

   Loadbalancer *GetLoadbalancer(boost::uuids::uuid pool_id) {
        LoadbalancerKey key(pool_id);
        return static_cast<Loadbalancer *>(
            agent_->loadbalancer_table()->Find(&key, true));
    }

    void AddLoadbalancerMember(std::string &pool_name,
                               const char *ip_address, int port) {
        boost::uuids::random_generator gen;
        boost::uuids::uuid member_id = gen();
        map<string, AutogenProperty *> pmap;
        autogen::LoadbalancerMemberType attr;
        attr.address = ip_address;
        attr.protocol_port = port;
        pmap.insert(make_pair("loadbalancer-member-properties", &attr));
        ifmap_test_util::IFMapMsgPropertySet(
            agent_->db(), "loadbalancer-member", UuidToString(member_id),
            pmap, 0);

        ifmap_test_util::IFMapMsgLink(
            agent_->db(), "loadbalancer-pool", pool_name,
            "loadbalancer-member", UuidToString(member_id),
            "loadbalancer-pool-loadbalancer-member");
    }

    boost::uuids::uuid AddLoadbalancer(std::string pool_name) {

        map<string, AutogenProperty *> pmap;
        autogen::LoadbalancerPoolType pool_attr;
        pool_attr.protocol = "HTTP";
        pool_attr.loadbalancer_method = "ROUND_ROBIN";
        pmap.insert(make_pair("loadbalancer-pool-properties", &pool_attr));
        ifmap_test_util::IFMapMsgPropertySet(
            agent_->db(), "loadbalancer-pool", pool_name, pmap, 0);

        AddLoadbalancerVip(pool_name);

        task_util::WaitForIdle();
        IFMapTable *table = IFMapTable::FindTable(agent_->db(),
                                                  "loadbalancer-pool");
        IFMapNode *node = table->FindNode(pool_name);
        if (node == NULL) {
            return boost::uuids::nil_uuid();
        }

       AddLoadbalancerMember(pool_name, "127.0.0.1", 80);
       AddLoadbalancerMember(pool_name, "127.0.0.2", 80);

        DBRequest request;
        agent_->loadbalancer_table()->IFNodeToReq(node, request);
        agent_->loadbalancer_table()->Enqueue(&request);

       task_util::WaitForIdle();
        autogen::LoadbalancerPool *lb_object =
                static_cast<autogen::LoadbalancerPool *>(node->GetObject());
        const autogen::IdPermsType &id = lb_object->id_perms();
        boost::uuids::uuid instance_id = IdPermsGetUuid(id);

        return instance_id;
    }

    void DeleteServiceInstance(const string &name) {
        ifmap_test_util::IFMapMsgNodeDelete(agent_->db(), "service-instance", name);
    }

    void MarkServiceInstanceAsDeleted(boost::uuids::uuid id) {
        ServiceInstanceKey key(id);
        ServiceInstance *svc_instance =
                static_cast<ServiceInstance
                *>(agent_->service_instance_table()->Find(&key, true));
        if (svc_instance != NULL) {
            svc_instance->MarkDelete();
            agent_->service_instance_table()->Change(svc_instance);
        }
    }

    bool UpdateProperties(boost::uuids::uuid id, bool usable) {
        ServiceInstanceKey key(id);
        ServiceInstance *svc_instance = static_cast<ServiceInstance *>
                (agent_->service_instance_table()->Find(&key, true));
        if (svc_instance == NULL) {
            return false;
        }
        UpdateProperties(svc_instance, usable);

        return true;
    }

    ServiceInstance *GetServiceInstance(boost::uuids::uuid id) {
        ServiceInstanceKey key(id);
        ServiceInstance *svc_instance = static_cast<ServiceInstance *>
            (agent_->service_instance_table()->Find(&key, true));
        if (svc_instance == NULL) {
            return NULL;
        }
        return svc_instance;
    }

    InstanceState *ServiceInstanceState(boost::uuids::uuid id) {
        ServiceInstance *svc_instance = GetServiceInstance(id);
        if (svc_instance == NULL) {
            return NULL;
        }
        return agent_->oper_db()->instance_manager()->GetState(svc_instance);
    }

    InstanceTaskQueue *GetTaskQueue(boost::uuids::uuid id) {
        ServiceInstance *svc_instance = GetServiceInstance(id);
        if (svc_instance == NULL) {
            return NULL;
        }

        stringstream ss;
        ss << svc_instance->properties().instance_id;
        return agent_->oper_db()->instance_manager()->GetTaskQueue(ss.str());
    }

    void TriggerSigChild(pid_t pid, int status) {
        boost::system::error_code ec;
        agent_->oper_db()->instance_manager()->HandleSigChild(ec, SIGCHLD, pid, status);
    }

    void NotifyChange(boost::uuids::uuid id) {
        ServiceInstance *svc_instance = GetServiceInstance(id);
        if (svc_instance == NULL) {
            return;
        }
        agent_->service_instance_table()->Change(svc_instance);
    }

    void UpdateProperties(ServiceInstance* svc_instance, bool usable) {
        /*
         * Set non-nil uuids
         */
        ServiceInstance::Properties prop;
        prop.Clear();
        prop.virtualization_type = ServiceInstance::NetworkNamespace;
        boost::uuids::random_generator gen;
        prop.instance_id = svc_instance->uuid();
        prop.vmi_inside = gen();
        prop.vmi_outside = gen();
        prop.ip_addr_inside = "10.0.0.1";
        prop.ip_addr_outside = "10.0.0.2";
        prop.ip_prefix_len_inside = 24;
        if (usable) {
            prop.ip_prefix_len_outside = 24;
        }
        svc_instance->set_properties(prop);
        if (usable) {
            EXPECT_TRUE(svc_instance->IsUsable());
        } else {
            EXPECT_FALSE(svc_instance->IsUsable());
        }
        agent_->service_instance_table()->Change(svc_instance);
    }

    const std::string &loadbalancer_config_path() const {
        return agent_->oper_db()->instance_manager()->loadbalancer_config_path_;
    }
protected:
    std::auto_ptr<Agent> agent_;
    std::auto_ptr<AgentConfig> agent_config_;
    std::auto_ptr<OperDB> oper_db_;
};

TEST_F(InstanceManagerTest, ExecTrue) {
    agent_->oper_db()->instance_manager()->Initialize(agent_->db(),
            agent_->agent_signal(), "/bin/true", "/bin/true", 1, 10);
    boost::uuids::uuid id = AddServiceInstance("exec-true");
    EXPECT_FALSE(id.is_nil());
    task_util::WaitForIdle();
    InstanceState *ns_state = ServiceInstanceState(id);
    ASSERT_TRUE(ns_state != NULL);

    EXPECT_EQ(0, ns_state->status());
    EXPECT_EQ(InstanceState::Starting, ns_state->status_type());
    EXPECT_NE(0, ns_state->pid());

    task_util::WaitForCondition(agent_->event_manager(),
            boost::bind(&InstanceManagerTest::IsExpectedStatusType, this, ns_state, InstanceState::Started),
            kTimeoutSeconds);

    EXPECT_EQ(InstanceState::Started, ns_state->status_type());
    EXPECT_EQ(0, ns_state->status());

    MarkServiceInstanceAsDeleted(id);
    task_util::WaitForIdle();
}

TEST_F(InstanceManagerTest, ExecFalse) {
    agent_->oper_db()->instance_manager()->Initialize(agent_->db(), agent_->agent_signal(), "/bin/false", "/bin/false", 1, 10);
    boost::uuids::uuid id = AddServiceInstance("exec-false");
    EXPECT_FALSE(id.is_nil());
    task_util::WaitForIdle();
    InstanceState *ns_state = ServiceInstanceState(id);
    ASSERT_TRUE(ns_state != NULL);

    EXPECT_EQ(0, ns_state->status());
    EXPECT_EQ(InstanceState::Starting, ns_state->status_type());

    task_util::WaitForCondition(agent_->event_manager(),
            boost::bind(&InstanceManagerTest::IsExpectedStatusType, this, ns_state, InstanceState::Error),
            kTimeoutSeconds);

    EXPECT_EQ(InstanceState::Error, ns_state->status_type());
    EXPECT_NE(0, ns_state->status());

    MarkServiceInstanceAsDeleted(id);
    task_util::WaitForIdle();
}

TEST_F(InstanceManagerTest, Update) {
    agent_->oper_db()->instance_manager()->Initialize(agent_->db(), agent_->agent_signal(), "/bin/true", "/bin/true", 1, 10);
    boost::uuids::uuid id = AddServiceInstance("exec-update");
    EXPECT_FALSE(id.is_nil());
    task_util::WaitForIdle();
    InstanceState *ns_state = ServiceInstanceState(id);
    ASSERT_TRUE(ns_state != NULL);

    EXPECT_EQ(0, ns_state->status());
    EXPECT_EQ(InstanceState::Starting, ns_state->status_type());

    task_util::WaitForCondition(agent_->event_manager(),
            boost::bind(&InstanceManagerTest::IsExpectedStatusType, this, ns_state, InstanceState::Started),
            kTimeoutSeconds);

    EXPECT_EQ(InstanceState::Started, ns_state->status_type());
    EXPECT_EQ(0, ns_state->status());

    bool updated = UpdateProperties(id, true);
    EXPECT_EQ(true, updated);

    task_util::WaitForCondition(agent_->event_manager(),
            boost::bind(&InstanceManagerTest::IsUpdateCommand, this, ns_state),
            kTimeoutSeconds);

    EXPECT_TRUE(IsUpdateCommand(ns_state));

    MarkServiceInstanceAsDeleted(id);
    task_util::WaitForIdle();
}

TEST_F(InstanceManagerTest, UpdateProperties) {
    agent_->oper_db()->instance_manager()->Initialize(agent_->db(), agent_->agent_signal(), "/bin/true", "/bin/true", 1, 10);
    boost::uuids::uuid id = AddServiceInstance("exec-update");
    EXPECT_FALSE(id.is_nil());
    task_util::WaitForIdle();
    InstanceState *ns_state = ServiceInstanceState(id);
    ASSERT_TRUE(ns_state != NULL);

    EXPECT_EQ(0, ns_state->status());
    EXPECT_EQ(InstanceState::Starting, ns_state->status_type());

    task_util::WaitForCondition(agent_->event_manager(),
            boost::bind(&InstanceManagerTest::IsExpectedStatusType, this, ns_state, InstanceState::Started),
            kTimeoutSeconds);

    EXPECT_EQ(InstanceState::Started, ns_state->status_type());
    EXPECT_EQ(0, ns_state->status());

    NotifyChange(id);
    task_util::WaitForIdle();

    EXPECT_NE(InstanceState::Starting, ns_state->status_type());

    bool updated = UpdateProperties(id, true);
    EXPECT_EQ(true, updated);

    task_util::WaitForCondition(agent_->event_manager(),
            boost::bind(&InstanceManagerTest::IsExpectedStatusType, this, ns_state, InstanceState::Starting),
            kTimeoutSeconds);

    MarkServiceInstanceAsDeleted(id);
    task_util::WaitForIdle();
}

/**
 * Timeout test works by not plugin in the signal manager and receiving
 * SIGCHLD rather than by executing a sleep.
 */
TEST_F(InstanceManagerTest, Timeout) {

    agent_->oper_db()->instance_manager()->Initialize(
        agent_->db(), NULL, "/bin/true", "/bin/true", 1, 1);
    boost::uuids::uuid id = AddServiceInstance("exec-timeout");
    EXPECT_FALSE(id.is_nil());
    task_util::WaitForIdle();
    InstanceState *ns_state = ServiceInstanceState(id);
    ASSERT_TRUE(ns_state != NULL);

    EXPECT_EQ(0, ns_state->status());
    EXPECT_EQ(InstanceState::Starting, ns_state->status_type());

    time_t now = time(NULL);
    task_util::WaitForCondition(agent_->event_manager(),
            boost::bind(&InstanceManagerTest::WaitForAWhile, this, now + 5),
            kTimeoutSeconds);

    EXPECT_EQ(InstanceState::Timeout, ns_state->status_type());

    MarkServiceInstanceAsDeleted(id);
    task_util::WaitForIdle();
}
TEST_F(InstanceManagerTest, TaskQueue) {
    static const int kNumUpdate = 5;
    agent_->oper_db()->instance_manager()->Initialize(agent_->db(), NULL, "/bin/true", "/bin/true", 10, 1);
    boost::uuids::uuid id = AddServiceInstance("exec-queue");
    EXPECT_FALSE(id.is_nil());
    task_util::WaitForIdle();
    InstanceState *ns_state = ServiceInstanceState(id);
    ASSERT_TRUE(ns_state != NULL);

    EXPECT_EQ(0, ns_state->status());
    EXPECT_EQ(InstanceState::Starting, ns_state->status_type());
    InstanceTaskQueue *queue = GetTaskQueue(id);

    EXPECT_EQ(1, queue->Size());

    for (int i = 0; i != kNumUpdate; i++) {
        bool updated = UpdateProperties(id, true);
        EXPECT_EQ(true, updated);
        task_util::WaitForIdle();

        EXPECT_EQ(i + 2, queue->Size());
    }

    for (int i = 0; i != kNumUpdate; i++) {
        TriggerSigChild(ns_state->pid(), 0);
        task_util::WaitForIdle();

        EXPECT_EQ(kNumUpdate - i, queue->Size());
    }

    MarkServiceInstanceAsDeleted(id);
    task_util::WaitForIdle();
}

TEST_F(InstanceManagerTest, Usable) {
    agent_->oper_db()->instance_manager()->Initialize(agent_->db(), agent_->agent_signal(), "/bin/true", "/bin/true", 1, 10);
    boost::uuids::uuid id = AddServiceInstance("exec-usable");
    EXPECT_FALSE(id.is_nil());
    task_util::WaitForIdle();
    InstanceState *ns_state = ServiceInstanceState(id);
    ASSERT_TRUE(ns_state != NULL);

    EXPECT_EQ(0, ns_state->status());
    EXPECT_EQ(InstanceState::Starting, ns_state->status_type());

    task_util::WaitForCondition(agent_->event_manager(),
            boost::bind(&InstanceManagerTest::IsExpectedStatusType, this, ns_state, InstanceState::Started),
            kTimeoutSeconds);

    EXPECT_EQ(InstanceState::Started, ns_state->status_type());
    EXPECT_EQ(0, ns_state->status());

    NotifyChange(id);
    task_util::WaitForIdle();

    EXPECT_NE(InstanceState::Starting, ns_state->status_type());

    bool updated = UpdateProperties(id, false);
    EXPECT_EQ(true, updated);

    task_util::WaitForCondition(agent_->event_manager(),
            boost::bind(&InstanceManagerTest::IsExpectedStatusType, this, ns_state, InstanceState::Stopped),
            kTimeoutSeconds);

    MarkServiceInstanceAsDeleted(id);
    task_util::WaitForIdle();
}

TEST_F(InstanceManagerTest, LoadbalancerConfig) {
    agent_->oper_db()->instance_manager()->Initialize(agent_->db(), agent_->agent_signal(), "/bin/true", "/bin/true", 1, 10);

    boost::uuids::random_generator gen;
    std::string pool_name(UuidToString(gen()));

    boost::uuids::uuid lbid = AddLoadbalancer(pool_name);
    task_util::WaitForIdle();

    AddServiceInstance("/bin/true");
    ifmap_test_util::IFMapMsgLink(
            agent_->db(), "loadbalancer-pool", pool_name,
            "service-instance", "/bin/true",
            "loadbalancer-pool-service-instance");
    task_util::WaitForIdle();

    stringstream pathgen;
    pathgen << loadbalancer_config_path() << lbid
            << "/etc/haproxy/haproxy.cfg";
    boost::filesystem::path config(pathgen.str());
    std::time_t old_time =
        boost::filesystem::last_write_time(pathgen.str());
    EXPECT_TRUE(boost::filesystem::exists(config));

    lbid = AddLoadbalancer(UuidToString(gen()));
    pathgen.str("");
    pathgen.clear();
    pathgen << loadbalancer_config_path() << lbid
            << "/etc/haproxy/haproxy.cfg";
    boost::filesystem::path config1(pathgen.str());

    //Make sure that both files exists
    EXPECT_TRUE(boost::filesystem::exists(config1));
    EXPECT_TRUE(boost::filesystem::exists(config));
    task_util::WaitForIdle();

    //Lets add a new member to Pool and verify that config file is
    //updated
    AddLoadbalancerMember(pool_name, "127.0.0.3", 80);
    task_util::WaitForIdle();
    std::time_t new_time =
        boost::filesystem::last_write_time(pathgen.str());
    EXPECT_TRUE(old_time <= new_time);
}

TEST_F(InstanceManagerTest, InstanceStaleCleanup) {
    agent_->oper_db()->instance_manager()->Initialize(agent_->db(),
            agent_->agent_signal(), "/bin/true", "/bin/true", 1, 10);

    boost::uuids::random_generator gen;
    std::string vm_uuid = UuidToString(gen());
    std::string lb_uuid = UuidToString(gen());

    boost::filesystem::path curr_dir (boost::filesystem::current_path());
    stringstream ss;
    ss << curr_dir.string() << "/" << getpid() << "/";
    string store_path = ss.str();

    agent_->oper_db()->instance_manager()->SetNamespaceStorePath(store_path.c_str());
    store_path +=  ("vrouter-" + vm_uuid + ":" + lb_uuid);
    boost::system::error_code error;
    if (!boost::filesystem::exists(store_path, error)) {
        boost::filesystem::create_directories(store_path, error);
        if (error) {
            LOG(ERROR, "Error : " << error.message() << "in creating directory");
        }
    }
    store_path  = loadbalancer_config_path() + lb_uuid;

    if (!boost::filesystem::exists(store_path, error)) {
        boost::filesystem::create_directories(store_path, error);
        if (error) {
            LOG(ERROR, "Error : " << error.message() << "in creating directory");
        }
    }
    EXPECT_EQ(1, boost::filesystem::exists(store_path));

    agent_->oper_db()->instance_manager()->StaleTimeout();
    task_util::WaitForIdle();
    EXPECT_EQ(0, boost::filesystem::exists(store_path));

    boost::filesystem::remove_all(ss.str(), error);
    if (error) {
        LOG(ERROR, "Error : " << error.message() << "in removing directory");
    }
}

int main(int argc, char **argv) {
    LoggingInit();
    ::testing::InitGoogleTest(&argc, argv);

    int result = RUN_ALL_TESTS();
    return result;
}

ext: cc


text:
/* CirKit: A circuit toolkit
 * Copyright (C) 2009-2015  University of Bremen
 * Copyright (C) 2015-2017  EPFL
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include "aig_to_cirkit_bdd.hpp"

#include <boost/assign/std/vector.hpp>

#include <classical/utils/aig_utils.hpp>

using namespace boost::assign;

namespace cirkit
{

/******************************************************************************
 * Types                                                                      *
 ******************************************************************************/

/******************************************************************************
 * Private functions                                                          *
 ******************************************************************************/

/******************************************************************************
 * Public functions                                                           *
 ******************************************************************************/

cirkit_bdd_simulator::cirkit_bdd_simulator( const aig_graph& aig, unsigned log_max_objs )
    : mgr( bdd_manager::create( aig_info( aig ).inputs.size(), log_max_objs ) )
{
}

cirkit_bdd_simulator::cirkit_bdd_simulator( const bdd_manager_ptr& mgr )
  : mgr( mgr )
{
}

bdd cirkit_bdd_simulator::get_input( const aig_node& node, const std::string& name, unsigned pos, const aig_graph& aig ) const
{
  return mgr->bdd_var( pos );
}

bdd cirkit_bdd_simulator::get_constant() const
{
  return mgr->bdd_bot();
}

bdd cirkit_bdd_simulator::invert( const bdd& v ) const
{
  return !v;
}

bdd cirkit_bdd_simulator::and_op( const aig_node& node, const bdd& v1, const bdd& v2 ) const
{
  return v1 && v2;
}

std::vector<bdd> aig_to_bdd( const aig_graph& aig, const bdd_manager_ptr& mgr )
{
  auto info = aig_info( aig );

  std::vector<bdd> fs;
  cirkit_bdd_simulator sim( mgr );
  auto map = simulate_aig( aig, sim );

  for ( const auto& out : info.outputs )
  {
    fs += map[out.first];
  }

  return fs;
}

}

// Local Variables:
// c-basic-offset: 2
// eval: (c-set-offset 'substatement-open 0)
// eval: (c-set-offset 'innamespace 0)
// End:

max_stars_repo_name: eletesta/cirkit

max_issues_repo_name: eletesta/cirkit

max_forks_repo_name: eletesta/cirkit


text:
/***************************************************************************
 * blitz/array/ops.cc  Basic operators for arrays.
 *
 * Copyright (C) 1997-2001 Todd Veldhuizen <tveldhui@oonumerics.org>
 *
 * This code was relicensed under the modified BSD license for use in SciPy
 * by Todd Veldhuizen (see LICENSE.txt in the weave directory).
 *
 *
 * Suggestions:          blitz-dev@oonumerics.org
 * Bugs:                 blitz-bugs@oonumerics.org
 *
 * For more information, please see the Blitz++ Home Page:
 *    http://oonumerics.org/blitz/
 *
 ****************************************************************************/
#ifndef BZ_ARRAYOPS_CC
#define BZ_ARRAYOPS_CC

#ifndef BZ_ARRAY_H
 #error <blitz/array/ops.cc> must be included via <blitz/array.h>
#endif

#include <blitz/update.h>

BZ_NAMESPACE(blitz)

/*
 * Constant operands
 */

template<typename P_numtype, int N_rank>
Array<P_numtype, N_rank>& Array<P_numtype,N_rank>::initialize(T_numtype x)
{
    (*this) = _bz_ArrayExpr<_bz_ArrayExprConstant<T_numtype> >(x);
    return *this;
}

#ifdef BZ_NEW_EXPRESSION_TEMPLATES

template<typename P_numtype, int N_rank> template<typename T_expr>
inline Array<P_numtype,N_rank>&
Array<P_numtype,N_rank>::operator=(const ETBase<T_expr>& expr)
{
    evaluate(expr.unwrap(),
        _bz_update<T_numtype, _bz_typename T_expr::T_numtype>());
    return *this;
}

template<typename P_numtype, int N_rank>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator=(const Array<T_numtype,N_rank>& x)
{
    (*this) = _bz_ArrayExpr<FastArrayIterator<T_numtype, N_rank> >
        (x.beginFast());
    return *this;
}

#define BZ_ARRAY_UPDATE(op,name) \
template<typename P_numtype, int N_rank> \
template<typename T> \
inline Array<P_numtype,N_rank>& \
Array<P_numtype,N_rank>::operator op(const T& expr) \
{ \
    evaluate(_bz_typename asExpr<T>::T_expr(expr), \
      name<T_numtype, _bz_typename asExpr<T>::T_expr::T_numtype>()); \
    return *this; \
}

BZ_ARRAY_UPDATE(+=, _bz_plus_update)
BZ_ARRAY_UPDATE(-=, _bz_minus_update)
BZ_ARRAY_UPDATE(*=, _bz_multiply_update)
BZ_ARRAY_UPDATE(/=, _bz_divide_update)
BZ_ARRAY_UPDATE(%=, _bz_mod_update)
BZ_ARRAY_UPDATE(^=, _bz_xor_update)
BZ_ARRAY_UPDATE(&=, _bz_bitand_update)
BZ_ARRAY_UPDATE(|=, _bz_bitor_update)
BZ_ARRAY_UPDATE(<<=, _bz_shiftl_update)
BZ_ARRAY_UPDATE(>>=, _bz_shiftr_update)

#else

template<typename P_numtype, int N_rank>
inline Array<P_numtype, N_rank>&
Array<P_numtype,N_rank>::operator+=(T_numtype x)
{
    (*this) += _bz_ArrayExpr<_bz_ArrayExprConstant<T_numtype> >(x);
    return *this;
}

template<typename P_numtype, int N_rank>
inline Array<P_numtype, N_rank>&
Array<P_numtype,N_rank>::operator-=(T_numtype x)
{
    (*this) -= _bz_ArrayExpr<_bz_ArrayExprConstant<T_numtype> >(x);
    return *this;
}

template<typename P_numtype, int N_rank>
inline Array<P_numtype, N_rank>&
Array<P_numtype,N_rank>::operator*=(T_numtype x)
{
    (*this) *= _bz_ArrayExpr<_bz_ArrayExprConstant<T_numtype> >(x);
    return *this;
}

template<typename P_numtype, int N_rank>
inline Array<P_numtype, N_rank>&
Array<P_numtype,N_rank>::operator/=(T_numtype x)
{
    (*this) /= _bz_ArrayExpr<_bz_ArrayExprConstant<T_numtype> >(x);
    return *this;
}

template<typename P_numtype, int N_rank>
inline Array<P_numtype, N_rank>&
Array<P_numtype,N_rank>::operator%=(T_numtype x)
{
    (*this) %= _bz_ArrayExpr<_bz_ArrayExprConstant<T_numtype> >(x);
    return *this;
}

template<typename P_numtype, int N_rank>
inline Array<P_numtype, N_rank>&
Array<P_numtype,N_rank>::operator^=(T_numtype x)
{
    (*this) ^= _bz_ArrayExpr<_bz_ArrayExprConstant<T_numtype> >(x);
    return *this;
}

template<typename P_numtype, int N_rank>
inline Array<P_numtype, N_rank>&
Array<P_numtype,N_rank>::operator&=(T_numtype x)
{
    (*this) &= _bz_ArrayExpr<_bz_ArrayExprConstant<T_numtype> >(x);
    return *this;
}

template<typename P_numtype, int N_rank>
inline Array<P_numtype, N_rank>&
Array<P_numtype,N_rank>::operator|=(T_numtype x)
{
    (*this) |= _bz_ArrayExpr<_bz_ArrayExprConstant<T_numtype> >(x);
    return *this;
}

template<typename P_numtype, int N_rank>
inline Array<P_numtype, N_rank>&
Array<P_numtype,N_rank>::operator>>=(T_numtype x)
{
    (*this) <<= _bz_ArrayExpr<_bz_ArrayExprConstant<T_numtype> >(x);
    return *this;
}

template<typename P_numtype, int N_rank>
inline Array<P_numtype, N_rank>&
Array<P_numtype,N_rank>::operator<<=(T_numtype x)
{
    (*this) <<= _bz_ArrayExpr<_bz_ArrayExprConstant<T_numtype> >(x);
    return *this;
}

/*
 * Array operands
 */

template<typename P_numtype, int N_rank>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator=(const Array<T_numtype,N_rank>& x)
{
    (*this) = _bz_ArrayExpr<FastArrayIterator<T_numtype, N_rank> >(x.beginFast());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename P_numtype2>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator=(const Array<P_numtype2,N_rank>& x)
{
    (*this) = _bz_ArrayExpr<FastArrayIterator<P_numtype2, N_rank> >(x.beginFast());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename P_numtype2>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator+=(const Array<P_numtype2,N_rank>& x)
{
    (*this) += _bz_ArrayExpr<FastArrayIterator<P_numtype2, N_rank> >(x.beginFast());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename P_numtype2>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator-=(const Array<P_numtype2,N_rank>& x)
{
    (*this) -= _bz_ArrayExpr<FastArrayIterator<P_numtype2, N_rank> >(x.beginFast());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename P_numtype2>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator*=(const Array<P_numtype2,N_rank>& x)
{
    (*this) *= _bz_ArrayExpr<FastArrayIterator<P_numtype2, N_rank> >(x.beginFast());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename P_numtype2>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator/=(const Array<P_numtype2,N_rank>& x)
{
    (*this) /= _bz_ArrayExpr<FastArrayIterator<P_numtype2, N_rank> >(x.beginFast());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename P_numtype2>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator%=(const Array<P_numtype2,N_rank>& x)
{
    (*this) %= _bz_ArrayExpr<FastArrayIterator<P_numtype2, N_rank> >(x.beginFast());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename P_numtype2>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator^=(const Array<P_numtype2,N_rank>& x)
{
    (*this) ^= _bz_ArrayExpr<FastArrayIterator<P_numtype2, N_rank> >(x.beginFast());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename P_numtype2>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator&=(const Array<P_numtype2,N_rank>& x)
{
    (*this) &= _bz_ArrayExpr<FastArrayIterator<P_numtype2, N_rank> >(x.beginFast());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename P_numtype2>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator|=(const Array<P_numtype2,N_rank>& x)
{
    (*this) |= _bz_ArrayExpr<FastArrayIterator<P_numtype2, N_rank> >(x.beginFast());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename P_numtype2>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator>>=(const Array<P_numtype2,N_rank>& x)
{
    (*this) >>= _bz_ArrayExpr<FastArrayIterator<P_numtype2, N_rank> >(x.beginFast());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename P_numtype2>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator<<=(const Array<P_numtype2,N_rank>& x)
{
    (*this) <<= _bz_ArrayExpr<FastArrayIterator<P_numtype2, N_rank> >(x.beginFast());
    return *this;
}

/*
 * Array expression operands
 */

template<typename P_numtype, int N_rank> template<typename T_expr>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator=(BZ_ETPARM(_bz_ArrayExpr<T_expr>) expr)
{
    evaluate(expr, _bz_update<T_numtype, _bz_typename T_expr::T_numtype>());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename T_expr>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator+=(BZ_ETPARM(_bz_ArrayExpr<T_expr>) expr)
{
    evaluate(expr, _bz_plus_update<T_numtype, _bz_typename T_expr::T_numtype>());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename T_expr>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator-=(BZ_ETPARM(_bz_ArrayExpr<T_expr>) expr)
{
    evaluate(expr, _bz_minus_update<T_numtype,
        _bz_typename T_expr::T_numtype>());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename T_expr>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator*=(BZ_ETPARM(_bz_ArrayExpr<T_expr>) expr)
{
    evaluate(expr, _bz_multiply_update<T_numtype,
        _bz_typename T_expr::T_numtype>());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename T_expr>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator/=(BZ_ETPARM(_bz_ArrayExpr<T_expr>) expr)
{
    evaluate(expr, _bz_divide_update<T_numtype,
        _bz_typename T_expr::T_numtype>());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename T_expr>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator%=(BZ_ETPARM(_bz_ArrayExpr<T_expr>) expr)
{
    evaluate(expr, _bz_mod_update<T_numtype,
        _bz_typename T_expr::T_numtype>());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename T_expr>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator^=(BZ_ETPARM(_bz_ArrayExpr<T_expr>) expr)
{
    evaluate(expr, _bz_xor_update<T_numtype,
        _bz_typename T_expr::T_numtype>());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename T_expr>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator&=(BZ_ETPARM(_bz_ArrayExpr<T_expr>) expr)
{
    evaluate(expr, _bz_bitand_update<T_numtype,
        _bz_typename T_expr::T_numtype>());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename T_expr>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator|=(BZ_ETPARM(_bz_ArrayExpr<T_expr>) expr)
{
    evaluate(expr, _bz_bitor_update<T_numtype,
        _bz_typename T_expr::T_numtype>());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename T_expr>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator>>=(BZ_ETPARM(_bz_ArrayExpr<T_expr>) expr)
{
    evaluate(expr, _bz_shiftr_update<T_numtype,
        _bz_typename T_expr::T_numtype>());
    return *this;
}

template<typename P_numtype, int N_rank> template<typename T_expr>
inline Array<P_numtype, N_rank>&
Array<P_numtype, N_rank>::operator<<=(BZ_ETPARM(_bz_ArrayExpr<T_expr>) expr)
{
    evaluate(expr, _bz_shiftl_update<T_numtype,
        _bz_typename T_expr::T_numtype>());
    return *this;
}

#endif // BZ_NEW_EXPRESSION_TEMPLATES

BZ_NAMESPACE_END

#endif // BZ_ARRAYOPS_CC

max_stars_repo_name: lefevre-fraser/openmeta-mms

max_issues_repo_name: lefevre-fraser/openmeta-mms

max_forks_repo_name: lefevre-fraser/openmeta-mms


text:
/**
 *    Copyright (C) 2018-present MongoDB, Inc.
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the Server Side Public License, version 1,
 *    as published by MongoDB, Inc.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    Server Side Public License for more details.
 *
 *    You should have received a copy of the Server Side Public License
 *    along with this program. If not, see
 *    <http://www.mongodb.com/licensing/server-side-public-license>.
 *
 *    As a special exception, the copyright holders give permission to link the
 *    code of portions of this program with the OpenSSL library under certain
 *    conditions as described in each individual source file and distribute
 *    linked combinations including the program with the OpenSSL library. You
 *    must comply with the Server Side Public License in all respects for
 *    all of the code used other than as permitted herein. If you modify file(s)
 *    with this exception, you may extend this exception to your version of the
 *    file(s), but you are not obligated to do so. If you do not wish to do so,
 *    delete this exception statement from your version. If you delete this
 *    exception statement from all source files in the program, then also delete
 *    it in the license file.
 */

#include "mongo/platform/basic.h"

#include <algorithm>
#include <boost/optional/optional_io.hpp>
#include <functional>
#include <map>
#include <utility>
#include <vector>

#include "mongo/db/client.h"
#include "mongo/db/concurrency/lock_manager_test_help.h"
#include "mongo/db/db_raii.h"
#include "mongo/db/field_parser.h"
#include "mongo/db/repl/oplog.h"
#include "mongo/db/repl/oplog_entry.h"
#include "mongo/db/repl/oplog_interface_local.h"
#include "mongo/db/repl/repl_client_info.h"
#include "mongo/db/repl/replication_coordinator_mock.h"
#include "mongo/db/repl/tenant_migration_decoration.h"
#include "mongo/db/service_context_d_test_fixture.h"
#include "mongo/platform/mutex.h"
#include "mongo/unittest/barrier.h"
#include "mongo/util/concurrency/thread_pool.h"

namespace mongo {
namespace repl {
namespace {

class OplogTest : public ServiceContextMongoDTest {
private:
    void setUp() override;
};

void OplogTest::setUp() {
    // Set up mongod.
    ServiceContextMongoDTest::setUp();

    auto service = getServiceContext();
    auto opCtx = cc().makeOperationContext();

    // Set up ReplicationCoordinator and create oplog.
    ReplicationCoordinator::set(service, std::make_unique<ReplicationCoordinatorMock>(service));
    createOplog(opCtx.get());

    // Ensure that we are primary.
    auto replCoord = ReplicationCoordinator::get(opCtx.get());
    ASSERT_OK(replCoord->setFollowerMode(MemberState::RS_PRIMARY));
}

/**
 * Assert that oplog only has a single entry and return that oplog entry.
 */
OplogEntry _getSingleOplogEntry(OperationContext* opCtx) {
    OplogInterfaceLocal oplogInterface(opCtx);
    auto oplogIter = oplogInterface.makeIterator();
    auto opEntry = unittest::assertGet(oplogIter->next());
    ASSERT_EQUALS(ErrorCodes::CollectionIsEmpty, oplogIter->next().getStatus())
        << "Expected only 1 document in the oplog collection " << NamespaceString::kRsOplogNamespace
        << " but found more than 1 document instead";
    return unittest::assertGet(OplogEntry::parse(opEntry.first));
}

TEST_F(OplogTest, LogOpReturnsOpTimeOnSuccessfulInsertIntoOplogCollection) {
    auto opCtx = cc().makeOperationContext();

    const NamespaceString nss("test.coll");
    auto msgObj = BSON("msg"
                       << "hello, world!");

    // Write to the oplog.
    OpTime opTime;
    {
        MutableOplogEntry oplogEntry;
        oplogEntry.setOpType(repl::OpTypeEnum::kNoop);
        oplogEntry.setNss(nss);
        oplogEntry.setObject(msgObj);
        oplogEntry.setWallClockTime(Date_t::now());
        AutoGetDb autoDb(opCtx.get(), nss.db(), MODE_X);
        WriteUnitOfWork wunit(opCtx.get());
        opTime = logOp(opCtx.get(), &oplogEntry);
        ASSERT_FALSE(opTime.isNull());
        wunit.commit();
    }

    OplogEntry oplogEntry = _getSingleOplogEntry(opCtx.get());

    // Ensure that msg fields were properly added to the oplog entry.
    ASSERT_EQUALS(opTime, oplogEntry.getOpTime())
        << "OpTime returned from logOp() did not match that in the oplog entry written to the "
           "oplog: "
        << oplogEntry.toBSONForLogging();
    ASSERT(OpTypeEnum::kNoop == oplogEntry.getOpType())
        << "Expected 'n' op type but found '" << OpType_serializer(oplogEntry.getOpType())
        << "' instead: " << oplogEntry.toBSONForLogging();
    ASSERT_BSONOBJ_EQ(msgObj, oplogEntry.getObject());

    // Ensure that the msg optime returned is the same as the last optime in the ReplClientInfo.
    ASSERT_EQUALS(ReplClientInfo::forClient(&cc()).getLastOp(), opTime);
}

/**
 * Checks optime and namespace in oplog entry.
 */
void _checkOplogEntry(const OplogEntry& oplogEntry,
                      const OpTime& expectedOpTime,
                      const NamespaceString& expectedNss) {
    ASSERT_EQUALS(expectedOpTime, oplogEntry.getOpTime()) << oplogEntry.toBSONForLogging();
    ASSERT_EQUALS(expectedNss, oplogEntry.getNss()) << oplogEntry.toBSONForLogging();
}
void _checkOplogEntry(const OplogEntry& oplogEntry,
                      const std::pair<OpTime, NamespaceString>& expectedOpTimeAndNss) {
    _checkOplogEntry(oplogEntry, expectedOpTimeAndNss.first, expectedOpTimeAndNss.second);
}

/**
 * Test function that schedules two concurrent logOp() tasks using a thread pool.
 * Checks the state of the oplog collection against the optimes returned from logOp().
 * Before returning, updates 'opTimeNssMap' with the optimes from logOp() and 'oplogEntries' with
 * the contents of the oplog collection.
 */
using OpTimeNamespaceStringMap = std::map<OpTime, NamespaceString>;
template <typename F>
void _testConcurrentLogOp(const F& makeTaskFunction,
                          OpTimeNamespaceStringMap* opTimeNssMap,
                          std::vector<OplogEntry>* oplogEntries,
                          std::size_t expectedNumOplogEntries) {
    ASSERT_LESS_THAN_OR_EQUALS(expectedNumOplogEntries, 2U);

    // Run 2 concurrent logOp() requests using the thread pool.
    ThreadPool::Options options;
    options.maxThreads = 2U;
    options.onCreateThread = [](const std::string& name) { Client::initThread(name); };
    ThreadPool pool(options);
    pool.startup();

    // Run 2 concurrent logOp() requests using the thread pool.
    // Use a barrier with a thread count of 3 to ensure both logOp() tasks are complete before this
    // test thread can proceed with shutting the thread pool down.
    auto mtx = MONGO_MAKE_LATCH();
    unittest::Barrier barrier(3U);
    const NamespaceString nss1("test1.coll");
    const NamespaceString nss2("test2.coll");
    pool.schedule([&](auto status) mutable {
        ASSERT_OK(status) << "Failed to schedule logOp() task for namespace " << nss1;
        makeTaskFunction(nss1, &mtx, opTimeNssMap, &barrier)();
    });
    pool.schedule([&](auto status) mutable {
        ASSERT_OK(status) << "Failed to schedule logOp() task for namespace " << nss2;
        makeTaskFunction(nss2, &mtx, opTimeNssMap, &barrier)();
    });
    barrier.countDownAndWait();

    // Shut thread pool down.
    pool.shutdown();
    pool.join();

    // Read oplog entries from the oplog collection starting with the entry with the most recent
    // optime.
    auto opCtx = cc().makeOperationContext();
    OplogInterfaceLocal oplogInterface(opCtx.get());
    auto oplogIter = oplogInterface.makeIterator();
    auto nextValue = oplogIter->next();
    while (nextValue.isOK()) {
        const auto& doc = nextValue.getValue().first;
        oplogEntries->emplace_back(unittest::assertGet(OplogEntry::parse(doc)));
        nextValue = oplogIter->next();
    }
    ASSERT_EQUALS(expectedNumOplogEntries, oplogEntries->size());

    // Reverse 'oplogEntries' because we inserted the oplog entries in descending order by optime.
    std::reverse(oplogEntries->begin(), oplogEntries->end());

    // Look up namespaces and their respective optimes (returned by logOp()) in the map.
    stdx::lock_guard<Latch> lock(mtx);
    ASSERT_EQUALS(2U, opTimeNssMap->size());
}

/**
 * Inserts noop oplog entry with embedded namespace string.
 * Inserts optime/namespace pair into map while holding a lock on the mutex.
 * Returns optime of generated oplog entry.
 */
OpTime _logOpNoopWithMsg(OperationContext* opCtx,
                         Mutex* mtx,
                         OpTimeNamespaceStringMap* opTimeNssMap,
                         const NamespaceString& nss) {
    MutableOplogEntry oplogEntry;
    oplogEntry.setOpType(repl::OpTypeEnum::kNoop);
    oplogEntry.setNss(nss);
    oplogEntry.setObject(BSON("msg" << nss.ns()));
    oplogEntry.setWallClockTime(Date_t::now());
    auto opTime = logOp(opCtx, &oplogEntry);
    ASSERT_FALSE(opTime.isNull());

    stdx::lock_guard<Latch> lock(*mtx);
    ASSERT(opTimeNssMap->find(opTime) == opTimeNssMap->end())
        << "Unable to add namespace " << nss << " to map - map contains duplicate entry for optime "
        << opTime;
    opTimeNssMap->insert(std::make_pair(opTime, nss));

    return opTime;
}

TEST_F(OplogTest, ConcurrentLogOp) {
    OpTimeNamespaceStringMap opTimeNssMap;
    std::vector<OplogEntry> oplogEntries;

    _testConcurrentLogOp(
        [](const NamespaceString& nss,
           Mutex* mtx,
           OpTimeNamespaceStringMap* opTimeNssMap,
           unittest::Barrier* barrier) {
            return [=] {
                auto opCtx = cc().makeOperationContext();
                AutoGetDb autoDb(opCtx.get(), nss.db(), MODE_X);
                WriteUnitOfWork wunit(opCtx.get());

                _logOpNoopWithMsg(opCtx.get(), mtx, opTimeNssMap, nss);

                // It is okay for multiple threads to maintain uncommitted WUOWs upon returning from
                // logOp() because each thread will hold an implicit MODE_IX lock on the oplog
                // collection.
                barrier->countDownAndWait();
                wunit.commit();
            };
        },
        &opTimeNssMap,
        &oplogEntries,
        2U);

    _checkOplogEntry(oplogEntries[0], *(opTimeNssMap.begin()));
    _checkOplogEntry(oplogEntries[1], *(opTimeNssMap.rbegin()));
}

TEST_F(OplogTest, ConcurrentLogOpRevertFirstOplogEntry) {
    OpTimeNamespaceStringMap opTimeNssMap;
    std::vector<OplogEntry> oplogEntries;

    _testConcurrentLogOp(
        [](const NamespaceString& nss,
           Mutex* mtx,
           OpTimeNamespaceStringMap* opTimeNssMap,
           unittest::Barrier* barrier) {
            return [=] {
                auto opCtx = cc().makeOperationContext();
                AutoGetDb autoDb(opCtx.get(), nss.db(), MODE_X);
                WriteUnitOfWork wunit(opCtx.get());

                auto opTime = _logOpNoopWithMsg(opCtx.get(), mtx, opTimeNssMap, nss);

                // It is okay for multiple threads to maintain uncommitted WUOWs upon returning from
                // logOp() because each thread will hold an implicit MODE_IX lock on the oplog
                // collection.
                barrier->countDownAndWait();

                // Revert the first logOp() call and confirm that there are no holes in the
                // oplog after committing the oplog entry with the more recent optime.
                {
                    stdx::lock_guard<Latch> lock(*mtx);
                    auto firstOpTimeAndNss = *(opTimeNssMap->cbegin());
                    if (opTime == firstOpTimeAndNss.first) {
                        ASSERT_EQUALS(nss, firstOpTimeAndNss.second)
                            << "optime matches entry in optime->nss map but namespace in map  is "
                               "different.";
                        // Abort WUOW by returning early. The oplog entry for this task should not
                        // be present in the oplog.
                        return;
                    }
                }

                wunit.commit();
            };
        },
        &opTimeNssMap,
        &oplogEntries,
        1U);

    _checkOplogEntry(oplogEntries[0], *(opTimeNssMap.crbegin()));
}

TEST_F(OplogTest, ConcurrentLogOpRevertLastOplogEntry) {
    OpTimeNamespaceStringMap opTimeNssMap;
    std::vector<OplogEntry> oplogEntries;

    _testConcurrentLogOp(
        [](const NamespaceString& nss,
           Mutex* mtx,
           OpTimeNamespaceStringMap* opTimeNssMap,
           unittest::Barrier* barrier) {
            return [=] {
                auto opCtx = cc().makeOperationContext();
                AutoGetDb autoDb(opCtx.get(), nss.db(), MODE_X);
                WriteUnitOfWork wunit(opCtx.get());

                auto opTime = _logOpNoopWithMsg(opCtx.get(), mtx, opTimeNssMap, nss);

                // It is okay for multiple threads to maintain uncommitted WUOWs upon returning from
                // logOp() because each thread will hold an implicit MODE_IX lock on the oplog
                // collection.
                barrier->countDownAndWait();

                // Revert the last logOp() call and confirm that there are no holes in the
                // oplog after committing the oplog entry with the earlier optime.
                {
                    stdx::lock_guard<Latch> lock(*mtx);
                    auto lastOpTimeAndNss = *(opTimeNssMap->crbegin());
                    if (opTime == lastOpTimeAndNss.first) {
                        ASSERT_EQUALS(nss, lastOpTimeAndNss.second)
                            << "optime matches entry in optime->nss map but namespace in map is "
                               "different.";
                        // Abort WUOW by returning early. The oplog entry for this task should not
                        // be present in the oplog.
                        return;
                    }
                }

                wunit.commit();
            };
        },
        &opTimeNssMap,
        &oplogEntries,
        1U);

    _checkOplogEntry(oplogEntries[0], *(opTimeNssMap.cbegin()));
}

TEST_F(OplogTest, MigrationIdAddedToOplog) {
    auto opCtx = cc().makeOperationContext();
    auto migrationUuid = UUID::gen();
    tenantMigrationRecipientInfo(opCtx.get()) =
        boost::make_optional<TenantMigrationRecipientInfo>(migrationUuid);

    const NamespaceString nss("test.coll");
    auto msgObj = BSON("msg"
                       << "hello, world!");

    // Write to the oplog.
    OpTime opTime;
    {
        MutableOplogEntry oplogEntry;
        oplogEntry.setOpType(repl::OpTypeEnum::kNoop);
        oplogEntry.setNss(nss);
        oplogEntry.setObject(msgObj);
        oplogEntry.setWallClockTime(Date_t::now());
        AutoGetDb autoDb(opCtx.get(), nss.db(), MODE_X);
        WriteUnitOfWork wunit(opCtx.get());
        opTime = logOp(opCtx.get(), &oplogEntry);
        ASSERT_FALSE(opTime.isNull());
        wunit.commit();
    }

    OplogEntry oplogEntry = _getSingleOplogEntry(opCtx.get());

    // Ensure that msg fields were properly added to the oplog entry.
    ASSERT_EQUALS(opTime, oplogEntry.getOpTime())
        << "OpTime returned from logOp() did not match that in the oplog entry written to the "
           "oplog: "
        << oplogEntry.toBSONForLogging();
    ASSERT(OpTypeEnum::kNoop == oplogEntry.getOpType())
        << "Expected 'n' op type but found '" << OpType_serializer(oplogEntry.getOpType())
        << "' instead: " << oplogEntry.toBSONForLogging();
    ASSERT_BSONOBJ_EQ(msgObj, oplogEntry.getObject());
    ASSERT_EQ(migrationUuid, oplogEntry.getFromTenantMigration());
}

}  // namespace
}  // namespace repl
}  // namespace mongo

max_stars_repo_name: benety/mongo

max_issues_repo_name: benety/mongo

max_forks_repo_name: benety/mongo