<commit_before>// ------------------------------------------------------------------
// pion-net: a C++ framework for building lightweight HTTP interfaces
// ------------------------------------------------------------------
// Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
//
// Distributed under the Boost Software License, Version 1.0.
// See http://www.boost.org/LICENSE_1_0.txt
//

#ifndef __PION_HTTPREQUEST_HEADER__
#define __PION_HTTPREQUEST_HEADER__

#include <boost/shared_ptr.hpp>
#include <pion/PionConfig.hpp>
#include <pion/net/HTTPMessage.hpp>
#include <pion/net/PionUser.hpp>

namespace pion {	// begin namespace pion
namespace net {		// begin namespace net (Pion Network Library)

///
/// HTTPRequest: container for HTTP request information
///
class HTTPRequest
	: public HTTPMessage
{
public:

	/**
	 * constructs a new HTTPRequest object
	 *
	 * @param resource the HTTP resource to request
	 */
	HTTPRequest(const std::string& resource)
		: m_method(REQUEST_METHOD_GET), m_resource(resource) {}

	/// constructs a new HTTPRequest object (default constructor)
	HTTPRequest(void) : m_method(REQUEST_METHOD_GET) {}

	/// virtual destructor
	virtual ~HTTPRequest() {}

	/// clears all request data
	virtual void clear(void) {
		HTTPMessage::clear();
		m_method.erase();
		m_resource.erase();
		m_query_string.erase();
		m_query_params.clear();
		m_cookie_params.clear();
		m_user_record.reset();
		m_charset.clear();
	}

	/// the content length of the message can never be implied for requests
	virtual bool isContentLengthImplied(void) const { return false; }

	/// returns the request method (i.e. GET, POST, PUT)
	inline const std::string& getMethod(void) const { return m_method; }

	/// returns the resource uri-stem to be delivered (possibly the result of a redirect)
	inline const std::string& getResource(void) const { return m_resource; }

	/// returns the resource uri-stem originally requested
	inline const std::string& getOriginalResource(void) const { return m_original_resource; }

	/// returns the uri-query or query string requested
	inline const std::string& getQueryString(void) const { return m_query_string; }

	/// returns a value for the query key if any are defined; otherwise, an empty string
	inline const std::string& getQuery(const std::string& key) const {
		return getValue(m_query_params, key);
	}

	/// returns a value for the cookie if any are defined; otherwise, an empty string
	/// since cookie names are insensitive, key should use lowercase alpha chars
	inline const std::string& getCookie(const std::string& key) const {
		return getValue(m_cookie_params, key);
	}

	/// returns the query parameters
	inline QueryParams& getQueryParams(void) {
		return m_query_params;
	}

	/// returns the cookie parameters
	inline CookieParams& getCookieParams(void) {
		return m_cookie_params;
	}

	/// returns true if at least one value for the query key is defined
	inline bool hasQuery(const std::string& key) const {
		return(m_query_params.find(key) != m_query_params.end());
	}

	/// returns true if at least one value for the cookie is defined
	/// since cookie names are insensitive, key should use lowercase alpha chars
	inline bool hasCookie(const std::string& key) const {
		return(m_cookie_params.find(key) != m_cookie_params.end());
	}

	/// sets the HTTP request method (i.e. GET, POST, PUT)
	inline void setMethod(const std::string& str) {
		m_method = str;
		clearFirstLine();
	}

	/// sets the resource or uri-stem originally requested
	inline void setResource(const std::string& str) {
		m_resource = m_original_resource = str;
		clearFirstLine();
	}

	/// changes the resource or uri-stem to be delivered (called as the result of a redirect)
	inline void changeResource(const std::string& str) { m_resource = str; }

	/// sets the uri-query or query string requested
	inline void setQueryString(const std::string& str) {
		m_query_string = str;
		clearFirstLine();
	}

	/// adds a value for the query key
	inline void addQuery(const std::string& key, const std::string& value) {
		m_query_params.insert(std::make_pair(key, value));
	}

	/// changes the value of a query key
	inline void changeQuery(const std::string& key, const std::string& value) {
		changeValue(m_query_params, key, value);
	}

	/// removes all values for a query key
	inline void deleteQuery(const std::string& key) {
		deleteValue(m_query_params, key);
	}

	/// use the query parameters to build a query string for the request
	inline void useQueryParamsForQueryString(void) {
		setQueryString(make_query_string(m_query_params));
	}

	/// use the query parameters to build POST content for the request
	inline void useQueryParamsForPostContent(void) {
		std::string post_content(make_query_string(m_query_params));
		setContentLength(post_content.size());
		char *ptr = createContentBuffer();	// null-terminates buffer
		if (! post_content.empty())
			memcpy(ptr, post_content.c_str(), post_content.size());
		setMethod(REQUEST_METHOD_POST);
		setContentType(CONTENT_TYPE_URLENCODED);
	}

	/// adds a value for the cookie
	/// since cookie names are insensitive, key should use lowercase alpha chars
	inline void addCookie(const std::string& key, const std::string& value) {
		m_cookie_params.insert(std::make_pair(key, value));
	}

	/// changes the value of a cookie
	/// since cookie names are insensitive, key should use lowercase alpha chars
	inline void changeCookie(const std::string& key, const std::string& value) {
		changeValue(m_cookie_params, key, value);
	}

	/// removes all values for a cookie
	/// since cookie names are insensitive, key should use lowercase alpha chars
	inline void deleteCookie(const std::string& key) {
		deleteValue(m_cookie_params, key);
	}

	/// sets the user record for HTTP request after authentication
	inline void setUser(PionUserPtr user) { m_user_record = user; }

	/// get the user record for HTTP request after authentication
	inline PionUserPtr getUser() const { return m_user_record; }

protected:

	/// updates the string containing the first line for the HTTP message
	virtual void updateFirstLine(void) const {
		// start out with the request method
		m_first_line = m_method;
		m_first_line += ' ';
		// append the resource requested
		m_first_line += m_resource;
		if (! m_query_string.empty()) {
			// append query string if not empty
			m_first_line += '?';
			m_first_line += m_query_string;
		}
		m_first_line += ' ';
		// append HTTP version
		m_first_line += getVersionString();
	}

private:

	/// request method (GET, POST, PUT, etc.)
	std::string						m_method;

	/// name of the resource or uri-stem to be delivered
	std::string						m_resource;

	/// name of the resource or uri-stem originally requested
	std::string						m_original_resource;

	/// query string portion of the URI
	std::string						m_query_string;

	/// HTTP query parameters parsed from the request line and post content
	QueryParams						m_query_params;

	/// HTTP cookie parameters parsed from the "Cookie" request headers
	CookieParams					m_cookie_params;

	/// pointer to PionUser record if this request had been authenticated
	PionUserPtr						m_user_record;

	/// charset value from
	/// Content-Type: application/x-www-form-urlencoded; charset=<VALUE>
	std::string						m_charset;
};

/// data type for a HTTP request pointer
typedef boost::shared_ptr<HTTPRequest>		HTTPRequestPtr;

}	// end namespace net
}	// end namespace pion

#endif
<commit_msg>Updated clear() function to clear all members<commit_after>// ------------------------------------------------------------------
// pion-net: a C++ framework for building lightweight HTTP interfaces
// ------------------------------------------------------------------
// Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
//
// Distributed under the Boost Software License, Version 1.0.
// See http://www.boost.org/LICENSE_1_0.txt
//

#ifndef __PION_HTTPREQUEST_HEADER__
#define __PION_HTTPREQUEST_HEADER__

#include <boost/shared_ptr.hpp>
#include <pion/PionConfig.hpp>
#include <pion/net/HTTPMessage.hpp>
#include <pion/net/PionUser.hpp>

namespace pion {	// begin namespace pion
namespace net {		// begin namespace net (Pion Network Library)

///
/// HTTPRequest: container for HTTP request information
///
class HTTPRequest
	: public HTTPMessage
{
public:

	/**
	 * constructs a new HTTPRequest object
	 *
	 * @param resource the HTTP resource to request
	 */
	HTTPRequest(const std::string& resource)
		: m_method(REQUEST_METHOD_GET), m_resource(resource) {}

	/// constructs a new HTTPRequest object (default constructor)
	HTTPRequest(void) : m_method(REQUEST_METHOD_GET) {}

	/// virtual destructor
	virtual ~HTTPRequest() {}

	/// clears all request data
	virtual void clear(void) {
		HTTPMessage::clear();
		m_method.erase();
		m_resource.erase();
		m_original_resource.erase();
		m_query_string.erase();
		m_query_params.clear();
		m_cookie_params.clear();
		m_user_record.reset();
		m_charset.clear();
	}

	/// the content length of the message can never be implied for requests
	virtual bool isContentLengthImplied(void) const { return false; }

	/// returns the request method (i.e. GET, POST, PUT)
	inline const std::string& getMethod(void) const { return m_method; }

	/// returns the resource uri-stem to be delivered (possibly the result of a redirect)
	inline const std::string& getResource(void) const { return m_resource; }

	/// returns the resource uri-stem originally requested
	inline const std::string& getOriginalResource(void) const { return m_original_resource; }

	/// returns the uri-query or query string requested
	inline const std::string& getQueryString(void) const { return m_query_string; }

	/// returns a value for the query key if any are defined; otherwise, an empty string
	inline const std::string& getQuery(const std::string& key) const {
		return getValue(m_query_params, key);
	}

	/// returns a value for the cookie if any are defined; otherwise, an empty string
	/// since cookie names are insensitive, key should use lowercase alpha chars
	inline const std::string& getCookie(const std::string& key) const {
		return getValue(m_cookie_params, key);
	}

	/// returns the query parameters
	inline QueryParams& getQueryParams(void) {
		return m_query_params;
	}

	/// returns the cookie parameters
	inline CookieParams& getCookieParams(void) {
		return m_cookie_params;
	}

	/// returns true if at least one value for the query key is defined
	inline bool hasQuery(const std::string& key) const {
		return(m_query_params.find(key) != m_query_params.end());
	}

	/// returns true if at least one value for the cookie is defined
	/// since cookie names are insensitive, key should use lowercase alpha chars
	inline bool hasCookie(const std::string& key) const {
		return(m_cookie_params.find(key) != m_cookie_params.end());
	}

	/// sets the HTTP request method (i.e. GET, POST, PUT)
	inline void setMethod(const std::string& str) {
		m_method = str;
		clearFirstLine();
	}

	/// sets the resource or uri-stem originally requested
	inline void setResource(const std::string& str) {
		m_resource = m_original_resource = str;
		clearFirstLine();
	}

	/// changes the resource or uri-stem to be delivered (called as the result of a redirect)
	inline void changeResource(const std::string& str) { m_resource = str; }

	/// sets the uri-query or query string requested
	inline void setQueryString(const std::string& str) {
		m_query_string = str;
		clearFirstLine();
	}

	/// adds a value for the query key
	inline void addQuery(const std::string& key, const std::string& value) {
		m_query_params.insert(std::make_pair(key, value));
	}

	/// changes the value of a query key
	inline void changeQuery(const std::string& key, const std::string& value) {
		changeValue(m_query_params, key, value);
	}

	/// removes all values for a query key
	inline void deleteQuery(const std::string& key) {
		deleteValue(m_query_params, key);
	}

	/// use the query parameters to build a query string for the request
	inline void useQueryParamsForQueryString(void) {
		setQueryString(make_query_string(m_query_params));
	}

	/// use the query parameters to build POST content for the request
	inline void useQueryParamsForPostContent(void) {
		std::string post_content(make_query_string(m_query_params));
		setContentLength(post_content.size());
		char *ptr = createContentBuffer();	// null-terminates buffer
		if (! post_content.empty())
			memcpy(ptr, post_content.c_str(), post_content.size());
		setMethod(REQUEST_METHOD_POST);
		setContentType(CONTENT_TYPE_URLENCODED);
	}

	/// adds a value for the cookie
	/// since cookie names are insensitive, key should use lowercase alpha chars
	inline void addCookie(const std::string& key, const std::string& value) {
		m_cookie_params.insert(std::make_pair(key, value));
	}

	/// changes the value of a cookie
	/// since cookie names are insensitive, key should use lowercase alpha chars
	inline void changeCookie(const std::string& key, const std::string& value) {
		changeValue(m_cookie_params, key, value);
	}

	/// removes all values for a cookie
	/// since cookie names are insensitive, key should use lowercase alpha chars
	inline void deleteCookie(const std::string& key) {
		deleteValue(m_cookie_params, key);
	}

	/// sets the user record for HTTP request after authentication
	inline void setUser(PionUserPtr user) { m_user_record = user; }

	/// get the user record for HTTP request after authentication
	inline PionUserPtr getUser() const { return m_user_record; }

protected:

	/// updates the string containing the first line for the HTTP message
	virtual void updateFirstLine(void) const {
		// start out with the request method
		m_first_line = m_method;
		m_first_line += ' ';
		// append the resource requested
		m_first_line += m_resource;
		if (! m_query_string.empty()) {
			// append query string if not empty
			m_first_line += '?';
			m_first_line += m_query_string;
		}
		m_first_line += ' ';
		// append HTTP version
		m_first_line += getVersionString();
	}

private:

	/// request method (GET, POST, PUT, etc.)
	std::string						m_method;

	/// name of the resource or uri-stem to be delivered
	std::string						m_resource;

	/// name of the resource or uri-stem originally requested
	std::string						m_original_resource;

	/// query string portion of the URI
	std::string						m_query_string;

	/// HTTP query parameters parsed from the request line and post content
	QueryParams						m_query_params;

	/// HTTP cookie parameters parsed from the "Cookie" request headers
	CookieParams					m_cookie_params;

	/// pointer to PionUser record if this request had been authenticated
	PionUserPtr						m_user_record;

	/// charset value from
	/// Content-Type: application/x-www-form-urlencoded; charset=<VALUE>
	std::string						m_charset;
};

/// data type for a HTTP request pointer
typedef boost::shared_ptr<HTTPRequest>		HTTPRequestPtr;

}	// end namespace net
}	// end namespace pion

#endif
<|endoftext|>


<commit_before>// @(#)root/net:$Id$
// Author: Philippe Canal October 2011.

/*************************************************************************
 * Copyright (C) 1995-2011, Rene Brun, Fons Rademakers and al.           *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TParallelMergingFile                                                 //
//                                                                      //
// Specialization of TMemFile to connect to a parallel file merger.     //
// Upon a call to UploadAndReset, the content already written to the    //
// file is upload to the server and the object implementing the function//
// ResetAfterMerge (like TTree) are reset.                              //
// The parallel file merger will then collate the information coming    //
// from this client and any other client in to the file described by    //
// the filename of this object.                                         //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TParallelMergingFile.h"
#include "TSocket.h"
#include "TArrayC.h"

//______________________________________________________________________________
TParallelMergingFile::TParallelMergingFile(const char *filename, Option_t *option /* = "" */,
                                           const char *ftitle /* = "" */, Int_t compress /* = 1 */) :
   TMemFile(filename,option,ftitle,compress),fSocket(0),fServerIdx(-1),fServerVersion(0),fClassSent(0),fMessage(kMESS_OBJECT)
{
   // Constructor.
   // We do no yet open any connection to the server.  This will be done at the
   // time the first upload will be requested.

   TString serverurl = strstr(fUrl.GetOptions(),"pmerge=");
   if (serverurl.Length()) {
      serverurl.ReplaceAll("pmerge=","pmerge://");
      fServerLocation = TUrl(serverurl);
   }
}

//______________________________________________________________________________
TParallelMergingFile::~TParallelMergingFile()
{
   // Destructor.

   // We need to call Close, right here so that it is executed _before_
   // the data member of TParallelMergingFile are destructed.
   Close();
   delete fClassSent;
}

//______________________________________________________________________________
void TParallelMergingFile::Close(Option_t *option)
{
   TMemFile::Close(option);
   if (fSocket) {
      if (0==fSocket->Send("Finished")) {          // tell server we are finished
         Warning("Close","Failed to send the finishing message to the server %s:%d",fServerLocation.GetHost(),fServerLocation.GetPort());
      }
      fSocket->Close();
      delete fSocket;
   }
   fSocket = 0;
}

//______________________________________________________________________________
Bool_t TParallelMergingFile::UploadAndReset()
{
   // Upload the current file data to the merging server.
   // Reset the file and return true in case of success.

   // Open connection to server
   if (fSocket == 0) {
      const char *host = fServerLocation.GetHost();
      Int_t port = fServerLocation.GetPort();
      if (host == 0 || host[0] == '\0') {
         host = "localhost";
      }
      if (port <= 0) {
         port = 1095;
      }
      fSocket = new TSocket(host,port);
      if (!fSocket->IsValid()) {
         Error("UploadAndReset","Could not contact the server %s:%d\n",host,port);
         delete fSocket;
         fSocket = 0;
         return kFALSE;
      }
      // Wait till we get the start message
      // server tells us who we are
      Int_t kind;
      Int_t n = fSocket->Recv(fServerIdx, kind);

      if (n < 0 && kind != 0 /* kStartConnection */)
      {
         Error("UploadAndReset","Unexpected server message: kind=%d idx=%d\n",kind,fServerIdx);
         delete fSocket;
         fSocket = 0;
         return kTRUE;
      }
      n = fSocket->Recv(fServerVersion, kind);
      if (n < 0 && kind != 1 /* kProtocol */)
      {
         Fatal("UploadAndReset","Unexpected server message: kind=%d status=%d\n",kind,fServerVersion);
      } else {
         Info("UploadAndReset","Connected to fastMergeServer version %d with index %d\n",fServerVersion,fServerIdx);
      }
      TMessage::EnableSchemaEvolutionForAll(kTRUE);
   }

   fMessage.Reset(kMESS_ANY); // re-use TMessage object
   fMessage.WriteInt(fServerIdx);
   fMessage.WriteTString(GetName());
   fMessage.WriteLong64(GetEND());
   CopyTo(fMessage);

   if (int error = fSocket->Send(fMessage) <= 0) {
      Error("UploadAndReset","Upload to the merging server failed with %d\n",error);
      delete fSocket;
      fSocket = 0;
      return kFALSE;
   }

   // Record the StreamerInfo we sent over.
   Int_t isize = fClassIndex->GetSize();
   if (!fClassSent) {
      fClassSent = new TArrayC(isize);
   } else {
      if (isize > fClassSent->GetSize()) {
         fClassSent->Set(isize);
      }
   }
   for(Int_t c = 0; c < isize; ++c) {
      if (fClassIndex->fArray[c]) {
         fClassSent->fArray[c] = 1;
      }
   }
   ResetAfterMerge(0);

   return kTRUE;
}

//______________________________________________________________________________
Int_t TParallelMergingFile::Write(const char *, Int_t opt, Int_t bufsiz)
{
   // Write memory objects to this file and upload them to the parallel merge server.
   // Then reset all the resetable object (those with a ResetAfterMerge routine,
   // like TTree).
   //
   // Loop on all objects in memory (including subdirectories).
   // A new key is created in the KEYS linked list for each object.
   // The list of keys is then saved on the file (via WriteKeys)
   // as a single data record.
   // For values of opt see TObject::Write().
   // The directory header info is rewritten on the directory header record.
   // The linked list of FREE segments is written.
   // The file header is written (bytes 1->fBEGIN).

   Int_t nbytes = TMemFile::Write(0,opt,bufsiz);
   if (nbytes) {
      UploadAndReset();
   }
   return nbytes;
}

//______________________________________________________________________________
Int_t TParallelMergingFile::Write(const char *n, Int_t opt, Int_t bufsize) const
{
   // One can not save a const TDirectory object.

   Error("Write const","A const TFile object should not be saved. We try to proceed anyway.");
   return const_cast<TParallelMergingFile*>(this)->Write(n, opt, bufsize);
}

//______________________________________________________________________________
void TParallelMergingFile::WriteStreamerInfo()
{
   // Write the list of TStreamerInfo as a single object in this file
   // The class Streamer description for all classes written to this file
   // is saved. See class TStreamerInfo.

   if (!fWritable) return;
   if (!fClassIndex) return;
   //no need to update the index if no new classes added to the file
   if (fClassIndex->fArray[0] == 0) return;

   // clear fClassIndex for anything we already sent.
   if (fClassSent) {
      Int_t isize = fClassIndex->GetSize();
      Int_t ssize = fClassSent->GetSize();
      for(Int_t c = 0; c < isize && c < ssize; ++c) {
         if (fClassSent->fArray[c]) {
            fClassIndex->fArray[c] = 0;
         }
      }
   }

   TMemFile::WriteStreamerInfo();
}<commit_msg>missing newline at eof<commit_after>// @(#)root/net:$Id$
// Author: Philippe Canal October 2011.

/*************************************************************************
 * Copyright (C) 1995-2011, Rene Brun, Fons Rademakers and al.           *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TParallelMergingFile                                                 //
//                                                                      //
// Specialization of TMemFile to connect to a parallel file merger.     //
// Upon a call to UploadAndReset, the content already written to the    //
// file is upload to the server and the object implementing the function//
// ResetAfterMerge (like TTree) are reset.                              //
// The parallel file merger will then collate the information coming    //
// from this client and any other client in to the file described by    //
// the filename of this object.                                         //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TParallelMergingFile.h"
#include "TSocket.h"
#include "TArrayC.h"

//______________________________________________________________________________
TParallelMergingFile::TParallelMergingFile(const char *filename, Option_t *option /* = "" */,
                                           const char *ftitle /* = "" */, Int_t compress /* = 1 */) :
   TMemFile(filename,option,ftitle,compress),fSocket(0),fServerIdx(-1),fServerVersion(0),fClassSent(0),fMessage(kMESS_OBJECT)
{
   // Constructor.
   // We do no yet open any connection to the server.  This will be done at the
   // time the first upload will be requested.

   TString serverurl = strstr(fUrl.GetOptions(),"pmerge=");
   if (serverurl.Length()) {
      serverurl.ReplaceAll("pmerge=","pmerge://");
      fServerLocation = TUrl(serverurl);
   }
}

//______________________________________________________________________________
TParallelMergingFile::~TParallelMergingFile()
{
   // Destructor.

   // We need to call Close, right here so that it is executed _before_
   // the data member of TParallelMergingFile are destructed.
   Close();
   delete fClassSent;
}

//______________________________________________________________________________
void TParallelMergingFile::Close(Option_t *option)
{
   TMemFile::Close(option);
   if (fSocket) {
      if (0==fSocket->Send("Finished")) {          // tell server we are finished
         Warning("Close","Failed to send the finishing message to the server %s:%d",fServerLocation.GetHost(),fServerLocation.GetPort());
      }
      fSocket->Close();
      delete fSocket;
   }
   fSocket = 0;
}

//______________________________________________________________________________
Bool_t TParallelMergingFile::UploadAndReset()
{
   // Upload the current file data to the merging server.
   // Reset the file and return true in case of success.

   // Open connection to server
   if (fSocket == 0) {
      const char *host = fServerLocation.GetHost();
      Int_t port = fServerLocation.GetPort();
      if (host == 0 || host[0] == '\0') {
         host = "localhost";
      }
      if (port <= 0) {
         port = 1095;
      }
      fSocket = new TSocket(host,port);
      if (!fSocket->IsValid()) {
         Error("UploadAndReset","Could not contact the server %s:%d\n",host,port);
         delete fSocket;
         fSocket = 0;
         return kFALSE;
      }
      // Wait till we get the start message
      // server tells us who we are
      Int_t kind;
      Int_t n = fSocket->Recv(fServerIdx, kind);

      if (n < 0 && kind != 0 /* kStartConnection */)
      {
         Error("UploadAndReset","Unexpected server message: kind=%d idx=%d\n",kind,fServerIdx);
         delete fSocket;
         fSocket = 0;
         return kTRUE;
      }
      n = fSocket->Recv(fServerVersion, kind);
      if (n < 0 && kind != 1 /* kProtocol */)
      {
         Fatal("UploadAndReset","Unexpected server message: kind=%d status=%d\n",kind,fServerVersion);
      } else {
         Info("UploadAndReset","Connected to fastMergeServer version %d with index %d\n",fServerVersion,fServerIdx);
      }
      TMessage::EnableSchemaEvolutionForAll(kTRUE);
   }

   fMessage.Reset(kMESS_ANY); // re-use TMessage object
   fMessage.WriteInt(fServerIdx);
   fMessage.WriteTString(GetName());
   fMessage.WriteLong64(GetEND());
   CopyTo(fMessage);

   if (int error = fSocket->Send(fMessage) <= 0) {
      Error("UploadAndReset","Upload to the merging server failed with %d\n",error);
      delete fSocket;
      fSocket = 0;
      return kFALSE;
   }

   // Record the StreamerInfo we sent over.
   Int_t isize = fClassIndex->GetSize();
   if (!fClassSent) {
      fClassSent = new TArrayC(isize);
   } else {
      if (isize > fClassSent->GetSize()) {
         fClassSent->Set(isize);
      }
   }
   for(Int_t c = 0; c < isize; ++c) {
      if (fClassIndex->fArray[c]) {
         fClassSent->fArray[c] = 1;
      }
   }
   ResetAfterMerge(0);

   return kTRUE;
}

//______________________________________________________________________________
Int_t TParallelMergingFile::Write(const char *, Int_t opt, Int_t bufsiz)
{
   // Write memory objects to this file and upload them to the parallel merge server.
   // Then reset all the resetable object (those with a ResetAfterMerge routine,
   // like TTree).
   //
   // Loop on all objects in memory (including subdirectories).
   // A new key is created in the KEYS linked list for each object.
   // The list of keys is then saved on the file (via WriteKeys)
   // as a single data record.
   // For values of opt see TObject::Write().
   // The directory header info is rewritten on the directory header record.
   // The linked list of FREE segments is written.
   // The file header is written (bytes 1->fBEGIN).

   Int_t nbytes = TMemFile::Write(0,opt,bufsiz);
   if (nbytes) {
      UploadAndReset();
   }
   return nbytes;
}

//______________________________________________________________________________
Int_t TParallelMergingFile::Write(const char *n, Int_t opt, Int_t bufsize) const
{
   // One can not save a const TDirectory object.

   Error("Write const","A const TFile object should not be saved. We try to proceed anyway.");
   return const_cast<TParallelMergingFile*>(this)->Write(n, opt, bufsize);
}

//______________________________________________________________________________
void TParallelMergingFile::WriteStreamerInfo()
{
   // Write the list of TStreamerInfo as a single object in this file
   // The class Streamer description for all classes written to this file
   // is saved. See class TStreamerInfo.

   if (!fWritable) return;
   if (!fClassIndex) return;
   //no need to update the index if no new classes added to the file
   if (fClassIndex->fArray[0] == 0) return;

   // clear fClassIndex for anything we already sent.
   if (fClassSent) {
      Int_t isize = fClassIndex->GetSize();
      Int_t ssize = fClassSent->GetSize();
      for(Int_t c = 0; c < isize && c < ssize; ++c) {
         if (fClassSent->fArray[c]) {
            fClassIndex->fArray[c] = 0;
         }
      }
   }

   TMemFile::WriteStreamerInfo();
}
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/url_request/view_cache_helper.h"

#include "base/string_util.h"
#include "net/base/escape.h"
#include "net/base/io_buffer.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/http_cache.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
#include "net/url_request/url_request_context.h"

#define VIEW_CACHE_HEAD \
  "
"

#define VIEW_CACHE_TAIL \
  "
"

static void HexDump(const char *buf, size_t buf_len, std::string* result) {
  const size_t kMaxRows = 16;
  int offset = 0;

  const unsigned char *p;
  while (buf_len) {
    StringAppendF(result, "%08x:  ", offset);
    offset += kMaxRows;

    p = (const unsigned char *) buf;

    size_t i;
    size_t row_max = std::min(kMaxRows, buf_len);

    // print hex codes:
    for (i = 0; i < row_max; ++i)
      StringAppendF(result, "%02x  ", *p++);
    for (i = row_max; i < kMaxRows; ++i)
      result->append("    ");

    // print ASCII glyphs if possible:
    p = (const unsigned char *) buf;
    for (i = 0; i < row_max; ++i, ++p) {
      if (*p < 0x7F && *p > 0x1F) {
        AppendEscapedCharForHTML(*p, result);
      } else {
        result->push_back('.');
      }
    }

    result->push_back('\n');

    buf += row_max;
    buf_len -= row_max;
  }
}

static std::string FormatEntryInfo(disk_cache::Entry* entry,
                                   const std::string& url_prefix) {
  std::string key = entry->GetKey();
  GURL url = GURL(url_prefix + key);
  std::string row =
      "
" + EscapeForHTML(key) +
      "
";
  return row;
}

static std::string FormatEntryDetails(disk_cache::Entry* entry) {
  std::string result = EscapeForHTML(entry->GetKey());

  net::HttpResponseInfo response;
  bool truncated;
  if (net::HttpCache::ReadResponseInfo(entry, &response, &truncated) &&
      response.headers) {
    result.append("

");
    result.append(EscapeForHTML(response.headers->GetStatusLine()));
    result.push_back('\n');

    void* iter = NULL;
    std::string name, value;
    while (response.headers->EnumerateHeaderLines(&iter, &name, &value)) {
      result.append(EscapeForHTML(name));
      result.append(": ");
      result.append(EscapeForHTML(value));
      result.push_back('\n');
    }
    result.append("
");
  }

  for (int i = 0; i < 2; ++i) {
    result.append("

");

    int data_size = entry->GetDataSize(i);

    if (data_size) {
      scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(data_size);
      if (entry->ReadData(i, 0, buffer, data_size, NULL) == data_size)
        HexDump(buffer->data(), data_size, &result);
    }

    result.append("
");
  }

  return result;
}

static disk_cache::Backend* GetDiskCache(URLRequestContext* context) {
  if (!context)
    return NULL;

  if (!context->http_transaction_factory())
    return NULL;

  net::HttpCache* http_cache = context->http_transaction_factory()->GetCache();
  if (!http_cache)
    return NULL;

  return http_cache->GetBackend();
}

static std::string FormatStatistics(disk_cache::Backend* disk_cache) {
  std::vector<std::pair<std::string, std::string> > stats;
  disk_cache->GetStats(&stats);
  std::string result;

  for (size_t index = 0; index < stats.size(); index++) {
    result.append(stats[index].first);
    result.append(": ");
    result.append(stats[index].second);
    result.append("
\n");
  }

  return result;
}

// static
void ViewCacheHelper::GetEntryInfoHTML(const std::string& key,
                                       URLRequestContext* context,
                                       const std::string& url_prefix,
                                       std::string* data) {
  disk_cache::Backend* disk_cache = GetDiskCache(context);
  if (!disk_cache) {
    data->assign("no disk cache");
    return;
  }

  if (key.empty()) {
    data->assign(VIEW_CACHE_HEAD);
    void* iter = NULL;
    disk_cache::Entry* entry;
    while (disk_cache->OpenNextEntry(&iter, &entry)) {
      data->append(FormatEntryInfo(entry, url_prefix));
      entry->Close();
    }
    data->append(VIEW_CACHE_TAIL);
  } else {
    disk_cache::Entry* entry;
    if (disk_cache->OpenEntry(key, &entry)) {
      data->assign(FormatEntryDetails(entry));
      entry->Close();
    } else {
      data->assign("no matching cache entry for: " + key);
    }
  }
}

// static
void ViewCacheHelper::GetStatisticsHTML(URLRequestContext* context,
                                        std::string* data) {
  disk_cache::Backend* disk_cache = GetDiskCache(context);
  if (!disk_cache) {
    data->append("no disk cache");
    return;
  }
  data->append(FormatStatistics(disk_cache));
}
<commit_msg>Disk cache: Add the name of the relevant flag to truncated entries on the view-cache details page.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/url_request/view_cache_helper.h"

#include "base/string_util.h"
#include "net/base/escape.h"
#include "net/base/io_buffer.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/http_cache.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
#include "net/url_request/url_request_context.h"

#define VIEW_CACHE_HEAD \
  "
"

#define VIEW_CACHE_TAIL \
  "
"

static void HexDump(const char *buf, size_t buf_len, std::string* result) {
  const size_t kMaxRows = 16;
  int offset = 0;

  const unsigned char *p;
  while (buf_len) {
    StringAppendF(result, "%08x:  ", offset);
    offset += kMaxRows;

    p = (const unsigned char *) buf;

    size_t i;
    size_t row_max = std::min(kMaxRows, buf_len);

    // print hex codes:
    for (i = 0; i < row_max; ++i)
      StringAppendF(result, "%02x  ", *p++);
    for (i = row_max; i < kMaxRows; ++i)
      result->append("    ");

    // print ASCII glyphs if possible:
    p = (const unsigned char *) buf;
    for (i = 0; i < row_max; ++i, ++p) {
      if (*p < 0x7F && *p > 0x1F) {
        AppendEscapedCharForHTML(*p, result);
      } else {
        result->push_back('.');
      }
    }

    result->push_back('\n');

    buf += row_max;
    buf_len -= row_max;
  }
}

static std::string FormatEntryInfo(disk_cache::Entry* entry,
                                   const std::string& url_prefix) {
  std::string key = entry->GetKey();
  GURL url = GURL(url_prefix + key);
  std::string row =
      "
" + EscapeForHTML(key) +
      "
";
  return row;
}

static std::string FormatEntryDetails(disk_cache::Entry* entry) {
  std::string result = EscapeForHTML(entry->GetKey());

  net::HttpResponseInfo response;
  bool truncated;
  if (net::HttpCache::ReadResponseInfo(entry, &response, &truncated) &&
      response.headers) {
    if (truncated)
      result.append("
RESPONSE_INFO_TRUNCATED
");

    result.append("

");
    result.append(EscapeForHTML(response.headers->GetStatusLine()));
    result.push_back('\n');

    void* iter = NULL;
    std::string name, value;
    while (response.headers->EnumerateHeaderLines(&iter, &name, &value)) {
      result.append(EscapeForHTML(name));
      result.append(": ");
      result.append(EscapeForHTML(value));
      result.push_back('\n');
    }
    result.append("
");
  }

  for (int i = 0; i < 2; ++i) {
    result.append("

");

    int data_size = entry->GetDataSize(i);

    if (data_size) {
      scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(data_size);
      if (entry->ReadData(i, 0, buffer, data_size, NULL) == data_size)
        HexDump(buffer->data(), data_size, &result);
    }

    result.append("
");
  }

  return result;
}

static disk_cache::Backend* GetDiskCache(URLRequestContext* context) {
  if (!context)
    return NULL;

  if (!context->http_transaction_factory())
    return NULL;

  net::HttpCache* http_cache = context->http_transaction_factory()->GetCache();
  if (!http_cache)
    return NULL;

  return http_cache->GetBackend();
}

static std::string FormatStatistics(disk_cache::Backend* disk_cache) {
  std::vector<std::pair<std::string, std::string> > stats;
  disk_cache->GetStats(&stats);
  std::string result;

  for (size_t index = 0; index < stats.size(); index++) {
    result.append(stats[index].first);
    result.append(": ");
    result.append(stats[index].second);
    result.append("
\n");
  }

  return result;
}

// static
void ViewCacheHelper::GetEntryInfoHTML(const std::string& key,
                                       URLRequestContext* context,
                                       const std::string& url_prefix,
                                       std::string* data) {
  disk_cache::Backend* disk_cache = GetDiskCache(context);
  if (!disk_cache) {
    data->assign("no disk cache");
    return;
  }

  if (key.empty()) {
    data->assign(VIEW_CACHE_HEAD);
    void* iter = NULL;
    disk_cache::Entry* entry;
    while (disk_cache->OpenNextEntry(&iter, &entry)) {
      data->append(FormatEntryInfo(entry, url_prefix));
      entry->Close();
    }
    data->append(VIEW_CACHE_TAIL);
  } else {
    disk_cache::Entry* entry;
    if (disk_cache->OpenEntry(key, &entry)) {
      data->assign(FormatEntryDetails(entry));
      entry->Close();
    } else {
      data->assign("no matching cache entry for: " + key);
    }
  }
}

// static
void ViewCacheHelper::GetStatisticsHTML(URLRequestContext* context,
                                        std::string* data) {
  disk_cache::Backend* disk_cache = GetDiskCache(context);
  if (!disk_cache) {
    data->append("no disk cache");
    return;
  }
  data->append(FormatStatistics(disk_cache));
}
<|endoftext|>


<commit_before>/*
 * GraphBuilder.cpp
 *
 *  Created on: 15.07.2014
 *      Author: Marvin Ritter (marvin.ritter@gmail.com)
 */

#include <stdexcept>
#include <omp.h>

#include "GraphBuilder.h"

namespace NetworKit {

GraphBuilder::GraphBuilder(count n, bool weighted, bool directed, bool directSwap) :
	n(n),
	selfloops(0),
	weighted(weighted),
	directed(directed),
	usedirectswap(directSwap),
	halfEdges(n),
	halfEdgeWeights(weighted ? n : 0)
{
	if (directed && directSwap) throw std::runtime_error("Cannot use direct swap in directed graph.");
}

index GraphBuilder::indexHalfEdgeArray(node u, node v) const {
	for (index i = 0; i < halfEdges[u].size(); i++) {
		node x = halfEdges[u][i];
		if (x == v) {
			return i;
		}
	}
	return none;
}

node GraphBuilder::addNode() {
	halfEdges.push_back(std::vector<node>{});
	if (weighted) {
		halfEdgeWeights.push_back(std::vector<edgeweight>{});
	}
	return n++;
}

void GraphBuilder::addEdge(node u, node v, edgeweight ew) {
	assert(u < n);
	assert(v < n);
	halfEdges[u].push_back(v);
	if (weighted) {
		halfEdgeWeights[u].push_back(ew);
	}
	if (u == v) {
	#pragma omp atomic
		selfloops++;
	}
}

void GraphBuilder::swapNeighborhood(node u, std::vector<node> &neighbours, std::vector<edgeweight> &weights, bool selfloop) {
	if (weighted) assert(neighbours.size() == weights.size());
	halfEdges[u].swap(neighbours);
	if (weighted) {
		halfEdgeWeights[u].swap(weights);
	}

	if (selfloop) {
	#pragma omp atomic
		selfloops++;
	}
}

void GraphBuilder::setWeight(node u, node v, edgeweight ew) {
	assert(u < n);
	assert(v < n);

	if (!weighted) {
		throw std::runtime_error("Cannot set edge weight in unweighted graph.");
	}

	index vi = indexHalfEdgeArray(u, v);

	if (vi != none) {
		halfEdgeWeights[u][vi] = ew;
	} else {
		addEdge(u, v, ew);
	}
}

void GraphBuilder::increaseWeight(node u, node v, edgeweight ew) {
	assert(u < n);
	assert(v < n);

	if (!weighted) {
		throw std::runtime_error("Cannot increase edge weight in unweighted graph.");
	}

	index vi = indexHalfEdgeArray(u, v);

	if (vi != none) {
		halfEdgeWeights[u][vi] += ew;
	} else {
		addEdge(u, v, ew);
	}
}

void GraphBuilder::directSwap(Graph &G) {
	if (directed) throw std::runtime_error("Cannot swap directly in directed Graph.");
	G.outEdges.swap(halfEdges);
	G.outEdgeWeights.swap(halfEdgeWeights);

	//maybe this loop should not be parallelized, since distributing the edge array to all processors takes too long
	#pragma omp parallel for
	for (node v = 0; v < n; v++) {
		G.outDeg[v] = G.outEdges[v].size();
	}

	correctNumberOfEdges(G, selfloops);

	reset();
}

void GraphBuilder::toGraphParallel(Graph &G) {

	// basic idea of the parallelization:
	// 1) each threads collects its own data
	// 2) each node collects all its data from all threads

	int maxThreads = omp_get_max_threads();

	using adjacencylists = std::vector< std::vector<node> >;
	using weightlists = std::vector< std::vector<edgeweight> >;

	std::vector<adjacencylists> inEdgesPerThread(maxThreads, adjacencylists(n));
	std::vector<weightlists> inWeightsPerThread(weighted ? maxThreads : 0, weightlists(n));
	std::vector<count> numberOfSelfLoopsPerThread(maxThreads, 0);

	// step 1
	parallelForNodes([&](node v) {
		int tid = omp_get_thread_num();
		for (index i = 0; i < halfEdges[v].size(); i++) {
			node u = halfEdges[v][i];
			if (directed || u != v) { // self loops don't need to be added twice in undirected graphs
				inEdgesPerThread[tid][u].push_back(v);
				if (weighted) {
					edgeweight ew = halfEdgeWeights[v][i];
					inWeightsPerThread[tid][u].push_back(ew);
				}
			} else {
				numberOfSelfLoopsPerThread[tid]++;
			}
		}
	});

	// we have already half of the edges
	G.outEdges.swap(halfEdges);
	G.outEdgeWeights.swap(halfEdgeWeights);

	// step 2
	parallelForNodes([&](node v) {
		count inDeg = 0;
		count outDeg = G.outEdges[v].size();
		for (int tid = 0; tid < maxThreads; tid++) {
			inDeg += inEdgesPerThread[tid][v].size();
		}

		// allocate enough memory for all edges/weights
		if (directed) {
			G.inEdges[v].reserve(inDeg);
			if (weighted) {
				G.inEdgeWeights[v].reserve(inDeg);
			}
		} else {
			G.outEdges[v].reserve(outDeg + inDeg);
			if (weighted) {
				G.outEdgeWeights[v].reserve(outDeg + inDeg);
			}
		}

		// collect 'second' half of the edges
		if (directed) {
			G.inDeg[v] = inDeg;
			G.outDeg[v] = outDeg;
			for (int tid = 0; tid < maxThreads; tid++) {
				copyAndClear(inEdgesPerThread[tid][v], G.inEdges[v]);
			}
			if (weighted) {
				for (int tid = 0; tid < maxThreads; tid++) {
					copyAndClear(inWeightsPerThread[tid][v], G.inEdgeWeights[v]);
				}
			}
		} else {
			G.outDeg[v] = inDeg + outDeg;
			for (int tid = 0; tid < maxThreads; tid++) {
				copyAndClear(inEdgesPerThread[tid][v], G.outEdges[v]);
			}
			if (weighted) {
				for (int tid = 0; tid < maxThreads; tid++) {
					copyAndClear(inWeightsPerThread[tid][v], G.outEdgeWeights[v]);
				}
			}
		}
	});

	// calculate correct m
	count numberOfSelfLoops = 0;
	for (auto c : numberOfSelfLoopsPerThread) {
		numberOfSelfLoops += c;
	}
	correctNumberOfEdges(G, numberOfSelfLoops);

	// bring the builder into an empty, but valid state
	reset();
}

void GraphBuilder::toGraphSequential(Graph &G) {
	std::vector<count> missingEdgesCounts(n, 0);
	count numberOfSelfLoops = 0;

	// 'first' half of the edges
	G.outEdges.swap(halfEdges);
	G.outEdgeWeights.swap(halfEdgeWeights);
	parallelForNodes([&](node v) {
		G.outDeg[v] = G.outEdges[v].size();
	});

	// count missing edges for each node
	G.forNodes([&](node v) {
		// increase count of incoming edges for all neighbors
		for (node u : G.outEdges[v]) {
			if (directed || u != v) {
				missingEdgesCounts[u]++;
			} else {
				// self loops don't need to be added again
				// but we need to count them to correct the number of edges later
				numberOfSelfLoops++;
			}
		}
	});

	// 'second' half the edges
	if (directed) {
		// directed: outEdges is complete, missing half edges are the inEdges
		// missingEdgesCounts are our inDegrees
		G.inDeg = missingEdgesCounts;

		// reserve the exact amount of space needed
		G.forNodes([&](node v) {
			G.inEdges[v].reserve(G.inDeg[v]);
			if (weighted) {
				G.inEdgeWeights[v].reserve(G.inDeg[v]);
			}
		});

		// copy values
		G.forNodes([&](node v) {
			for (index i = 0; i < G.outDeg[v]; i++) {
				node u = G.outEdges[v][i];
				G.inEdges[u].push_back(v);
				if (weighted) {
					edgeweight ew = G.outEdgeWeights[v][i];
					G.inEdgeWeights[u].push_back(ew);
				}
			}
		});
	} else {
		// undirected: so far each edge is just saved at one node
		// add it to the other node as well

		// reserve the exact amount of space needed
		G.forNodes([&](node v) {
			G.outEdges[v].reserve(G.outDeg[v] + missingEdgesCounts[v]);
			if (weighted) {
				G.outEdgeWeights[v].reserve(G.outDeg[v] + missingEdgesCounts[v]);
			}
		});

		// cope value
		G.forNodes([&](node v) {
			// the first G.outDeg[v] edges in G.outEdges[v] are the first half edges
			// we are adding after G.outDeg[v]
			for (index i = 0; i < G.outDeg[v]; i++) {
				node u = G.outEdges[v][i];
				if (u != v) {
					G.outEdges[u].push_back(v);
					if (weighted) {
						edgeweight ew = G.outEdgeWeights[v][i];
						G.outEdgeWeights[u].push_back(ew);
					}
				} else {
					// ignore self loops here
				}
			}
		});

		// correct degree
		G.forNodes([&](node v) {
			G.outDeg[v] += missingEdgesCounts[v];
		});
	}

	// calculate correct m
	correctNumberOfEdges(G, numberOfSelfLoops);

	// bring the builder into an empty, but valid state
	reset();
}

void GraphBuilder::reset() {
	n = 0;
	selfloops = 0;
	halfEdges.clear();
	halfEdgeWeights.clear();
}

void GraphBuilder::correctNumberOfEdges(Graph& G, count numberOfSelfLoops) {
	count edges = 0;
	#pragma omp parallel for reduction(+:edges)
	for (node v = 0; v < G.z; v++) {
		edges += G.degree(v);
	}
	G.m = edges;
	if (!G.isDirected()) {
		// self loops are just counted once
		G.m = numberOfSelfLoops + (G.m - numberOfSelfLoops) / 2;
	}
}

bool GraphBuilder::checkConsistency(Graph& G) {
	return G.checkConsistency();
}

} /* namespace NetworKit */
<commit_msg>added assertions and tracing messages to graph builder<commit_after>/*
 * GraphBuilder.cpp
 *
 *  Created on: 15.07.2014
 *      Author: Marvin Ritter (marvin.ritter@gmail.com)
 */

#include <stdexcept>
#include <omp.h>

#include "GraphBuilder.h"

namespace NetworKit {

GraphBuilder::GraphBuilder(count n, bool weighted, bool directed, bool directSwap) :
	n(n),
	selfloops(0),
	weighted(weighted),
	directed(directed),
	usedirectswap(directSwap),
	halfEdges(n),
	halfEdgeWeights(weighted ? n : 0)
{
	if (directed && directSwap) throw std::runtime_error("Cannot use direct swap in directed graph.");
}

index GraphBuilder::indexHalfEdgeArray(node u, node v) const {
	for (index i = 0; i < halfEdges[u].size(); i++) {
		node x = halfEdges[u][i];
		if (x == v) {
			return i;
		}
	}
	return none;
}

node GraphBuilder::addNode() {
	halfEdges.push_back(std::vector<node>{});
	if (weighted) {
		halfEdgeWeights.push_back(std::vector<edgeweight>{});
	}
	return n++;
}

void GraphBuilder::addEdge(node u, node v, edgeweight ew) {
	assert(u < n);
	assert(v < n);
	halfEdges[u].push_back(v);
	if (weighted) {
		halfEdgeWeights[u].push_back(ew);
	}
	if (u == v) {
	#pragma omp atomic
		selfloops++;
	}
}

void GraphBuilder::swapNeighborhood(node u, std::vector<node> &neighbours, std::vector<edgeweight> &weights, bool selfloop) {
	if (weighted) assert(neighbours.size() == weights.size());
	halfEdges[u].swap(neighbours);
	if (weighted) {
		halfEdgeWeights[u].swap(weights);
	}

	if (selfloop) {
	#pragma omp atomic
		selfloops++;
	}
}

void GraphBuilder::setWeight(node u, node v, edgeweight ew) {
	assert(u < n);
	assert(v < n);

	if (!weighted) {
		throw std::runtime_error("Cannot set edge weight in unweighted graph.");
	}

	index vi = indexHalfEdgeArray(u, v);

	if (vi != none) {
		halfEdgeWeights[u][vi] = ew;
	} else {
		addEdge(u, v, ew);
	}
}

void GraphBuilder::increaseWeight(node u, node v, edgeweight ew) {
	assert(u < n);
	assert(v < n);

	if (!weighted) {
		throw std::runtime_error("Cannot increase edge weight in unweighted graph.");
	}

	index vi = indexHalfEdgeArray(u, v);

	if (vi != none) {
		halfEdgeWeights[u][vi] += ew;
	} else {
		addEdge(u, v, ew);
	}
}

void GraphBuilder::directSwap(Graph &G) {
	if (directed) throw std::runtime_error("Cannot swap directly in directed Graph.");
	G.outEdges.swap(halfEdges);
	G.outEdgeWeights.swap(halfEdgeWeights);

	//maybe this loop should not be parallelized, since distributing the edge array to all processors takes too long
	#pragma omp parallel for
	for (node v = 0; v < n; v++) {
		G.outDeg[v] = G.outEdges[v].size();
	}

	correctNumberOfEdges(G, selfloops);

	reset();
}

void GraphBuilder::toGraphParallel(Graph &G) {

	// basic idea of the parallelization:
	// 1) each threads collects its own data
	// 2) each node collects all its data from all threads
	TRACE("Called toGraphParallel");
	int maxThreads = omp_get_max_threads();

	using adjacencylists = std::vector< std::vector<node> >;
	using weightlists = std::vector< std::vector<edgeweight> >;

	std::vector<adjacencylists> inEdgesPerThread(maxThreads, adjacencylists(n));
	std::vector<weightlists> inWeightsPerThread(weighted ? maxThreads : 0, weightlists(n));
	std::vector<count> numberOfSelfLoopsPerThread(maxThreads, 0);
	TRACE("Allocated vectors for ", maxThreads, " threads.");
	// step 1
	parallelForNodes([&](node v) {
		int tid = omp_get_thread_num();
		assert(tid < maxThreads);
		for (index i = 0; i < halfEdges[v].size(); i++) {
			node u = halfEdges[v][i];
			assert(u < n);
			if (directed || u != v) { // self loops don't need to be added twice in undirected graphs
				inEdgesPerThread[tid][u].push_back(v);
				if (weighted) {
					edgeweight ew = halfEdgeWeights[v][i];
					inWeightsPerThread[tid][u].push_back(ew);
				}
			} else {
				numberOfSelfLoopsPerThread[tid]++;
			}
		}
	});

	TRACE("First step complete");
	// we have already half of the edges
	G.outEdges.swap(halfEdges);
	G.outEdgeWeights.swap(halfEdgeWeights);

	// step 2
	parallelForNodes([&](node v) {
		count inDeg = 0;
		count outDeg = G.outEdges[v].size();
		for (int tid = 0; tid < maxThreads; tid++) {
			inDeg += inEdgesPerThread[tid][v].size();
		}

		assert(inDeg <= n);
		assert(outDeg <= n);
		// allocate enough memory for all edges/weights
		if (directed) {
			G.inEdges[v].reserve(inDeg);
			if (weighted) {
				G.inEdgeWeights[v].reserve(inDeg);
			}
		} else {
			G.outEdges[v].reserve(outDeg + inDeg);
			if (weighted) {
				G.outEdgeWeights[v].reserve(outDeg + inDeg);
			}
		}

		// collect 'second' half of the edges
		if (directed) {
			G.inDeg[v] = inDeg;
			G.outDeg[v] = outDeg;
			for (int tid = 0; tid < maxThreads; tid++) {
				copyAndClear(inEdgesPerThread[tid][v], G.inEdges[v]);
			}
			assert(G.inDeg[v] == G.inEdges[v].size());
			if (weighted) {
				for (int tid = 0; tid < maxThreads; tid++) {
					copyAndClear(inWeightsPerThread[tid][v], G.inEdgeWeights[v]);
				}
				assert(G.inDeg[v] == G.inEdgeWeights[v].size());
			}

		} else {
			G.outDeg[v] = inDeg + outDeg;
			assert(G.outDeg[v] <= n);
			for (int tid = 0; tid < maxThreads; tid++) {
				copyAndClear(inEdgesPerThread[tid][v], G.outEdges[v]);
			}
			assert(G.outDeg[v] == G.outEdges[v].size());
			if (weighted) {
				for (int tid = 0; tid < maxThreads; tid++) {
					copyAndClear(inWeightsPerThread[tid][v], G.outEdgeWeights[v]);
				}
			}
		}
	});

	TRACE("Second step complete.");

	// calculate correct m
	count numberOfSelfLoops = 0;
	for (auto c : numberOfSelfLoopsPerThread) {
		numberOfSelfLoops += c;
	}
	correctNumberOfEdges(G, numberOfSelfLoops);

	TRACE("Graph Builder complete");

	// bring the builder into an empty, but valid state
	reset();
}

void GraphBuilder::toGraphSequential(Graph &G) {
	std::vector<count> missingEdgesCounts(n, 0);
	count numberOfSelfLoops = 0;

	// 'first' half of the edges
	G.outEdges.swap(halfEdges);
	G.outEdgeWeights.swap(halfEdgeWeights);
	parallelForNodes([&](node v) {
		G.outDeg[v] = G.outEdges[v].size();
	});

	// count missing edges for each node
	G.forNodes([&](node v) {
		// increase count of incoming edges for all neighbors
		for (node u : G.outEdges[v]) {
			if (directed || u != v) {
				missingEdgesCounts[u]++;
			} else {
				// self loops don't need to be added again
				// but we need to count them to correct the number of edges later
				numberOfSelfLoops++;
			}
		}
	});

	// 'second' half the edges
	if (directed) {
		// directed: outEdges is complete, missing half edges are the inEdges
		// missingEdgesCounts are our inDegrees
		G.inDeg = missingEdgesCounts;

		// reserve the exact amount of space needed
		G.forNodes([&](node v) {
			G.inEdges[v].reserve(G.inDeg[v]);
			if (weighted) {
				G.inEdgeWeights[v].reserve(G.inDeg[v]);
			}
		});

		// copy values
		G.forNodes([&](node v) {
			for (index i = 0; i < G.outDeg[v]; i++) {
				node u = G.outEdges[v][i];
				G.inEdges[u].push_back(v);
				if (weighted) {
					edgeweight ew = G.outEdgeWeights[v][i];
					G.inEdgeWeights[u].push_back(ew);
				}
			}
		});
	} else {
		// undirected: so far each edge is just saved at one node
		// add it to the other node as well

		// reserve the exact amount of space needed
		G.forNodes([&](node v) {
			G.outEdges[v].reserve(G.outDeg[v] + missingEdgesCounts[v]);
			if (weighted) {
				G.outEdgeWeights[v].reserve(G.outDeg[v] + missingEdgesCounts[v]);
			}
		});

		// cope value
		G.forNodes([&](node v) {
			// the first G.outDeg[v] edges in G.outEdges[v] are the first half edges
			// we are adding after G.outDeg[v]
			for (index i = 0; i < G.outDeg[v]; i++) {
				node u = G.outEdges[v][i];
				if (u != v) {
					G.outEdges[u].push_back(v);
					if (weighted) {
						edgeweight ew = G.outEdgeWeights[v][i];
						G.outEdgeWeights[u].push_back(ew);
					}
				} else {
					// ignore self loops here
				}
			}
		});

		// correct degree
		G.forNodes([&](node v) {
			G.outDeg[v] += missingEdgesCounts[v];
		});
	}

	// calculate correct m
	correctNumberOfEdges(G, numberOfSelfLoops);

	// bring the builder into an empty, but valid state
	reset();
}

void GraphBuilder::reset() {
	n = 0;
	selfloops = 0;
	halfEdges.clear();
	halfEdgeWeights.clear();
}

void GraphBuilder::correctNumberOfEdges(Graph& G, count numberOfSelfLoops) {
	count edges = 0;
	#pragma omp parallel for reduction(+:edges)
	for (node v = 0; v < G.z; v++) {
		edges += G.degree(v);
	}
	G.m = edges;
	if (!G.isDirected()) {
		// self loops are just counted once
		G.m = numberOfSelfLoops + (G.m - numberOfSelfLoops) / 2;
	}
}

bool GraphBuilder::checkConsistency(Graph& G) {
	return G.checkConsistency();
}

} /* namespace NetworKit */
<|endoftext|>


<commit_before>//@author A0097630B
#include "stdafx.h"
#include "exceptions/parse_error_exception.h"
#include "query_parser.h"

namespace You {
namespace NLP {

namespace spirit = boost::spirit;
namespace qi = spirit::qi;
namespace phoenix = boost::phoenix;

QUERY QueryParser::parse(const StringType& string) {
	QUERY result;

	if (parse(string, result)) {
		return result;
	} else {
		throw ParserException();
	}
}

bool QueryParser::parse(const StringType& string, QUERY& result) {
	return qi::parse(
		begin(string),
		end(string),
		QueryParser(),
		result);
}

QueryParser::QueryParser() : QueryParser::base_type(start) {
	start %= (
		(qi::lit(L'/') > explicitCommand) |
		addCommand
	) >> qi::eoi;
	BOOST_SPIRIT_DEBUG_NODE(start);

	explicitCommand %= (
		(qi::lit(L"add") >> space >> addCommand) |
		(qi::lit(L"show") >> space >> showCommand) |
		(qi::lit(L"edit") >> space >> editCommand) |
		(qi::lit(L"delete") >> space >> deleteCommand) |
		(qi::lit(L"undo") >> undoCommand)
	);
	BOOST_SPIRIT_DEBUG_NODE(explicitCommand);

	#pragma region Adding tasks
	addCommand %= (
		addCommandDescription
	);
	BOOST_SPIRIT_DEBUG_NODE(addCommand);

	addCommandDescription = (
		ParserCharTraits::char_ >> addCommandDescriptionTail
	)[qi::_val = phoenix::bind(&constructAddQuery, qi::_1, qi::_2)];
	BOOST_SPIRIT_DEBUG_NODE(addCommandDescription);

	addCommandDescriptionTail %= (
		addCommandPriority |
		addCommandDescription
	);
	BOOST_SPIRIT_DEBUG_NODE(addCommandDescriptionTail);

	addCommandPriority %= (
		(-space >> qi::lit('!') >> addCommandDeadlineOptional)
			[qi::_val = phoenix::bind(&constructAddQueryWithPriority, qi::_1)] |
		addCommandDeadlineOptional
	);
	BOOST_SPIRIT_DEBUG_NODE(addCommandPriority);

	addCommandDeadline = (
		space >> (qi::lit("by") | qi::lit("before")) >>
		utilityTime
	)[qi::_val = phoenix::bind(&constructAddQueryWithDeadline, qi::_1)];
	BOOST_SPIRIT_DEBUG_NODE(addCommandDeadline);

	addCommandDeadlineOptional = (
		addCommandDeadline || qi::eoi
	)[qi::_val = phoenix::bind(&constructAddQueryWithOptionalDeadline, qi::_1)];
	BOOST_SPIRIT_DEBUG_NODE(addCommandDeadlineOptional);
	#pragma endregion

	#pragma region Showing tasks
	showCommand = (
		-showCommandFiltering >>
		-(-space >> (qi::lit(L"sorted by") | qi::lit(L"order by")) >> space >>
		showCommandSorting)
	)[qi::_val = phoenix::bind(&constructShowQuery, qi::_1, qi::_2)];
	BOOST_SPIRIT_DEBUG_NODE(showCommand);

	showCommandFiltering %=
		showCommandFilteringColumn % (
			(-space >> qi::lit(L",") >> -space) |
			(space >> qi::lit(L"and") >> space));
	BOOST_SPIRIT_DEBUG_NODE(showCommandFiltering);

	showCommandFilteringColumn = (
		showCommandFields >>
		-space >> showCommandFilteringPredicate >> -space >>
		utilityValue
	)[qi::_val = phoenix::bind(&constructShowQueryFilteringColumn,
		qi::_1,
		qi::_2,
		qi::_3)];
	BOOST_SPIRIT_DEBUG_NODE(showCommandFiltering);

	showCommandFilteringPredicate.add
		(L"=", SHOW_QUERY::Predicate::EQ)
		(L"!=", SHOW_QUERY::Predicate::NOT_EQ)
		(L"<", SHOW_QUERY::Predicate::LESS_THAN)
		(L"<=", SHOW_QUERY::Predicate::LESS_THAN_EQ)
		(L">", SHOW_QUERY::Predicate::GREATER_THAN)
		(L">=", SHOW_QUERY::Predicate::GREATER_THAN_EQ);

	showCommandSorting %= (
		showCommandSortingColumn % (
			(-space >> qi::lit(L",") >> -space) |
			(space >> qi::lit(L"then") >> space)));
	BOOST_SPIRIT_DEBUG_NODE(showCommandSorting);

	showCommandSortingColumn = (
		showCommandFields >> -(space >> showCommandSortingOrders)
	)[qi::_val = phoenix::bind(&constructShowQuerySortColumn, qi::_1, qi::_2)];
	BOOST_SPIRIT_DEBUG_NODE(showCommandSortingColumn);

	showCommandSortingOrders.add
		(L"asc", SHOW_QUERY::Order::ASCENDING)
		(L"ascending", SHOW_QUERY::Order::ASCENDING)
		(L"desc", SHOW_QUERY::Order::DESCENDING)
		(L"descending", SHOW_QUERY::Order::DESCENDING);

	showCommandFields.add
		(L"description", TaskField::DESCRIPTION)
		(L"deadline", TaskField::DEADLINE)
		(L"priority", TaskField::PRIORITY)
		(L"done", TaskField::COMPLETE)
		(L"complete", TaskField::COMPLETE);
	#pragma endregion

	#pragma region Editing tasks
	editCommand = (
		qi::uint_ >> space >> qi::lit(L"set") >> space >> editCommandRule
	)[qi::_val = phoenix::bind(&constructEditQuery,
		qi::_1, qi::_2)];
	BOOST_SPIRIT_DEBUG_NODE(editCommand);

	editCommandRule %= (
		editCommandRuleNullary |
		editCommandRuleUnary |
		editCommandRulePriorities
	);
	BOOST_SPIRIT_DEBUG_NODE(editCommandRule);

	editCommandRuleNullary = (
		editCommandFieldsNullary
	)[qi::_val = phoenix::bind(&constructEditQueryNullary, qi::_1)];
	BOOST_SPIRIT_DEBUG_NODE(editCommandRuleNullary);

	editCommandRuleUnary = (
		editCommandFieldsUnary >>
		-space >> qi::lit('=') >> -space >>
		utilityValue)
	[qi::_val = phoenix::bind(&constructEditQueryUnary, qi::_1, qi::_2)];
	BOOST_SPIRIT_DEBUG_NODE(editCommandRuleUnary);

	editCommandRulePriorities = (
		qi::lit(L"priority") >>
		-space >> qi::lit('=') >> -space >>
		utilityTaskPriority
	)[qi::_val = phoenix::bind(&constructEditQueryPriority, qi::_1)];
	BOOST_SPIRIT_DEBUG_NODE(editCommandRulePriorities);

	editCommandFieldsUnary.add
		(L"description", TaskField::DESCRIPTION)
		(L"deadline", TaskField::DEADLINE);

	editCommandFieldsNullary.add
		(L"done", TaskField::COMPLETE)
		(L"complete", TaskField::COMPLETE);
	#pragma endregion

	#pragma region Deleting tasks
	deleteCommand = (
		qi::uint_
	)[qi::_val = phoenix::bind(&constructDeleteQuery, qi::_1)];
	BOOST_SPIRIT_DEBUG_NODE(deleteCommand);
	#pragma endregion

	#pragma region Undoing tasks
	undoCommand = (
		qi::eps
	)[qi::_val = phoenix::construct<UNDO_QUERY>()];
	BOOST_SPIRIT_DEBUG_NODE(undoCommand);
	#pragma endregion

	space = qi::omit[+ParserCharTraits::blank];

	utilityValue = (
		(qi::int_) |
		(qi::bool_) |
		utilityTaskPriority |
		utilityLexeme
	)[qi::_val = phoenix::bind(&constructValue, qi::_1)];

	utilityTaskPriority.add
		(L"normal", TaskPriority::NORMAL)
		(L"high", TaskPriority::HIGH);

	utilityLexeme %= qi::as_wstring[(
		qi::lit('\'') > *utilityLexemeContents > qi::lit('\'')
	)];
	BOOST_SPIRIT_DEBUG_NODE(utilityLexeme);

	utilityLexemeContents %= (
		qi::lit("\\'")[qi::_val = L'\''] |
		qi::lit("\\\\")[qi::_val = L'\\'] |
		(ParserCharTraits::char_ - qi::lit('\''))
	);
	BOOST_SPIRIT_DEBUG_NODE(utilityLexemeContents);

	qi::on_error<qi::fail>(start,
		phoenix::bind(&onFailure, qi::_1, qi::_2, qi::_3, qi::_4));
}

void QueryParser::onFailure(
	ParserIteratorType /*begin*/,
	ParserIteratorType end,
	ParserIteratorType errorPos,
	const spirit::info& message) {
	StringType lexeme(errorPos, end);
	throw ParseErrorException(message, lexeme);
}

}  // namespace NLP
}  // namespace You
<commit_msg>Use narrow characters to save space.<commit_after>//@author A0097630B
#include "stdafx.h"
#include "exceptions/parse_error_exception.h"
#include "query_parser.h"

namespace You {
namespace NLP {

namespace spirit = boost::spirit;
namespace qi = spirit::qi;
namespace phoenix = boost::phoenix;

QUERY QueryParser::parse(const StringType& string) {
	QUERY result;

	if (parse(string, result)) {
		return result;
	} else {
		throw ParserException();
	}
}

bool QueryParser::parse(const StringType& string, QUERY& result) {
	return qi::parse(
		begin(string),
		end(string),
		QueryParser(),
		result);
}

QueryParser::QueryParser() : QueryParser::base_type(start) {
	start %= (
		(qi::lit('/') > explicitCommand) |
		addCommand
	) >> qi::eoi;
	BOOST_SPIRIT_DEBUG_NODE(start);

	explicitCommand %= (
		(qi::lit("add") >> space >> addCommand) |
		(qi::lit("show") >> space >> showCommand) |
		(qi::lit("edit") >> space >> editCommand) |
		(qi::lit("delete") >> space >> deleteCommand) |
		(qi::lit("undo") >> undoCommand)
	);
	BOOST_SPIRIT_DEBUG_NODE(explicitCommand);

	#pragma region Adding tasks
	addCommand %= (
		addCommandDescription
	);
	BOOST_SPIRIT_DEBUG_NODE(addCommand);

	addCommandDescription = (
		ParserCharTraits::char_ >> addCommandDescriptionTail
	)[qi::_val = phoenix::bind(&constructAddQuery, qi::_1, qi::_2)];
	BOOST_SPIRIT_DEBUG_NODE(addCommandDescription);

	addCommandDescriptionTail %= (
		addCommandPriority |
		addCommandDescription
	);
	BOOST_SPIRIT_DEBUG_NODE(addCommandDescriptionTail);

	addCommandPriority %= (
		(-space >> qi::lit('!') >> addCommandDeadlineOptional)
			[qi::_val = phoenix::bind(&constructAddQueryWithPriority, qi::_1)] |
		addCommandDeadlineOptional
	);
	BOOST_SPIRIT_DEBUG_NODE(addCommandPriority);

	addCommandDeadline = (
		space >> (qi::lit("by") | qi::lit("before")) >> space >>
		utilityTime
	)[qi::_val = phoenix::bind(&constructAddQueryWithDeadline, qi::_1)];
	BOOST_SPIRIT_DEBUG_NODE(addCommandDeadline);

	addCommandDeadlineOptional = (
		addCommandDeadline || qi::eoi
	)[qi::_val = phoenix::bind(&constructAddQueryWithOptionalDeadline, qi::_1)];
	BOOST_SPIRIT_DEBUG_NODE(addCommandDeadlineOptional);
	#pragma endregion

	#pragma region Showing tasks
	showCommand = (
		-showCommandFiltering >>
		-(-space >> (qi::lit("sorted by") | qi::lit("order by")) >> space >>
		showCommandSorting)
	)[qi::_val = phoenix::bind(&constructShowQuery, qi::_1, qi::_2)];
	BOOST_SPIRIT_DEBUG_NODE(showCommand);

	showCommandFiltering %=
		showCommandFilteringColumn % (
			(-space >> qi::lit(',') >> -space) |
			(space >> qi::lit("and") >> space));
	BOOST_SPIRIT_DEBUG_NODE(showCommandFiltering);

	showCommandFilteringColumn = (
		showCommandFields >>
		-space >> showCommandFilteringPredicate >> -space >>
		utilityValue
	)[qi::_val = phoenix::bind(&constructShowQueryFilteringColumn,
		qi::_1,
		qi::_2,
		qi::_3)];
	BOOST_SPIRIT_DEBUG_NODE(showCommandFiltering);

	showCommandFilteringPredicate.add
		(L"=", SHOW_QUERY::Predicate::EQ)
		(L"!=", SHOW_QUERY::Predicate::NOT_EQ)
		(L"<", SHOW_QUERY::Predicate::LESS_THAN)
		(L"<=", SHOW_QUERY::Predicate::LESS_THAN_EQ)
		(L">", SHOW_QUERY::Predicate::GREATER_THAN)
		(L">=", SHOW_QUERY::Predicate::GREATER_THAN_EQ);

	showCommandSorting %= (
		showCommandSortingColumn % (
			(-space >> qi::lit(",") >> -space) |
			(space >> qi::lit("then") >> space)));
	BOOST_SPIRIT_DEBUG_NODE(showCommandSorting);

	showCommandSortingColumn = (
		showCommandFields >> -(space >> showCommandSortingOrders)
	)[qi::_val = phoenix::bind(&constructShowQuerySortColumn, qi::_1, qi::_2)];
	BOOST_SPIRIT_DEBUG_NODE(showCommandSortingColumn);

	showCommandSortingOrders.add
		(L"asc", SHOW_QUERY::Order::ASCENDING)
		(L"ascending", SHOW_QUERY::Order::ASCENDING)
		(L"desc", SHOW_QUERY::Order::DESCENDING)
		(L"descending", SHOW_QUERY::Order::DESCENDING);

	showCommandFields.add
		(L"description", TaskField::DESCRIPTION)
		(L"deadline", TaskField::DEADLINE)
		(L"priority", TaskField::PRIORITY)
		(L"done", TaskField::COMPLETE)
		(L"complete", TaskField::COMPLETE);
	#pragma endregion

	#pragma region Editing tasks
	editCommand = (
		qi::uint_ >> space >> qi::lit("set") >> space >> editCommandRule
	)[qi::_val = phoenix::bind(&constructEditQuery,
		qi::_1, qi::_2)];
	BOOST_SPIRIT_DEBUG_NODE(editCommand);

	editCommandRule %= (
		editCommandRuleNullary |
		editCommandRuleUnary |
		editCommandRulePriorities
	);
	BOOST_SPIRIT_DEBUG_NODE(editCommandRule);

	editCommandRuleNullary = (
		editCommandFieldsNullary
	)[qi::_val = phoenix::bind(&constructEditQueryNullary, qi::_1)];
	BOOST_SPIRIT_DEBUG_NODE(editCommandRuleNullary);

	editCommandRuleUnary = (
		editCommandFieldsUnary >>
		-space >> qi::lit('=') >> -space >>
		utilityValue)
	[qi::_val = phoenix::bind(&constructEditQueryUnary, qi::_1, qi::_2)];
	BOOST_SPIRIT_DEBUG_NODE(editCommandRuleUnary);

	editCommandRulePriorities = (
		qi::lit("priority") >>
		-space >> qi::lit('=') >> -space >>
		utilityTaskPriority
	)[qi::_val = phoenix::bind(&constructEditQueryPriority, qi::_1)];
	BOOST_SPIRIT_DEBUG_NODE(editCommandRulePriorities);

	editCommandFieldsUnary.add
		(L"description", TaskField::DESCRIPTION)
		(L"deadline", TaskField::DEADLINE);

	editCommandFieldsNullary.add
		(L"done", TaskField::COMPLETE)
		(L"complete", TaskField::COMPLETE);
	#pragma endregion

	#pragma region Deleting tasks
	deleteCommand = (
		qi::uint_
	)[qi::_val = phoenix::bind(&constructDeleteQuery, qi::_1)];
	BOOST_SPIRIT_DEBUG_NODE(deleteCommand);
	#pragma endregion

	#pragma region Undoing tasks
	undoCommand = (
		qi::eps
	)[qi::_val = phoenix::construct<UNDO_QUERY>()];
	BOOST_SPIRIT_DEBUG_NODE(undoCommand);
	#pragma endregion

	space = qi::omit[+ParserCharTraits::blank];

	utilityValue = (
		(qi::int_) |
		(qi::bool_) |
		utilityTaskPriority |
		utilityLexeme
	)[qi::_val = phoenix::bind(&constructValue, qi::_1)];

	utilityTaskPriority.add
		(L"normal", TaskPriority::NORMAL)
		(L"high", TaskPriority::HIGH);

	utilityLexeme %= qi::as_wstring[(
		qi::lit('\'') > *utilityLexemeContents > qi::lit('\'')
	)];
	BOOST_SPIRIT_DEBUG_NODE(utilityLexeme);

	utilityLexemeContents %= (
		qi::lit("\\'")[qi::_val = '\''] |
		qi::lit("\\\\")[qi::_val = '\\'] |
		(ParserCharTraits::char_ - qi::lit('\''))
	);
	BOOST_SPIRIT_DEBUG_NODE(utilityLexemeContents);

	qi::on_error<qi::fail>(start,
		phoenix::bind(&onFailure, qi::_1, qi::_2, qi::_3, qi::_4));
}

void QueryParser::onFailure(
	ParserIteratorType /*begin*/,
	ParserIteratorType end,
	ParserIteratorType errorPos,
	const spirit::info& message) {
	StringType lexeme(errorPos, end);
	throw ParseErrorException(message, lexeme);
}

}  // namespace NLP
}  // namespace You
<|endoftext|>


<commit_before>/*************************************************************************
*                                                                       *
* Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith.       *
* All rights reserved.  Email: russ@q12.org   Web: www.q12.org          *
*                                                                       *
* This library is free software; you can redistribute it and/or         *
* modify it under the terms of EITHER:                                  *
*   (1) 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. The text of the GNU Lesser      *
*       General Public License is included with this library in the     *
*       file LICENSE.TXT.                                               *
*   (2) The BSD-style license that is included with this library in     *
*       the file LICENSE-BSD.TXT.                                       *
*                                                                       *
* 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 files    *
* LICENSE.TXT and LICENSE-BSD.TXT for more details.                     *
*                                                                       *
*************************************************************************/

/*
 * Cylinder-Plane collider by Christoph Beyer ( boernerb@web.de )
 *
 * This testing basically comes down to testing the intersection
 * of the cylinder caps (discs) with the plane.
 *
 */

#include <ode/collision.h>
#include <ode/matrix.h>
#include <ode/rotation.h>
#include <ode/odemath.h>
#include <ode/objects.h>

#include "collision_kernel.h"	// for dxGeom
#include "collision_util.h"

int dCollideCylinderPlane(dxGeom *Cylinder, dxGeom *Plane, int flags, dContactGeom *contact, int skip)
{
	dIASSERT (skip >= (int)sizeof(dContactGeom));
	dIASSERT (Cylinder->type == dCylinderClass);
	dIASSERT (Plane->type == dPlaneClass);
	dIASSERT ((flags & NUMC_MASK) >= 1);

	unsigned char* pContactData = (unsigned char*)contact;
	int GeomCount = 0; // count of used contactgeoms

#ifdef dSINGLE
	const dReal toleranz = 0.0001f;
#endif
#ifdef dDOUBLE
	const dReal toleranz = 0.0000001;
#endif

	// Get the properties of the cylinder (length+radius)
	dReal radius, length;
	dGeomCylinderGetParams(Cylinder, &radius, &length);
	dVector3 &cylpos = Cylinder->final_posr->pos;
	// and the plane
	dVector4 planevec;
	dGeomPlaneGetParams(Plane, planevec);
	dVector3 PlaneNormal = {planevec[0],planevec[1],planevec[2]};
	dVector3 PlanePos = {planevec[0] * planevec[3],planevec[1] * planevec[3],planevec[2] * planevec[3]};

	dVector3 G1Pos1, G1Pos2, vDir1;
	vDir1[0] = Cylinder->final_posr->R[2];
	vDir1[1] = Cylinder->final_posr->R[6];
	vDir1[2] = Cylinder->final_posr->R[10];

	dReal s;
	s = length * dReal(0.5);
	G1Pos2[0] = vDir1[0] * s + cylpos[0];
	G1Pos2[1] = vDir1[1] * s + cylpos[1];
	G1Pos2[2] = vDir1[2] * s + cylpos[2];

	G1Pos1[0] = vDir1[0] * -s + cylpos[0];
	G1Pos1[1] = vDir1[1] * -s + cylpos[1];
	G1Pos1[2] = vDir1[2] * -s + cylpos[2];

	dVector3 C;

	// parallel-check
	s = vDir1[0] * PlaneNormal[0] + vDir1[1] * PlaneNormal[1] + vDir1[2] * PlaneNormal[2];
	if(s < 0)
		s += dReal(1.0); // is ca. 0, if vDir1 and PlaneNormal are parallel
	else
		s -= dReal(1.0); // is ca. 0, if vDir1 and PlaneNormal are parallel
	if(s < toleranz && s > (-toleranz))
	{
		// discs are parallel to the plane

		// 1.compute if, and where contacts are
		dVector3 P;
		s = planevec[3] - dVector3Dot(planevec, G1Pos1);
		dReal t;
		t = planevec[3] - dVector3Dot(planevec, G1Pos2);
		if(s >= t) // s == t does never happen,
		{
			if(s >= 0)
			{
				// 1. Disc
				dVector3Copy(G1Pos1, P);
			}
			else
				return GeomCount; // no contacts
		}
		else
		{
			if(t >= 0)
			{
				// 2. Disc
				dVector3Copy(G1Pos2, P);
			}
			else
				return GeomCount; // no contacts
		}

		// 2. generate a coordinate-system on the disc
		dVector3 V1, V2;
		if(vDir1[0] < toleranz && vDir1[0] > (-toleranz))
		{
			// not x-axis
			V1[0] = vDir1[0] + dReal(1.0); // random value
			V1[1] = vDir1[1];
			V1[2] = vDir1[2];
		}
		else
		{
			// maybe x-axis
			V1[0] = vDir1[0];
			V1[1] = vDir1[1] + dReal(1.0); // random value
			V1[2] = vDir1[2];
		}
		// V1 is now another direction than vDir1
		// Cross-product
	    dVector3Cross(V1, vDir1, V2);
		// make unit V2
		t = dVector3Length(V2);
		t = radius / t;
		dVector3Scale(V2, t);
		// cross again
		dVector3Cross(V2, vDir1, V1);
		// |V2| is 'radius' and vDir1 unit, so |V1| is 'radius'
		// V1 = first axis
		// V2 = second axis

		// 3. generate contactpoints

		// Potential contact 1
		dVector3Add(P, V1, contact->pos);
		contact->depth = planevec[3] - dVector3Dot(planevec, contact->pos);
		if(contact->depth > 0)
		{
			dVector3Copy(PlaneNormal, contact->normal);
			contact->g1 = Cylinder;
			contact->g2 = Plane;
			GeomCount++;
			if( GeomCount >= (flags & NUMC_MASK))
				return GeomCount; // enough contactgeoms
			pContactData += skip;
			contact = (dContactGeom*)pContactData;
		}

		// Potential contact 2
		dVector3Subtract(P, V1, contact->pos);
		contact->depth = planevec[3] - dVector3Dot(planevec, contact->pos);
		if(contact->depth > 0)
		{
			dVector3Copy(PlaneNormal, contact->normal);
			contact->g1 = Cylinder;
			contact->g2 = Plane;
			GeomCount++;
			if( GeomCount >= (flags & NUMC_MASK))
				return GeomCount; // enough contactgeoms
			pContactData += skip;
			contact = (dContactGeom*)pContactData;
		}

		// Potential contact 3
		dVector3Add(P, V2, contact->pos);
		contact->depth = planevec[3] - dVector3Dot(planevec, contact->pos);
		if(contact->depth > 0)
		{
			dVector3Copy(PlaneNormal, contact->normal);
			contact->g1 = Cylinder;
			contact->g2 = Plane;
			GeomCount++;
			if( GeomCount >= (flags & NUMC_MASK))
				return GeomCount; // enough contactgeoms
			pContactData += skip;
			contact = (dContactGeom*)pContactData;
		}

		// Potential contact 4
		dVector3Subtract(P, V2, contact->pos);
		contact->depth = planevec[3] - dVector3Dot(planevec, contact->pos);
		if(contact->depth > 0)
		{
			dVector3Copy(PlaneNormal, contact->normal);
			contact->g1 = Cylinder;
			contact->g2 = Plane;
			GeomCount++;
			if( GeomCount >= (flags & NUMC_MASK))
				return GeomCount; // enough contactgeoms
			pContactData += skip;
			contact = (dContactGeom*)pContactData;
		}
	}
	else
	{
		dReal t = dVector3Dot(PlaneNormal, vDir1);
		C[0] = vDir1[0] * t - PlaneNormal[0];
		C[1] = vDir1[1] * t - PlaneNormal[1];
		C[2] = vDir1[2] * t - PlaneNormal[2];
		s = dVector3Length(C);
		// move C onto the circle
		s = radius / s;
		dVector3Scale(C, s);

		// deepest point of disc 1
		dVector3Add(C, G1Pos1, contact->pos);

		// depth of the deepest point
		contact->depth = planevec[3] - dVector3Dot(planevec, contact->pos);
		if(contact->depth >= 0)
		{
			dVector3Copy(PlaneNormal, contact->normal);
			contact->g1 = Cylinder;
			contact->g2 = Plane;
			GeomCount++;
			if( GeomCount >= (flags & NUMC_MASK))
				return GeomCount; // enough contactgeoms
			pContactData += skip;
			contact = (dContactGeom*)pContactData;
		}

		// C is still computed

		// deepest point of disc 2
		dVector3Add(C, G1Pos2, contact->pos);

		// depth of the deepest point
		contact->depth = planevec[3] - planevec[0] * contact->pos[0] - planevec[1] * contact->pos[1] - planevec[2] * contact->pos[2];
		if(contact->depth >= 0)
		{
			dVector3Copy(PlaneNormal, contact->normal);
			contact->g1 = Cylinder;
			contact->g2 = Plane;
			GeomCount++;
			if( GeomCount >= (flags & NUMC_MASK))
				return GeomCount; // enough contactgeoms
			pContactData += skip;
			contact = (dContactGeom*)pContactData;
		}
	}
	return GeomCount;
}
<commit_msg>Improvement: unnecessary variable removed<commit_after>/*************************************************************************
*                                                                       *
* Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith.       *
* All rights reserved.  Email: russ@q12.org   Web: www.q12.org          *
*                                                                       *
* This library is free software; you can redistribute it and/or         *
* modify it under the terms of EITHER:                                  *
*   (1) 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. The text of the GNU Lesser      *
*       General Public License is included with this library in the     *
*       file LICENSE.TXT.                                               *
*   (2) The BSD-style license that is included with this library in     *
*       the file LICENSE-BSD.TXT.                                       *
*                                                                       *
* 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 files    *
* LICENSE.TXT and LICENSE-BSD.TXT for more details.                     *
*                                                                       *
*************************************************************************/

/*
 * Cylinder-Plane collider by Christoph Beyer ( boernerb@web.de )
 *
 * This testing basically comes down to testing the intersection
 * of the cylinder caps (discs) with the plane.
 *
 */

#include <ode/collision.h>
#include <ode/matrix.h>
#include <ode/rotation.h>
#include <ode/odemath.h>
#include <ode/objects.h>

#include "collision_kernel.h"	// for dxGeom
#include "collision_util.h"

int dCollideCylinderPlane(dxGeom *Cylinder, dxGeom *Plane, int flags, dContactGeom *contact, int skip)
{
	dIASSERT (skip >= (int)sizeof(dContactGeom));
	dIASSERT (Cylinder->type == dCylinderClass);
	dIASSERT (Plane->type == dPlaneClass);
	dIASSERT ((flags & NUMC_MASK) >= 1);

	int GeomCount = 0; // count of used contactgeoms

#ifdef dSINGLE
	const dReal toleranz = 0.0001f;
#endif
#ifdef dDOUBLE
	const dReal toleranz = 0.0000001;
#endif

	// Get the properties of the cylinder (length+radius)
	dReal radius, length;
	dGeomCylinderGetParams(Cylinder, &radius, &length);
	dVector3 &cylpos = Cylinder->final_posr->pos;
	// and the plane
	dVector4 planevec;
	dGeomPlaneGetParams(Plane, planevec);
	dVector3 PlaneNormal = {planevec[0],planevec[1],planevec[2]};
	dVector3 PlanePos = {planevec[0] * planevec[3],planevec[1] * planevec[3],planevec[2] * planevec[3]};

	dVector3 G1Pos1, G1Pos2, vDir1;
	vDir1[0] = Cylinder->final_posr->R[2];
	vDir1[1] = Cylinder->final_posr->R[6];
	vDir1[2] = Cylinder->final_posr->R[10];

	dReal s;
	s = length * dReal(0.5);
	G1Pos2[0] = vDir1[0] * s + cylpos[0];
	G1Pos2[1] = vDir1[1] * s + cylpos[1];
	G1Pos2[2] = vDir1[2] * s + cylpos[2];

	G1Pos1[0] = vDir1[0] * -s + cylpos[0];
	G1Pos1[1] = vDir1[1] * -s + cylpos[1];
	G1Pos1[2] = vDir1[2] * -s + cylpos[2];

	dVector3 C;

	// parallel-check
	s = vDir1[0] * PlaneNormal[0] + vDir1[1] * PlaneNormal[1] + vDir1[2] * PlaneNormal[2];
	if(s < 0)
		s += dReal(1.0); // is ca. 0, if vDir1 and PlaneNormal are parallel
	else
		s -= dReal(1.0); // is ca. 0, if vDir1 and PlaneNormal are parallel
	if(s < toleranz && s > (-toleranz))
	{
		// discs are parallel to the plane

		// 1.compute if, and where contacts are
		dVector3 P;
		s = planevec[3] - dVector3Dot(planevec, G1Pos1);
		dReal t;
		t = planevec[3] - dVector3Dot(planevec, G1Pos2);
		if(s >= t) // s == t does never happen,
		{
			if(s >= 0)
			{
				// 1. Disc
				dVector3Copy(G1Pos1, P);
			}
			else
				return GeomCount; // no contacts
		}
		else
		{
			if(t >= 0)
			{
				// 2. Disc
				dVector3Copy(G1Pos2, P);
			}
			else
				return GeomCount; // no contacts
		}

		// 2. generate a coordinate-system on the disc
		dVector3 V1, V2;
		if(vDir1[0] < toleranz && vDir1[0] > (-toleranz))
		{
			// not x-axis
			V1[0] = vDir1[0] + dReal(1.0); // random value
			V1[1] = vDir1[1];
			V1[2] = vDir1[2];
		}
		else
		{
			// maybe x-axis
			V1[0] = vDir1[0];
			V1[1] = vDir1[1] + dReal(1.0); // random value
			V1[2] = vDir1[2];
		}
		// V1 is now another direction than vDir1
		// Cross-product
	    dVector3Cross(V1, vDir1, V2);
		// make unit V2
		t = dVector3Length(V2);
		t = radius / t;
		dVector3Scale(V2, t);
		// cross again
		dVector3Cross(V2, vDir1, V1);
		// |V2| is 'radius' and vDir1 unit, so |V1| is 'radius'
		// V1 = first axis
		// V2 = second axis

		// 3. generate contactpoints

		// Potential contact 1
		dVector3Add(P, V1, contact->pos);
		contact->depth = planevec[3] - dVector3Dot(planevec, contact->pos);
		if(contact->depth > 0)
		{
			dVector3Copy(PlaneNormal, contact->normal);
			contact->g1 = Cylinder;
			contact->g2 = Plane;
			GeomCount++;
			if( GeomCount >= (flags & NUMC_MASK))
				return GeomCount; // enough contactgeoms
			contact = (dContactGeom *)((char *)contact + skip);
		}

		// Potential contact 2
		dVector3Subtract(P, V1, contact->pos);
		contact->depth = planevec[3] - dVector3Dot(planevec, contact->pos);
		if(contact->depth > 0)
		{
			dVector3Copy(PlaneNormal, contact->normal);
			contact->g1 = Cylinder;
			contact->g2 = Plane;
			GeomCount++;
			if( GeomCount >= (flags & NUMC_MASK))
				return GeomCount; // enough contactgeoms
			contact = (dContactGeom *)((char *)contact + skip);
		}

		// Potential contact 3
		dVector3Add(P, V2, contact->pos);
		contact->depth = planevec[3] - dVector3Dot(planevec, contact->pos);
		if(contact->depth > 0)
		{
			dVector3Copy(PlaneNormal, contact->normal);
			contact->g1 = Cylinder;
			contact->g2 = Plane;
			GeomCount++;
			if( GeomCount >= (flags & NUMC_MASK))
				return GeomCount; // enough contactgeoms
			contact = (dContactGeom *)((char *)contact + skip);
		}

		// Potential contact 4
		dVector3Subtract(P, V2, contact->pos);
		contact->depth = planevec[3] - dVector3Dot(planevec, contact->pos);
		if(contact->depth > 0)
		{
			dVector3Copy(PlaneNormal, contact->normal);
			contact->g1 = Cylinder;
			contact->g2 = Plane;
			GeomCount++;
			if( GeomCount >= (flags & NUMC_MASK))
				return GeomCount; // enough contactgeoms
			contact = (dContactGeom *)((char *)contact + skip);
		}
	}
	else
	{
		dReal t = dVector3Dot(PlaneNormal, vDir1);
		C[0] = vDir1[0] * t - PlaneNormal[0];
		C[1] = vDir1[1] * t - PlaneNormal[1];
		C[2] = vDir1[2] * t - PlaneNormal[2];
		s = dVector3Length(C);
		// move C onto the circle
		s = radius / s;
		dVector3Scale(C, s);

		// deepest point of disc 1
		dVector3Add(C, G1Pos1, contact->pos);

		// depth of the deepest point
		contact->depth = planevec[3] - dVector3Dot(planevec, contact->pos);
		if(contact->depth >= 0)
		{
			dVector3Copy(PlaneNormal, contact->normal);
			contact->g1 = Cylinder;
			contact->g2 = Plane;
			GeomCount++;
			if( GeomCount >= (flags & NUMC_MASK))
				return GeomCount; // enough contactgeoms
			contact = (dContactGeom *)((char *)contact + skip);
		}

		// C is still computed

		// deepest point of disc 2
		dVector3Add(C, G1Pos2, contact->pos);

		// depth of the deepest point
		contact->depth = planevec[3] - planevec[0] * contact->pos[0] - planevec[1] * contact->pos[1] - planevec[2] * contact->pos[2];
		if(contact->depth >= 0)
		{
			dVector3Copy(PlaneNormal, contact->normal);
			contact->g1 = Cylinder;
			contact->g2 = Plane;
			GeomCount++;
			if( GeomCount >= (flags & NUMC_MASK))
				return GeomCount; // enough contactgeoms
			contact = (dContactGeom *)((char *)contact + skip);
		}
	}
	return GeomCount;
}
<|endoftext|>


<commit_before>// Copyright 2019 DeepMind Technologies Ltd. All rights reserved.
//
// 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.

#include "open_spiel/simultaneous_move_game.h"

#include <numeric>
#include <string>
#include <vector>

#include "open_spiel/abseil-cpp/absl/strings/str_cat.h"
#include "open_spiel/action_view.h"
#include "open_spiel/spiel.h"
#include "open_spiel/spiel_utils.h"

namespace open_spiel {

std::vector<Action> SimMoveState::FlatJointActionToActions(
    Action flat_action) const {
  std::vector<Action> actions(num_players_, kInvalidAction);
  for (Player player = 0; player < num_players_; ++player) {
    // For each player with legal actions available:
    const auto legal_actions = LegalActions(player);
    int num_actions = legal_actions.size();
    if (num_actions > 0) {
      // Extract the least-significant digit (radix = the number legal actions
      // for the current player) from flat_action. Use the digit as an index
      // into the player's set of legal actions.
      actions[player] = legal_actions[flat_action % num_actions];
      // Update the flat_action to be for the remaining players only.
      flat_action /= num_actions;
    }
  }
  return actions;
}

void SimMoveState::ApplyFlatJointAction(Action flat_action) {
  ApplyActions(FlatJointActionToActions(flat_action));
}

std::vector<Action> SimMoveState::LegalFlatJointActions() const {
  ActionView view(*this);
  FlatJointActions flat_joint_actions = view.flat_joint_actions();
  std::vector<Action> joint_actions(flat_joint_actions.num_flat_joint_actions);
  for (Action flat_joint_action : flat_joint_actions) {
    joint_actions.push_back(flat_joint_action);
  }
  return joint_actions;
}

std::string SimMoveState::FlatJointActionToString(Action flat_action) const {
  // Assembles the string for each individual player action into a single
  // string. For example, [Heads, Tails] would mean than player 0 chooses Heads,
  // and player 1 chooses Tails.
  std::string str;
  for (auto player = Player{0}; player < num_players_; ++player) {
    if (!str.empty()) str.append(", ");
    const auto legal_actions = LegalActions(player);
    int num_actions = legal_actions.size();
    str.append(
        ActionToString(player, legal_actions[flat_action % num_actions]));
    flat_action /= num_actions;
  }
  return absl::StrCat("[", str, "]");
}

}  // namespace open_spiel
<commit_msg>Bug fix - reserve, not init size.<commit_after>// Copyright 2019 DeepMind Technologies Ltd. All rights reserved.
//
// 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.

#include "open_spiel/simultaneous_move_game.h"

#include <numeric>
#include <string>
#include <vector>

#include "open_spiel/abseil-cpp/absl/strings/str_cat.h"
#include "open_spiel/action_view.h"
#include "open_spiel/spiel.h"
#include "open_spiel/spiel_utils.h"

namespace open_spiel {

std::vector<Action> SimMoveState::FlatJointActionToActions(
    Action flat_action) const {
  std::vector<Action> actions(num_players_, kInvalidAction);
  for (Player player = 0; player < num_players_; ++player) {
    // For each player with legal actions available:
    const auto legal_actions = LegalActions(player);
    int num_actions = legal_actions.size();
    if (num_actions > 0) {
      // Extract the least-significant digit (radix = the number legal actions
      // for the current player) from flat_action. Use the digit as an index
      // into the player's set of legal actions.
      actions[player] = legal_actions[flat_action % num_actions];
      // Update the flat_action to be for the remaining players only.
      flat_action /= num_actions;
    }
  }
  return actions;
}

void SimMoveState::ApplyFlatJointAction(Action flat_action) {
  ApplyActions(FlatJointActionToActions(flat_action));
}

std::vector<Action> SimMoveState::LegalFlatJointActions() const {
  ActionView view(*this);
  FlatJointActions flat_joint_actions = view.flat_joint_actions();
  std::vector<Action> joint_actions;
  joint_actions.reserve(flat_joint_actions.num_flat_joint_actions);
  for (Action flat_joint_action : flat_joint_actions) {
    joint_actions.push_back(flat_joint_action);
  }
  return joint_actions;
}

std::string SimMoveState::FlatJointActionToString(Action flat_action) const {
  // Assembles the string for each individual player action into a single
  // string. For example, [Heads, Tails] would mean than player 0 chooses Heads,
  // and player 1 chooses Tails.
  std::string str;
  for (auto player = Player{0}; player < num_players_; ++player) {
    if (!str.empty()) str.append(", ");
    const auto legal_actions = LegalActions(player);
    int num_actions = legal_actions.size();
    str.append(
        ActionToString(player, legal_actions[flat_action % num_actions]));
    flat_action /= num_actions;
  }
  return absl::StrCat("[", str, "]");
}

}  // namespace open_spiel
<|endoftext|>