text:
/* LICENSE:
Copyright (c) Members of the EGEE Collaboration. 2010.
See http://www.eu-egee.org/partners/ for details on the copyright
holders.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
See the License for the specific language governing permissions and
limitations under the License.

END LICENSE */

#include "IceCommandLBLogging.h"
#include "IceCommandDelegationRenewal.h"
#include "utils/DelegationManager.h"
#include "utils/CreamProxyMethod.h"
#include "utils/DNProxyManager.h"
#include "utils/IceConfManager.h"
#include "main/Main.h"
#include "utils/IceUtils.h"
#include "db/GetJobsByDNMyProxy.h"
#include "db/GetJobByGid.h"
#include "db/Transaction.h"
#include "common/src/configuration/ICEConfiguration.h"
#include "common/src/configuration/CommonConfiguration.h"
/**
 *
 * Cream Client API C++ Headers
 *
 */
//#include "glite/ce/cream-client-api-c/creamApiLogger.h"
#include "glite/ce/cream-client-api-c/CEUrl.h"
#include "glite/ce/cream-client-api-c/VOMSWrapper.h"

/**
 *
 * OS Headers
 *
 */
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctime>
#include <cerrno>

#include <boost/algorithm/string/predicate.hpp>

#include "utils/logging.h"
#include "glite/wms/common/logger/edglog.h"
#include "glite/wms/common/logger/manipulators.h"

namespace cream_api = glite::ce::cream_client_api;

using namespace std;
using namespace glite::wms::ice::util;

#define DELEGATION_EXPIRATION_THRESHOLD_TIME 3600

//______________________________________________________________________________
IceCommandDelegationRenewal::IceCommandDelegationRenewal( ) :
    IceAbstractCommand( "iceCommandDelegationRenewal", "" ),
    //m_log_dev( cream_api::util::creamApiLogger::instance()->getLogger() ),
    m_ctx( NULL )
{
}

//______________________________________________________________________________
IceCommandDelegationRenewal::~IceCommandDelegationRenewal( )
{
}

//______________________________________________________________________________
string IceCommandDelegationRenewal::get_grid_job_id( ) const
{
  ostringstream randid( "" );
  struct timeval T;
  gettimeofday( &T, 0 );
  randid << T.tv_sec << "." << T.tv_usec;
  return randid.str();
}

//______________________________________________________________________________
void IceCommandDelegationRenewal::execute( const std::string& tid ) throw()
{
#ifdef ICE_PROFILE
  ice_timer timer("IceCommandDelegationRenewal::execute");
#endif
  this->renewAllDelegations();

}

//______________________________________________________________________________
void IceCommandDelegationRenewal::renewAllDelegations( void ) throw()
{
    //static const char* method_name = "IceCommandDelegationRenewal::renewAllDelegations() - ";
    edglog_fn("IceCommandDelegationRenewal::renewAllDelegations");
    /**
       Now, let's check all delegations for expiration and renew them
    */

    vector< DelegationManager::table_entry > allDelegations;

    int alldeleg_size = DelegationManager::instance()->getDelegationEntries( allDelegations, true );

    map<string, pair<time_t, int> > mapDelegTime; // delegationID -> (Expiration time, Absolute Duration)

    //CREAM_SAFE_LOG( m_log_dev->debugStream() << method_name
               edglog(debug)     << "There are [" << alldeleg_size
                    << "] Delegation(s) to check..." << endl;
                    //);

    if( alldeleg_size == 0 ) return;

    /**
       Loop over all different delegations
    */
    for( vector<DelegationManager::table_entry >::const_iterator it = allDelegations.begin();
	 it != allDelegations.end();
	 ++it)
      {

	time_t thisExpTime   = it->m_expiration_time;
	string thisDelegID   = it->m_delegation_id;
	int    thisDuration  = it->m_delegation_duration;
	int    remainingTime = thisExpTime - time(0);
	if( (remainingTime > 0.2 * thisDuration) &&
	    (remainingTime > DELEGATION_EXPIRATION_THRESHOLD_TIME) )
	  {
	    //CREAM_SAFE_LOG( m_log_dev->debugStream() << method_name
			edglog(debug)    << "Delegation ID ["
			    << thisDelegID << "] will expire in ["
			    << remainingTime << "] seconds (on ["
			    << IceUtils::time_t_to_string(thisExpTime) << "]). Duration is ["
			    << thisDuration << "] seconds. Will NOT renew it now..." << endl;
			   // );
	    mapDelegTime.erase( thisDelegID );
	    continue;

	  }

	/**
	   Let's try to download from MyProxy Service
	   a fresh and hopefully most long-living proxy for current user
	*/
	boost::tuple<string, time_t, long long int> SBP( DNProxyManager::getInstance()->getExactBetterProxyByDN(it->m_user_dn, it->m_myproxyserver) );
	string certfile( SBP.get<0>() );
	if( certfile.empty() ) {
	 // CREAM_SAFE_LOG( m_log_dev->errorStream() << method_name
			edglog(error)  << "There is NOT a better proxy for user ["
			  << it->m_user_dn << "] related to current delegation id to check ["
			  << it->m_delegation_id <<"]. Skipping this delegation renewal check..." << endl;
			 // );
	  mapDelegTime.erase( thisDelegID );
	  continue;
	}
	int timeout = IceConfManager::instance()->getConfiguration()->ice( )->proxy_renewal_timeout( );
	string timeoutStr( boost::lexical_cast<string>(timeout) );
	//CREAM_SAFE_LOG( m_log_dev->debugStream() << method_name
			edglog(debug)<< "Contacting MyProxy server ["
			<< it->m_myproxyserver << "] for user dn ["
			<< it->m_user_dn << "] with proxy certificate ["
			<< certfile << "] to renew it..." << endl;
			//);

        string newcert = certfile+".renewed";

	char *renewal_args[] = { "/usr/bin/glite-wms-ice-proxy-renew",
	                         "--timeout",
				 (char*)timeoutStr.c_str( ),
				 "-s",
				 (char*)it->m_myproxyserver.c_str(),
				 "-p",
				 (char*)certfile.c_str(),
				 "-o",
				 (char*)newcert.c_str(),
				 0 };

	string env_cert = string("X509_USER_CERT=") + IceConfManager::instance()->getConfiguration()->common()->host_proxy_file();
	string env_key  = string("X509_USER_KEY=") + IceConfManager::instance()->getConfiguration()->common()->host_proxy_file();
/*
	char *renewal_envs[] = { "X509_USER_CERT", (char*)IceConfManager::instance()->getConfiguration()->common()->host_proxy_file().c_str(),
				 "X509_USER_KEY", (char*)IceConfManager::instance()->getConfiguration()->common()->host_proxy_file().c_str(), 0
				};
*/

	char *renewal_envs[] = { (char*)env_cert.c_str(), (char*)env_key.c_str(), 0 };

	pid_t renewal_process_pid = fork();
	if(renewal_process_pid == -1 ) {
	//  CREAM_SAFE_LOG( m_log_dev->errorStream() << method_name
			edglog(error)  << "fork returned error: ["
			  << strerror(errno) << "]"
			  << ". Cannot get a fresh proxy for user ["
			  << it->m_user_dn << "]." << endl;
			 // );
	} else {
	  if(renewal_process_pid == 0 ) {
	    // child that has to do execve
	    ::execve( "/usr/bin/glite-wms-ice-proxy-renew", renewal_args, renewal_envs );
	  ///  CREAM_SAFE_LOG( m_log_dev->errorStream() << method_name
			edglog(error)  << "execve returned error: ["
			  << strerror(errno) << "]"
			  << ". Cannot get a fresh proxy for user ["
			  << it->m_user_dn << "]." << endl;
			//  );
	  } else {
	    // parent that has to wait for the child termination
	    int status;
    	    int retwaitpid = ::waitpid(renewal_process_pid, &status, WUNTRACED);
	    if(retwaitpid == renewal_process_pid) {
	      // the child finised. must retrieve command's output from file in tmp and parse it to
	      // check if OK or ERROR
	      ifstream in;
	      in.open((string("/tmp/glite-wms-ice-proxy-renew.output.") + boost::lexical_cast<string>(retwaitpid)).c_str(), ios::in);
	      string renewal_command_output;
	      string line;
   	      while (getline(in, line)) renewal_command_output += line;
   	      in.close();
	      ::unlink( (string("/tmp/glite-wms-ice-proxy-renew.output.") + boost::lexical_cast<string>(retwaitpid)).c_str() );

	      /**
	       *
	       * ERROR
	       *
	       */
	      if(boost::starts_with(renewal_command_output, "ERROR")) {
	      //  CREAM_SAFE_LOG( m_log_dev->errorStream() << method_name
			 edglog(error)       << "Proxy renewal failed: ["
			        << renewal_command_output << "]" << endl;
			    //  );
	      }

	      /**
	       *
	       * OK
	       *
	       */
	      if(boost::starts_with(renewal_command_output, "OK")) {
	      //  CREAM_SAFE_LOG( m_log_dev->debugStream() << method_name
			 edglog(debug)   << "Proxy renewal successful for DN=["
			    << it->m_user_dn
			    <<"] MyProxyURL=["
			    << it->m_myproxyserver
			    << "]: new proxy is ["
			    << newcert << "]. It will overwrite the better one..." << endl;
			 //   );
	        /**
	           Must substitute old better proxy with new downloaded one, if it's more long-living
	         */
	        cream_api::soap_proxy::VOMSWrapper V( certfile + ".renewed", !::getenv("GLITE_WMS_ICE_DISABLE_ACVER") );
	        if( !V.IsValid( ) ) {
	         // CREAM_SAFE_LOG(m_log_dev->errorStream()
		   	  edglog(error)
			     << "Cannot parse downloaded new proxy ["
			     << certfile + ".renewed" << "] from MyProxy Service ["
			     << it->m_myproxyserver << "]. Error is: "
			     << V.getErrorMessage() << endl;
			    // );
	      //
	        } else {

	        /** SUBSTITUTE old better proxy with downloaded one.
	         */
	        boost::tuple<string, time_t, long long int> newPrx = boost::make_tuple( certfile + ".renewed", V.getProxyTimeEnd(), (long long int)-1 ); // -1 means to not modify the job counter
	        DNProxyManager::getInstance()->updateBetterProxy( it->m_user_dn,
								it->m_myproxyserver,
								newPrx );
	        ::unlink( (certfile + ".renewed").c_str() );

	       }
	      }
	    }
	  }

	}

	mapDelegTime[ thisDelegID ] = make_pair(thisExpTime, thisDuration);

	/**
	   if the current delegation ID is not expiring then skip it
	   but remove it from the map mapDelegTime anyway, in order to not update
	   the job cache later
	*/
	string thisUserDN    = it->m_user_dn;
	string thisCEUrl     = it->m_cream_url;
	string thisMyPR      = it->m_myproxyserver;

	/**
	   Obtain the better proxy for the DN-FQAN related to current
	   delegation ID.
	   Looking at the "new" code of DNProxyManager one can see that this better
	   proxy can be obtained in two different ways:
	   1. as the proxy that ICE registered to the myproxyserver and that is automatically renewed by it
	   2. as the ICE-calculated better proxy (calculated at each submission)
	*/

	//CREAM_SAFE_LOG(m_log_dev->debugStream()
		  edglog(debug)
		       << "Looking for the better proxy for DN ["
		       << thisUserDN << "] MyProxy Server name ["
		       << thisMyPR << "]..." << endl;
		     //  );

	boost::tuple<string, time_t, long long int> thisBetterPrx = DNProxyManager::getInstance()->getExactBetterProxyByDN( thisUserDN, thisMyPR );

	/**
	   Proxy NOT found for the userdn related to this delegation.
	   Remove the delegation
	*/
	if(thisBetterPrx.get<0>().empty()) {
	//  CREAM_SAFE_LOG(m_log_dev->debugStream()
			edglog(debug)
			 << "DNProxyManager::getExactBetterProxyByDN didn't return any better proxy for DN ["
			 << thisUserDN << "] and MyProxy Server name ["
			 << thisMyPR << "]... Skipping renew of this delegation ["
			 << thisDelegID << "]." << endl;
			// );
	  mapDelegTime.erase( thisDelegID );

	  DelegationManager::instance()->removeDelegation( thisDelegID );

	  continue;
	}

	/**
	   if the returned better proxy is the "new" one must get the actual expiration time
	   (remember that it is renewed by myproxyserver asynchronously and ICE doesn't know
	   when this happen)
	*/

	//	cream_api::soap_proxy::VOMSWrapper V( thisBetterPrx.get<0>() );

	pair<bool, time_t> result_validity = IceUtils::is_good_proxy( thisBetterPrx.get<0>() );
	if(!result_validity.first) {
	//  CREAM_SAFE_LOG(m_log_dev->errorStream()
			edglog(error) << "IceCommandDelegationRenewal::renewAllDelegations() - "
			 << "iceUtil::isgood() function reported an error while"
			 << " parsing proxy [" << thisBetterPrx.get<0>()
			 << "]. Skipping renew of delegation ["
			 << thisDelegID <<"]..." << endl;
			// );
	  mapDelegTime.erase( thisDelegID );
	  continue;
	}

	time_t proxy_time_end = result_validity.second;

	/**
	   If the super better proxy for this delegation ID is expired, it
	   means that there're no new jobs for this DN-FQAN_MYPROXY since
	   long and/or has not been done connections to myProxy or those connections
	   have failed... then we can remove the delegatiob ID from memory.
	*/
	if( proxy_time_end <= (time(0)-5) ) {
	 // CREAM_SAFE_LOG( m_log_dev->errorStream() << method_name
			edglog(error)  << "For current Delegation ID [" << thisDelegID
			  <<"] DNProxyManager returned the Better Proxy ["
			  << thisBetterPrx.get<0>()
			  << "] that is EXPIRED! Removing this delegation ID from "
			  << "DelegationManager, removing proxy from DNProxyManager's cache, won't renew delegation ..." << endl;
			//  );

	  DelegationManager::instance()->removeDelegation( thisDelegID );

	  DNProxyManager::getInstance()->removeBetterProxy( thisUserDN, thisMyPR );

	  list<CreamJob> toRemove;
	  {
//	    list<pair<string, string> > clause;
//	    clause.push_back( make_pair( util::CreamJob::user_dn_field(), thisUserDN) );
//	    clause.push_back( make_pair( util::CreamJob::myproxy_address_field(), thisMyPR) );
	    //db::GetJobs getter( clause, toRemove, "IceCommandDelegationRenewal::renewAllDelegations" );
	    db::GetJobsByDNMyProxy getter( toRemove, thisUserDN, thisMyPR, "IceCommandDelegationRenewal::renewAllDelegations" );
	    db::Transaction tnx(false, false);
	    tnx.execute( &getter );
	  }

	  list<CreamJob>::iterator jobit;// = toRemove.begin();
	  for( jobit = toRemove.begin(); jobit != toRemove.end(); ++jobit ) {
//	  while( jobit != toRemove.end() ) {

    	    jobit->set_status( cream_api::job_statuses::ABORTED );
    	    jobit->set_failure_reason( "Proxy expired" );
//	    ++jobit;
	  }

	  while( Main::instance()->get_ice_lblog_pool()->get_command_count() > 2 )
	    sleep(2);

	  Main::instance()->get_ice_lblog_pool()->add_request( new IceCommandLBLogging( toRemove ) );

	  // TODO: Bisogna loggare un aborted per tutti i job relativi a questa delega
	  // che non verra' piu' rinnovata ed e' stata rimossa dal DB di ICE.

	  mapDelegTime.erase( thisDelegID );
	  continue;
	}

	/**
	   If the BetterProxy for this delegation is not expiring after the delegation itself
	   let's continue with next delegation...
	*/
	if( !(proxy_time_end > thisExpTime)) {
	//  CREAM_SAFE_LOG(m_log_dev->warnStream()
			edglog(warning) << "IceCommandDelegationRenewal::renewAllDelegations() - "
			 << "The better proxy ["
			 << thisBetterPrx.get<0>() << "] is expiring NOT AFTER the current delegation ["
			 << thisDelegID << "]. Skipping ... " << endl;
			// );

	  mapDelegTime.erase( thisDelegID );
	  continue;
	}

	//CREAM_SAFE_LOG( m_log_dev->infoStream() << method_name
		edglog(info)	<< "Will Renew Delegation ID ["
			<< thisDelegID << "] with BetterProxy ["
			<< thisBetterPrx.get<0>()
			<< "] that will expire on ["
			<< IceUtils::time_t_to_string(thisBetterPrx.get<1>()) << "]" << endl;
			//);
	try {

	  string thisDelegUrl = thisCEUrl;

	  boost::replace_all( thisDelegUrl,
			      IceConfManager::instance()->getConfiguration()->ice()->cream_url_postfix(),
			      IceConfManager::instance()->getConfiguration()->ice()->creamdelegation_url_postfix()
			      );

	  CreamProxy_ProxyRenew( thisDelegUrl,
				 thisBetterPrx.get<0>(),
				 thisDelegID,
				 false /* ignore blacklisted CE */).execute( 3 );

	  mapDelegTime[ thisDelegID ] = make_pair(thisBetterPrx.get<1>(), thisBetterPrx.get<1>() - time(0) );

	} catch( exception& ex ) {
	//  CREAM_SAFE_LOG( m_log_dev->errorStream() << method_name
		edglog(error)	  << "Proxy renew for delegation ID ["
			  << thisDelegID << "] failed: " << ex.what()  << endl;
			  //);

	  mapDelegTime.erase( thisDelegID );
	  continue;
	}

      }

    for(map<string, pair<time_t, int> >::iterator it = mapDelegTime.begin();
        it != mapDelegTime.end(); ++it) {

      DelegationManager::instance()->updateDelegation( boost::make_tuple((*it).first, (*it).second.first, (*it).second.second ) );

    }
}

max_stars_repo_name: italiangrid/wms

max_issues_repo_name: italiangrid/wms

max_forks_repo_name: italiangrid/wms


text:
#define _WIN32_WINNT 0x0A00
#include <cstdint>
#include <algorithm>
#include <string>
#define BOOST_DATE_TIME_NO_LIB
#define BOOST_REGEX_NO_LIB
#define BOOST_ERROR_CODE_HEADER_ONLY
#define BOOST_SYSTEM_NO_LIB
#include <boost/algorithm/string/erase.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>
#pragma warning(push)
#pragma warning(disable: 4244 4267)
#include "crow/crow.h"
#pragma warning(pop)
#undef BOOST_DATE_TIME_NO_LIB
#undef BOOST_REGEX_NO_LIB
#undef BOOST_ERROR_CODE_HEADER_ONLY
#undef BOOST_SYSTEM_NO_LIB
#include "nlohmann/json.hpp"
#pragma warning(push)
#pragma warning(disable: 4267)
#include "cmdline/cmdline.h"
#pragma warning(pop)
#include "blockchain.hpp"

int main(int argc, char* argv[]) {
	cmdline::parser parser;
	parser.add<int>("port", 'p', "port to listen on", false, 5000, cmdline::range(1, 65535));
	parser.parse_check(argc, argv);
	auto const port = static_cast<uint16_t>(parser.get<int>("port"));

	crow::SimpleApp app;

	boost::uuids::random_generator gen;
	auto const uuid = boost::uuids::to_string(gen());
	auto const node_identifier = boost::algorithm::erase_all_copy(uuid, "-");

	blockchain::blockchain blockchain;

	CROW_ROUTE(app, "/mine").methods("GET"_method)(
		[&blockchain, &node_identifier](crow::request const& req) {
		auto const& last_block = blockchain.last_block();
		auto const proof = blockchain::blockchain::proof_of_work(last_block);

		blockchain.new_transaction("0", node_identifier, 1);

		auto const previous_hash = blockchain::blockchain::hash(last_block);
		auto const& block = blockchain.new_block(proof, previous_hash);

		nlohmann::json const response{
			{ "message", "New Block Forged" },
			{ "index", block.index },
			{ "transactions", block.transactions },
			{ "proof", block.proof },
			{ "previous_hash", block.previous_hash },
		};
		auto res = crow::response(200, response.dump());
		res.add_header("Content-Type", "application/json");
		return res;
	});

	CROW_ROUTE(app, "/transactions/new").methods("POST"_method)(
		[&blockchain](crow::request const& req) {
		auto const values = nlohmann::json::parse(req.body);
		std::string const required[] = { "sender", "recipient", "amount", };
		if (std::any_of(
			std::begin(required), std::end(required),
			[&values](auto& x){ return values.find(x) == values.end(); }))
			return crow::response(400, "Missing values");

		auto const index = blockchain.new_transaction(
			values.at("sender").get<decltype(blockchain::transaction::sender)>(),
			values.at("recipient").get<decltype(blockchain::transaction::recipient)>(),
			values.at("amount").get<decltype(blockchain::transaction::amount)>());

		nlohmann::json const response{
			{ "message", "Transaction will be added to Block " + std::to_string(index) },
		};
		auto res = crow::response(201, response.dump());
		res.add_header("Content-Type", "application/json");
		return res;
	});

	CROW_ROUTE(app, "/chain").methods("GET"_method)(
		[&blockchain](crow::request const& req) {
		nlohmann::json const response{
			{ "chain", blockchain.chain },
			{ "length", blockchain.chain.size() },
		};
		auto res = crow::response(200, response.dump());
		res.add_header("Content-Type", "application/json");
		return res;
	});

	CROW_ROUTE(app, "/nodes/register").methods("POST"_method)(
		[&blockchain](crow::request const& req) {
		auto const values = nlohmann::json::parse(req.body);

		auto const nodes = values.find("nodes");
		if (nodes == values.end())
			return crow::response(400, "Error: Please supply a valid list of nodes");

		for (auto const& node : *nodes)
			blockchain.register_node(node.get<std::string>());

		nlohmann::json const response{
			{ "message", "New nodes have been added" },
			{ "total_nodes", blockchain.nodes },
		};
		auto res = crow::response(201, response.dump());
		res.add_header("Content-Type", "application/json");
		return res;
	});

	CROW_ROUTE(app, "/nodes/resolve").methods("GET"_method)(
		[&blockchain](crow::request const& req) {
		auto const replaced = blockchain.resolve_conflicts();

		auto const response =
			replaced ?
			nlohmann::json{
				{ "message", "Our chain was replaced" },
				{ "new_chain", blockchain.chain },
			} :
			nlohmann::json{
				{ "message", "Our chain is authoritative" },
				{ "chain", blockchain.chain },
			};

		auto res = crow::response(200, response.dump());
		res.add_header("Content-Type", "application/json");
		return res;
	});

	app.port(port).multithreaded().run();

	return 1;
}

ext: cpp

max_stars_repo_name: teheperor/dvf-blockchain

max_issues_repo_name: teheperor/dvf-blockchain

max_forks_repo_name: teheperor/dvf-blockchain


text:
///
/// Copyright (c) 2009-2014 Nous Xiong (348944179 at qq dot com)
///
/// Distributed under the Boost Software License, Version 1.0. (See accompanying
/// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
///
/// See https://github.com/nousxiong/gce for latest version.
///

#include <gce/actor/detail/socket.hpp>
#include <gce/actor/detail/cache_pool.hpp>
#include <gce/actor/spawn.hpp>
#include <gce/actor/thread_mapped_actor.hpp>
#include <gce/actor/context.hpp>
#include <gce/actor/impl/tcp/socket.hpp>
#include <gce/actor/detail/mailbox.hpp>
#include <gce/actor/message.hpp>
#include <gce/detail/scope.hpp>
#include <gce/actor/impl/protocol.hpp>
#include <gce/amsg/amsg.hpp>
#include <gce/amsg/zerocopy.hpp>
#include <boost/asio/placeholders.hpp>
#include <boost/bind.hpp>
#include <boost/variant/get.hpp>
#include <boost/static_assert.hpp>
#include <boost/foreach.hpp>

namespace gce
{
namespace detail
{
///----------------------------------------------------------------------------
socket::socket(cache_pool* user)
  : basic_actor(&user->get_context(), user, user->get_index())
  , stat_(ready)
  , hb_(snd_)
  , sync_(ctx_->get_io_service())
  , recv_cache_(recv_buffer_, GCE_SOCKET_RECV_CACHE_SIZE)
  , conn_(false)
  , curr_reconn_(0)
  , is_router_(false)
{
}
///----------------------------------------------------------------------------
socket::~socket()
{
}
///----------------------------------------------------------------------------
void socket::init(net_option opt)
{
  BOOST_ASSERT_MSG(stat_ == ready, "socket status error");
  opt_ = opt;
  curr_reconn_ = u32_nil;

  base_type::update_aid();
}
///----------------------------------------------------------------------------
void socket::connect(
  aid_t sire, remote_func_list_t const& remote_func_list, ctxid_t target,
  std::string const& ep, bool target_is_router
  )
{
  BOOST_FOREACH(remote_func_t const& f, remote_func_list)
  {
    remote_list_.insert(std::make_pair(f.first, f.second));
  }
  ctxid_pair_t ctxid_pr =
    std::make_pair(
      target,
      target_is_router ? socket_router : socket_comm
      );
  user_->register_socket(ctxid_pr, get_aid());

  message m(msg_login);
  m << ctxid_;
  send(m);

  boost::asio::spawn(
    snd_,
    boost::bind(
      &socket::run_conn,
      this, sire, ctxid_pr, ep, _1
      ),
    boost::coroutines::attributes(default_stacksize())
    );
}
///----------------------------------------------------------------------------
void socket::start(
  std::map<match_t, remote_func> const& remote_list,
  socket_ptr skt, bool is_router
  )
{
  conn_ = true;
  remote_list_ = remote_list;
  is_router_ = is_router;

  boost::asio::spawn(
    snd_,
    boost::bind(
      &socket::run, this, skt, _1
      ),
    boost::coroutines::attributes(default_stacksize())
    );
}
///----------------------------------------------------------------------------
void socket::stop()
{
  close();
}
///----------------------------------------------------------------------------
void socket::on_free()
{
  base_type::on_free();

  stat_ = ready;
  recv_cache_.clear();
  conn_ = false;
  conn_cache_.clear();
  curr_reconn_ = 0;
  straight_link_list_.clear();
  remote_list_.clear();
  is_router_ = false;
}
///----------------------------------------------------------------------------
void socket::on_recv(pack& pk, base_type::send_hint)
{
  snd_.dispatch(
    boost::bind(
      &socket::handle_recv, this, pk
      )
    );
}
///----------------------------------------------------------------------------
void socket::handle_net_msg(message& msg)
{
  pack pk;

  bool has_tag =
    msg.pop_tag(
      pk.tag_, pk.recver_, pk.svc_,
      pk.skt_, pk.is_err_ret_
      );
  BOOST_ASSERT(has_tag);
  pk.msg_ = msg;

  if (link_t* link = boost::get<detail::link_t>(&pk.tag_))
  {
    if (is_router_)
    {
      sktaid_t skt = user_->select_joint_socket(pk.recver_.ctxid_);
      if (!skt)
      {
        /// no socket found, send already exit back
        base_type::send_already_exited(link->get_aid(), pk.recver_);
      }
      else
      {
        pk.tag_ = fwd_link_t(link->get_type(), link->get_aid(), get_aid());
        pk.skt_ = skt;
        if (link->get_type() == linked)
        {
          add_router_link(pk.recver_, link->get_aid(), skt);
        }
        base_type::send(pk.skt_, pk, sync);
      }
    }
    else
    {
      if (check(pk.recver_, ctxid_, timestamp_))
      {
        pk.skt_ = get_aid();
        if (link->get_type() == linked)
        {
          add_straight_link(pk.recver_, link->get_aid());
        }
        base_type::send(pk.recver_, pk, sync);
      }
      else
      {
        /// aid already expires, send exit msg
        base_type::send_already_exited(link->get_aid(), pk.recver_);
      }
    }
  }
  else if (exit_t* ex = boost::get<exit_t>(&pk.tag_))
  {
    if (is_router_)
    {
      sktaid_t skt = remove_router_link(pk.recver_, ex->get_aid());
      BOOST_ASSERT(skt);
      pk.tag_ = fwd_exit_t(ex->get_code(), ex->get_aid(), get_aid());
      pk.skt_ = skt;
      base_type::send(pk.skt_, pk, sync);
    }
    else
    {
      if (check(pk.recver_, ctxid_, timestamp_))
      {
        remove_straight_link(pk.recver_, ex->get_aid());
        base_type::send(pk.recver_, pk, sync);
      }
    }
  }
  else if (spawn_t* spw = boost::get<spawn_t>(&pk.tag_))
  {
    if (is_router_)
    {
      sktaid_t skt = user_->select_joint_socket(spw->get_ctxid());
      if (!skt)
      {
        send_spawn_ret(spw, pk, spawn_no_socket, aid_t(), true);
      }
      else
      {
        pk.skt_ = skt;
        base_type::send(pk.skt_, pk, sync);
      }
    }
    else
    {
      /// spawn actor
      remote_list_t::iterator itr(remote_list_.find(spw->get_func()));
      if (itr == remote_list_.end())
      {
        send_spawn_ret(spw, pk, spawn_func_not_found, aid_t(), true);
      }
      else
      {
        context& ctx = user_->get_context();
        cache_pool* user = ctx.select_cache_pool();
        user->get_strand().post(
          boost::bind(
            &socket::spawn_remote_actor, this,
            user, *spw, itr->second
            )
          );
      }
    }
  }
  else if (spawn_ret_t* spr = boost::get<spawn_ret_t>(&pk.tag_))
  {
    if (is_router_)
    {
      sktaid_t skt = user_->select_joint_socket(pk.recver_.ctxid_);
      if (skt)
      {
        pk.skt_ = skt;
        base_type::send(pk.skt_, pk, sync);
      }
    }
    else
    {
      if (check(pk.recver_, ctxid_, timestamp_))
      {
        /// fwd to spawner
        message m(msg_spawn_ret);
        m << (boost::uint16_t)spr->get_error() << spr->get_id();
        aid_t aid = spr->get_aid();
        if (!aid)
        {
          /// we should make sure no timeout miss.
          aid = get_aid();
        }
        pk.tag_ = aid;
        pk.msg_ = m;

        base_type::send(pk.recver_, pk, sync);
      }
    }
  }
  else
  {
    bool is_svc = pk.svc_;
    if (is_router_)
    {
      ctxid_t ctxid = is_svc ? pk.svc_.ctxid_ : pk.recver_.ctxid_;
      sktaid_t skt = user_->select_joint_socket(ctxid);
      if (detail::request_t* req = boost::get<detail::request_t>(&pk.tag_))
      {
        if (!skt && !is_svc)
        {
          /// reply actor exit msg
          response_t res(req->get_id(), pk.recver_);
          base_type::send_already_exited(req->get_aid(), res);
        }
      }

      if (skt)
      {
        pk.skt_ = skt;
        base_type::send(pk.skt_, pk, sync);
      }
    }
    else
    {
      if (is_svc)
      {
        pk.recver_ = user_->find_service(pk.svc_.name_);
      }

      if (check(pk.recver_, ctxid_, timestamp_))
      {
        base_type::send(pk.recver_, pk, sync);
      }
    }
  }
}
///----------------------------------------------------------------------------
void socket::spawn_remote_actor(cache_pool* user, spawn_t spw, remote_func f)
{
  aid_t aid;
  spawn_type type = spw.get_type();
  if (type == spw_stacked)
  {
    aid = make_stackful_actor(aid_t(), user, f.af_, spw.get_stack_size());
  }
  else
  {
    aid = make_stackless_actor(aid_t(), user, f.ef_, spw.get_stack_size());
  }
  user_->get_strand().post(
    boost::bind(
      &socket::end_spawn_remote_actor, this, spw, aid
      )
    );
}
///----------------------------------------------------------------------------
void socket::end_spawn_remote_actor(spawn_t spw, aid_t aid)
{
  pack pk;
  send_spawn_ret(&spw, pk, spawn_ok, aid, false);
}
///----------------------------------------------------------------------------
void socket::send_spawn_ret(
  spawn_t* spw, pack& pk, spawn_error err,
  aid_t aid, bool is_err_ret
  )
{
  pk.recver_ = spw->get_aid();
  pk.skt_ = spw->get_aid();
  pk.tag_ = spawn_ret_t(err, spw->get_id(), aid);
  pk.is_err_ret_ = is_err_ret;
  pk.msg_.set_type(msg_spawn_ret);
  pk.msg_.push_tag(
    pk.tag_, pk.recver_, pk.svc_,
    pk.skt_, pk.is_err_ret_
    );
  send(pk.msg_);
}
///----------------------------------------------------------------------------
void socket::send(message const& m)
{
  match_t type = m.get_type();
  std::size_t size = m.size();
  if (conn_)
  {
    BOOST_ASSERT(skt_);
    while (!conn_cache_.empty())
    {
      message const& m = conn_cache_.front();
      send_msg(m);
      conn_cache_.pop_front();
    }
    send_msg(m);
  }
  else
  {
    conn_cache_.push_back(m);
  }
}
///----------------------------------------------------------------------------
void socket::send_msg(message const& m)
{
  BOOST_ASSERT(skt_);
  msg::header hdr;
  hdr.size_ = (boost::uint32_t)m.size();
  hdr.type_ = m.get_type();
  hdr.tag_offset_ = m.tag_offset_;

  byte_t buf[sizeof(msg::header)];
  boost::amsg::zero_copy_buffer zbuf(buf, sizeof(msg::header));
  boost::amsg::write(zbuf, hdr);
  skt_->send(
    buf, zbuf.write_length(),
    m.data(), hdr.size_
    );
}
///----------------------------------------------------------------------------
void socket::send_msg_hb()
{
  send(message(detail::msg_hb));
}
///----------------------------------------------------------------------------
void send_ret(socket* s, aid_t sire, ctxid_pair_t ctxid_pr)
{
  gce::send(*s, sire, msg_new_conn, ctxid_pr);
}
///----------------------------------------------------------------------------
void socket::run_conn(aid_t sire, ctxid_pair_t target, std::string const& ep, yield_t yield)
{
  exit_code_t exc = exit_normal;
  std::string exit_msg("exit normal");
  ctxid_pair_t curr_pr = target;

  if (!user_->stopped())
  {
    context& ctx = user_->get_context();
    user_->add_socket(this);
    ctx.register_socket(target, get_aid(), user_->get_index());

    try
    {
      stat_ = on;
      {
        scope scp(boost::bind(&send_ret, this, sire, target));
        skt_ = make_socket(ep);
        connect(yield, true);
      }

      if (!conn_)
      {
        connect(yield);
      }

      while (stat_ == on)
      {
        message msg;
        errcode_t ec = recv(msg, yield);
        if (ec)
        {
          on_neterr(get_aid(), ec);
          --curr_reconn_;
          if (curr_reconn_ == 0)
          {
            exc = exit_neterr;
            exit_msg = ec.message();
            close();
            break;
          }
          connect(yield);
        }
        else
        {
          match_t type = msg.get_type();
          if (type == msg_login_ret)
          {
            ctxid_pair_t ctxid_pr;
            msg >> ctxid_pr;
            curr_pr = sync_ctxid(ctxid_pr, curr_pr);
          }
          else if (type != detail::msg_hb)
          {
            handle_net_msg(msg);
          }
          hb_.beat();
        }
      }
    }
    catch (std::exception& ex)
    {
      exc = exit_except;
      exit_msg = ex.what();
      close();
    }
    catch (...)
    {
      exc = exit_except;
      exit_msg = "unexpected exception";
      close();
    }
  }
  else
  {
    gce::send(*this, sire, msg_new_conn, target);
  }
  free_self(curr_pr, exc, exit_msg, yield);
}
///----------------------------------------------------------------------------
void socket::run(socket_ptr skt, yield_t yield)
{
  exit_code_t exc = exit_normal;
  std::string exit_msg("exit normal");
  ctxid_pair_t curr_pr =
    std::make_pair(
      ctxid_nil,
      is_router_ ? socket_joint : socket_comm
      );

  if (!user_->stopped())
  {
    context& ctx = user_->get_context();
    user_->add_socket(this);

    try
    {
      stat_ = on;
      skt_ = skt;
      skt_->init(user_);
      start_heartbeat(boost::bind(&socket::close, this));

      while (stat_ == on)
      {
        message msg;
        errcode_t ec = recv(msg, yield);
        if (ec)
        {
          on_neterr(get_aid(), ec);
          close();
          exc = exit_neterr;
          exit_msg = ec.message();
          break;
        }
        else
        {
          match_t type = msg.get_type();
          if (type == msg_login)
          {
            ctxid_pair_t ctxid_pr =
              std::make_pair(
                ctxid_nil,
                is_router_ ? socket_joint : socket_comm
                );
            msg >> ctxid_pr.first;
            curr_pr = sync_ctxid(ctxid_pr, curr_pr);
            message m(msg_login_ret);
            m << std::make_pair(
              ctxid_,
              is_router_ ? socket_router : socket_comm
              );
            send(m);
          }
          else if (type != detail::msg_hb)
          {
            handle_net_msg(msg);
          }
          hb_.beat();
        }
      }
    }
    catch (std::exception& ex)
    {
      exc = exit_except;
      exit_msg = ex.what();
      close();
    }
    catch (...)
    {
      exc = exit_except;
      exit_msg = "unexpected exception";
      close();
    }
  }
  free_self(curr_pr, exc, exit_msg, yield);
}
///----------------------------------------------------------------------------
socket_ptr socket::make_socket(std::string const& ep)
{
  /// find protocol name
  std::size_t pos = ep.find("://");
  if (pos == std::string::npos)
  {
    throw std::runtime_error("protocol name parse failed");
  }

  std::string prot_name = ep.substr(0, pos);
  if (prot_name == "tcp")
  {
    /// parse address
    std::size_t begin = pos + 3;
    pos = ep.find(':', begin);
    if (pos == std::string::npos)
    {
      throw std::runtime_error("tcp address parse failed");
    }

    std::string address = ep.substr(begin, pos - begin);

    /// parse port
    begin = pos + 1;
    pos = ep.size();

    std::string port = ep.substr(begin, pos - begin);
    socket_ptr skt(
      new tcp::socket(
        user_->get_strand().get_io_service(),
        address, port
        )
      );
    skt->init(user_);
    return skt;
  }

  throw std::runtime_error("unsupported protocol");
}
///----------------------------------------------------------------------------
void socket::handle_recv(pack& pk)
{
  if (check(pk.skt_, ctxid_, timestamp_))
  {
    BOOST_ASSERT(!check_local(pk.recver_, ctxid_));
    if (link_t* link = boost::get<link_t>(&pk.tag_))
    {
      add_straight_link(link->get_aid(), pk.recver_);
    }
    else if (exit_t* ex = boost::get<exit_t>(&pk.tag_))
    {
      remove_straight_link(ex->get_aid(), pk.recver_);
    }
    else if (fwd_link_t* link = boost::get<fwd_link_t>(&pk.tag_))
    {
      add_router_link(link->get_aid(), pk.recver_, link->get_skt());
      pk.tag_ = link_t(link->get_type(), link->get_aid());
    }
    else if (fwd_exit_t* ex = boost::get<fwd_exit_t>(&pk.tag_))
    {
      remove_router_link(ex->get_aid(), pk.recver_);
      pk.tag_ = exit_t(ex->get_code(), ex->get_aid());
    }
    pk.msg_.push_tag(
      pk.tag_, pk.recver_, pk.svc_,
      pk.skt_, pk.is_err_ret_
      );
    send(pk.msg_);
  }
  else if (!pk.is_err_ret_)
  {
    if (detail::link_t* link = boost::get<detail::link_t>(&pk.tag_))
    {
      /// send actor exit msg
      base_type::send_already_exited(link->get_aid(), pk.recver_);
    }
    else if (detail::request_t* req = boost::get<detail::request_t>(&pk.tag_))
    {
      /// reply actor exit msg
      response_t res(req->get_id(), pk.recver_);
      base_type::send_already_exited(req->get_aid(), res);
    }
  }
}
///----------------------------------------------------------------------------
void socket::add_straight_link(aid_t src, aid_t des)
{
  if (des)
  {
    std::pair<straight_link_list_t::iterator, bool> pr =
      straight_link_list_.insert(std::make_pair(src, straight_dummy_));
    pr.first->second.insert(des);
  }
}
///----------------------------------------------------------------------------
void socket::remove_straight_link(aid_t src, aid_t des)
{
  if (des)
  {
    straight_link_list_t::iterator itr(
      straight_link_list_.find(src)
      );
    if (itr != straight_link_list_.end())
    {
      itr->second.erase(des);
    }
  }
}
///----------------------------------------------------------------------------
void socket::add_router_link(aid_t src, aid_t des, sktaid_t skt)
{
  if (des)
  {
    std::pair<router_link_list_t::iterator, bool> pr =
      router_link_list_.insert(std::make_pair(src, router_dummy_));
    pr.first->second.insert(std::make_pair(des, skt));
  }
}
///----------------------------------------------------------------------------
sktaid_t socket::remove_router_link(aid_t src, aid_t des)
{
  sktaid_t skt;
  if (des)
  {
    router_link_list_t::iterator itr(
      router_link_list_.find(src)
      );
    if (itr != router_link_list_.end())
    {
      std::map<aid_t, sktaid_t>& skt_list = itr->second;
      std::map<aid_t, sktaid_t>::iterator skt_itr(skt_list.find(des));
      if (skt_itr != skt_list.end())
      {
        skt = skt_itr->second;
        skt_list.erase(skt_itr);
      }
    }
  }
  return skt;
}
///----------------------------------------------------------------------------
void socket::on_neterr(aid_t self_aid, errcode_t ec)
{
  conn_ = false;
  conn_cache_.clear();
  std::string errmsg("net error");
  if (ec)
  {
    errmsg = ec.message();
  }

  message m(exit);
  m << exit_neterr << errmsg;

  BOOST_FOREACH(straight_link_list_t::value_type& pr, straight_link_list_)
  {
    BOOST_FOREACH(aid_t const& des, pr.second)
    {
      detail::pack pk;
      pk.tag_ = detail::exit_t(exit_neterr, des);
      pk.recver_ = pr.first;
      pk.skt_ = pr.first;
      pk.msg_ = m;

      base_type::send(pk.recver_, pk, sync);
    }
  }
  straight_link_list_.clear();

  BOOST_FOREACH(router_link_list_t::value_type& pr, router_link_list_)
  {
    BOOST_FOREACH(router_link_list_t::mapped_type::value_type& des, pr.second)
    {
      detail::pack pk;
      pk.tag_ = fwd_exit_t(exit_neterr, des.first, self_aid);
      pk.recver_ = pr.first;
      pk.skt_ = des.second;
      pk.msg_ = m;

      base_type::send(pk.skt_, pk, sync);
    }
  }
  router_link_list_.clear();
}
///----------------------------------------------------------------------------
ctxid_pair_t socket::sync_ctxid(ctxid_pair_t new_pr, ctxid_pair_t curr_pr)
{
  if (new_pr != curr_pr)
  {
    context& ctx = user_->get_context();
    aid_t skt = get_aid();
    user_->deregister_socket(curr_pr, skt);
    user_->register_socket(new_pr, skt);
    ctx.deregister_socket(curr_pr, skt, user_->get_index());
    ctx.register_socket(new_pr, skt, user_->get_index());
  }
  return new_pr;
}
///----------------------------------------------------------------------------
bool socket::parse_message(message& msg)
{
  msg::header hdr;
  byte_t* data = recv_cache_.get_read_data();
  std::size_t remain_size = recv_cache_.remain_read_size();
  boost::amsg::zero_copy_buffer zbuf(data, remain_size);
  boost::amsg::read(zbuf, hdr);
  if (zbuf.bad())
  {
    return false;
  }

  if (hdr.size_ > GCE_MAX_MSG_SIZE)
  {
    throw std::runtime_error("message overlength");
  }

  std::size_t header_size = zbuf.read_length();
  if (remain_size - header_size < hdr.size_)
  {
    return false;
  }

  recv_cache_.read(header_size + hdr.size_);
  msg = message(hdr.type_, data + header_size, hdr.size_, hdr.tag_offset_);

  /// reset read_cache
  if (recv_cache_.read_size() > GCE_SOCKET_RECV_MAX_SIZE)
  {
    BOOST_ASSERT(recv_cache_.write_size() >= recv_cache_.read_size());
    std::size_t copy_size =
      recv_cache_.write_size() - recv_cache_.read_size();
    std::memmove(recv_buffer_, recv_cache_.get_read_data(), copy_size);
    recv_cache_.clear();
    recv_cache_.write(copy_size);
  }
  return true;
}
///----------------------------------------------------------------------------
void socket::connect(yield_t yield, bool init)
{
  errcode_t ec;
  if (stat_ == on)
  {
    seconds_t reconn_period = init ? opt_.init_reconn_period_ : opt_.reconn_period_;
    std::size_t const reconn_try = init ? opt_.init_reconn_try_ : opt_.reconn_try_;
    for (std::size_t i=0, retry=0; i<u32_nil; ++i, ++retry)
    {
      if (retry > reconn_try)
      {
        retry = 0;
        on_neterr(get_aid());
        if (init)
        {
          return;
        }
      }

      if (i > 0)
      {
        sync_.expires_from_now(reconn_period);
        sync_.async_wait(yield[ec]);
        if (stat_ != on)
        {
          break;
        }
      }

      skt_->connect(yield[ec]);
      if (!ec || stat_ != on)
      {
        break;
      }
    }

    if (stat_ != on)
    {
      return;
    }

    if (ec)
    {
      throw std::runtime_error(ec.message());
    }

    conn_ = true;
    start_heartbeat(boost::bind(&socket::reconn, this));

    message m(msg_login);
    m << ctxid_;
    send(m);
  }
}
///----------------------------------------------------------------------------
errcode_t socket::recv(message& msg, yield_t yield)
{
  BOOST_STATIC_ASSERT((GCE_SOCKET_RECV_CACHE_SIZE > GCE_SOCKET_RECV_MAX_SIZE));

  errcode_t ec;
  while (stat_ != off && !parse_message(msg))
  {
    std::size_t size =
      skt_->recv(
        recv_cache_.get_write_data(),
        recv_cache_.remain_write_size(),
        yield[ec]
        );
    if (ec)
    {
      break;
    }

    recv_cache_.write(size);
  }

  if (stat_ == off && !ec)
  {
    ec =
      boost::asio::error::make_error_code(
        boost::asio::error::operation_aborted
        );
  }

  return ec;
}
///----------------------------------------------------------------------------
void socket::close()
{
  stat_ = off;
  hb_.stop();
  if (skt_)
  {
    skt_->close();
  }
  errcode_t ignore_ec;
  sync_.cancel(ignore_ec);
}
///----------------------------------------------------------------------------
void socket::reconn()
{
  skt_->reset();
}
///----------------------------------------------------------------------------
template <typename F>
void socket::start_heartbeat(F f)
{
  hb_.init(
    opt_.heartbeat_period_, opt_.heartbeat_count_,
    f, boost::bind(&socket::send_msg_hb, this)
    );
  hb_.start();
}
///----------------------------------------------------------------------------
void socket::free_self(
  ctxid_pair_t ctxid_pr, exit_code_t exc,
  std::string const& exit_msg, yield_t yield
  )
{
  try
  {
    hb_.wait_end(yield);
    if (skt_)
    {
      skt_->wait_end(yield);
    }
  }
  catch (...)
  {
  }

  skt_.reset();

  hb_.clear();
  if (ctxid_pr.first != ctxid_nil)
  {
    user_->deregister_socket(ctxid_pr, get_aid());
    ctx_->deregister_socket(ctxid_pr, get_aid(), user_->get_index());
  }

  user_->remove_socket(this);
  aid_t self_aid = get_aid();
  base_type::update_aid();
  on_neterr(self_aid);
  base_type::send_exit(self_aid, exc, exit_msg);
  user_->free_socket(this);
}
///----------------------------------------------------------------------------
}
}

max_stars_repo_name: nousxiong/gce

max_issues_repo_name: txwdyzcb/gce

max_forks_repo_name: txwdyzcb/gce


text:
/*
**  DeSR
**  src/MlParser.cpp
**  ----------------------------------------------------------------------
**  Copyright (c) 2009  Giuseppe Attardi (attardi@di.unipi.it).
**  ----------------------------------------------------------------------
**
**  This file is part of DeSR.
**
**  DeSR is free software; you can redistribute it and/or modify it
**  under the terms of the GNU General Public License, version 3,
**  as published by the Free Software Foundation.
**
**  DeSR 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
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with this program.  If not, see <http://www.gnu.org/licenses/>.
**  ----------------------------------------------------------------------
*/

/// ======================================================================
/// Multi Layer Perceptron parser

// Must be first, since it includes <Python.h>
#include "State.h"
#include "Parser.h"
#include "conf/conf_int.h"
#include "conf/conf_float.h"
#include "EventStream.h"
#include "WordCounts.h"

#ifdef _WIN32
#include "lib/strtok_r.h"
#endif

// standard
#include <algorithm>		// swap
#include <list>

#ifdef HAVE_TR1_RANDOM
#include <random>
#endif

// Boost library
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>

using namespace std;
using namespace boost::numeric::ublas;

#include "io/Format.h"
using IXE::io::Format;

using Tanl::Classifier::Event;
using Tanl::Classifier::Context;
using Tanl::Classifier::PID;
using Tanl::Classifier::ClassID;

namespace Parser {

////////////////////////////////////////////////////////////////////////
// parameters

struct MlpConfig : public ParserConfig
{
  MlpConfig(IXE::Configuration::Params& params) :
    ParserConfig(params),	// inclue generic parameters
    numHidden		("MlpHidden", 100, params),
    numLayers		("MlpLayers", 1, params),
    LR			("MlpLearningRate", 0.01F, params),
    randomRange		("RandomRange", 0.05F, params),
    mlpIterations	("MlpIterations", 100, params),
    mlpMinIterations	("MlpMinIterations", 5, params),
    mlpVainIterations	("MlpVainIterations", 3, params)
  { }

  /** Number of hidden variables. */
  IXE::conf<int>	numHidden;

  /** Number of hidden layers. */
  IXE::conf<int>	numLayers;

  /** Learning rate. */
  IXE::conf<float>	LR;

  /** Random weights in interval [-RandomRange, +RandomRange]. */
  IXE::conf<float>	randomRange;

  /** Maximum number of iterations to perform. */
  IXE::conf<int>	mlpIterations;
  /** Minimum number of iterations to perform. */
  IXE::conf<int>	mlpMinIterations;

  /** Terminate if no updates occurr for these many iterations. */
  IXE::conf<int>	mlpVainIterations;
};

////////////////////////////////////////////////////////////////////////

// Use beam search
#define BEAM

#define MAX_LINE_LEN 8196

typedef boost::numeric::ublas::vector<double>	Vector;
typedef matrix<double>	Matrix;

static void softsign(Vector& x)
{
  for (size_t i = 0; i < x.size(); i++)
    x[i] /=  1.0 + fabs(x[i]);
}

#define READLINE(line, file)   if (!ifs.getline(line, MAX_LINE_LEN)) \
    throw IXE::FileError(string("Wrong file format: ") + file)

#define READ_WEIGHT(w, file) if (!ifs.read((char*)&w, sizeof(w)))	\
    throw IXE::FileError(string("Wrong file format: ") + file)

/**
 *	@ingroup parser
 */

struct MlpModel : public Tanl::Classifier::Classifier
{
  typedef std::vector<Tanl::Classifier::PID> X;
  typedef Tanl::Classifier::ClassID Y;
  typedef std::pair<X, Y> Case;         // (x_i, y_i)
  typedef std::vector<Case> Cases;      // (X, Y)^N, N = size of training data
  typedef std::vector<Case*> ValidationSet;

  MlpModel(MlpConfig& config) :
    Tanl::Classifier::Classifier(config.verbose),
    numHidden(config.numHidden),
    numLayers(config.numLayers),
    LR(config.LR),
    minIterations(config.mlpMinIterations),
    vainIterations(config.mlpVainIterations),
    featureCutoff(config.featureCutoff),
    randomRange(config.randomRange)
  { }

  MlpModel(int numFeatures, int numOutcomes, int numHidden, int numLayers = 1) :
    w1(numFeatures, numHidden),
    b1(numHidden),
    w2(numHidden, numOutcomes),
    b2(numOutcomes),
    numHidden(numHidden),
    numLayers(numLayers)
  {
    if (numLayers == 2) {
      wh.resize(numHidden, numHidden);
      bh.resize(numHidden);
    }
  }

  ~MlpModel() {
    FOR_EACH (std::vector<char const*>, outcomeLabels, it)
      free((void*)*it);
  }

  list<Event*>
  		collectEvents(Enumerator<Sentence*>& sentenceReader, GlobalInfo& info);

  /** Create numeric @param cases out of training @param events. */
  void		buildCases(list<Event*>& events, Cases& cases);

  double	train(Case&, int&);

  /**
   * Train model with @param cases, performing @param epoch iterations,
   * saving intermediate model weights to @param ofs.
   */
  void		train(Cases& cases, int epoch, ofstream& ofs);

  void		validate(ValidationSet& vs, double& avg, double& std);

  int		crossentropy_softmax(Vector& x, double sm[]);

  Vector	gradCrossentropy(Vector& x, int y);

  /** @return the most likely output. */
  ClassID	estimate(const std::vector<PID>& features, double prob[]);

  void		load(ifstream& ifs, char const* file = "");

  void		save(ofstream& ofs);

  //protected:
  void		writeLabels(ofstream& ofs);

  /** @return start stream position, so that it can be rewritten. */
  streampos	writeData(ofstream& ofs);

  void		clearLabels();

protected:
  Matrix	w1, w2, wh;
  Vector	b1, b2, bh;
  int		numLayers;	///< number of hidden layers
  int		numHidden;	///< number of hidden variables
  int		numFeatures;	///< number of features
  WordIndex	outcomeIndex;
  float		LR;
  int		minIterations;
  int		vainIterations;
  int		featureCutoff;
  float		randomRange;
};

/**
Yoshua Bengio:

    My preferred style of moving average is the following. Let's say you
    have a series x_t and you want to estimate the mean m of previous
    (recent) x's:

    m <-- m + (2/t) (x_t - m)

    Note that with (1/t) learning rate instead of (2/t) you get the exact
    historical average. With a larger learning rate (like 2/t) you give
    a bit more importance to recent stuff, which makes sense if x's are
    non-stationary (very likely here [in the setting of computing the
    moving average of the training error]). With a constant learning rate
    (independent of t) you get an exponential moving average.

    You can estimate a running average of the gradient variance by running
    averages of the mean gradient and of the
    square of the difference to the moving mean.
*/
struct MovingAverage
{
  double mean;
  double variance;
  int count;

  MovingAverage() :
    mean(0.0),
    variance(0.0),
    count(0)
  { }

  /**        Add value @param v to the moving average. */
  void add(double v) {
    count++;
    mean += (2. / count) * (v - mean);
    double this_variance = (v - mean) * (v - mean);
    variance += (2. / count) * (this_variance - variance);
  }
};

void MlpModel::save(ofstream& ofs)
{
  writeLabels(ofs);
  writeData(ofs);
}

void MlpModel::writeLabels(ofstream& ofs)
{
  // dump labels
  ofs << outcomeLabels.size() << endl;
  FOR_EACH (std::vector<char const*>, outcomeLabels, pit)
    ofs << *pit << endl;
  // dump predLabels
  ofs << predLabels.size() << endl;
  FOR_EACH (std::vector<string>, predLabels, pit)
    ofs << *pit << endl;
}

void MlpModel::clearLabels()
{
  predIndex.clear();
  WordIndex().swap(predIndex); // STL map do not deallocate. resize(0) has no effect
  predLabels.clear();
  std::vector<std::string>().swap(predLabels);

  outcomeIndex.clear();
  WordIndex().swap(outcomeIndex);
  FOR_EACH (std::vector<char const*>, outcomeLabels, it)
    free((void*)*it);
  outcomeLabels.clear();
  std::vector<char const*>().swap(outcomeLabels);
}

streampos MlpModel::writeData(ofstream& ofs)
{
  streampos startPos = ofs.tellp();

  // write layers
  ofs << numLayers << endl;

  // read hidden size
  ofs << numHidden << endl;

  // write weights

  // w1
  ofs << numFeatures * numHidden << endl;
  for (int i = 0; i < numFeatures; i++)
    for (int j = 0; j < numHidden; j++)
      ofs.write((char*)&w1(i, j), sizeof(double));

  // b1
  ofs << numHidden << endl;
  for (int i = 0; i < numHidden; i++)
    ofs.write((char*)&b1[i], sizeof(double));

  // w2
  ofs << numHidden * numOutcomes << endl;
  for (int i = 0; i < numHidden; i++)
    for (unsigned j = 0; j < numOutcomes; j++)
      ofs.write((char*)&w2(i, j), sizeof(double));

  // b2
  ofs << numOutcomes << endl;
  for (unsigned i = 0; i < numOutcomes; i++)
    ofs.write((char*)&b2[i], sizeof(double));

  // optional second layer
  if (numLayers == 2) {
    // wh
    ofs << numHidden * numHidden << endl;
    for (int i = 0; i < numHidden; i++)
      for (int j = 0; j < numHidden; j++)
	ofs.write((char*)&wh(i, j), sizeof(double));

    // bh
    ofs << numHidden << endl;
    for (int i = 0; i < numHidden; i++)
      ofs.write((char*)&bh[i], sizeof(double));
  }
  return startPos;
}

void MlpModel::load(ifstream& ifs, char const* file)
{
  // load class labels
  char line[MAX_LINE_LEN];
  READLINE(line, file);
  int len = atoi(line);
  numOutcomes = len;
  outcomeLabels.resize(numOutcomes);
  int n = 0;
  while (len--) {
    READLINE(line, file);
    outcomeLabels[n] = strdup(line);
    outcomeIndex[(char const*)line] = n++;
  }
  // load feature labels
  READLINE(line, file);
  numFeatures = len = atoi(line);
  predLabels.resize(numFeatures);
  n = 0;
  while (len--) {
    READLINE(line, file);
    predLabels[n] = line;
    predIndex[(char const*)line] = n++;
  }

  // read layers
  READLINE(line, file);
  numLayers = atoi(line);

  // back compatibility:
  if (numLayers > 3) {
    numHidden = numLayers;
    numLayers = 1;
  } else {
    // read hidden size
    READLINE(line, file);
    numHidden = atoi(line);
  }

  w1.resize(numFeatures, numHidden);
  // load weights

  // w1
  READLINE(line, file);
  len = atoi(line);		// numFeatures * numHidden
  if (len != numFeatures * numHidden)
    throw IXE::FileError(string("Wrong w1 size: ") + file);
  n = 0;
  double w;
  while (len--) {
    READ_WEIGHT(w, file);
    w1(n / numHidden, n % numHidden) = w;
    n++;
  }

  // b1
  b1.resize(numHidden);
  READLINE(line, file);
  len = atoi(line);		// numHidden
  n = 0;
  while (len--) {
    READ_WEIGHT(w, file);
    b1[n++] = w;
  }

  // w2
  w2.resize(numHidden, numOutcomes);
  READLINE(line, file);
  len = atoi(line);		// numHidden * numOutcomes
  if (len != numHidden * numOutcomes)
    throw IXE::FileError(string("Wrong w2 size: ") + file);
  n = 0;
  while (len--) {
    READ_WEIGHT(w, file);
    w2(n / numOutcomes, n % numOutcomes) = w;
    n++;
  }

  // b2
  b2.resize(numOutcomes);
  READLINE(line, file);
  len = atoi(line);		// numOutcomes
  n = 0;
  while (len--) {
    READ_WEIGHT(w, file);
    b2[n++] = w;
  }

  // optional second layer
  if (numLayers == 2) {
    // wh
    wh.resize(numHidden, numHidden);
    READLINE(line, file);
    len = atoi(line);		// numHidden * numHidden
    if (len != numHidden * numHidden)
      throw IXE::FileError(string("Wrong wh size: ") + file);
    n = 0;
    while (len--) {
      READ_WEIGHT(w, file);
      wh(n / numHidden, n % numHidden) = w;
      n++;
    }

    // bh
    bh.resize(numHidden);
    READLINE(line, file);
    len = atoi(line);		// numHidden
    n = 0;
    while (len--) {
      READ_WEIGHT(w, file);
      bh[n++] = w;
    }
  }
}

/**
 * Compute:
 *
 *     softmax(x)[i] = exp(x[i]) / sum_j(exp(x[j]))
 *
 * We compute this by subtracting off the max of x. This avoids numerical instability.
 *
 *     m = max_j x[j]
 *     softmax(x)[i] = exp(x[i] -m) / sum_j(exp(x[j] - m))
 *
 * Negative log likelihood at index t is:
 *
 *     nll(x,t) = -log(softmax(x)[t])
 *
 *	@return argmax(softmax)
 */
int MlpModel::crossentropy_softmax(Vector& x, double sm[])
{
  // get the maximum value of x for numerically safe softmax
  double m = 0.0;
  int am = 0;
  for (unsigned i = 0; i < numOutcomes; i++)
    if (x[i] > m) {
      am = i;
      m = x[i];
    }
  // compute the unnormalized softmax, and normalization constant
  double sum_j = 0.0;
  for (unsigned i = 0; i < numOutcomes; i++)
    sum_j += sm[i] = exp(x[i] - m);

  // normalized softmax
  for (unsigned i = 0; i < numOutcomes; i++)
    sm[i] /= sum_j;
  return am;
}

Vector MlpModel::gradCrossentropy(Vector& x, int y)
{
  Vector sm(numOutcomes);
  crossentropy_softmax(x, &sm[0]);
  sm[y] -= 1.0;                 // scalar decrement
  return sm;
}

ClassID MlpModel::estimate(const std::vector<PID>& features, double prob[])
{
  Vector h(numHidden);
  for (int i = 0; i < numHidden; i++)
    h[i] = 0.0;
  for (size_t f = 0; f < features.size(); f++)
    h += row(w1, features[f]);
  h += b1;
  softsign(h);		// first layer
  if (numLayers > 1) {
    h = prod(wh, h) + bh;
    softsign(h);		// second layer
  }
  Vector o(numOutcomes);
  noalias(o) = prod(h, w2) + b2;
  return crossentropy_softmax(o, prob);
}

void show(Vector& x)
{
  for (size_t i = 0; i < x.size(); i++) {
    cerr << x[i] << ' '; if ((i+1) % 5 == 0) cerr << endl; }
  cerr << endl;
}

/**
 *  Compute the gradients with respect to negative log likelihodd:
 *
 *  x = h w2 + b2
 *  xw1 = SUM_f w1[f]
 *  h = softsign(xw1 + b1)
 *  h' = 1 / (1 + abs(xw1 + b1))^2
 *  nll = -x[t] + log(Sum_j(exp(x[j])))
 *  d nll/dx = - d x[t]/dx + 1/Sum_j(exp(x[j])) Sum_j(exp(x[j]) d x[j]/ dx)
 *           = [0 .. -1@t .. 0] + 1
 *
 *  d nll/dw1 = dnll/dx dx/dw1 = dnll/dx (dh/dw1 w2) = dnll/dx (h' w2) dxw1/dx
 *  d nll/db1 = dnll/dx dx/db1 = dnll/dx (dh/db1 w2) = dnll/dx (h' w2)
 *  d nll/dw2 = dnll/dx dx/dw2 = dnll/dx h
 *  d nll/db2 = dnll/dx dx/db2 = dnll/dx
 *
 *  Return in @param argmax the most likely result.
 *
 *	@return the negative log likelihood.
 */
double MlpModel::train(Case& cas, int& argmax)
{
  X& features = cas.first;
  Y y = cas.second;
  Vector xw1(numHidden);
  for (int i = 0; i < numHidden; i++)
    xw1[i] = 0.0;
  for (size_t f = 0; f < features.size(); f++)
    xw1 += row(w1, features[f]);
  Vector h(numHidden);
  noalias(h) = xw1 + b1;
  softsign(h);
  /*
  if (numLayers > 1) {
    noalias(h2) = prod(wh, h) + bh;
    softsign(h2);		// second layer
  }
  */
  Vector o(numOutcomes);
  noalias(o) = prod(h, w2) + b2;

  std::vector<double> softmax(numOutcomes);
  argmax = crossentropy_softmax(o, &softmax[0]);
  double kl = -log(softmax[y]);

  Vector gb2(numOutcomes);
  gb2 = gradCrossentropy(o, y);

  Matrix gw2(numHidden, numOutcomes);
  noalias(gw2) = outer_prod(h, gb2); // h x gb2

  Vector hprimeInv(numHidden);	// (1 + |xw1 + b1|)^2
  for (int i = 0; i < numHidden; i++) {
    double hi = 1. + abs(xw1[i] + b1[i]);
    hprimeInv[i] = hi * hi;
  }

  Vector gb1(numHidden);
  noalias(gb1) = element_div(prod(w2, gb2), hprimeInv);

  // Only sum the gradient along the non-zeroes.
  for (size_t i = 0; i < features.size(); i++)
    for (int j = 0; j < numHidden; j++)
      w1(features[i], j) -= gb1(j) * LR;

  b1 -= gb1 * LR;
  if (numLayers == 2) {
    Matrix gwh(numHidden, numHidden);
    Vector gbh(numHidden);
    wh -= gwh * LR;
    bh -= gbh * LR;
  }
  w2 -= gw2 * LR;
  b2 -= gb2 * LR;

  return kl;
}

/**
 * Produces a permutation of the vector @c v.
 * This is useful for randomizing the processing order of the training data.
 * @see http://www.techuser.net/randpermgen.html
 */
// FIXME: share this with ap.cpp
static void rand_permutation(std::vector<int>& v) {
  size_t N = v.size();
  for (size_t i = 0; i < N; i++)
    v[i] = i;			// initialize to a sequence
  for (size_t i = 0; i < N-1; i++) { // nothing to do for i==N-1
    int r = i + rand() % (N-i); // no need to use high-order bits
    std::swap(v[i], v[r]);
  }
}

void MlpModel::train(Cases& cases, int epochs, ofstream& ofs)
{
  if (verbose)
    cerr << "Starting training.." << endl;

  // save current stream position
  streampos dataStart = ofs.tellp();

  MovingAverage mvgavg_accuracy;
  MovingAverage mvgavg_loss;

  double best_validation_accuracy = 0.0;
  int best_validation_at = 0;
  int cnt = 0;

  ValidationSet vs;

  int numCases = cases.size();
  std::vector<int> perm(numCases);
  for (int it = 0; it < epochs; ++it) {
    if (verbose)
      cerr << " EPOCH #" << it << " (" << numCases*it << " examples)" << endl;
    rand_permutation(perm);
    vs.clear();
    for (int i = 0; i < numCases; i++) {
      cnt++;
      Case& cas = cases[perm[i]];
      // save 1% random cases for validation
      if (rand() < RAND_MAX / 100)
	vs.push_back(&cas);
      else {
	int argmax;
	double kl = train(cas, argmax);
	double accuracy = (argmax == cas.second) ? 100.0 : 0.0;
	mvgavg_accuracy.add(accuracy);
	mvgavg_loss.add(kl);
      }
      if (verbose && cnt % 1000 == 0) {
	if (cnt % 10000 == 0) {
	  cerr << '+' << flush;
	  if (cnt % 80000 == 0) {
	    cerr << endl
		 << Format("After %d examples: training accuracy = %f, loss = %f",
			   cnt, mvgavg_accuracy.mean, mvgavg_loss.mean)
		 << endl;
	  }
	} else if (cnt % 1000 == 0)
	  cerr << '.' << flush;
      }
    }
    // perform validation
    double valacc, valstd;
    validate(vs, valacc, valstd);
    if (verbose)
      cerr << endl
	   << Format("After %d examples: validation accuracy = %.2f%%, stddev= %.2f%% (former best=%.2f%% at %d)\n",
		     cnt, valacc*100, valstd*100, best_validation_accuracy*100, best_validation_at)
	   << Parser::procStat() << endl;;
    // check for improvement
    if (best_validation_accuracy < valacc) {
	best_validation_accuracy = valacc;
	best_validation_at = cnt;
	if (verbose)
	  cerr << Format("NEW BEST VALIDATION ACCURACY: %.2f%%.", valacc*100.)
	       << " Saving model...";
	ofs.seekp(dataStart);
	writeData(ofs);
	if (verbose)
	  cerr << endl;
      } else if (cnt - best_validation_at >= vainIterations * numCases &&
		 cnt / numCases >= minIterations) {
      if (verbose) {
	cerr << "Have not beaten best validation accuracy for "
	     << vainIterations << " iterations. Terminating training..."
	     << endl;
      }
      break;
    }
  }
}

void MlpModel::validate(ValidationSet& vs, double& avg, double& std)
{
  avg = 0.0;		// average accuracy
  std = 0.0;
  for (size_t i = 0; i < vs.size(); i++) {
    Case& cas = *vs[i];
    X& x  = cas.first;
    Y y = cas.second;

    std::vector<double> sm(numOutcomes);
    int argmax = estimate(x, &sm[0]);

    double acc = double(argmax == y);
    avg = (avg * i + acc) / (i+1);

    std = (std * i + (acc - avg)*(acc - avg)) / (i+1);
  }
}

/**
 *	@ingroup parser
 *
 *	A Parser using a Multi Layer Perceptron classifier.
 */
struct MlpParser : public Parser
{
  MlpParser(char const* modelFile);

  void		train(SentenceReader* sentenceReader, char const* modelFile);

  Sentence*	parse(Sentence* sentence);

  void		revise(SentenceReader* sentenceReader, char const* actionFile = 0);

  MlpModel	model;

  int		iter;		///< number of epochs

  MlpConfig	config;		///< full configuration (inherits from ParseConfig)
};

/**
 *	@ingroup parser
 *
 *	Constructor for MlpParser
 */
Parser*	MlpParserFactory(char const* modelFile = 0)
{
  return new MlpParser(modelFile);
}

REGISTER_PARSER(MLP, MlpParserFactory);

MlpParser::MlpParser(char const* modelFile) :
  Parser(model.PredIndex(), config), // use our own config
  config(params),	      // use our own params, inherited from Parser
  model(config)
{
  model.verbose = config.verbose;
  if (!modelFile)
    return;			// training
  ifstream ifs(modelFile, ios::binary);
  if (!ifs)
    throw IXE::FileError(string("Missing model file: ") + modelFile);
  // load header
  config.load(ifs);
  model.load(ifs);
  // read entities
  info.load(ifs);
}

/**
 * Collect events from @c sentenceReader
 */
list<Event*>
MlpModel::collectEvents(Enumerator<Sentence*>& sentenceReader, GlobalInfo& info)
{
  if (verbose)
    cerr << "Collecting events.." << endl;

  list<Event*> events;

  WordCounts predCount; // count predicate occurrences

  int evCount = 0;
  PID pID = 0;
  // create inverted index of predicate names
  // used to create vector of pIDs
  EventStream eventStream(&sentenceReader, &info);
  ClassID oID = 0;

  while (eventStream.hasNext()) {
    Event* ev = eventStream.next();
    events.push_back(ev);
    evCount++;		      // count them explicitly, since size() is costly
    if (verbose) {
      if (evCount % 10000 == 0)
	cerr << '+' << flush;
      else if (evCount % 1000 == 0)
	cerr << '.' << flush;
    }
    char const* c = ev->className.c_str();
    if (outcomeIndex.find(c) == outcomeIndex.end()) {
      outcomeIndex[c] = oID++;
      outcomeLabels.push_back(strdup(c));
    }
    std::vector<string>& ec = ev->features; // ec = {p1, ... , pn}
    // ASSERT: code will always be found
    for (unsigned j = 0; j < ec.size(); j++) {
      string& pred = ec[j];
      // decide whether to retain it (# occurrences > cutoff)
      if (predIndex.find(pred.c_str()) == predIndex.end()) {
	// not yet among those retained
	// increment # of occurrences
	int count = predCount.add(pred);
	if (count >= featureCutoff) {
	  predLabels.push_back(pred); // accept it into predLabels
	  predIndex[pred.c_str()] = pID++;
	  predCount.erase(pred);
	}
      }
    }
  }
  if (verbose)
    cerr << endl;
  numOutcomes = oID;
  numFeatures = pID;		// predIndex will be cleared later.
  // free memory
  predCount.clear();
  WordCounts().swap(predCount);

  return events;
}

#ifdef HAVE_TR1_RANDOM
#define TWO48 281474976710656
std::tr1::linear_congruential<unsigned long long, 25214903917, 11, TWO48> drand48Gen;
#define srand48(x) drand48Gen.seed(x)
#define drand48() drand48Gen()/double(TWO48)
#endif

#define RAND_WEIGHT (randomRange * (2.0 * drand48() - 1.0))

void MlpModel::buildCases(list<Event*>& events, Cases& cases)
{
  // initialize weights
  srand48(1);

  w1.resize(numFeatures, numHidden);
  //double sqrtin = sqrt(numFeatures);
  for (int i = 0; i < numFeatures; i++)
    for (int j = 0; j < numHidden; j++)
      w1(i, j) = RAND_WEIGHT;
     //w1(i, j) = 0.01 * j * (j % 2 - 0.5)/ sqrtin;
  b1.resize(numHidden);
  for (int i = 0; i < numHidden; i++)
    b1(i) = 0.0;
  w2.resize(numHidden, numOutcomes);
  //double sqrth = sqrt(numHidden);
  for (int i = 0; i < numHidden; i++)
    for (unsigned j = 0; j < numOutcomes; j++)
      w2(i, j) = RAND_WEIGHT;
      //w2(i, j) = 0.01 * j * (j % 2 - 0.5)/ sqrth;
  b2.resize(numOutcomes);
  for (unsigned i = 0; i < numOutcomes; i++)
    b2(i) = 0.0;
  if (numLayers == 2) {
    wh.resize(numHidden, numHidden);
    for (int i = 0; i < numHidden; i++)
      for (int j = 0; j < numHidden; j++)
	wh(i, j) = RAND_WEIGHT;
    bh.resize(numHidden);
    for (int i = 0; i < numHidden; i++)
      bh(i) = 0.0;
  }

  size_t evCount = events.size();
  cases.reserve(evCount);
  int n = 0;
  while (!events.empty()) {
    Event* ev = events.front();
    events.pop_front();
    cases.push_back(Case());
    X& x = cases[n].first;	// features
    // add features
    std::vector<string>& ec = ev->features; // ec = {p1, ... , pn}
    char const* c = ev->className.c_str();
    for (unsigned j = 0; j < ec.size(); j++) {
      string& pred = ec[j];
      WordIndex::const_iterator pit = predIndex.find(pred.c_str());
      if (pit != predIndex.end()) {
	x.push_back(pit->second);
      }
    }
    if (x.size()) {
      cases[n].second = outcomeIndex[c];
      n++;
      if (verbose) {
	if (n % 10000 == 0)
	  cerr << '+' << flush;
	else if (n % 1000 == 0)
	  cerr << '.' << flush;
      }
      //x.push_back(0);		// bias
    }
    delete ev;
  }
  cases.resize(n);
  if (verbose)
    cerr << endl;
  if (verbose) {
    cerr << "\t    Number of events: " << evCount << endl;
    cerr << "\t   Number of Classes: " << outcomeLabels.size() << endl;
    cerr << "\tNumber of Predicates: " << predIndex.size() << endl;
  }
}

void MlpParser::train(SentenceReader* sentenceReader, char const* modelFile)
{
  // collect sentences and replace unfrequent token attributes with UNKNOWN.
  std::deque<Sentence*> sentences = collectSentences(sentenceReader);
  Tanl::SentenceQueueReader sr(sentences);

  // set params from config:
  model = MlpModel(config);

  list<Event*> events = model.collectEvents(sr, info);

  MlpModel::Cases cases;
  model.buildCases(events, cases);

  ofstream ofs(modelFile, ios::binary | ios::trunc);
  // dump configuration settings
  config.writeHeader(ofs);

  // dump predicate and outcome labels
  model.writeLabels(ofs);
  streampos dataStart = model.writeData(ofs);

  // free memory
  model.clearLabels();

  // clear memory for unfrequent entities
  info.clearRareEntities();
  // dump entities
  info.save(ofs);
  info.clear();
  ofs.seekp(dataStart);		// rewind

  // compute weights and dump best values to ofs.
  model.train(cases, config.mlpIterations, ofs);
}

#ifdef BEAM
// FIXME: this is duplicated in MlpParser.cpp
/**
 * Insert @param s in beam vector @param states.
 * Assume insertion is possible, i.e. there is either space in the beam
 * or @c s has better @c lprob than the worst in the beam.
 */
static double addState(ParseState* s, std::vector<ParseState*>& states)
{
  int size = states.size();
  int beam = states.capacity();
  assert(size < beam || s->lprob > states[size-1]->lprob);
  s->incRef();		// will be referenced from states
  // create space
  if (size == beam) {
    ParseState* last = states.back();
    last->decRef();		// out of states
    last->prune();
    states.pop_back();
  }
  TO_EACH (std::vector<ParseState*>, states, it)
    if (s->lprob > (*it)->lprob) {
      states.insert(it, s);
      return states.back()->lprob;
    }
  states.push_back(s);
  return s->lprob;
}
#endif

Sentence* MlpParser::parse(Sentence* sentence)
{
  int numOutcomes = model.NumOutcomes();
# ifdef _MSC_VER
  std::vector<double> paramsV(numOutcomes);
  double* params = &paramsV[0];
# else
  double params[numOutcomes];
# endif
  preprocess(sentence);
  ParseState* state = new ParseState(*sentence, &info, predIndex);

# ifdef BEAM
  Language const* lang = sentence->language;
  int beam = config.beam;

  std::vector<ParseState*> currStates; currStates.reserve(beam);
  std::vector<ParseState*> nextStates; nextStates.reserve(beam);
  std::vector<ParseState*>* bestStates = &currStates;
  std::vector<ParseState*>* bestNextStates = &nextStates;
  addState(state, *bestStates);	// calls incRef()
  int step = 0;

  while (true) {
#   ifdef DEBUG_MORPH
    cerr << "STEP: " << step++ << endl;
#   endif
    int finished = 0;
    int numBest = bestStates->size();
    double worstProb = -numeric_limits<double>::infinity();
    for (int i = 0; i < numBest; i++) {
      state = (*bestStates)[i];
      if (state->hasNext()) {
	Tanl::Classifier::Context& context = *state->next();
	model.estimate(context, params);
	for (int o = 0; o < numOutcomes; o++) {
	  double prob = params[o];
	  if (prob < 1e-4)
	    continue;		// not worth considering
	  double lprob = log(prob) + state->lprob;
	  if (bestNextStates->size() == beam && lprob < worstProb)
	    continue;		// not worth considering
	  char const* outcome = model.OutcomeName(o);
	  if (state->stack.size() > 1 && state->input.size()) {
	    Token* top = state->stack.back()->token;
	    Token* next = state->input.back()->token;
	    if ((outcome[0] == 'L' && !lang->morphoRight(*next->pos()) ||
		 outcome[0] == 'R' && !lang->morphoLeft(*top->pos())) &&
		((top->morpho.number && next->morpho.number &&
		  !lang->numbAgree(top->morpho.number, next->morpho.number)) ||
		 (top->morpho.gender && next->morpho.gender &&
		  !lang->gendAgree(top->morpho.gender, next->morpho.gender)))) {
	      lprob = log(prob / 10.) + state->lprob; // best: 10, LAS 87.85
	      if (bestNextStates->size() == beam && lprob < worstProb)
		continue;
	    }
	  }
	  ParseState* next = state->transition(outcome);
	  if (!next) // dead end.
	    continue;
#	  ifdef DEBUG_MORPH
	  cerr << i << " " << outcome << " " << prob << " " << lprob << " ";
	  for (ParseState* s = next; s; s = (ParseState*)s->previous)
	    if (s->action)
	      cerr << s->action << ' ';
	  cerr << endl;
#	  endif
	  next->lprob = lprob;
	  worstProb = addState(next, *bestNextStates); // does incRef()
	}
      } else if (bestNextStates->size() < (size_t)beam || state->lprob > worstProb) {
	// already finished: promote to bestNextStates
	worstProb = addState(state, *bestNextStates); // does incRef()
	finished++;
      }
    }
    if (bestNextStates->empty())
      break;			// no advance
    // clear bestStates, purge dead ends
    // purge after cycle, since if bestNextStates->empty()
    // these states will be the outputs and will be pruned later.
    FOR_EACH (std::vector<ParseState*>, *bestStates, it) {
      ParseState* state = (*it);
      state->decRef();		// no longer in bestStates
      state->prune();
    }
    bestStates->clear();
    // swap vectors
    std::vector<ParseState*>* tmp = bestStates;
    bestStates = bestNextStates;
    bestNextStates = tmp;
    if (finished == numBest)
      break;
  }
  // choose best sentence as output, discard the rest.
  Sentence* s = (*bestStates)[0]->getSentence();
  if (config.ShowActions) {
    ParseState* state = (*bestStates)[0];
    std::vector<char const*> seq;
    for (ParseState* s = state; s; s = (ParseState*)s->previous)
      if (s->action)
	seq.push_back(s->action);
    for (int i = seq.size()-1; i >= 0; i--)
      cerr << seq[i] << ' ';
    cerr << endl;
  }
# ifdef DEBUG_MORPH
  for (int i = 0; i < bestStates->size(); i++) {
    ParseState* state = (*bestStates)[i];
    std::vector<char const*> seq;
    for (ParseState* s = state; s; s = (ParseState*)s->previous)
      if (s->action)
	seq.push_back(s->action);
    for (int i = seq.size()-1; i >= 0; i--)
      cerr << seq[i] << ' ';
    cerr << endl;
    cerr << "lprob: " << state->lprob << endl;
  }
# endif
  if (config.ShowPerplexity) {
    double avg = 0.0;
    double min = 1.0;
    double n = 0.0;
    for (ParseState* s = (*bestStates)[0]; s; s = (ParseState*)s->previous) {
      double prob = exp(s->lprob);
      avg += prob;
      if (prob < min)
	min = prob;
      n++;
    }
    avg /= n;
    cout << "<LogLikelihood all=" << (*bestStates)[0]->lprob;
    cout << " avg=" << avg << " min=" << min << " />" << endl;
  }
  FOR_EACH (std::vector<ParseState*>, *bestStates, it) {
    (*it)->decRef();
    (*it)->prune();
  }

# ifdef ORACLE
  // check classifier accuracy with oracle
  TrainState ts(*sentence, &info);
  while (ts.hasNext()) {
    Event* ev = ts.next();
    string& action = ev->className;
    Context context(ev->features, predIndex);
    ClassID best = model.estimate(context, params);
    if (action == model.OutcomeName(best))
      oracleCorrect++;
    oracleCount++;
    ts.transition(action.c_str());
  }
# endif
  return s;

# else // !BEAM

  while (state->hasNext()) {
    Context& context = *state->next();
    model.estimate(context, params);
    int best = model.BestOutcome(params);
    char const* outcome = model.OutcomeName(best);
    ParseState* next = state->transition(outcome);
    if (!next)
      next = state->transition("S");
    state = next;
  }
  Sentence* s = state->getSentence();
  state.prune();
  return s;
# endif // BEAM
}

void MlpParser::revise(SentenceReader* sentenceReader, char const* actionFile)
{}

} // namespace Parser

void Register_MLP_Parser()
{
 Parser::ParserMap __dummy("MLP", Parser::MlpParserFactory );
}

max_forks_repo_name: qwazer/GrammarEngine


text:
/*=============================================================================
    Copyright (c) 2006 Eric Niebler

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
==============================================================================*/
#if !defined(FUSION_FOR_EACH_S_05022006_1027)
#define FUSION_FOR_EACH_S_05022006_1027

#include <boost/mpl/assert.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/sequence/intrinsic/ext_/segments.hpp>
#include <boost/fusion/support/ext_/is_segmented.hpp>

// fwd declarations
namespace boost { namespace fusion
{
    template <typename Sequence, typename F>
    void
    for_each_s(Sequence& seq, F const& f);

    template <typename Sequence, typename F>
    void
    for_each_s(Sequence const& seq, F const& f);
}}

namespace boost { namespace fusion { namespace detail
{
    template<typename F>
    struct for_each_s_bind
    {
        explicit for_each_s_bind(F const &f)
          : f_(f)
        {}

        template<typename Sequence>
        void operator ()(Sequence &seq) const
        {
            fusion::for_each_s(seq, this->f_);
        }

        template<typename Sequence>
        void operator ()(Sequence const &seq) const
        {
            fusion::for_each_s(seq, this->f_);
        }
    private:
        F const &f_;
    };

    template<typename Sequence, typename F>
    void for_each_s(Sequence &seq, F const &f, mpl::true_)
    {
        fusion::for_each_s(fusion::segments(seq), for_each_s_bind<F>(f));
    }

    template<typename Sequence, typename F>
    void for_each_s(Sequence &seq, F const &f, mpl::false_)
    {
        fusion::for_each(seq, f);
    }
}}}

namespace boost { namespace fusion
{
    namespace result_of
    {
        template <typename Sequence, typename F>
        struct for_each_s
        {
            typedef void type;
        };
    }

    template <typename Sequence, typename F>
    inline void
    for_each_s(Sequence& seq, F const& f)
    {
        detail::for_each_s(seq, f, traits::is_segmented<Sequence>());
    }

    template <typename Sequence, typename F>
    inline void
    for_each_s(Sequence const& seq, F const& f)
    {
        detail::for_each_s(seq, f, traits::is_segmented<Sequence>());
    }
}}

#endif

ext: hpp

max_stars_repo_name: dyzmapl/BumpTop

max_issues_repo_name: foxostro/CheeseTesseract

max_forks_repo_name: foxostro/CheeseTesseract


text:
#pragma once

#include <boost/enable_shared_from_this.hpp>
#include <boost/filesystem.hpp>

#include <deque>
#include <thread>
#include <time.h>

namespace filemonitor {

static const uint16_t POLLING_DELAY = 200;

class file_monitor_impl :
public boost::enable_shared_from_this<file_monitor_impl>
{

public:
	file_monitor_impl()
	: run_(true),
	work_thread_( &file_monitor_impl::work_thread, this )
	{}

	~file_monitor_impl()
	{
		// The work thread is stopped and joined.
		stop_work_thread();
		work_thread_.join();
	}

	void add_file( const boost::filesystem::path &path )
	{
		std::lock_guard<std::mutex> lock( add_remove_mutex_ );
		add_queue_.push_back( path );
	}

	void remove_file( const boost::filesystem::path &path )
	{
		std::lock_guard<std::mutex> lock( add_remove_mutex_ );
		remove_queue_.push_back( path );
	}

	void destroy()
	{
		std::lock_guard<std::mutex> lock( events_mutex_ );
		run_ = false;
		events_cond_.notify_all();
	}

	file_monitor_event popfront_event( boost::system::error_code &ec )
	{
		std::unique_lock<std::mutex> lock( events_mutex_ );
		while( run_ && events_.empty() ) {
			events_cond_.wait( lock );
		}
		file_monitor_event ev;
		if( ! events_.empty() ) {
			ec = boost::system::error_code();
			ev = events_.front();
			events_.pop_front();
		} else {
			ec = boost::asio::error::operation_aborted;
		}
		return ev;
	}

	void pushback_event( const file_monitor_event &ev )
	{
		std::lock_guard<std::mutex> lock( events_mutex_ );
		if( run_ ) {
			events_.push_back( ev );
			events_cond_.notify_all();
		}
	}

private:

	void work_thread()
	{
		while( running() ) {

			// deal with removes
			{
				std::lock_guard<std::mutex> lock( add_remove_mutex_ );
				for( const auto& name : remove_queue_ ) {
					file_timestamps_.erase( name );
				}
				remove_queue_.clear();
			}

			// deal with adds
			{
				std::lock_guard<std::mutex> lock( add_remove_mutex_ );
				for( const auto& file : add_queue_ ) {
					// overwrite existing items to re-fresh the timestamp
					file_timestamps_[file] = boost::filesystem::last_write_time( file );
				}
				add_queue_.clear();
			}

			// process timestamps
			for( const auto& it : file_timestamps_ ) {
				// check if it still exists
				if( ! boost::filesystem::exists( it.first ) ) {
					// renamed or deleted, assume deleted
					pushback_event( file_monitor_event( it.first, file_monitor_event::remove ) );
					{
						std::lock_guard<std::mutex> lock( add_remove_mutex_ );
						remove_queue_.push_back( it.first );
					}
				} else if ( boost::filesystem::last_write_time( it.first ) != it.second ) {
					// file modified/written
					pushback_event( file_monitor_event( it.first, file_monitor_event::write ) );
					file_timestamps_[it.first] = boost::filesystem::last_write_time( it.first );
				}
			}

			std::this_thread::sleep_for( std::chrono::milliseconds( POLLING_DELAY ) );
		}
	}

	bool running()
	{
		// Access to run_ is sychronized with stop_work_thread().
		std::lock_guard<std::mutex> lock( work_thread_mutex_ );
		return run_;
	}

	void stop_work_thread()
	{
		// Access to run_ is sychronized with running().
		std::lock_guard<std::mutex> lock( work_thread_mutex_ );
		run_ = false;
	}

	bool run_;
	std::mutex work_thread_mutex_;
	std::thread work_thread_;

	// filepath -> timestamp
	std::map<boost::filesystem::path, std::time_t> file_timestamps_;

	// for adding and removing outside of the worker thread
	std::mutex add_remove_mutex_;
	std::deque<boost::filesystem::path> add_queue_;
	std::deque<boost::filesystem::path> remove_queue_;

	std::mutex events_mutex_;
	std::condition_variable events_cond_;
	std::deque< file_monitor_event > events_;
};

} // asio namespace

max_stars_repo_name: lucasvickers/cinder-asio-file-monitor

max_issues_repo_name: lucasvickers/Cinder-FileMonitor

max_forks_repo_name: lucasvickers/cinder-asio-file-monitor


text:
#include <petuum_ps_common/util/lock.hpp>
#include <cassert>
#include <boost/thread.hpp>   // pthread status code
#include <pthread.h>

namespace petuum {

// ==================== SharedMutex ===================

SharedMutex::SharedMutex() {
  int status = pthread_rwlock_init(&rw_lock_, NULL);
  assert(status == 0);
}

SharedMutex::~SharedMutex() {
  int status = pthread_rwlock_destroy(&rw_lock_);
  assert(status == 0);
}

void SharedMutex::lock() {
  int status = pthread_rwlock_wrlock(&rw_lock_);
  assert(status == 0);
}

bool SharedMutex::try_lock() {
  int status = pthread_rwlock_trywrlock(&rw_lock_);

  switch (status) {
    case 0:
      return true;

    case EBUSY:
      return false;

    case EDEADLK:
    default:
      assert(false);
  }
}

// glibc seems to be buggy ... don't unlock more often
// than it has been locked
// http://sourceware.org/bugzilla/show_bug.cgi?id=4825
void SharedMutex::unlock() {
  int status = pthread_rwlock_unlock(&rw_lock_);
  assert(status == 0);
}

void SharedMutex::lock_shared() {
  int status = pthread_rwlock_rdlock(&rw_lock_);
  assert(status == 0);
}

bool SharedMutex::try_lock_shared() {
  int status = pthread_rwlock_tryrdlock(&rw_lock_);

  if (status == 0)
    return true;
  if (status == EBUSY)
    return false;

  assert(false);
}

void SharedMutex::unlock_shared() {
  unlock();
}

// ==================== RecursiveSharedMutex ===================

RecursiveSharedMutex::RecursiveSharedMutex()
  : write_lock_count_(0), writer_id(0) { }

RecursiveSharedMutex::~RecursiveSharedMutex() {
  assert(writer_id == 0);
}

void RecursiveSharedMutex::lock() {
  int status = pthread_rwlock_wrlock(&rw_lock_);

  switch (status) {
    case 0:
      writer_id = pthread_self();
      assert(write_lock_count_ == 0);

    case EDEADLK:
      assert(writer_id == pthread_self());
      ++write_lock_count_;
      return;

    default:
      assert(false);
  }
}

bool RecursiveSharedMutex::try_lock() {
  int status = pthread_rwlock_trywrlock(&rw_lock_);

  switch (status) {
    case 0:
      assert(writer_id == 0);
      assert(write_lock_count_ == 0);
      writer_id = pthread_self();
      ++write_lock_count_;
      return true;

    case EDEADLK:
      assert(writer_id == pthread_self());

    case EBUSY:
      if (writer_id == pthread_self())
      {
        assert(write_lock_count_ > 0);
        ++write_lock_count_;
        return true;
      }
      else
      {
        assert(writer_id != pthread_self());
        return false;
      }

    default:
      assert(false);
  }
}

void RecursiveSharedMutex::unlock() {
  assert(write_lock_count_ > 0);
  assert(writer_id);
  if (--write_lock_count_ == 0) {
    writer_id = 0;
    SharedMutex::unlock();
  }
}

void RecursiveSharedMutex::lock_shared() {
  if (writer_id == pthread_self())
    // we're already owning it as writer
    return;
  SharedMutex::lock_shared();
}

bool RecursiveSharedMutex::try_lock_shared() {
  if (writer_id == pthread_self())
    // we're already owning it as writer
    return true;

  return SharedMutex::try_lock_shared();
}

void RecursiveSharedMutex::unlock_shared() {
  if (writer_id == pthread_self())
    // we're already owning it as writer
    return;

  int status = pthread_rwlock_unlock(&rw_lock_);
  assert(status == 0);
}

}   // namespace petuum


text:
//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2006-2009. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// See http://www.boost.org/libs/interprocess for documentation.
//
//////////////////////////////////////////////////////////////////////////////
#include <boost/interprocess/detail/config_begin.hpp>
#include <boost/interprocess/detail/workaround.hpp>
//[doc_move_containers
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <cassert>
//<-
#include "../test/get_process_id_name.hpp"
//->

int main ()
{
   using namespace boost::interprocess;

   //Typedefs
   typedef managed_shared_memory::segment_manager     SegmentManager;
   typedef allocator<char, SegmentManager>            CharAllocator;
   typedef basic_string<char, std::char_traits<char>
                        ,CharAllocator>                MyShmString;
   typedef allocator<MyShmString, SegmentManager>     StringAllocator;
   typedef vector<MyShmString, StringAllocator>       MyShmStringVector;

   //Remove shared memory on construction and destruction
   struct shm_remove
   {
   //<-
   #if 1
      shm_remove() { shared_memory_object::remove(test::get_process_id_name()); }
      ~shm_remove(){ shared_memory_object::remove(test::get_process_id_name()); }
   #else
   //->
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   //<-
   #endif
   //->
   } remover;

   //<-
   #if 1
   managed_shared_memory shm(create_only, test::get_process_id_name(), 65536);
   #else
   //->
   managed_shared_memory shm(create_only, "MySharedMemory", 10000);
   //<-
   #endif
   //->

   //Create allocators
   CharAllocator     charallocator  (shm.get_segment_manager());
   StringAllocator   stringallocator(shm.get_segment_manager());

   //Create a vector of strings in shared memory.
   MyShmStringVector *myshmvector =
      shm.construct<MyShmStringVector>("myshmvector")(stringallocator);

   //Insert 50 strings in shared memory. The strings will be allocated
   //only once and no string copy-constructor will be called when inserting
   //strings, leading to a great performance.
   MyShmString string_to_compare(charallocator);
   string_to_compare = "this is a long, long, long, long, long, long, string...";

   myshmvector->reserve(50);
   for(int i = 0; i < 50; ++i){
      MyShmString move_me(string_to_compare);
      //In the following line, no string copy-constructor will be called.
      //"move_me"'s contents will be transferred to the string created in
      //the vector
      myshmvector->push_back(boost::interprocess::move(move_me));

      //The source string is in default constructed state
      assert(move_me.empty());

      //The newly created string will be equal to the "move_me"'s old contents
      assert(myshmvector->back() == string_to_compare);
   }

   //Now erase a string...
   myshmvector->pop_back();

   //...And insert one in the first position.
   //No string copy-constructor or assignments will be called, but
   //move constructors and move-assignments. No memory allocation
   //function will be called in this operations!!
   myshmvector->insert(myshmvector->begin(), boost::interprocess::move(string_to_compare));

   //Destroy vector. This will free all strings that the vector contains
   shm.destroy_ptr(myshmvector);
   return 0;
}
//]
#include <boost/interprocess/detail/config_end.hpp>

max_issues_repo_name: boost-cmake/vintage

max_forks_repo_name: boost-cmake/vintage


text:
/**********************************************************************
 *  Copyright (c) 2008-2014, Alliance for Sustainable Energy.
 *  All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library 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 GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 **********************************************************************/

#ifndef OPENSTUDIO_MAINRIGHTCOLUMNCONTROLLER_HPP
#define OPENSTUDIO_MAINRIGHTCOLUMNCONTROLLER_HPP

#include "../shared_gui_components/OSQObjectController.hpp"
#include <boost/smart_ptr.hpp>
#include "../model/Model.hpp"
#include "../shared_gui_components/EditController.hpp"
#include "../shared_gui_components/LocalLibraryController.hpp"

#include <QSharedPointer>

class QStackedWidget;

namespace openstudio {

class HorizontalTabWidget;
class InspectorController;
class LocalLibraryController;
class EditController;
class SystemItem;

class MainRightColumnController : public OSQObjectController
{
  Q_OBJECT

  public:

  MainRightColumnController(const model::Model & model, const openstudio::path &resourcesPath);

  virtual ~MainRightColumnController() {};

  HorizontalTabWidget * mainRightColumnView() const;

  QSharedPointer<LocalLibraryController> measureLibraryController();

  void hideMyModelTab(bool hide);

  bool isMyModelTabHidden(){return m_myModelTabIsHidden;}

  enum RightColumnTabID
  {
    MY_MODEL,
    LIBRARY,
    EDIT
  };

  // Methods to store and retrieve SystemItem objects.
  // These are used to get plenum colors and potentially other tasks.
  void registerSystemItem(const Handle & systemHandle, SystemItem * systemItem);
  void unregisterSystemItem(const Handle & systemHandle);
  SystemItem * systemItem(const Handle & systemHandle) const;

  signals:

  void toggleUnitsClicked(bool displayIP);

  public slots:

  void configureForSiteSubTab(int subTabID);

  void configureForSchedulesSubTab(int subTabID);

  void configureForConstructionsSubTab(int subTabID);

  void configureForLoadsSubTab(int subTabID);

  void configureForSpaceTypesSubTab(int subTabID);

  void configureForBuildingStoriesSubTab(int subTabID);

  void configureForFacilitySubTab(int subTabID);

  void configureForThermalZonesSubTab(int subTabID);

  void configureForHVACSystemsSubTab(int subTabID);

  void configureForBuildingSummarySubTab(int subTabID);

  void configureForOutputVariablesSubTab(int subTabID);

  void configureForSimulationSettingsSubTab(int subTabID);

  void configureForScriptsSubTab(int subTabID);

  void configureForRunSimulationSubTab(int subTabID);

  void configureForResultsSummarySubTab(int subTabID);

  void inspectModelObject(model::OptionalModelObject & modelObject, bool readOnly);

  void toggleUnits(bool displayIP);

  void chooseEditTab();

  QSharedPointer<LocalLibraryController> measuresLibraryController();

  QSharedPointer<EditController> measuresEditController();

  private:
  REGISTER_LOGGER("openstudio.openstudio_lib.MainRightColumnController");

  void setMyModelView(QWidget * widget);

  void setLibraryView(QWidget * widget);

  void setEditView(QWidget * widget);

  HorizontalTabWidget * m_horizontalTabWidget;

  std::shared_ptr<InspectorController> m_inspectorController;

  QStackedWidget * m_myModelView;

  QStackedWidget * m_libraryView;

  QStackedWidget * m_editView;

  std::map<Handle,SystemItem *> m_systemItemMap;

  model::Model m_model;

  openstudio::path m_resourcesPath;

  QSharedPointer<LocalLibraryController> m_measureLibraryController;

  QSharedPointer<EditController> m_measureEditController;

  bool m_myModelTabIsHidden;
};

} // openstudio

#endif // OPENSTUDIO_MAINRIGHTCOLUMNCONTROLLER_HPP