<commit_before>/*
 * Copyright (C) 2009, Gostai S.A.S.
 *
 * This software is provided "as is" without warranty of any kind,
 * either expressed or implied, including but not limited to the
 * implied warranties of fitness for a particular purpose.
 *
 * See the LICENSE file for more information.
 */
#ifndef LIBPORT_SERIALIZE_BINARY_I_SERIALIZER_HXX
# define LIBPORT_SERIALIZE_BINARY_I_SERIALIZER_HXX

# include <vector>

# include <libport/arpa/inet.h>
# include <libport/contract.hh>
# include <libport/foreach.hh>
# include <libport/hierarchy.hh>
# include <libport/meta.hh>
# include <serialize/fwd.hh>

namespace libport
{
  namespace serialize
  {
    /*----------------.
    | Generic class.  |
    `----------------*/
    template <typename T>
    struct ICImpl
    {
      static T
      get(const std::string&, std::istream&, BinaryISerializer& ser)
      {
        return T(ser);
      }
    };

    /*------------.
    | Hierarchy.  |
    `------------*/
    template <typename T>
    struct ISerialize
    {
      static void
      res(const std::pair<typename T::root**, BinaryISerializer*>& c)
      {
        *c.first = reinterpret_cast<typename T::root*>(new T(*c.second));
      }
    };

    template <typename T>
    struct IHImpl
    {
      static T*
      get(const std::string&,
          std::istream&, BinaryISerializer& ser)
      {
        unsigned id = ser.unserialize<unsigned char>("id");
        typename T::root* res = 0;
        typedef std::pair<typename T::root**, BinaryISerializer*> Cookie;
        Cookie c(&res, &ser);
        T::template dispatch<ISerialize, Cookie>(id, c);
        return reinterpret_cast<T*>(res);
      }
    };

    /*-----------.
    | Fallback.  |
    `-----------*/
    template <typename T>
    struct BinaryISerializer::Impl
    {
      static typename meta::If<meta::Inherits<T, meta::BaseHierarchy>::res,
                               T*, T>::res
      get(const std::string& name,
          std::istream& output, BinaryISerializer& ser)
      {
        return meta::If<meta::Inherits<T, meta::BaseHierarchy>::res,
          IHImpl<T>, ICImpl<T> >::res::get(name, output, ser);
      }
    };

    /*-------.
    | char.  |
    `-------*/
    template <>
    struct BinaryISerializer::Impl<char>
    {
      static char get(const std::string&, std::istream& input,
                      BinaryISerializer&)
      {
        char res;
        input.read(&res, sizeof(char));
        return res;
      }
    };

    /*---------------------.
    | unsigned int/short.  |
    `---------------------*/
# define SERIALIZE_NET_INTEGRAL(Type, Function)                         \
    template <>                                                         \
    struct BinaryISerializer::Impl<Type>                                \
    {                                                                   \
      static Type                                                       \
      get(const std::string&, std::istream& input,                      \
          BinaryISerializer&)                                           \
      {                                                                 \
        Type normalized;                                                \
        input.read(reinterpret_cast<char*>(&normalized), sizeof(Type)); \
        return Function(normalized);                                    \
      }                                                                 \
    };

    SERIALIZE_NET_INTEGRAL(unsigned int,   ntohl);
    SERIALIZE_NET_INTEGRAL(unsigned short, ntohs);
#undef SERIALIZE_NET_INTEGRAL

    /*---------.
    | double.  |
    `---------*/
    template <>
    struct BinaryISerializer::Impl<double>
    {
      static double get(const std::string&, std::istream& input,
                        BinaryISerializer&)
      {
        // FIXME: non-portable
        double res;
        input.read(reinterpret_cast<char*>(&res), sizeof(double));
        return res;
      }
    };

#define BOUNCE(From, To)                                                \
    template <>                                                         \
    struct BinaryISerializer::Impl<From>                                \
    {                                                                   \
      static To get(const std::string& name,                            \
                    std::istream& input, BinaryISerializer& ser)        \
      {                                                                 \
        return static_cast<To>(Impl<From>::get(name, input, ser));      \
      }                                                                 \
    }

    BOUNCE(bool,          char);
    BOUNCE(unsigned char, char);
    BOUNCE(int,           unsigned);
    BOUNCE(short,         unsigned short);
#undef BOUNCE

    /*-----------.
    | Pointers.  |
    `-----------*/
    template <typename T>
    struct BinaryISerializer::PCImpl
    {
      static T*
      res(BinaryISerializer& ser, std::istream& input)
      {
        T* res = reinterpret_cast<T*>(new char[sizeof(T)]);
        ser.ptr_map_.push_back(res);
        // FIXME: copy ctor
        new (res) T(BinaryISerializer::Impl<T>::get("value", input, ser));
        return res;
      }
    };

    template <typename T>
    struct BinaryISerializer::PHImpl
    {
      static T*
      res(BinaryISerializer& ser, std::istream& input)
      {
        unsigned id = ser.ptr_map_.size();
        ser.ptr_map_.push_back(0);
        // FIXME: loops
        T* res = BinaryISerializer::Impl<T>::get("value", input, ser);
        ser.ptr_map_[id] = res;
        return res;
      }
    };

    template <typename T>
    struct BinaryISerializer::Impl<T*>
    {
      static T* get(const std::string&, std::istream& input,
                    BinaryISerializer& ser)
      {
        pointer_status status =
          static_cast<pointer_status>(Impl<char>::get("opt", input, ser));
        switch (status)
        {
          case null:
            return 0;
          case cached:
          {
            unsigned id = Impl<unsigned>::get("id", input, ser);
            assert(id < ser.ptr_map_.size());
            return reinterpret_cast<T*>(ser.ptr_map_[id]);
          }
          case serialized:
          {
            return meta::If<meta::Inherits<T, meta::BaseHierarchy>::res,
              PHImpl<T>, PCImpl<T> >::res
              ::res(ser, input);
          }
        }
        unreached();
      }
    };

    /*--------------.
    | std::string.  |
    `--------------*/
    template <>
    struct BinaryISerializer::Impl<std::string>
    {
      static std::string get(const std::string& name, std::istream& input,
                             BinaryISerializer& ser)
      {
        size_t l = Impl<unsigned short>::get(name, input, ser);
        // FIXME: alloca
        char* buf = new char[l];
        input.read(buf, l);
        std::string res(buf, l);
        delete [] buf;
        return res;
      }
    };

    /*--------------.
    | std::vector.  |
    `--------------*/
    template <typename T, typename A>
    struct BinaryISerializer::Impl<std::vector<T, A> >
    {
      static std::vector<T, A>
      get(const std::string& name,
          std::istream& input, BinaryISerializer& ser)
      {
        unsigned short size = Impl<unsigned short>::get(name, input, ser);
        std::vector<T, A> res;
        for (unsigned i = 0; i < size; ++i)
          res.push_back(Impl<T>::get(name, input, ser));
        return res;
      }
    };

    // Hash and Symbol serialization is defined here because of
    // serialization/hash/symbol dependency loop.

    /*------------------.
    | libport::Symbol.  |
    `------------------*/
    template <>
    struct BinaryISerializer::Impl<libport::Symbol>
    {
      static libport::Symbol
      get(const std::string& name, std::istream& input,
          BinaryISerializer& ser)
      {
        bool cached = Impl<bool>::get("opt", input, ser);
        if (cached)
        {
          unsigned id = Impl<unsigned>::get("id", input, ser);
          return ser.sym_map_[id];
        }
        Symbol res(Impl<std::string>::get(name, input, ser));
        ser.sym_map_.push_back(res);
        return res;
      }
    };

    /*----------------.
    | libport::hash.  |
    `----------------*/
    template <typename K, typename V>
    struct BinaryISerializer::Impl<libport::hash_map<K, V> >
    {
      typedef libport::hash_map<K, V> result_type;
      static result_type
      get(const std::string&, std::istream&, BinaryISerializer& ser)
      {
        typedef typename result_type::value_type Value;
        size_t size = ser.unserialize<unsigned short>("size");
        result_type res;
        for (unsigned i = 0; i < size; ++i)
        {
          K k = ser.template unserialize<K>("key");
          V v = ser.template unserialize<V>("value");
          res[k] = v;
        }
        return res;
      }
    };
  }
}

#endif
<commit_msg>Serialize: Fix.<commit_after>/*
 * Copyright (C) 2009, Gostai S.A.S.
 *
 * This software is provided "as is" without warranty of any kind,
 * either expressed or implied, including but not limited to the
 * implied warranties of fitness for a particular purpose.
 *
 * See the LICENSE file for more information.
 */
#ifndef LIBPORT_SERIALIZE_BINARY_I_SERIALIZER_HXX
# define LIBPORT_SERIALIZE_BINARY_I_SERIALIZER_HXX

# include <vector>

# include <libport/arpa/inet.h>
# include <libport/contract.hh>
# include <libport/foreach.hh>
# include <libport/hierarchy.hh>
# include <libport/meta.hh>
# include <serialize/fwd.hh>

namespace libport
{
  namespace serialize
  {
    /*----------------.
    | Generic class.  |
    `----------------*/
    template <typename T>
    struct ICImpl
    {
      static T
      get(const std::string&, std::istream&, BinaryISerializer& ser)
      {
        return T(ser);
      }
    };

    /*------------.
    | Hierarchy.  |
    `------------*/
    template <typename T>
    struct ISerialize
    {
      static void
      res(const std::pair<typename T::root**, BinaryISerializer*>& c)
      {
        *c.first = reinterpret_cast<typename T::root*>(new T(*c.second));
      }
    };

    template <typename T>
    struct IHImpl
    {
      static T*
      get(const std::string&,
          std::istream&, BinaryISerializer& ser)
      {
        unsigned id = ser.unserialize<unsigned char>("id");
        typename T::root* res = 0;
        typedef std::pair<typename T::root**, BinaryISerializer*> Cookie;
        Cookie c(&res, &ser);
        T::template dispatch<ISerialize, Cookie>(id, c);
        return reinterpret_cast<T*>(res);
      }
    };

    /*-----------.
    | Fallback.  |
    `-----------*/
    template <typename T>
    struct BinaryISerializer::Impl
    {
      static typename meta::If<meta::Inherits<T, meta::BaseHierarchy>::res,
                               T*, T>::res
      get(const std::string& name,
          std::istream& output, BinaryISerializer& ser)
      {
        return meta::If<meta::Inherits<T, meta::BaseHierarchy>::res,
          IHImpl<T>, ICImpl<T> >::res::get(name, output, ser);
      }
    };

    /*-------.
    | char.  |
    `-------*/
    template <>
    struct BinaryISerializer::Impl<char>
    {
      static char get(const std::string&, std::istream& input,
                      BinaryISerializer&)
      {
        char res;
        input.read(&res, sizeof(char));
        return res;
      }
    };

    /*---------------------.
    | unsigned int/short.  |
    `---------------------*/
# define SERIALIZE_NET_INTEGRAL(Type, Function)                         \
    template <>                                                         \
    struct BinaryISerializer::Impl<Type>                                \
    {                                                                   \
      static Type                                                       \
      get(const std::string&, std::istream& input,                      \
          BinaryISerializer&)                                           \
      {                                                                 \
        Type normalized;                                                \
        input.read(reinterpret_cast<char*>(&normalized), sizeof(Type)); \
        return Function(normalized);                                    \
      }                                                                 \
    };

    SERIALIZE_NET_INTEGRAL(unsigned int,   ntohl);
    SERIALIZE_NET_INTEGRAL(unsigned short, ntohs);
#undef SERIALIZE_NET_INTEGRAL

    /*---------.
    | double.  |
    `---------*/
    template <>
    struct BinaryISerializer::Impl<double>
    {
      static double get(const std::string&, std::istream& input,
                        BinaryISerializer&)
      {
        // FIXME: non-portable
        double res;
        input.read(reinterpret_cast<char*>(&res), sizeof(double));
        return res;
      }
    };

/// Define the handling of To using the implementation for From.
#define BOUNCE(From, To)                                                \
    template <>                                                         \
    struct BinaryISerializer::Impl<From>                                \
    {                                                                   \
      static From get(const std::string& name,                          \
                      std::istream& input, BinaryISerializer& ser)      \
      {                                                                 \
        return static_cast<From>(Impl<To>::get(name, input, ser));      \
      }                                                                 \
    }

    BOUNCE(bool,          char);
    BOUNCE(unsigned char, char);
    BOUNCE(int,           unsigned);
    BOUNCE(short,         unsigned short);
#undef BOUNCE

    /*-----------.
    | Pointers.  |
    `-----------*/
    template <typename T>
    struct BinaryISerializer::PCImpl
    {
      static T*
      res(BinaryISerializer& ser, std::istream& input)
      {
        T* res = reinterpret_cast<T*>(new char[sizeof(T)]);
        ser.ptr_map_.push_back(res);
        // FIXME: copy ctor
        new (res) T(BinaryISerializer::Impl<T>::get("value", input, ser));
        return res;
      }
    };

    template <typename T>
    struct BinaryISerializer::PHImpl
    {
      static T*
      res(BinaryISerializer& ser, std::istream& input)
      {
        unsigned id = ser.ptr_map_.size();
        ser.ptr_map_.push_back(0);
        // FIXME: loops
        T* res = BinaryISerializer::Impl<T>::get("value", input, ser);
        ser.ptr_map_[id] = res;
        return res;
      }
    };

    template <typename T>
    struct BinaryISerializer::Impl<T*>
    {
      static T* get(const std::string&, std::istream& input,
                    BinaryISerializer& ser)
      {
        pointer_status status =
          static_cast<pointer_status>(Impl<char>::get("opt", input, ser));
        switch (status)
        {
          case null:
            return 0;
          case cached:
          {
            unsigned id = Impl<unsigned>::get("id", input, ser);
            assert(id < ser.ptr_map_.size());
            return reinterpret_cast<T*>(ser.ptr_map_[id]);
          }
          case serialized:
          {
            return meta::If<meta::Inherits<T, meta::BaseHierarchy>::res,
              PHImpl<T>, PCImpl<T> >::res
              ::res(ser, input);
          }
        }
        unreached();
      }
    };

    /*--------------.
    | std::string.  |
    `--------------*/
    template <>
    struct BinaryISerializer::Impl<std::string>
    {
      static std::string get(const std::string& name, std::istream& input,
                             BinaryISerializer& ser)
      {
        size_t l = Impl<unsigned short>::get(name, input, ser);
        // FIXME: alloca
        char* buf = new char[l];
        input.read(buf, l);
        std::string res(buf, l);
        delete [] buf;
        return res;
      }
    };

    /*--------------.
    | std::vector.  |
    `--------------*/
    template <typename T, typename A>
    struct BinaryISerializer::Impl<std::vector<T, A> >
    {
      static std::vector<T, A>
      get(const std::string& name,
          std::istream& input, BinaryISerializer& ser)
      {
        unsigned short size = Impl<unsigned short>::get(name, input, ser);
        std::vector<T, A> res;
        for (unsigned i = 0; i < size; ++i)
          res.push_back(Impl<T>::get(name, input, ser));
        return res;
      }
    };

    // Hash and Symbol serialization is defined here because of
    // serialization/hash/symbol dependency loop.

    /*------------------.
    | libport::Symbol.  |
    `------------------*/
    template <>
    struct BinaryISerializer::Impl<libport::Symbol>
    {
      static libport::Symbol
      get(const std::string& name, std::istream& input,
          BinaryISerializer& ser)
      {
        bool cached = Impl<bool>::get("opt", input, ser);
        if (cached)
        {
          unsigned id = Impl<unsigned>::get("id", input, ser);
          return ser.sym_map_[id];
        }
        Symbol res(Impl<std::string>::get(name, input, ser));
        ser.sym_map_.push_back(res);
        return res;
      }
    };

    /*----------------.
    | libport::hash.  |
    `----------------*/
    template <typename K, typename V>
    struct BinaryISerializer::Impl<libport::hash_map<K, V> >
    {
      typedef libport::hash_map<K, V> result_type;
      static result_type
      get(const std::string&, std::istream&, BinaryISerializer& ser)
      {
        typedef typename result_type::value_type Value;
        size_t size = ser.unserialize<unsigned short>("size");
        result_type res;
        for (unsigned i = 0; i < size; ++i)
        {
          K k = ser.template unserialize<K>("key");
          V v = ser.template unserialize<V>("value");
          res[k] = v;
        }
        return res;
      }
    };
  }
}

#endif
<|endoftext|>


<commit_before>//Copyright (c) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "UnionFind.h" //The header we're implementing.

namespace cura
{

template <class E>
size_t UnionFind<E>::add(const E& item)
{
    items.push_back(item);
    size_t set_handle = parent_index.size(); //Guaranteed to be unique because there has never been any item with this index (can't remove from this data structure!)
    parent_index.push_back(set_handle);
    return set_handle;
}

template <class E>
size_t UnionFind<E>::find(const E& item) const
{
    const size_t index = element_to_position[item];
    const size_t parent = parent_index[index];
    if (parent == (size_t)-1) //This is a root.
    {
        return index;
    }
    //TODO: Implement path compression.
    return find(parent);
}

template <class E>
size_t UnionFind<E>::unite(const size_t first, const size_t second)
{
    parent_index[first] = second;
}

}<commit_msg>Also return in unite method<commit_after>//Copyright (c) 2018 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "UnionFind.h" //The header we're implementing.

namespace cura
{

template <class E>
size_t UnionFind<E>::add(const E& item)
{
    items.push_back(item);
    size_t set_handle = parent_index.size(); //Guaranteed to be unique because there has never been any item with this index (can't remove from this data structure!)
    parent_index.push_back(set_handle);
    return set_handle;
}

template <class E>
size_t UnionFind<E>::find(const E& item) const
{
    const size_t index = element_to_position[item];
    const size_t parent = parent_index[index];
    if (parent == (size_t)-1) //This is a root.
    {
        return index;
    }
    //TODO: Implement path compression.
    return find(parent);
}

template <class E>
size_t UnionFind<E>::unite(const size_t first, const size_t second)
{
    parent_index[first] = second;
    return second;
}

}<|endoftext|>


<commit_before>/*
	Flexisip, a flexible SIP proxy server with media capabilities.
	Copyright (C) 2010  Belledonne Communications SARL.

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as
	published by the Free Software Foundation, either version 3 of the
	License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "threadpool.hh"
#include "log/logmanager.hh"

// Constructor.
ThreadPool::ThreadPool(unsigned int threads, unsigned int max_queue_size)
	: max_queue_size(max_queue_size), terminate(false), stopped(false) {
	SLOGE << "[POOL] Init with " << threads << " threads and queue size " << max_queue_size;

	// Create number of required threads and add them to the thread pool vector.
	for (unsigned int i = 0; i < threads; i++) {
		threadPool.emplace_back(thread(&ThreadPool::Invoke, this));
	}
}

bool ThreadPool::Enqueue(function<void()> f) {
	bool enqueued = false;
	// Scope based locking.
	{
		// Put unique lock on task mutex.
		unique_lock<mutex> lock(tasksMutex);

		// Push task into queue.
		if (tasks.size() < max_queue_size) {

			SLOGE << "[POOL] Enqueue(" << tasks.size() << ")";

			tasks.push(f);
			enqueued = true;
		}
	}

	// Wake up one thread if the task was successfully queued
	if (enqueued)
		condition.notify_one();

	return enqueued;
}

bool ThreadPool::conditionCheck() const {
	return !tasks.empty() || terminate;
}

void ThreadPool::Invoke() {

	function<void()> task;
	while (true) {
		// Scope based locking.
		{
			// Put unique lock on task mutex.
			unique_lock<mutex> lock(tasksMutex);

			auto predicate = std::bind(&ThreadPool::conditionCheck, this);

			// Wait until queue is not empty or termination signal is sent.
			condition.wait(lock, predicate);

			// If termination signal received and queue is empty then exit else continue clearing the queue.
			if (terminate && tasks.empty()) {
				SLOGE << "[POOL] Terminate thread";
				return;
			}

			// Get next task in the queue.
			task = tasks.front();

			// Remove it from the queue.
			tasks.pop();
			SLOGE << "[POOL] Pop task " << tasks.size();
		}

		// Execute the task.
		SLOGE << "[POOL] Task()";
		task();
	}
}

void ThreadPool::ShutDown() {
	SLOGE << "[POOL] Shutdown";
	// Scope based locking.
	{
		// Put unique lock on task mutex.
		unique_lock<mutex> lock(tasksMutex);

		// Set termination flag to true.
		terminate = true;
	}

	// Wake up all threads.
	condition.notify_all();

	// Join all threads.
	for (thread &thread : threadPool) {
		thread.join();
	}

	// Empty workers vector.
	threadPool.empty();

	// Indicate that the pool has been shut down.
	stopped = true;
}

// Destructor.
ThreadPool::~ThreadPool() {
	if (!stopped) {
		ShutDown();
	}
}
<commit_msg>Fix another 'too recent c++' error<commit_after>/*
	Flexisip, a flexible SIP proxy server with media capabilities.
	Copyright (C) 2010  Belledonne Communications SARL.

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as
	published by the Free Software Foundation, either version 3 of the
	License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "threadpool.hh"
#include "log/logmanager.hh"

// Constructor.
ThreadPool::ThreadPool(unsigned int threads, unsigned int max_queue_size)
	: max_queue_size(max_queue_size), terminate(false), stopped(false) {
	SLOGE << "[POOL] Init with " << threads << " threads and queue size " << max_queue_size;

	// Create number of required threads and add them to the thread pool vector.
	for (unsigned int i = 0; i < threads; i++) {
		threadPool.emplace_back(thread(&ThreadPool::Invoke, this));
	}
}

bool ThreadPool::Enqueue(function<void()> f) {
	bool enqueued = false;
	// Scope based locking.
	{
		// Put unique lock on task mutex.
		unique_lock<mutex> lock(tasksMutex);

		// Push task into queue.
		if (tasks.size() < max_queue_size) {

			SLOGE << "[POOL] Enqueue(" << tasks.size() << ")";

			tasks.push(f);
			enqueued = true;
		}
	}

	// Wake up one thread if the task was successfully queued
	if (enqueued)
		condition.notify_one();

	return enqueued;
}

bool ThreadPool::conditionCheck() const {
	return !tasks.empty() || terminate;
}

void ThreadPool::Invoke() {

	function<void()> task;
	while (true) {
		// Scope based locking.
		{
			// Put unique lock on task mutex.
			unique_lock<mutex> lock(tasksMutex);

			auto predicate = std::bind(&ThreadPool::conditionCheck, this);

			// Wait until queue is not empty or termination signal is sent.
			condition.wait(lock, predicate);

			// If termination signal received and queue is empty then exit else continue clearing the queue.
			if (terminate && tasks.empty()) {
				SLOGE << "[POOL] Terminate thread";
				return;
			}

			// Get next task in the queue.
			task = tasks.front();

			// Remove it from the queue.
			tasks.pop();
			SLOGE << "[POOL] Pop task " << tasks.size();
		}

		// Execute the task.
		SLOGE << "[POOL] Task()";
		task();
	}
}

void ThreadPool::ShutDown() {
	SLOGE << "[POOL] Shutdown";
	// Scope based locking.
	{
		// Put unique lock on task mutex.
		unique_lock<mutex> lock(tasksMutex);

		// Set termination flag to true.
		terminate = true;
	}

	// Wake up all threads.
	condition.notify_all();

	// Join all threads.
	for (auto it = threadPool.begin(); it != threadPool.end(); ++it) {
		it->join();
	}

	// Empty workers vector.
	threadPool.empty();

	// Indicate that the pool has been shut down.
	stopped = true;
}

// Destructor.
ThreadPool::~ThreadPool() {
	if (!stopped) {
		ShutDown();
	}
}
<|endoftext|>


<commit_before>/********** tell emacs we use -*- c++ -*- style comments *******************
 $Revision: 1.1 $  $Author: trey $  $Date: 2006-04-07 19:40:41 $

 @file    solveMDP.cc
 @brief   No brief

 Copyright (c) 2002-2006, Trey Smith.  All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
 met:

 * The software may not be sold or incorporated into a commercial
   product without specific prior written permission.
 * The above copyright notice and this permission notice shall be
   included in all copies or substantial portions of the software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

 ***************************************************************************/

#include <assert.h>
#include <sys/time.h>
#include <getopt.h>

#include <iostream>
#include <fstream>

#include "Interleave.h"
#include "stdinInterface.h"

// search strategies
#include "RTDP.h"
#include "LRTDP.h"
#include "HDP.h"
#include "FRTDP.h"

// problem types
#include "RaceTrack.h"
#include "Pomdp.h"
#include "MDPSim.h"
#include "PomdpSim.h"

// value function representations
#include "PointBounds.h"
#include "ConvexBounds.h"

// initialization code
#include "RelaxUBInitializer.h"

using namespace std;
using namespace MatrixUtils;
using namespace zmdp;

struct SolveMDPParams {
  int strategy;
  int probType;
  int valueRepr;
  char *probName;
  int minOrder;
  int maxOrder;
  bool useFastParser;
  int numIterations;
  bool past_options;
  double targetPrecision;
  double minWait;
  bool useInterleave;
  bool useHeuristic;
};

struct EnumEntry {
  const char* key;
  int val;
};

enum StrategiesEnum {
  S_RTDP,
  S_LRTDP,
  S_HDP,
  S_FRTDP
};

EnumEntry strategiesG[] = {
  {"rtdp",  S_RTDP},
  {"lrtdp", S_LRTDP},
  {"hdp",   S_HDP},
  {"frtdp", S_FRTDP},
  {NULL, -1}
};

enum ProbTypesEnum {
  T_RACETRACK,
  T_POMDP
};

EnumEntry probTypesG[] = {
  {"racetrack", T_RACETRACK},
  {"pomdp",     T_POMDP},
  {NULL, -1}
};

enum ValueReprsEnum {
  V_POINT,
  V_CONVEX
};

EnumEntry valueReprsG[] = {
  {"point",  V_POINT},
  {"convex", V_CONVEX},
  {NULL, -1}
};

void usage(const char* cmdName);

int getEnum(const char* key, EnumEntry* table, const char* cmdName, char opt)
{
  EnumEntry* i = table;
  for (; NULL != i->key; i++) {
    if (0 == strcmp(i->key, key)) {
      return i->val;
    }
  }
  fprintf(stderr, "ERROR: invalid parameter %s for option -%c\n\n", key, opt);
  usage(cmdName);
  exit(EXIT_FAILURE);
}

bool endsWith(const std::string& s,
	      const std::string& suffix)
{
  if (s.size() < suffix.size()) return false;
  return (s.substr(s.size() - suffix.size()) == suffix);
}

void testBatchIncremental(const SolveMDPParams& p)
{

  cout << "CFLAGS = " << CFLAGS << endl;

  MDP* problem;
  AbstractSim* sim;
  switch (p.probType) {
  case T_RACETRACK:
    problem = new RaceTrack(p.probName);
    sim = new MDPSim(problem);
    break;
  case T_POMDP:
    problem = new Pomdp(p.probName, p.useFastParser);
    sim = new PomdpSim((Pomdp*) problem);
    break;
  default:
    assert(0); // never reach this point
  }

  // this is a bit of a hack...
  bool useLowerBound;
  switch (p.strategy) {
  case S_RTDP:
  case S_LRTDP:
    useLowerBound = false;
    break;
  case S_HDP:
  case S_FRTDP:
    useLowerBound = true;
    break;
  default:
    assert(0); // never reach this point
  };

  IncrementalBounds* bounds;
  PointBounds* pb;
  switch (p.valueRepr) {
  case V_POINT:
    pb = new PointBounds();
    AbstractBound* initUpperBound;
    if (p.useHeuristic && T_POMDP != p.probType) {
      initUpperBound = new RelaxUBInitializer(problem);
    } else {
      initUpperBound = problem->newUpperBound();
    }
    pb->setBounds(problem->newLowerBound(), initUpperBound);
    bounds = pb;
    break;
  case V_CONVEX:
    bounds = new ConvexBounds(useLowerBound);
    break;
  default:
    assert(0); // never reach this point
  };

  Solver* solver;
  switch (p.strategy) {
  case S_RTDP:
    solver = new RTDP();
    break;
  case S_LRTDP:
    solver = new LRTDP();
   break;
  case S_HDP:
    solver = new HDP();
    break;
  case S_FRTDP:
    solver = new FRTDP();
    break;
  default:
    assert(0); // never reach this point
  };
  ((RTDPCore*) solver)->setBounds(bounds);

  Interleave x;
  if (p.useInterleave) {
    x.interleave(/* numIterations = */ p.numIterations,
		 sim, *solver,
		 /* numSteps = */ 251,
		 /* targetPrecision = */ p.targetPrecision,
		 /* minWait = */ p.minWait,
		 /* outFileName = */ "scatter.plot",
		 /* boundsFileNameFmt = */ "plots/bounds%04d.plot",
		 /* simFileNameFmt = */ "plots/sim%04d.plot");
  } else {
    x.batchTestIncremental(/* numIterations = */ p.numIterations,
			   sim, *solver,
			   /* numSteps = */ 251,
			   /* targetPrecision = */ p.targetPrecision,
			   /* minOrder = */ p.minOrder,
			   /* maxOrder = */ p.maxOrder,
			   /* ticksPerOrder = */ 10,
			   /* outFileName = */ "inc.plot",
			   /* boundsFileName = */ "bounds.plot",
			   /* simFileName = */ "sim.plot");
  }

  x.printRewards();
}

void usage(const char* cmdName) {
  cerr <<
    "usage: " << cmdName << " OPTIONS <model>\n"
    "\n"
    "Main flags:\n"
    "  -s or --search         Specifies search strategy. Valid choices:\n"
    "                           rtdp, lrtdp, hdp, frtdp [default: frtdp]\n"
    "  -t or --type           Specifies problem type. Valid choices:\n"
    "                           racetrack, pomdp [default: infer from model filename]\n"
    "  -v or --value          Specifies value function representation. Valid choices\n"
    "                         depend on problem type. With -t pomdp, choices are:\n"
    "                           point, convex [default: convex]\n"
    "                         For other problem types, choices are:\n"
    "                           point [default: point]\n"
    "\n"
    "Other flags:\n"
    "  -h or --help           Print this help\n"
    "  --version              Print version information (CFLAGS used at compilation)\n"
    "  -f or --fast           Use fast (but very picky) alternate POMDP parser\n"
    "  -i or --iterations     Set number of simulation iterations [default: 100]\n"
    "  -p or --precision      Set target precision [default: 1e-3]\n"
    "  --heuristic            Use a non-trivial upper bound heuristic (interpretation\n"
    "                           depends on the problem)\n"
    "  --min-eval             If minEval=k, start logging policy evaluation epochs\n"
    "                           at 10^k seconds of policy improvement [default: 0].\n"
    "  --max-eval             If maxEval=k, end run after 10^k seconds of policy\n"
    "                           improvement [default: 3].\n"
    "\n"
    "Experimental flags (you probably don't want to use these):\n"
    "  --interleave           Test planner in interleaved mode\n"
    "  -w or --min-wait       Set minimum planning time between actions (for interleaving)\n"
    "\n"
    "Examples:\n"
    "  " << cmdName << " RockSample_4_4.pomdp\n"
    "  " << cmdName << " large-b.racetrack\n"
    "  " << cmdName << " --min-eval -1 RockSample_4_4.pomdp\n"
    "  " << cmdName << " --search lrtdp --value point RockSample_4_4.pomdp\n"
    "  " << cmdName << " -f RockSample_5_7.pomdp\n"
    "\n"
;
  exit(-1);
}

int main(int argc, char **argv) {
  init_matrix_utils();

  static char shortOptions[] = "hs:t:v:fi:p:w:";
  static struct option longOptions[]={
    {"help",      0,NULL,'h'},
    {"search",    1,NULL,'s'},
    {"type",      1,NULL,'t'},
    {"value",     1,NULL,'v'},
    {"version",   0,NULL,'V'},
    {"fast",      0,NULL,'f'},
    {"iterations",1,NULL,'i'},
    {"precision", 1,NULL,'p'},
    {"heuristic", 0,NULL,'H'},
    {"min-eval",  1,NULL,'X'},
    {"max-eval",  1,NULL,'Y'},
    {"interleave",1,NULL,'I'},
    {"min-wait",  1,NULL,'w'},
    {NULL,0,0,0}
  };

  SolveMDPParams p;
  p.strategy = S_FRTDP;
  p.probType = -1;
  p.valueRepr = -1;
  p.probName = NULL;
  p.minOrder = -999;
  p.maxOrder = -999;
  p.useFastParser = false;
  p.numIterations = 100;
  p.targetPrecision = 1e-3;
  p.minWait = 0;
  p.useInterleave = false;
  p.useHeuristic = false;

  while (1) {
    char optchar = getopt_long(argc,argv,shortOptions,longOptions,NULL);
    if (optchar == -1) break;

    switch (optchar) {
    case 'h': // help
      usage(argv[0]);
      break;
    case 's': // search
      p.strategy = getEnum(optarg, strategiesG, argv[0], optchar);
      break;
    case 't': // type
      p.probType = getEnum(optarg, probTypesG, argv[0], optchar);
      break;
    case 'v': // value
      p.valueRepr = getEnum(optarg, valueReprsG, argv[0], optchar);
      break;
    case 'V': // version
      cout << "CFLAGS = " << CFLAGS << endl;
      exit(EXIT_SUCCESS);
      break;
    case 'f': // fast
      p.useFastParser = true;
      break;
    case 'i': // iterations
      p.numIterations = atoi(optarg);
      break;
    case 'p': // precision
      p.targetPrecision = atof(optarg);
      break;
    case 'H': // heuristic
      p.useHeuristic = true;
      break;
    case 'X': // min-eval
      p.minOrder = atoi(optarg);
      break;
    case 'Y': // max-eval
      p.maxOrder = atoi(optarg);
      break;
    case 'I': // interleave
      p.useInterleave = true;
      break;
    case 'w': // min-wait
      p.minWait = atof(optarg);
      break;

    case '?': // unknown option
    case ':': // option with missing parameter
      // getopt() prints an informative error message
      cerr << endl;
      usage(argv[0]);
      break;
    default:
      abort(); // never reach this point
    }
  }
  if (argc-optind != 1) {
    cerr << "ERROR: wrong number of arguments (should be 1)" << endl << endl;
    usage(argv[0]);
  }

  p.probName = argv[optind++];

  // fill in default and inferred values
  if (-1 == p.probType) {
    if (endsWith(p.probName, ".racetrack")) {
      p.probType = T_RACETRACK;
    } else if (endsWith(p.probName, ".pomdp")) {
      p.probType = T_POMDP;
    } else {
      fprintf(stderr, "ERROR: couldn't infer problem type from problem filename %s\n\n",
	      p.probName);
      usage(argv[0]);
      exit(EXIT_FAILURE);
    }
  }
  if (-1 == p.valueRepr) {
    if (T_POMDP == p.probType) {
      p.valueRepr = V_CONVEX;
    } else {
      p.valueRepr = V_POINT;
    }
  }
  if (-999 == p.minOrder) {
    p.minOrder = 0; // default
  }
  if (-999 == p.maxOrder) {
    p.maxOrder = 3; // default
  }

  cerr << "done arg parsing" << endl;

  testBatchIncremental(p);

  // signal we are done
  FILE *fp = fopen("/tmp/solveMDP_done", "w");
  fprintf(fp, "success\n");
  fclose(fp);

  return 0;
}

/***************************************************************************
 * REVISION HISTORY:
 * $Log: not supported by cvs2svn $
 *
 ***************************************************************************/
<commit_msg>solveMDP now uses a strong heuristic by default; improved usage() help<commit_after>/********** tell emacs we use -*- c++ -*- style comments *******************
 $Revision: 1.2 $  $Author: trey $  $Date: 2006-04-07 20:15:40 $

 @file    solveMDP.cc
 @brief   No brief

 Copyright (c) 2002-2006, Trey Smith.  All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
 met:

 * The software may not be sold or incorporated into a commercial
   product without specific prior written permission.
 * The above copyright notice and this permission notice shall be
   included in all copies or substantial portions of the software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

 ***************************************************************************/

#include <assert.h>
#include <sys/time.h>
#include <getopt.h>

#include <iostream>
#include <fstream>

#include "Interleave.h"
#include "stdinInterface.h"

// search strategies
#include "RTDP.h"
#include "LRTDP.h"
#include "HDP.h"
#include "FRTDP.h"

// problem types
#include "RaceTrack.h"
#include "Pomdp.h"
#include "MDPSim.h"
#include "PomdpSim.h"

// value function representations
#include "PointBounds.h"
#include "ConvexBounds.h"

// initialization code
#include "RelaxUBInitializer.h"

using namespace std;
using namespace MatrixUtils;
using namespace zmdp;

struct SolveMDPParams {
  int strategy;
  int probType;
  int valueRepr;
  char *probName;
  int minOrder;
  int maxOrder;
  bool useFastParser;
  int numIterations;
  bool past_options;
  double targetPrecision;
  double minWait;
  bool useInterleave;
  bool useHeuristic;
};

struct EnumEntry {
  const char* key;
  int val;
};

enum StrategiesEnum {
  S_RTDP,
  S_LRTDP,
  S_HDP,
  S_FRTDP
};

EnumEntry strategiesG[] = {
  {"rtdp",  S_RTDP},
  {"lrtdp", S_LRTDP},
  {"hdp",   S_HDP},
  {"frtdp", S_FRTDP},
  {NULL, -1}
};

enum ProbTypesEnum {
  T_RACETRACK,
  T_POMDP
};

EnumEntry probTypesG[] = {
  {"racetrack", T_RACETRACK},
  {"pomdp",     T_POMDP},
  {NULL, -1}
};

enum ValueReprsEnum {
  V_POINT,
  V_CONVEX
};

EnumEntry valueReprsG[] = {
  {"point",  V_POINT},
  {"convex", V_CONVEX},
  {NULL, -1}
};

void usage(const char* cmdName);

int getEnum(const char* key, EnumEntry* table, const char* cmdName, char opt)
{
  EnumEntry* i = table;
  for (; NULL != i->key; i++) {
    if (0 == strcmp(i->key, key)) {
      return i->val;
    }
  }
  fprintf(stderr, "ERROR: invalid parameter %s for option -%c\n\n", key, opt);
  usage(cmdName);
  exit(EXIT_FAILURE);
}

bool endsWith(const std::string& s,
	      const std::string& suffix)
{
  if (s.size() < suffix.size()) return false;
  return (s.substr(s.size() - suffix.size()) == suffix);
}

void testBatchIncremental(const SolveMDPParams& p)
{

  cout << "CFLAGS = " << CFLAGS << endl;

  MDP* problem;
  AbstractSim* sim;
  switch (p.probType) {
  case T_RACETRACK:
    problem = new RaceTrack(p.probName);
    sim = new MDPSim(problem);
    break;
  case T_POMDP:
    problem = new Pomdp(p.probName, p.useFastParser);
    sim = new PomdpSim((Pomdp*) problem);
    break;
  default:
    assert(0); // never reach this point
  }

  // this is a bit of a hack...
  bool useLowerBound;
  switch (p.strategy) {
  case S_RTDP:
  case S_LRTDP:
    useLowerBound = false;
    break;
  case S_HDP:
  case S_FRTDP:
    useLowerBound = true;
    break;
  default:
    assert(0); // never reach this point
  };

  IncrementalBounds* bounds;
  PointBounds* pb;
  switch (p.valueRepr) {
  case V_POINT:
    pb = new PointBounds();
    AbstractBound* initUpperBound;
    if (p.useHeuristic && T_POMDP != p.probType) {
      initUpperBound = new RelaxUBInitializer(problem);
    } else {
      initUpperBound = problem->newUpperBound();
    }
    pb->setBounds(problem->newLowerBound(), initUpperBound);
    bounds = pb;
    break;
  case V_CONVEX:
    bounds = new ConvexBounds(useLowerBound);
    break;
  default:
    assert(0); // never reach this point
  };

  Solver* solver;
  switch (p.strategy) {
  case S_RTDP:
    solver = new RTDP();
    break;
  case S_LRTDP:
    solver = new LRTDP();
   break;
  case S_HDP:
    solver = new HDP();
    break;
  case S_FRTDP:
    solver = new FRTDP();
    break;
  default:
    assert(0); // never reach this point
  };
  ((RTDPCore*) solver)->setBounds(bounds);

  Interleave x;
  if (p.useInterleave) {
    x.interleave(/* numIterations = */ p.numIterations,
		 sim, *solver,
		 /* numSteps = */ 251,
		 /* targetPrecision = */ p.targetPrecision,
		 /* minWait = */ p.minWait,
		 /* outFileName = */ "scatter.plot",
		 /* boundsFileNameFmt = */ "plots/bounds%04d.plot",
		 /* simFileNameFmt = */ "plots/sim%04d.plot");
  } else {
    x.batchTestIncremental(/* numIterations = */ p.numIterations,
			   sim, *solver,
			   /* numSteps = */ 251,
			   /* targetPrecision = */ p.targetPrecision,
			   /* minOrder = */ p.minOrder,
			   /* maxOrder = */ p.maxOrder,
			   /* ticksPerOrder = */ 10,
			   /* outFileName = */ "inc.plot",
			   /* boundsFileName = */ "bounds.plot",
			   /* simFileName = */ "sim.plot");
  }

  x.printRewards();
}

void usage(const char* cmdName) {
  cerr <<
    "usage: " << cmdName << " OPTIONS <model>\n"
    "\n"
    "Main flags:\n"
    "  -s or --search         Specifies search strategy. Valid choices:\n"
    "                           rtdp, lrtdp, hdp, frtdp [default: frtdp]\n"
    "  -t or --type           Specifies problem type. Valid choices:\n"
    "                           racetrack, pomdp [default: infer from model filename]\n"
    "  -v or --value          Specifies value function representation. Valid choices\n"
    "                         depend on problem type. With -t pomdp, choices are:\n"
    "                           point, convex [default: convex]\n"
    "                         For other problem types, choices are:\n"
    "                           point [default: point]\n"
    "\n"
    "Other flags:\n"
    "  -h or --help           Print this help\n"
    "  --version              Print version information (CFLAGS used at compilation)\n"
    "  -f or --fast           Use fast (but very picky) alternate POMDP parser\n"
    "  -i or --iterations     Set number of simulation iterations at each policy\n"
    "                           evaluation epoch [default: 100]\n"
    "  -p or --precision      Set target precision in solution quality; run ends when\n"
    "                           target is reached [default: 1e-3]\n"
    "  --weak-heuristic       Avoid spending time generating a good upper bound heuristic\n"
    "                           (only valid for some problems, interpretation depends on\n"
    "                            the problem; e.g. sets h_U = 0 for racetrack)\n"
    "  --min-eval             If minEval=k, start logging policy evaluation epochs\n"
    "                           after 10^k seconds of policy improvement [default: 0]\n"
    "  --max-eval             If maxEval=k, run ends after at most 10^k seconds of policy\n"
    "                           improvement [default: 3]\n"
    "\n"
    "Experimental flags (you probably don't want to use these):\n"
    "  --interleave           Test planner in interleaved mode\n"
    "  -w or --min-wait       Set minimum planning time between actions (for interleaving)\n"
    "\n"
    "Examples:\n"
    "  " << cmdName << " RockSample_4_4.pomdp\n"
    "  " << cmdName << " large-b.racetrack\n"
    "  " << cmdName << " --min-eval -1 RockSample_4_4.pomdp\n"
    "  " << cmdName << " --search lrtdp --value point RockSample_4_4.pomdp\n"
    "  " << cmdName << " -f RockSample_5_7.pomdp\n"
    "\n"
;
  exit(-1);
}

int main(int argc, char **argv) {
  init_matrix_utils();

  static char shortOptions[] = "hs:t:v:fi:p:w:";
  static struct option longOptions[]={
    {"help",          0,NULL,'h'},
    {"search",        1,NULL,'s'},
    {"type",          1,NULL,'t'},
    {"value",         1,NULL,'v'},
    {"version",       0,NULL,'V'},
    {"fast",          0,NULL,'f'},
    {"iterations",    1,NULL,'i'},
    {"precision",     1,NULL,'p'},
    {"weak-heuristic",0,NULL,'W'},
    {"min-eval",      1,NULL,'X'},
    {"max-eval",      1,NULL,'Y'},
    {"interleave",    1,NULL,'I'},
    {"min-wait",      1,NULL,'w'},
    {NULL,0,0,0}
  };

  SolveMDPParams p;
  p.strategy = S_FRTDP;
  p.probType = -1;
  p.valueRepr = -1;
  p.probName = NULL;
  p.minOrder = -999;
  p.maxOrder = -999;
  p.useFastParser = false;
  p.numIterations = 100;
  p.targetPrecision = 1e-3;
  p.minWait = 0;
  p.useInterleave = false;
  p.useHeuristic = true;

  while (1) {
    char optchar = getopt_long(argc,argv,shortOptions,longOptions,NULL);
    if (optchar == -1) break;

    switch (optchar) {
    case 'h': // help
      usage(argv[0]);
      break;
    case 's': // search
      p.strategy = getEnum(optarg, strategiesG, argv[0], optchar);
      break;
    case 't': // type
      p.probType = getEnum(optarg, probTypesG, argv[0], optchar);
      break;
    case 'v': // value
      p.valueRepr = getEnum(optarg, valueReprsG, argv[0], optchar);
      break;
    case 'V': // version
      cout << "CFLAGS = " << CFLAGS << endl;
      exit(EXIT_SUCCESS);
      break;
    case 'f': // fast
      p.useFastParser = true;
      break;
    case 'i': // iterations
      p.numIterations = atoi(optarg);
      break;
    case 'p': // precision
      p.targetPrecision = atof(optarg);
      break;
    case 'W': // weak-heuristic
      p.useHeuristic = false;
      break;
    case 'X': // min-eval
      p.minOrder = atoi(optarg);
      break;
    case 'Y': // max-eval
      p.maxOrder = atoi(optarg);
      break;
    case 'I': // interleave
      p.useInterleave = true;
      break;
    case 'w': // min-wait
      p.minWait = atof(optarg);
      break;

    case '?': // unknown option
    case ':': // option with missing parameter
      // getopt() prints an informative error message
      cerr << endl;
      usage(argv[0]);
      break;
    default:
      abort(); // never reach this point
    }
  }
  if (argc-optind != 1) {
    cerr << "ERROR: wrong number of arguments (should be 1)" << endl << endl;
    usage(argv[0]);
  }

  p.probName = argv[optind++];

  // fill in default and inferred values
  if (-1 == p.probType) {
    if (endsWith(p.probName, ".racetrack")) {
      p.probType = T_RACETRACK;
    } else if (endsWith(p.probName, ".pomdp")) {
      p.probType = T_POMDP;
    } else {
      fprintf(stderr, "ERROR: couldn't infer problem type from problem filename %s\n\n",
	      p.probName);
      usage(argv[0]);
      exit(EXIT_FAILURE);
    }
  }
  if (-1 == p.valueRepr) {
    if (T_POMDP == p.probType) {
      p.valueRepr = V_CONVEX;
    } else {
      p.valueRepr = V_POINT;
    }
  }
  if (-999 == p.minOrder) {
    p.minOrder = 0; // default
  }
  if (-999 == p.maxOrder) {
    p.maxOrder = 3; // default
  }

  cerr << "done arg parsing" << endl;

  testBatchIncremental(p);

  // signal we are done
  FILE *fp = fopen("/tmp/solveMDP_done", "w");
  fprintf(fp, "success\n");
  fclose(fp);

  return 0;
}

/***************************************************************************
 * REVISION HISTORY:
 * $Log: not supported by cvs2svn $
 * Revision 1.1  2006/04/07 19:40:41  trey
 * switched back to a unified binary for all algorithms, turns out it cuts down on code maintenance
 *
 *
 ***************************************************************************/
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
//#include <codecvt>
//#include <cstddef>
#include <locale>
#include <string>
#include <iomanip>
#include <ctime>

#include "cp-neural.h"

using std::wcout; using std::wstring;
using std::cerr; using std::vector;

class Text {
private:
    bool isinit=false;
    bool readDataFile(string inputfile) {
        // XXX: srand should be something more centrally chosen.
        std::srand(std::time(0));
        std::wifstream txtfile(inputfile, std::ios::binary);
        if (!txtfile.is_open()) {
            return false;
        }
        txtfile.imbue(std::locale("en_US.UTF8"));
        wstring ttext((std::istreambuf_iterator<wchar_t>(txtfile)),
                     std::istreambuf_iterator<wchar_t>());
        txtfile.close();
        text=ttext;
        filename=inputfile;
        return true;
    }
public:
    wstring text;
    string filename;
    std::map<wchar_t,int> freq;
    std::map<wchar_t,int> w2v;
    std::map<int,wchar_t> v2w;
    Text(string filename) {
        if (readDataFile(filename)) {
            for (auto wc : text) {
                freq[wc]++;
            }
            int it=0;
            for (auto wc : freq) {
                w2v[wc.first]=it;
                v2w[it]=wc.first;
                ++it;
            }
            isinit=true;
            cerr << "Freq:" << freq.size() << ", w2v:" << w2v.size() << ", v2w:" << v2w.size() << endl;
        }
    }
    ~Text() {

    }
    bool isInit() {
        return isinit;
    }
    int vocsize() {
        if (!isinit) return 0;
        return v2w.size();
    }

    wstring sample(int len) {
        int p=std::rand()%(text.size()-len);
        return text.substr(p,len);
    }
};

int main(int argc, char *argv[]) {
    std::setlocale (LC_ALL, "");
    wcout << L"Rnn-Readär" << std::endl;

    bool allOk=true;
    if (argc!=2) {
        cerr << "rnnreader <path-to-text-file>" << endl;
        exit(-1);
    }
    Text txt(argv[1]);
    if (!txt.isInit()) {
        cerr << "Cannot initialize Text object from inputfile: " << argv[1] << endl;
        exit(-1);
    }

    wcout << L"Text size: " << txt.text.size() << endl;

    wcout << L"Size of vocabulary:" << txt.freq.size() << std::endl;
/*    for (auto f : txt.freq) {
        int c=(int)f.first;
        wstring wc(1,f.first);
        wcout << wc << L"|" <<  wchar_t(f.first) << L"(0x" << std::hex << c << L")" ": " << std::dec <<  f.second << endl;
    }
*/
    Color::Modifier red(Color::FG_RED);
    Color::Modifier green(Color::FG_GREEN);
    Color::Modifier def(Color::FG_DEFAULT);

    int T=20;
    int N=txt.text.size() / (T+1);

    MatrixN Xr(N,T);
    MatrixN yr(N,T);

    wstring chunk,chunky;
    int n=0;
    for (int i=0; i<N; i++) {
        wstring smp = txt.sample(T+1);
        chunk=smp.substr(0,T);
        chunky=smp.substr(1,T);
        for (int t=0; t<T; t++) {
            Xr(i,t)=txt.w2v[chunk[t]];
            yr(i,t)=txt.w2v[chunky[t]];
            ++n;
        }
    }

    int maxN = 50000;
    if (n>maxN) n=maxN;
    int n1=n*0.9;
    int dn=(n-n1)/2;

    cerr << n1 << " datasets" << endl;

/*    MatrixN X(n1,T);
    MatrixN y(n1,T);
    MatrixN Xv(dn,T);
    MatrixN yv(dn,T);
    MatrixN Xt(dn,T);
    MatrixN yt(dn,T);
*/
    MatrixN X=Xr.block(0,0,n1,T);
    MatrixN y=yr.block(0,0,n1,T);
    MatrixN Xv=Xr.block(n1,0,dn,T);
    MatrixN yv=yr.block(n1,0,dn,T);
    MatrixN Xt=Xr.block(n1+dn,0,dn,T);
    MatrixN yt=yr.block(n1+dn,0,dn,T);

    cpInitCompute("Rnnreader");
    registerLayers();

    LayerBlock lb("{name='rnnreader';init='normal';initfactor=0.5}");
    int VS=txt.vocsize();
    int H=48;
    int D=16;
    int BS=32;
    float clip=5.0;

    CpParams cp0;
    cp0.setPar("inputShape",vector<int>{T});
    cp0.setPar("V",VS);
    cp0.setPar("D",D);
    cp0.setPar("clip",clip);
    cp0.setPar("init",(string)"orthonormal");
    lb.addLayer("WordEmbedding","WE0",cp0,{"input"});

    CpParams cp1;
    cp1.setPar("inputShape",vector<int>{D,T});
    cp1.setPar("N",BS);
    cp1.setPar("H",H);
    cp1.setPar("clip",clip);
    cp1.setPar("initfactor","0.01");
    lb.addLayer("RNN","rnn1",cp1,{"WE0"});

    CpParams cp2;
    cp2.setPar("inputShape",vector<int>{H,T});
    cp2.setPar("N",BS);
    cp2.setPar("H",H);
    cp2.setPar("clip",clip);
//    lb.addLayer("RNN","rnn2",cp2,{"rnn1"});

    CpParams cp3;
    cp3.setPar("inputShape",vector<int>{H,T});
    cp3.setPar("N",BS);
    cp3.setPar("H",H);
    cp3.setPar("clip",clip);
//    lb.addLayer("RNN","rnn3",cp3,{"rnn2"});

    CpParams cp10;
    cp10.setPar("inputShape",vector<int>{H,T});
    //cp10.setPar("T",T);
    //cp10.setPar("D",H);
    cp10.setPar("M",VS);
    lb.addLayer("TemporalAffine","af1",cp10,{"rnn1"});

    CpParams cp11;
    cp11.setPar("inputShape",vector<int>{VS,T});
    lb.addLayer("TemporalSoftmax","sm1",cp11,{"af1"});

    if (!lb.checkTopology(true)) {
        allOk=false;
        cerr << red << "Topology-check for LayerBlock: ERROR." << def << endl;
    } else {
        cerr << green << "Topology-check for LayerBlock: ok." << def << endl;
    }

/*    wstring chunk;
    chunk = txt.text.substr(512,128);
    wcout << chunk << endl;
*/
    // preseverstates not necessary for training!
    CpParams cpo("{verbose=true;shuffle=false;preservestates=false;epsilon=1e-8}");
    // CpParams cpo("{verbose=false;epsilon=1e-8}");
    cpo.setPar("learning_rate", (floatN)4e-3); //2.2e-2);
    cpo.setPar("lr_decay", (floatN)0.95);
    //cpo.setPar("regularization", (floatN)1.);

    floatN dep=20.0;
    floatN sep=0.0;
    cpo.setPar("epochs",(floatN)dep);
    cpo.setPar("batch_size",BS);

    MatrixN xg(1,1);
    MatrixN xg2(1,1);
    wstring sg;
    for (int i=0; i<10000; i++) {

        cpo.setPar("startepoch", (floatN)sep);
        //cpo.setPar("maxthreads", (int)1);  // XXX: can be increased now!
        t_cppl states;
        t_cppl statesv;
        states["y"] = new MatrixN(y);
        statesv["y"] = new MatrixN(yv);
        floatN cAcc=lb.train(X, &states, Xv, &statesv, "Adam", cpo);
        cppl_delete(&states);
        cppl_delete(&statesv);

        sep+=dep;
/*
        int pos=rand() % 1000 + 5000;
        wstring instr=txt.text.substr(pos,T);

        xg(0,0)='A'+rand()%20;

        //xg(0,0)=txt.w2v[sg[0]];
        // lb.cp.setPar("T-Steps",1);
*//*        Layer *prnn;
        prnn=lb.layerMap["WE0"];
        // prnn->cp.setPar("T-Steps",1);
        prnn=lb.layerMap["rnn1"];
        // prnn->cp.setPar("T-Steps",1);
        prnn=lb.layerMap["rnn2"];
        // prnn->cp.setPar("T-Steps",1);
        prnn=lb.layerMap["rnn3"];
        // prnn->cp.setPar("T-Steps",1);
        prnn=lb.layerMap["af1"];
        // prnn->cp.setPar("T-Steps",1);
        prnn=lb.layerMap["sm1"];
        // prnn->cp.setPar("T-Steps",1);
*//*
        int TT=1;
        for (int rep=0; rep<3; rep++) {
            MatrixN rnn1_ho = MatrixN(1,TT);
            rnn1_ho.setZero();
            MatrixN rnn2_ho = MatrixN(1,TT);
            rnn2_ho.setZero();
            MatrixN rnn3_ho = MatrixN(1,TT);
            rnn3_ho.setZero();
            /*cppl_set(&cache,"rnn2-ho",new MatrixN(1,TT));
            cache["rnn2-ho"]->setZero();
            cppl_set(&cache,"rnn3-ho",new MatrixN(1,TT));
            cache["rnn3-ho"]->setZero();*/
        /*    cerr << "------------" << rep << "--------------------------" << endl;
            for (int g=0; g<200; g++) {
                t_cppl cache{};
                t_cppl states{};
                //wcout << g << L">";
                MatrixN z(0,0);
                cppl_set(&states,"rnn1-hoi",new MatrixN(rnn1_ho));
                //cppl_set(&cache,"rnn2-hoi",new MatrixN(rnn2_ho));
                //cppl_set(&cache,"rnn3-hoi",new MatrixN(rnn3_ho));

                //for (int i; i<xg.cols(); i++) wcout << txt.v2w[xg(0,i)];
                //wcout << L"<" << endl << L">";
                MatrixN probst=lb.forward(xg,&cache, &states); //&cach  e);
                MatrixN probsd=MatrixN(N*TT,VS);
                for (int n=0; n<1; n++) {
                    for (int t=0; t<TT; t++) {
                        for (int d=0; d<VS; d++) {
                            probsd(n*TT+t,d)=probst(n,t*VS+d);
                        }
                    }
                }

                for (int t=0; t<TT; t++) {
                    vector<floatN> probs(VS);
                    vector<floatN> index(VS);
                    for (int d=0; d<VS; d++) {
                        probs[d]=probsd(0*TT+t,d);
                        index[d]=d;
                    }
                    int ind=(int)index[randomChoice(index, probs)];
*//*                    float mx=-1000.0;
                    int ind=-1;
                    for (int d=0; d<VS; d++) {
                        floatN p=yg(0,t*D+d);
                        floatN pr=p*((floatN)(rand()%100)/5000.0+0.98);
                        if (pr>mx) {
                            mx=pr; // yg(0,t*D+d);
                            ind=d;
                        }
                    }
*//*
                    wchar_t cw=txt.v2w[ind];
                    //if (t==0) wcout << L"[" << cw << L"<";
                    //wcout << L"<" << cw << L">";
                    if (t==0) wcout << cw;  //  << L"(" << ind << L")";
                    // if (ind==0) cerr << "probs: " << probs << endl;
                    xg2(0,t)=ind;
                }
                //wcout << L"<" << endl;

                //for (int t=T-1; t>0; t--) xg(0,t)=xg(0,t-1);
                //for (int t=0; t< T-1; t++) xg(0,t)=xg(0,t+1);
                xg(0,0)=xg2(0,0);

                rnn1_ho=*(cache["rnn1-ho"]);
                //rnn2_ho=*(cache["rnn2-ho"]);
                //rnn3_ho=*(cache["rnn3-ho"]);
                //xg=xg2;
                cppl_delete(&cache);
                cppl_delete(&states);
            }
            //cache.clear();
            wcout << endl;
        }
        cerr << "setting eliminated T-Steps param" << endl;
        lb.cp.setPar("T-Steps",T);

        prnn=lb.layerMap["WE0"];
        // prnn->cp.setPar("T-Steps",T);
        prnn=lb.layerMap["rnn1"];
        // kprnn->cp.setPar("T-Steps",T);
        prnn=lb.layerMap["rnn2"];
        // prnn->cp.setPar("T-Steps",T);
        prnn=lb.layerMap["rnn3"];
        // prnn->cp.setPar("T-Steps",T);
        prnn=lb.layerMap["af1"];
        // prnn->cp.setPar("T-Steps",T);
        prnn=lb.layerMap["sm1"];
        // prnn->cp.setPar("T-Steps",T);
*/
    }
    /*
    floatN train_err, val_err, test_err;
    bool evalFinal=true;
    if (evalFinal) {
        train_err=lb.test(X, y, cpo.getPar("batch_size", 50));
        val_err=lb.test(Xv, yv, cpo.getPar("batch_size", 50));
        test_err=lb.test(Xt, yt, cpo.getPar("batch_size", 50));

        cerr << "Final results on RnnReader after " << cpo.getPar("epochs",(floatN)0.0) << " epochs:" << endl;
        cerr << "      Train-error: " << train_err << " train-acc: " << 1.0-train_err << endl;
        cerr << " Validation-error: " << val_err <<   "   val-acc: " << 1.0-val_err << endl;
        cerr << "       Test-error: " << test_err <<  "  test-acc: " << 1.0-test_err << endl;
    }
    //return cAcc;
    */
    cpExitCompute();
}
<commit_msg>ongoing 2<commit_after>#include <iostream>
#include <fstream>
//#include <codecvt>
//#include <cstddef>
#include <locale>
#include <string>
#include <iomanip>
#include <ctime>

#include "cp-neural.h"

using std::wcout; using std::wstring;
using std::cerr; using std::vector;

class Text {
private:
    bool isinit=false;
    bool readDataFile(string inputfile) {
        // XXX: srand should be something more centrally chosen.
        std::srand(std::time(0));
        std::wifstream txtfile(inputfile, std::ios::binary);
        if (!txtfile.is_open()) {
            return false;
        }
        txtfile.imbue(std::locale("en_US.UTF8"));
        wstring ttext((std::istreambuf_iterator<wchar_t>(txtfile)),
                     std::istreambuf_iterator<wchar_t>());
        txtfile.close();
        text=ttext;
        filename=inputfile;
        return true;
    }
public:
    wstring text;
    string filename;
    std::map<wchar_t,int> freq;
    std::map<wchar_t,int> w2v;
    std::map<int,wchar_t> v2w;
    Text(string filename) {
        if (readDataFile(filename)) {
            for (auto wc : text) {
                freq[wc]++;
            }
            int it=0;
            for (auto wc : freq) {
                w2v[wc.first]=it;
                v2w[it]=wc.first;
                ++it;
            }
            isinit=true;
            cerr << "Freq:" << freq.size() << ", w2v:" << w2v.size() << ", v2w:" << v2w.size() << endl;
        }
    }
    ~Text() {

    }
    bool isInit() {
        return isinit;
    }
    int vocsize() {
        if (!isinit) return 0;
        return v2w.size();
    }

    wstring sample(int len) {
        int p=std::rand()%(text.size()-len);
        return text.substr(p,len);
    }
};

int main(int argc, char *argv[]) {
    std::setlocale (LC_ALL, "");
    wcout << L"Rnn-Readär" << std::endl;

    bool allOk=true;
    if (argc!=2) {
        cerr << "rnnreader <path-to-text-file>" << endl;
        exit(-1);
    }
    Text txt(argv[1]);
    if (!txt.isInit()) {
        cerr << "Cannot initialize Text object from inputfile: " << argv[1] << endl;
        exit(-1);
    }

    wcout << L"Text size: " << txt.text.size() << endl;

    wcout << L"Size of vocabulary:" << txt.freq.size() << std::endl;
/*    for (auto f : txt.freq) {
        int c=(int)f.first;
        wstring wc(1,f.first);
        wcout << wc << L"|" <<  wchar_t(f.first) << L"(0x" << std::hex << c << L")" ": " << std::dec <<  f.second << endl;
    }
*/
    Color::Modifier red(Color::FG_RED);
    Color::Modifier green(Color::FG_GREEN);
    Color::Modifier def(Color::FG_DEFAULT);

    int T=20;
    int N=txt.text.size() / (T+1);

    MatrixN Xr(N,T);
    MatrixN yr(N,T);

    wstring chunk,chunky;
    int n=0;
    int isf=10;
    for (int i=0; i<N; i++) {
        wstring smp = txt.sample(T+1);
        chunk=smp.substr(0,T);
        chunky=smp.substr(1,T);
        if (isf>0) {
            --isf;
            wcout << "1:" << chunk << endl;
            wcout << "y:" << chunky << endl;
        }
        for (int t=0; t<T; t++) {
            Xr(i,t)=txt.w2v[chunk[t]];
            yr(i,t)=txt.w2v[chunky[t]];
            ++n;
        }
    }

    int maxN = 50000;
    if (n>maxN) n=maxN;
    int n1=n*0.9;
    int dn=(n-n1)/2;

    cerr << n1 << " datasets" << endl;

/*    MatrixN X(n1,T);
    MatrixN y(n1,T);
    MatrixN Xv(dn,T);
    MatrixN yv(dn,T);
    MatrixN Xt(dn,T);
    MatrixN yt(dn,T);
*/
    MatrixN X=Xr.block(0,0,n1,T);
    MatrixN y=yr.block(0,0,n1,T);
    MatrixN Xv=Xr.block(n1,0,dn,T);
    MatrixN yv=yr.block(n1,0,dn,T);
    MatrixN Xt=Xr.block(n1+dn,0,dn,T);
    MatrixN yt=yr.block(n1+dn,0,dn,T);

    cpInitCompute("Rnnreader");
    registerLayers();

    LayerBlock lb("{name='rnnreader';init='normal';initfactor=0.5}");
    int VS=txt.vocsize();
    int H=48;
    int D=16;
    int BS=32;
    float clip=5.0;

    CpParams cp0;
    cp0.setPar("inputShape",vector<int>{T});
    cp0.setPar("V",VS);
    cp0.setPar("D",D);
    cp0.setPar("clip",clip);
    cp0.setPar("init",(string)"orthonormal");
    lb.addLayer("WordEmbedding","WE0",cp0,{"input"});

    CpParams cp1;
    cp1.setPar("inputShape",vector<int>{D,T});
    cp1.setPar("N",BS);
    cp1.setPar("H",H);
    cp1.setPar("clip",clip);
    cp1.setPar("initfactor","0.01");
    lb.addLayer("RNN","rnn1",cp1,{"WE0"});

    CpParams cp2;
    cp2.setPar("inputShape",vector<int>{H,T});
    cp2.setPar("N",BS);
    cp2.setPar("H",H);
    cp2.setPar("clip",clip);
//    lb.addLayer("RNN","rnn2",cp2,{"rnn1"});

    CpParams cp3;
    cp3.setPar("inputShape",vector<int>{H,T});
    cp3.setPar("N",BS);
    cp3.setPar("H",H);
    cp3.setPar("clip",clip);
//    lb.addLayer("RNN","rnn3",cp3,{"rnn2"});

    CpParams cp10;
    cp10.setPar("inputShape",vector<int>{H,T});
    //cp10.setPar("T",T);
    //cp10.setPar("D",H);
    cp10.setPar("M",VS);
    lb.addLayer("TemporalAffine","af1",cp10,{"rnn1"});

    CpParams cp11;
    cp11.setPar("inputShape",vector<int>{VS,T});
    lb.addLayer("TemporalSoftmax","sm1",cp11,{"af1"});

    if (!lb.checkTopology(true)) {
        allOk=false;
        cerr << red << "Topology-check for LayerBlock: ERROR." << def << endl;
    } else {
        cerr << green << "Topology-check for LayerBlock: ok." << def << endl;
    }

/*    wstring chunk;
    chunk = txt.text.substr(512,128);
    wcout << chunk << endl;
*/
    // preseverstates not necessary for training!
    CpParams cpo("{verbose=true;shuffle=false;preservestates=false;epsilon=1e-8}");
    // CpParams cpo("{verbose=false;epsilon=1e-8}");
    cpo.setPar("learning_rate", (floatN)4e-3); //2.2e-2);
    cpo.setPar("lr_decay", (floatN)0.95);
    //cpo.setPar("regularization", (floatN)1.);

    floatN dep=20.0;
    floatN sep=0.0;
    cpo.setPar("epochs",(floatN)dep);
    cpo.setPar("batch_size",BS);

    for (int i=0; i<10000; i++) {

        cpo.setPar("startepoch", (floatN)sep);
        //cpo.setPar("maxthreads", (int)1);  // XXX: can be increased now!
        t_cppl states;
        t_cppl statesv;
        states["y"] = new MatrixN(y);
        statesv["y"] = new MatrixN(yv);
        floatN cAcc=lb.train(X, &states, Xv, &statesv, "Adam", cpo);
        cppl_delete(&states);
        cppl_delete(&statesv);

        sep+=dep;

        int pos=rand() % 1000 + 5000;
        wstring instr=txt.text.substr(pos,T);

        MatrixN xg(1,T);
        for (int t=0; t<T; t++) {
            xg(0,t) = txt.w2v[' '];
        }

        Layer* prnn=lb.layerMap["rnn1"];
        t_cppl states;
        prnn->getZeroStates(&states, 1);

        for (int g=0; g<200; g++) {
            t_cppl cache{};

            MatrixN probst=lb.forward(xg,&cache, &states);
            MatrixN probsd=MatrixN(N*TT,VS);
            for (int n=0; n<1; n++) {
                for (int t=0; t<TT; t++) {
                    for (int d=0; d<VS; d++) {
                        probsd(n*TT+t,d)=probst(n,t*VS+d);
                    }
                }
            }

            for (int t=0; t<TT; t++) {
                vector<floatN> probs(VS);
                vector<floatN> index(VS);
                for (int d=0; d<VS; d++) {
                    probs[d]=probsd(0*TT+t,d);
                    index[d]=d;
                }
                int ind=(int)index[randomChoice(index, probs)];
*//*                    float mx=-1000.0;
                int ind=-1;
                for (int d=0; d<VS; d++) {
                    floatN p=yg(0,t*D+d);
                    floatN pr=p*((floatN)(rand()%100)/5000.0+0.98);
                    if (pr>mx) {
                        mx=pr; // yg(0,t*D+d);
                        ind=d;
                    }
                }
*//*
                    wchar_t cw=txt.v2w[ind];
                    //if (t==0) wcout << L"[" << cw << L"<";
                    //wcout << L"<" << cw << L">";
                    if (t==0) wcout << cw;  //  << L"(" << ind << L")";
                    // if (ind==0) cerr << "probs: " << probs << endl;
                    xg2(0,t)=ind;
                }
                //wcout << L"<" << endl;

                //for (int t=T-1; t>0; t--) xg(0,t)=xg(0,t-1);
                //for (int t=0; t< T-1; t++) xg(0,t)=xg(0,t+1);
                xg(0,0)=xg2(0,0);

                rnn1_ho=*(cache["rnn1-ho"]);
                //rnn2_ho=*(cache["rnn2-ho"]);
                //rnn3_ho=*(cache["rnn3-ho"]);
                //xg=xg2;
                cppl_delete(&cache);
                cppl_delete(&states);
            }
            //cache.clear();
            wcout << endl;
        }
        cerr << "setting eliminated T-Steps param" << endl;
        lb.cp.setPar("T-Steps",T);

*/
    }
    /*
    floatN train_err, val_err, test_err;
    bool evalFinal=true;
    if (evalFinal) {
        train_err=lb.test(X, y, cpo.getPar("batch_size", 50));
        val_err=lb.test(Xv, yv, cpo.getPar("batch_size", 50));
        test_err=lb.test(Xt, yt, cpo.getPar("batch_size", 50));

        cerr << "Final results on RnnReader after " << cpo.getPar("epochs",(floatN)0.0) << " epochs:" << endl;
        cerr << "      Train-error: " << train_err << " train-acc: " << 1.0-train_err << endl;
        cerr << " Validation-error: " << val_err <<   "   val-acc: " << 1.0-val_err << endl;
        cerr << "       Test-error: " << test_err <<  "  test-acc: " << 1.0-test_err << endl;
    }
    //return cAcc;
    */
    cpExitCompute();
}
<|endoftext|>


<commit_before>/**
  * Touhou Community Reliant Automatic Patcher
  * Cheap command-line patch stack configuration tool
  *
  * ----
  *
  * Game searching front-end code.
  */

#include <thcrap.h>
#include "configure.h"

static const char games_js_fn[] = "games.js";

static const char* ChooseLocation(const char *id, json_t *locs)
{
	size_t num_versions = json_object_size(locs);
	if(num_versions == 1) {
		const char *loc = json_object_iter_key(json_object_iter(locs));
		const char *variety = json_object_get_string(locs, loc);

		log_printf("Found %s (%s) at %s\n", id, variety, loc);

		return loc;
	} else if(num_versions > 1) {
		const char *loc;
		json_t *val;
		size_t i = 0;
		size_t loc_num;

		log_printf("Found %d versions of %s:\n\n", num_versions, id);

		json_object_foreach(locs, loc, val) {
			log_printf(" [%2d] %s: %s\n", ++i, loc, json_string_value(val));
		}
		printf("\n");
		while(1) {
			char buf[16];
			printf("Pick a version to run the patch on: (1 - %u): ", num_versions);

			fgets(buf, sizeof(buf), stdin);
			if(
				(sscanf(buf, "%u", &loc_num) == 1) &&
				(loc_num <= num_versions) &&
				(loc_num >= 1)
			) {
				break;
			}
		}
		i = 0;
		json_object_foreach(locs, loc, val) {
			if(++i == loc_num) {
				return loc;
			}
		}
	}
	return NULL;
}

// Work around a bug in Windows 7 and later by sending
// BFFM_SETSELECTION a second time.
// https://connect.microsoft.com/VisualStudio/feedback/details/518103/bffm-setselection-does-not-work-with-shbrowseforfolder-on-windows-7
typedef struct {
	ITEMIDLIST *path;
	int attempts;
} initial_path_t;

int CALLBACK SetInitialBrowsePathProc(HWND hWnd, UINT uMsg, LPARAM lp, LPARAM pData)
{
	initial_path_t *ip = (initial_path_t *)pData;
	if(ip) {
		switch(uMsg) {
		case BFFM_INITIALIZED:
			ip->attempts = 0;
			// fallthrough
		case BFFM_SELCHANGED:
			if(ip->attempts < 2) {
				SendMessageW(hWnd, BFFM_SETSELECTION, FALSE, (LPARAM)ip->path);
				ip->attempts++;
			}
		}
	}
	return 0;
}

json_t* ConfigureLocateGames(const char *games_js_path)
{
	json_t *games;
	initial_path_t ip = {0};
	BROWSEINFO bi = {0};
	int repeat = 0;

	cls(0);

	log_printf("--------------\n");
	log_printf("Locating games\n");
	log_printf("--------------\n");
	log_printf(
		"\n"
		"\n"
	);

	games = json_load_file_report("games.js");
	if(json_object_size(games) != 0) {
		log_printf("You already have a %s with the following contents:\n\n", games_js_fn);
		json_dump_log(games, JSON_INDENT(2) | JSON_SORT_KEYS);
		log_printf("\n\nPatch data will be downloaded or updated for all the games listed.\n\n");
		if(Ask("Should the paths of these games be re-scanned?") == 'y') {
			json_object_clear(games);
		} else if(Ask("Should new games be added to this list?") == 'n') {
			return games;
		}
	} else {
		games = json_object();
		log_printf(
			"You don't have a %s yet.\n"
			"\n"
			"Thus, I now need to search for patchable games installed on your system.\n"
			"This only has to be done once - unless, of course, you later move the games to\n"
			"different directories.\n"
			"\n"
			"Depending on the number of drives and your directory structure,\n"
			"this may take a while. You can speed up this process by giving me a\n"
			"common root path shared by all games you want to patch.\n"
			"\n"
			"For example, if you have 'Double Dealing Character' in \n"
			"\n"
				"\tC:\\Games\\Touhou\\DDC\\\n"
			"\n"
			"and 'Imperishable Night' in\n"
			"\n"
				"\tC:\\Games\\Touhou\\IN\\\n"
			"\n"
			"you would now specify \n"
			"\n"
				"\tC:\\Games\\Touhou\\\n"
			"\n"
			"... unless, of course, your Touhou games are spread out all over your drives -\n"
			"in which case there's no way around a complete search.\n"
			"\n"
			"\n",
			games_js_fn
		);
		pause();
	}

	// BFFM_SETSELECTION does this anyway if we pass a string, so we
	// might as well do the slash conversion in win32_utf8's wrapper
	// while we're at it.
	SHParseDisplayNameU(games_js_path, NULL, &ip.path, 0, NULL);

	bi.lpszTitle = "Root path for game search (cancel to search entire system):";
	bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NONEWFOLDERBUTTON | BIF_USENEWUI;
	bi.hwndOwner = GetConsoleWindow();
	bi.lpfn = SetInitialBrowsePathProc;
	bi.lParam = (LPARAM)&ip;

	do {
		char search_path[MAX_PATH * 2] = {0};
		json_t *found = NULL;

		PIDLIST_ABSOLUTE pidl = SHBrowseForFolder(&bi);
		repeat = 0;
		if(pidl && SHGetPathFromIDList(pidl, search_path)) {
			PathAddBackslashA(search_path);
			CoTaskMemFree(pidl);
		}
		log_printf(
			"Searching games%s%s... this may take a while...\n\n",
			search_path[0] ? " in " : " on the entire system",
			search_path[0] ? search_path: ""
		);
		found = SearchForGames(search_path, games);
		if(json_object_size(found)) {
			char *games_js_str = NULL;
			const char *id;
			json_t *locs;

			json_object_foreach(found, id, locs) {
				const char *loc = ChooseLocation(id, locs);
				json_object_set_new(games, id, json_string(loc));
				printf("\n");
			}

			SetCurrentDirectory(games_js_path);

			games_js_str = json_dumps(games, JSON_INDENT(2) | JSON_SORT_KEYS);
			if(!file_write_text(games_js_fn, games_js_str)) {
				log_printf("The following game locations have been identified and written to %s:\n", games_js_fn);
				log_printf(games_js_str);
				log_printf("\n");
			} else if(!file_write_error(games_js_fn)) {
				games = json_decref_safe(games);
			}
			SAFE_FREE(games_js_str);
		} else if(json_object_size(games)) {
			log_printf("No new game locations found.\n");
		} else {
			log_printf("No game locations found.\n");
			if(search_path[0]) {
				repeat = Ask("Search in a different directory?") == 'y';
			}
			if(!repeat) {
				log_printf(
					"No patch shortcuts will be created.\n"
					"Please re-run this configuration tool after you have acquired some games\n"
					"supported by the patches.\n"
				);
				pause();
			}
		}
		json_decref(found);
	} while(repeat);
	CoTaskMemFree(ip.path);
	return games;
}
<commit_msg>configure: Turn the two game location questions into a single clearer one. [V]<commit_after>/**
  * Touhou Community Reliant Automatic Patcher
  * Cheap command-line patch stack configuration tool
  *
  * ----
  *
  * Game searching front-end code.
  */

#include <thcrap.h>
#include "configure.h"

static const char games_js_fn[] = "games.js";

static const char* ChooseLocation(const char *id, json_t *locs)
{
	size_t num_versions = json_object_size(locs);
	if(num_versions == 1) {
		const char *loc = json_object_iter_key(json_object_iter(locs));
		const char *variety = json_object_get_string(locs, loc);

		log_printf("Found %s (%s) at %s\n", id, variety, loc);

		return loc;
	} else if(num_versions > 1) {
		const char *loc;
		json_t *val;
		size_t i = 0;
		size_t loc_num;

		log_printf("Found %d versions of %s:\n\n", num_versions, id);

		json_object_foreach(locs, loc, val) {
			log_printf(" [%2d] %s: %s\n", ++i, loc, json_string_value(val));
		}
		printf("\n");
		while(1) {
			char buf[16];
			printf("Pick a version to run the patch on: (1 - %u): ", num_versions);

			fgets(buf, sizeof(buf), stdin);
			if(
				(sscanf(buf, "%u", &loc_num) == 1) &&
				(loc_num <= num_versions) &&
				(loc_num >= 1)
			) {
				break;
			}
		}
		i = 0;
		json_object_foreach(locs, loc, val) {
			if(++i == loc_num) {
				return loc;
			}
		}
	}
	return NULL;
}

// Work around a bug in Windows 7 and later by sending
// BFFM_SETSELECTION a second time.
// https://connect.microsoft.com/VisualStudio/feedback/details/518103/bffm-setselection-does-not-work-with-shbrowseforfolder-on-windows-7
typedef struct {
	ITEMIDLIST *path;
	int attempts;
} initial_path_t;

int CALLBACK SetInitialBrowsePathProc(HWND hWnd, UINT uMsg, LPARAM lp, LPARAM pData)
{
	initial_path_t *ip = (initial_path_t *)pData;
	if(ip) {
		switch(uMsg) {
		case BFFM_INITIALIZED:
			ip->attempts = 0;
			// fallthrough
		case BFFM_SELCHANGED:
			if(ip->attempts < 2) {
				SendMessageW(hWnd, BFFM_SETSELECTION, FALSE, (LPARAM)ip->path);
				ip->attempts++;
			}
		}
	}
	return 0;
}

json_t* ConfigureLocateGames(const char *games_js_path)
{
	json_t *games;
	initial_path_t ip = {0};
	BROWSEINFO bi = {0};
	int repeat = 0;

	cls(0);

	log_printf("--------------\n");
	log_printf("Locating games\n");
	log_printf("--------------\n");
	log_printf(
		"\n"
		"\n"
	);

	games = json_load_file_report("games.js");
	if(json_object_size(games) != 0) {
		log_printf("You already have a %s with the following contents:\n\n", games_js_fn);
		json_dump_log(games, JSON_INDENT(2) | JSON_SORT_KEYS);
		log_printf(
			"\n"
			"\n"
			"Patch data will be downloaded or updated for all the games listed.\n"
			"\n"
			"\t* (A)dd new games to this list and keep existing ones?\n"
			"\t* Clear this list and (r)escan?\n"
			"\t* (K)eep this list and continue?\n"
			"\n"
		);
		char ret = Ask<3>(nullptr, { 'a', 'r', 'k' | DEFAULT_ANSWER });
		if(ret == 'k') {
			return games;
		} else if(ret == 'r') {
			json_object_clear(games);
		}
	} else {
		games = json_object();
		log_printf(
			"You don't have a %s yet.\n"
			"\n"
			"Thus, I now need to search for patchable games installed on your system.\n"
			"This only has to be done once - unless, of course, you later move the games to\n"
			"different directories.\n"
			"\n"
			"Depending on the number of drives and your directory structure,\n"
			"this may take a while. You can speed up this process by giving me a\n"
			"common root path shared by all games you want to patch.\n"
			"\n"
			"For example, if you have 'Double Dealing Character' in \n"
			"\n"
				"\tC:\\Games\\Touhou\\DDC\\\n"
			"\n"
			"and 'Imperishable Night' in\n"
			"\n"
				"\tC:\\Games\\Touhou\\IN\\\n"
			"\n"
			"you would now specify \n"
			"\n"
				"\tC:\\Games\\Touhou\\\n"
			"\n"
			"... unless, of course, your Touhou games are spread out all over your drives -\n"
			"in which case there's no way around a complete search.\n"
			"\n"
			"\n",
			games_js_fn
		);
		pause();
	}

	// BFFM_SETSELECTION does this anyway if we pass a string, so we
	// might as well do the slash conversion in win32_utf8's wrapper
	// while we're at it.
	SHParseDisplayNameU(games_js_path, NULL, &ip.path, 0, NULL);

	bi.lpszTitle = "Root path for game search (cancel to search entire system):";
	bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NONEWFOLDERBUTTON | BIF_USENEWUI;
	bi.hwndOwner = GetConsoleWindow();
	bi.lpfn = SetInitialBrowsePathProc;
	bi.lParam = (LPARAM)&ip;

	do {
		char search_path[MAX_PATH * 2] = {0};
		json_t *found = NULL;

		PIDLIST_ABSOLUTE pidl = SHBrowseForFolder(&bi);
		repeat = 0;
		if(pidl && SHGetPathFromIDList(pidl, search_path)) {
			PathAddBackslashA(search_path);
			CoTaskMemFree(pidl);
		}
		log_printf(
			"Searching games%s%s... this may take a while...\n\n",
			search_path[0] ? " in " : " on the entire system",
			search_path[0] ? search_path: ""
		);
		found = SearchForGames(search_path, games);
		if(json_object_size(found)) {
			char *games_js_str = NULL;
			const char *id;
			json_t *locs;

			json_object_foreach(found, id, locs) {
				const char *loc = ChooseLocation(id, locs);
				json_object_set_new(games, id, json_string(loc));
				printf("\n");
			}

			SetCurrentDirectory(games_js_path);

			games_js_str = json_dumps(games, JSON_INDENT(2) | JSON_SORT_KEYS);
			if(!file_write_text(games_js_fn, games_js_str)) {
				log_printf("The following game locations have been identified and written to %s:\n", games_js_fn);
				log_printf(games_js_str);
				log_printf("\n");
			} else if(!file_write_error(games_js_fn)) {
				games = json_decref_safe(games);
			}
			SAFE_FREE(games_js_str);
		} else if(json_object_size(games)) {
			log_printf("No new game locations found.\n");
		} else {
			log_printf("No game locations found.\n");
			if(search_path[0]) {
				repeat = Ask("Search in a different directory?") == 'y';
			}
			if(!repeat) {
				log_printf(
					"No patch shortcuts will be created.\n"
					"Please re-run this configuration tool after you have acquired some games\n"
					"supported by the patches.\n"
				);
				pause();
			}
		}
		json_decref(found);
	} while(repeat);
	CoTaskMemFree(ip.path);
	return games;
}
<|endoftext|>


<commit_before>/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */

#include <fstream>
#include <iostream>
#include <string>

#include "third_party/def_parser/def_parser.h"

static const char* ws = " \t\n\r\f\v";

inline void trim(std::string *str) {
  // Remove prefixing spaces
  str->erase(0, str->find_first_not_of(ws));
  // Remove suffixing spaces
  str->erase(str->find_last_not_of(ws) + 1);
}

int main(int argc, char* argv[]) {
  if (argc < 4) {
    std::cerr << "Usage: output_def_file dllname [objfile ...] [input_deffile ...] [@paramfile ...]\n";
    std::cerr << "output_deffile: the output DEF file\n";
    std::cerr << "\n";
    std::cerr << "dllname: the DLL name this DEF file is used for, if dllname is not empty\n";
    std::cerr << "         string (eg. ""), def_parser writes an 'LIBRARY <dllname>' entry\n";
    std::cerr << "         into DEF file.\n";
    std::cerr << "\n";
    std::cerr << "objfile: a object file, def_parser parses this file to find symbols,\n";
    std::cerr << "         then merges them into final result.\n";
    std::cerr << "         Can apppear multiple times.\n";
    std::cerr << "\n";
    std::cerr << "input_deffile: an existing def file, def_parser merges all symbols in this file.\n";
    std::cerr << "               Can appear multiple times.\n";
    std::cerr << "\n";
    std::cerr << "@paramfile: a parameter file that can contain objfile and input_deffile.\n";
    std::cerr << "            Can appear multiple time.\n";
    return 1;
  }

  FILE* fout = fopen(argv[1], "w+");
  if (!fout) {
    std::cerr << "Could not open output .def file: " << argv[1] << "\n";
    return 1;
  }

  DefParser deffile;

  deffile.SetDLLName(argv[2]);

  for (int i = 3; i < argc; i++) {
    // If the argument starts with @, then treat it as a parameter file.
    if (argv[i][0] == '@') {
      std::ifstream paramfile(argv[i] + 1, std::ios::in | std::ios::binary);
      if (!paramfile) {
        std::cerr << "Could not open parameter file: " << argv[i] << "\n";
        return 1;
      }
      std::string file;
      while (std::getline(paramfile, file)) {
        trim(&file);
        if (!deffile.AddFile(file)) {
          return 1;
        }
      }
    } else {
      std::string file(argv[i]);
      trim(&file);
      if (!deffile.AddFile(argv[i])) {
        return 1;
      }
    }
  }

  deffile.WriteFile(fout);
  fclose(fout);
  return 0;
}
<commit_msg>Def Parser: make it work with long path better<commit_after>/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */

#include <fstream>
#include <iostream>
#include <string>

#include "src/main/cpp/util/file_platform.h"
#include "third_party/def_parser/def_parser.h"

static const char* ws = " \t\n\r\f\v";

inline void trim(std::string *str) {
  // Remove prefixing spaces
  str->erase(0, str->find_first_not_of(ws));
  // Remove suffixing spaces
  str->erase(str->find_last_not_of(ws) + 1);
}

int main(int argc, char* argv[]) {
  if (argc < 4) {
    std::cerr << "Usage: output_def_file dllname [objfile ...] [input_deffile ...] [@paramfile ...]\n";
    std::cerr << "output_deffile: the output DEF file\n";
    std::cerr << "\n";
    std::cerr << "dllname: the DLL name this DEF file is used for, if dllname is not empty\n";
    std::cerr << "         string (eg. ""), def_parser writes an 'LIBRARY <dllname>' entry\n";
    std::cerr << "         into DEF file.\n";
    std::cerr << "\n";
    std::cerr << "objfile: a object file, def_parser parses this file to find symbols,\n";
    std::cerr << "         then merges them into final result.\n";
    std::cerr << "         Can apppear multiple times.\n";
    std::cerr << "\n";
    std::cerr << "input_deffile: an existing def file, def_parser merges all symbols in this file.\n";
    std::cerr << "               Can appear multiple times.\n";
    std::cerr << "\n";
    std::cerr << "@paramfile: a parameter file that can contain objfile and input_deffile.\n";
    std::cerr << "            Can appear multiple time.\n";
    return 1;
  }

  std::wstring filenameW;
  blaze_util::AsAbsoluteWindowsPath(argv[1], &filenameW);
  FILE* fout = _wfopen(filenameW.c_str(), L"w+");
  if (!fout) {
    std::cerr << "Could not open output .def file: " << argv[1] << "\n";
    return 1;
  }

  DefParser deffile;

  deffile.SetDLLName(argv[2]);

  for (int i = 3; i < argc; i++) {
    // If the argument starts with @, then treat it as a parameter file.
    if (argv[i][0] == '@') {
      blaze_util::AsAbsoluteWindowsPath(argv[i] + 1, &filenameW);
      std::ifstream paramfile(filenameW.c_str(), std::ios::in | std::ios::binary);
      if (!paramfile) {
        std::cerr << "Could not open parameter file: " << argv[i] << "\n";
        return 1;
      }
      std::string file;
      while (std::getline(paramfile, file)) {
        trim(&file);
        if (!deffile.AddFile(file)) {
          return 1;
        }
      }
    } else {
      std::string file(argv[i]);
      trim(&file);
      if (!deffile.AddFile(argv[i])) {
        return 1;
      }
    }
  }

  deffile.WriteFile(fout);
  fclose(fout);
  return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017 deepstreamHub GmbH
 *
 * 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 <cerrno>
#include <cstdint>

#include <Poco/Exception.h>
#include <Poco/Timespan.h>
#include <Poco/Net/NetException.h>

#include <buffer.hpp>
#include <exception.hpp>
#include <time.hpp>
#include <websockets.hpp>
#include <websockets/poco.hpp>

#include <cassert>

namespace net = Poco::Net;

namespace deepstream {
namespace websockets {
namespace poco
{

Client::Client(const std::string& uri_string) :
	uri_( uri_string ),
	session_( uri_.getHost(), uri_.getPort() ),
	request_(
		net::HTTPRequest::HTTP_GET,
		uri_.getPath(),
		net::HTTPRequest::HTTP_1_1
	),
	websocket_(session_, request_, response_)
{
}

std::string Client::uri_impl() const
{
	return uri_.toString();
}

std::size_t Client::num_bytes_available_impl()
{
	int num_bytes = websocket_.available();
	assert( num_bytes >= 0 );

	return num_bytes;
}

time::Duration Client::get_receive_timeout_impl()
{
	Poco::Timespan t = websocket_.getReceiveTimeout();

	return std::chrono::milliseconds( t.totalMilliseconds() );
}

void Client::set_receive_timeout_impl(time::Duration t)
{
	Poco::Timespan u =
		std::chrono::duration_cast<std::chrono::microseconds>(t).count();

	websocket_.setReceiveTimeout(u);
}

std::pair<State, std::unique_ptr<Frame> > Client::receive_frame_impl()
{
	typedef std::unique_ptr<Frame> FramePtr;

	const int eof_flags =
		Frame::Bit::FIN | Frame::Opcode::CONNECTION_CLOSE_FRAME;

	int ret = 0;
	int flags = 0;

	std::size_t buffer_size = num_bytes_available();
	Buffer buffer(buffer_size, 0);

	try
	{
		ret = websocket_.receiveFrame( buffer.data(), buffer.size(), flags );
	}
	catch(Poco::TimeoutException& e)
	{
		return std::make_pair(State::OPEN, nullptr);
	}
	catch(net::WebSocketException& e)
	{
		throw Exception( e.message() );
	}

	if( ret < 0 )
	{
		assert(0);
		throw std::logic_error("receiveFrame() returned a negative value");
	}

	if( ret == 0 )
	{
		return std::make_pair(State::CLOSED, nullptr);
	}

	if( flags == eof_flags && ret != 2 )
	{
		return std::make_pair(
			State::ERROR,
			FramePtr(new Frame(flags, buffer.data(), ret))
		);
	}

	if( flags == eof_flags && ret == 2)
	{
		return std::make_pair(
			State::CLOSED,
			FramePtr(new Frame(flags, buffer.data(), ret))
		);
	}

	return std::make_pair(
		State::OPEN,
		FramePtr(new Frame(flags, buffer.data(), ret))
	);
}

State Client::send_frame_impl(const Buffer& buffer, Frame::Flags flags)
{
	int ret = websocket_.sendFrame( buffer.data(), buffer.size(), flags );

	if( ret == 0 )
		return State::CLOSED;

	if( ret < 0 )
		throw SystemError(errno);

	if( ret > 0 && std::size_t(ret) < buffer.size() )
	{
		assert(0);
		throw Exception("Incomplete message sent");
	}

	return State::OPEN;
}

void Client::close_impl()
{
	websocket_.shutdown();
}

}
}
}
<commit_msg>Poco: ensure minimum buffer size<commit_after>/*
 * Copyright 2017 deepstreamHub GmbH
 *
 * 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 <cerrno>
#include <cstdint>

#include <algorithm>

#include <Poco/Exception.h>
#include <Poco/Timespan.h>
#include <Poco/Net/NetException.h>

#include <buffer.hpp>
#include <exception.hpp>
#include <time.hpp>
#include <websockets.hpp>
#include <websockets/poco.hpp>

#include <cassert>

namespace net = Poco::Net;

namespace deepstream {
namespace websockets {
namespace poco
{

Client::Client(const std::string& uri_string) :
	uri_( uri_string ),
	session_( uri_.getHost(), uri_.getPort() ),
	request_(
		net::HTTPRequest::HTTP_GET,
		uri_.getPath(),
		net::HTTPRequest::HTTP_1_1
	),
	websocket_(session_, request_, response_)
{
}

std::string Client::uri_impl() const
{
	return uri_.toString();
}

std::size_t Client::num_bytes_available_impl()
{
	int num_bytes = websocket_.available();
	assert( num_bytes >= 0 );

	return num_bytes;
}

time::Duration Client::get_receive_timeout_impl()
{
	Poco::Timespan t = websocket_.getReceiveTimeout();

	return std::chrono::milliseconds( t.totalMilliseconds() );
}

void Client::set_receive_timeout_impl(time::Duration t)
{
	Poco::Timespan u =
		std::chrono::duration_cast<std::chrono::microseconds>(t).count();

	websocket_.setReceiveTimeout(u);
}

std::pair<State, std::unique_ptr<Frame> > Client::receive_frame_impl()
{
	typedef std::unique_ptr<Frame> FramePtr;

	const int eof_flags =
		Frame::Bit::FIN | Frame::Opcode::CONNECTION_CLOSE_FRAME;

	int ret = 0;
	int flags = 0;

	const std::size_t min_buffer_size = 256;
	std::size_t buffer_size = std::max( num_bytes_available(),min_buffer_size );
	Buffer buffer(buffer_size, 0);

	try
	{
		ret = websocket_.receiveFrame( buffer.data(), buffer.size(), flags );
	}
	catch(Poco::TimeoutException& e)
	{
		return std::make_pair(State::OPEN, nullptr);
	}
	catch(net::WebSocketException& e)
	{
		throw Exception( e.message() );
	}

	if( ret < 0 )
	{
		assert(0);
		throw std::logic_error("receiveFrame() returned a negative value");
	}

	if( ret == 0 )
	{
		return std::make_pair(State::CLOSED, nullptr);
	}

	if( flags == eof_flags && ret != 2 )
	{
		return std::make_pair(
			State::ERROR,
			FramePtr(new Frame(flags, buffer.data(), ret))
		);
	}

	if( flags == eof_flags && ret == 2)
	{
		return std::make_pair(
			State::CLOSED,
			FramePtr(new Frame(flags, buffer.data(), ret))
		);
	}

	return std::make_pair(
		State::OPEN,
		FramePtr(new Frame(flags, buffer.data(), ret))
	);
}

State Client::send_frame_impl(const Buffer& buffer, Frame::Flags flags)
{
	int ret = websocket_.sendFrame( buffer.data(), buffer.size(), flags );

	if( ret == 0 )
		return State::CLOSED;

	if( ret < 0 )
		throw SystemError(errno);

	if( ret > 0 && std::size_t(ret) < buffer.size() )
	{
		assert(0);
		throw Exception("Incomplete message sent");
	}

	return State::OPEN;
}

void Client::close_impl()
{
	websocket_.shutdown();
}

}
}
}
<|endoftext|>