<commit_before>/* net6 - Library providing IPv4/IPv6 network access
 * Copyright (C) 2005 Armin Burgmeier / 0x539 dev group
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <sigc++/bind.h>
#include "server.hpp"

net6::server::peer::peer(unsigned int id)
 : net6::peer(id, ""), logined(false), conn(NULL)
{
}

net6::server::peer::peer(unsigned int id, const tcp_client_socket& sock,
                         const address& addr)
 : net6::peer(id, ""), logined(false), conn(new net6::connection(sock, addr) )
{
}

net6::server::peer::~peer()
{
	delete conn;
}

void net6::server::peer::login(const std::string& username)
{
	name = username;
	logined = true;
}

bool net6::server::peer::is_logined() const
{
	return logined;
}

void net6::server::peer::send(const packet& pack)
{
	conn->send(pack);
}

unsigned int net6::server::peer::send_queue_size() const
{
	return conn->send_queue_size();
}

const net6::tcp_client_socket& net6::server::peer::get_socket() const
{
	return conn->get_socket();
}

const net6::address& net6::server::peer::get_address() const
{
	return conn->get_remote_address();
}

net6::server::peer::signal_recv_type net6::server::peer::recv_event() const
{
	return conn->recv_event();
}

net6::server::peer::signal_send_type net6::server::peer::send_event() const
{
	return conn->send_event();
}

net6::server::peer::signal_close_type net6::server::peer::close_event() const
{
	return conn->close_event();
}

net6::server::server(bool ipv6)
 : serv_sock(NULL), use_ipv6(ipv6), id_counter(0)
{
}

net6::server::server(unsigned int port, bool ipv6)
 : serv_sock(NULL), use_ipv6(ipv6), id_counter(0)
{
	reopen(port);
}

net6::server::~server()
{
	shutdown();
}

void net6::server::shutdown()
{
	if(serv_sock)
	{
		sock_sel.remove(*serv_sock, socket::INCOMING);
		delete serv_sock;
		serv_sock = NULL;
	}

	std::list<peer*>::iterator peer_it;
	for(peer_it = peers.begin(); peer_it != peers.end(); ++ peer_it)
		delete *peer_it;
	peers.clear();
}

void net6::server::reopen(unsigned int port)
{
	if(use_ipv6)
	{
		ipv6_address bind_addr(port);
		serv_sock = new tcp_server_socket(bind_addr);
	}
	else
	{
		ipv4_address bind_addr(port);
		serv_sock = new tcp_server_socket(bind_addr);
	}

	sock_sel.add(*serv_sock, socket::INCOMING);

	serv_sock->read_event().connect(
		sigc::mem_fun(*this, &server::on_server_read) );
/*	serv_sock->error_event().connect(
		sigc::mem_fun(*this, &server::on_server_error) );*/
}

void net6::server::kick(peer& client)
{
	remove_client(&client);
}

void net6::server::select()
{
	sock_sel.select();
}

void net6::server::select(unsigned int timeout)
{
	sock_sel.select(timeout);
}

void net6::server::send(const packet& pack)
{
	std::list<peer*>::const_iterator peer_it;
	for(peer_it = peers.begin(); peer_it != peers.end(); ++ peer_it)
		if( (*peer_it)->is_logined() )
			send(pack, **peer_it);
}

void net6::server::send(const packet& pack, peer& to)
{
	if(to.send_queue_size() == 0)
		sock_sel.add(to.get_socket(), socket::OUTGOING);

	to.send(pack);
}

net6::server::peer* net6::server::find(unsigned int id) const
{
	std::list<peer*>::const_iterator peer_it;
	for(peer_it = peers.begin(); peer_it != peers.end(); ++ peer_it)
		if( (*peer_it)->get_id() == id)
			return *peer_it;
	return NULL;
}

net6::server::peer* net6::server::find(const std::string& name) const
{
	std::list<peer*>::const_iterator peer_it;
	for(peer_it = peers.begin(); peer_it != peers.end(); ++ peer_it)
	{
		if( (*peer_it)->get_name() == name)
			return *peer_it;
	}
	return NULL;
}

const net6::tcp_server_socket& net6::server::get_socket() const
{
	return *serv_sock;
}

net6::server::signal_join_type net6::server::join_event() const
{
	return signal_join;
}

net6::server::signal_part_type net6::server::part_event() const
{
	return signal_part;
}

net6::server::signal_pre_login_type net6::server::pre_login_event() const
{
	return signal_pre_login;
}

net6::server::signal_post_login_type net6::server::post_login_event() const
{
	return signal_post_login;
}

net6::server::signal_login_auth_type net6::server::login_auth_event() const
{
	return signal_login_auth;
}

net6::server::signal_login_extend_type net6::server::login_extend_event() const
{
	return signal_login_extend;
}

net6::server::signal_data_type net6::server::data_event() const
{
	return signal_data;
}

void net6::server::remove_client(peer* client)
{
	signal_part.emit(*client);
	peers.erase(std::remove(peers.begin(), peers.end(), client),
	            peers.end() );

	sock_sel.remove(client->get_socket(), socket::INCOMING | socket::ERROR);
	if(sock_sel.check(client->get_socket(), socket::OUTGOING) )
		sock_sel.remove(client->get_socket(), socket::OUTGOING);

	packet pack("net6_client_part");
	pack << client->get_id(); //static_cast<int>(client->get_id() );
	send(pack);
	delete client;
}

void net6::server::on_server_read(socket& sock, socket::condition io)
{
	address* new_addr;
	if(use_ipv6)
		new_addr = new ipv6_address;
	else
		new_addr = new ipv4_address;

	tcp_server_socket& tcp_sock = static_cast<tcp_server_socket&>(sock);
	tcp_client_socket new_sock = tcp_sock.accept(*new_addr);
	peer* new_client = new peer(++id_counter, new_sock, *new_addr);
	delete new_addr;

	peers.push_back(new_client);
	sock_sel.add(new_sock, socket::INCOMING | socket::ERROR);

	new_client->recv_event().connect(sigc::bind(
		sigc::mem_fun(*this, &server::on_client_recv),
		sigc::ref(*new_client)
	));

	new_client->send_event().connect(sigc::bind(
		sigc::mem_fun(*this, &server::on_client_send),
		sigc::ref(*new_client)
	));

	new_client->close_event().connect(sigc::bind(
		sigc::mem_fun(*this, &server::on_client_close),
		sigc::ref(*new_client)
	));

	on_join(*new_client);
}
/*
void net6::server::on_server_error(socket& sock, socket::condition io)
{

}
*/
void net6::server::on_client_recv(const packet& pack, peer& from)
{
	if(pack.get_command() == "net6_client_login")
	{
		if(from.is_logined() ) return;

		if(pack.get_param_count() < 1) return;
		if(pack.get_param(0).get_type() != packet::param::STRING)
			return;

		const std::string& name = pack.get_param(0).as_string();
		if(name.empty() )
		{
			packet pack("net6_login_failed");
			pack << "Invalid name";
			send(pack, from);
		}
		else if(find(name) != NULL)
		{
			packet pack("net6_login_failed");
			pack << "Name is already in use";
			send(pack, from);
		}
		else
		{
			std::string reason;
			if(!signal_login_auth.emit(from, pack, reason) )
			{
				packet pack("net6_login_failed");
				pack << reason;
				send(pack, from);
				return;
			}

			from.login(name);
			signal_pre_login.emit(from, pack);

			packet self_pack("net6_client_join");
			self_pack << from.get_id() << name;
			signal_login_extend.emit(from, self_pack);
			send(self_pack, from);

			std::list<peer*>::iterator it;
			for(it = peers.begin(); it != peers.end(); ++ it)
			{
				if(!( (*it)->is_logined()) ) continue;
				if(*it == &from) continue;

				packet join_pack("net6_client_join");
				join_pack << (*it)->get_id()
				          << (*it)->get_name();
				signal_login_extend.emit(**it, join_pack);

				send(join_pack, from);
				send(self_pack, **it);
			}

			signal_post_login.emit(from, pack);

		}
	}
	else
	{
		if(from.is_logined() )
		{
			signal_data.emit(pack, from);
		}
	}
}

void net6::server::on_client_send(const packet& pack, peer& to)
{
	// Do no longer select on socket::OUTGOING if there
	// are no more packets to write
	if(to.send_queue_size() == 0)
		sock_sel.remove(to.get_socket(), socket::OUTGOING);
}

void net6::server::on_client_close(peer& from)
{
	remove_client(&from);
}

void net6::server::on_join(peer& new_peer)
{
	signal_join.emit(new_peer);
}
<commit_msg>[project @ server-ctor does not call reopen() because it does not care about its virtualness]<commit_after>/* net6 - Library providing IPv4/IPv6 network access
 * Copyright (C) 2005 Armin Burgmeier / 0x539 dev group
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <sigc++/bind.h>
#include "server.hpp"

net6::server::peer::peer(unsigned int id)
 : net6::peer(id, ""), logined(false), conn(NULL)
{
}

net6::server::peer::peer(unsigned int id, const tcp_client_socket& sock,
                         const address& addr)
 : net6::peer(id, ""), logined(false), conn(new net6::connection(sock, addr) )
{
}

net6::server::peer::~peer()
{
	delete conn;
}

void net6::server::peer::login(const std::string& username)
{
	name = username;
	logined = true;
}

bool net6::server::peer::is_logined() const
{
	return logined;
}

void net6::server::peer::send(const packet& pack)
{
	conn->send(pack);
}

unsigned int net6::server::peer::send_queue_size() const
{
	return conn->send_queue_size();
}

const net6::tcp_client_socket& net6::server::peer::get_socket() const
{
	return conn->get_socket();
}

const net6::address& net6::server::peer::get_address() const
{
	return conn->get_remote_address();
}

net6::server::peer::signal_recv_type net6::server::peer::recv_event() const
{
	return conn->recv_event();
}

net6::server::peer::signal_send_type net6::server::peer::send_event() const
{
	return conn->send_event();
}

net6::server::peer::signal_close_type net6::server::peer::close_event() const
{
	return conn->close_event();
}

net6::server::server(bool ipv6)
 : serv_sock(NULL), use_ipv6(ipv6), id_counter(0)
{
}

net6::server::server(unsigned int port, bool ipv6)
 : serv_sock(NULL), use_ipv6(ipv6), id_counter(0)
{
	if(use_ipv6)
	{
		ipv6_address bind_addr(port);
		serv_sock = new tcp_server_socket(bind_addr);
	}
	else
	{
		ipv4_address bind_addr(port);
		serv_sock = new tcp_server_socket(bind_addr);
	}

	sock_sel.add(*serv_sock, socket::INCOMING);

	serv_sock->read_event().connect(
		sigc::mem_fun(*this, &server::on_server_read) );
}

net6::server::~server()
{
	if(serv_sock)
	{
		sock_sel.remove(*serv_sock, socket::INCOMING);
		delete serv_sock;
		serv_sock = NULL;
	}

	std::list<peer*>::iterator peer_it;
	for(peer_it = peers.begin(); peer_it != peers.end(); ++ peer_it)
		delete *peer_it;
}

void net6::server::shutdown()
{
	if(serv_sock)
	{
		sock_sel.remove(*serv_sock, socket::INCOMING);
		delete serv_sock;
		serv_sock = NULL;
	}

	std::list<peer*>::iterator peer_it;
	for(peer_it = peers.begin(); peer_it != peers.end(); ++ peer_it)
		delete *peer_it;
	peers.clear();
}

void net6::server::reopen(unsigned int port)
{
	if(use_ipv6)
	{
		ipv6_address bind_addr(port);
		serv_sock = new tcp_server_socket(bind_addr);
	}
	else
	{
		ipv4_address bind_addr(port);
		serv_sock = new tcp_server_socket(bind_addr);
	}

	sock_sel.add(*serv_sock, socket::INCOMING);

	serv_sock->read_event().connect(
		sigc::mem_fun(*this, &server::on_server_read) );
}

void net6::server::kick(peer& client)
{
	remove_client(&client);
}

void net6::server::select()
{
	sock_sel.select();
}

void net6::server::select(unsigned int timeout)
{
	sock_sel.select(timeout);
}

void net6::server::send(const packet& pack)
{
	std::list<peer*>::const_iterator peer_it;
	for(peer_it = peers.begin(); peer_it != peers.end(); ++ peer_it)
		if( (*peer_it)->is_logined() )
			send(pack, **peer_it);
}

void net6::server::send(const packet& pack, peer& to)
{
	if(to.send_queue_size() == 0)
		sock_sel.add(to.get_socket(), socket::OUTGOING);

	to.send(pack);
}

net6::server::peer* net6::server::find(unsigned int id) const
{
	std::list<peer*>::const_iterator peer_it;
	for(peer_it = peers.begin(); peer_it != peers.end(); ++ peer_it)
		if( (*peer_it)->get_id() == id)
			return *peer_it;
	return NULL;
}

net6::server::peer* net6::server::find(const std::string& name) const
{
	std::list<peer*>::const_iterator peer_it;
	for(peer_it = peers.begin(); peer_it != peers.end(); ++ peer_it)
	{
		if( (*peer_it)->get_name() == name)
			return *peer_it;
	}
	return NULL;
}

const net6::tcp_server_socket& net6::server::get_socket() const
{
	return *serv_sock;
}

net6::server::signal_join_type net6::server::join_event() const
{
	return signal_join;
}

net6::server::signal_part_type net6::server::part_event() const
{
	return signal_part;
}

net6::server::signal_pre_login_type net6::server::pre_login_event() const
{
	return signal_pre_login;
}

net6::server::signal_post_login_type net6::server::post_login_event() const
{
	return signal_post_login;
}

net6::server::signal_login_auth_type net6::server::login_auth_event() const
{
	return signal_login_auth;
}

net6::server::signal_login_extend_type net6::server::login_extend_event() const
{
	return signal_login_extend;
}

net6::server::signal_data_type net6::server::data_event() const
{
	return signal_data;
}

void net6::server::remove_client(peer* client)
{
	signal_part.emit(*client);
	peers.erase(std::remove(peers.begin(), peers.end(), client),
	            peers.end() );

	sock_sel.remove(client->get_socket(), socket::INCOMING | socket::ERROR);
	if(sock_sel.check(client->get_socket(), socket::OUTGOING) )
		sock_sel.remove(client->get_socket(), socket::OUTGOING);

	packet pack("net6_client_part");
	pack << client->get_id(); //static_cast<int>(client->get_id() );
	send(pack);
	delete client;
}

void net6::server::on_server_read(socket& sock, socket::condition io)
{
	address* new_addr;
	if(use_ipv6)
		new_addr = new ipv6_address;
	else
		new_addr = new ipv4_address;

	tcp_server_socket& tcp_sock = static_cast<tcp_server_socket&>(sock);
	tcp_client_socket new_sock = tcp_sock.accept(*new_addr);
	peer* new_client = new peer(++id_counter, new_sock, *new_addr);
	delete new_addr;

	peers.push_back(new_client);
	sock_sel.add(new_sock, socket::INCOMING | socket::ERROR);

	new_client->recv_event().connect(sigc::bind(
		sigc::mem_fun(*this, &server::on_client_recv),
		sigc::ref(*new_client)
	));

	new_client->send_event().connect(sigc::bind(
		sigc::mem_fun(*this, &server::on_client_send),
		sigc::ref(*new_client)
	));

	new_client->close_event().connect(sigc::bind(
		sigc::mem_fun(*this, &server::on_client_close),
		sigc::ref(*new_client)
	));

	on_join(*new_client);
}
/*
void net6::server::on_server_error(socket& sock, socket::condition io)
{

}
*/
void net6::server::on_client_recv(const packet& pack, peer& from)
{
	if(pack.get_command() == "net6_client_login")
	{
		if(from.is_logined() ) return;

		if(pack.get_param_count() < 1) return;
		if(pack.get_param(0).get_type() != packet::param::STRING)
			return;

		const std::string& name = pack.get_param(0).as_string();
		if(name.empty() )
		{
			packet pack("net6_login_failed");
			pack << "Invalid name";
			send(pack, from);
		}
		else if(find(name) != NULL)
		{
			packet pack("net6_login_failed");
			pack << "Name is already in use";
			send(pack, from);
		}
		else
		{
			std::string reason;
			if(!signal_login_auth.emit(from, pack, reason) )
			{
				packet pack("net6_login_failed");
				pack << reason;
				send(pack, from);
				return;
			}

			from.login(name);
			signal_pre_login.emit(from, pack);

			packet self_pack("net6_client_join");
			self_pack << from.get_id() << name;
			signal_login_extend.emit(from, self_pack);
			send(self_pack, from);

			std::list<peer*>::iterator it;
			for(it = peers.begin(); it != peers.end(); ++ it)
			{
				if(!( (*it)->is_logined()) ) continue;
				if(*it == &from) continue;

				packet join_pack("net6_client_join");
				join_pack << (*it)->get_id()
				          << (*it)->get_name();
				signal_login_extend.emit(**it, join_pack);

				send(join_pack, from);
				send(self_pack, **it);
			}

			signal_post_login.emit(from, pack);

		}
	}
	else
	{
		if(from.is_logined() )
		{
			signal_data.emit(pack, from);
		}
	}
}

void net6::server::on_client_send(const packet& pack, peer& to)
{
	// Do no longer select on socket::OUTGOING if there
	// are no more packets to write
	if(to.send_queue_size() == 0)
		sock_sel.remove(to.get_socket(), socket::OUTGOING);
}

void net6::server::on_client_close(peer& from)
{
	remove_client(&from);
}

void net6::server::on_join(peer& new_peer)
{
	signal_join.emit(new_peer);
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <csignal>
#include <iostream>
#include <limits>
#include <unordered_set>

#include "maxis/graph.hpp"
#include "maxis/genetic.hpp"
#include "maxis/solver.hpp"
#include "maxis/rng.hpp"

static volatile bool sigint_flag;

void sigint_handler(int signum) {
    sigint_flag = true;
}

namespace maxis {

// Brute Force Solver
// ==================

BruteForceMaxisSolver::BruteForceMaxisSolver(const Graph &g) : graph{g} {}

// Ripple carry increment for a collection of bits.
// Returns false on overflow.
bool
increment_bit_vector(BitVector &bv) {
    using std::begin; using std::end;

    bool carry = true;
    for (auto it = begin(bv); it != end(bv); ++it) {
        if (*it == 0){
            *it = 1;
            carry = false;
            break;
        } else {
            *it = 0;
        }
    }

    return !carry;
}

BitVector
BruteForceMaxisSolver::operator()() {
    // max_weight tracks the current maximum weight
    int max_weight = 0;
    BitVector max_set;

    auto adjacency_matrix = graph.adjacency_matrix();

    BitVector bv(graph.order(), 0);
    do {
        if (graph.is_independent_set(bv)) {
            auto weight = graph.weighted_total(bv);
            if (weight > max_weight) {
                max_weight = weight;
                max_set = bv;
            }
        }
    } while (increment_bit_vector(bv));

    return max_set;
}

// Genetic Maxis Solver
// ====================

GeneticMaxisSolver::GeneticMaxisSolver(
            const Graph &g,
            genetic::Selector &sel,
            genetic::Recombinator &rec,
            genetic::Mutator &mut
        ) : graph{g}, selector{sel}, recombinator{rec}, mutator{mut}, size{50} {

    using std::begin; using std::end;

    // Reorder graph by sorting vertices by their weight divided by
    // their degree. Vertices with a large weight and few neighbors have
    // the highest probability of appearing in the solution.
    permutation.resize(graph.order());
    std::iota(begin(permutation), end(permutation), 0);
    auto &weights = graph.weights;
    auto &adj = graph.adjacency_list;
    std::sort(begin(permutation), end(permutation), [&weights, &adj](size_t i, size_t j) {
        return weights[i] / adj[i].size() > weights[j] / adj[j].size();
    });

    graph.reorder(permutation);
}

// The heuristic feasability operator ensures that genotypes created
// from mutation and breeding are valid independent sets. It is
// essentially a greedy solver and was described by Beasley and Chu. It
// makes a higher mutation rate necessary because some of the mutated
// bits will be immediately cancelled out to make the set independent.
//
// Currently it is the bottleneck for a single iteration as it
// runs in O(|V|^2) time where |V| is the order of the graph.
void
GeneticMaxisSolver::heuristic_feasibility(size_t order, BitVector &adj, BitVector &chromosome) {
    using std::begin; using std::end;

    // Traverse the chromosome, marking vertices which are most likely
    // to appear in an optimal solution as kept. Delete all neighbors of
    // the kept vertices from the chromosome.
    for (auto it = std::make_pair(begin(adj), begin(chromosome)); it.second != end(chromosome); it.first += order, ++it.second) {
        // If vertex is not selected, there is no conflict
        if (!*it.second) {
            continue;
        }

        // Delete all neighbors of the vertex. Because we are assuming
        // that the graph is bidirectional, we only need to traverse
        // half of the adjacency matrix.
        for (auto jt = std::make_pair(std::next(it.first, std::distance(begin(chromosome), it.second)), it.second);
                    jt.second != chromosome.end(); ++jt.first, ++jt.second) {

            if (*jt.first) {
                *jt.second = 0;
            }
        }
    }

    // Add back vertices where we can
    for (auto it = std::make_pair(begin(adj), begin(chromosome)); it.second != end(chromosome); it.first += order, ++it.second) {
        if (*it.second) {
            continue;
        }
        if(std::inner_product(begin(chromosome), end(chromosome), it.first, 0) == 0) {
            *it.second = 1;
        }
    }
}

// initialize_set randomly creates an independent set on the graph with
// adjacency matrix adj
void
GeneticMaxisSolver::initialize_set(size_t order, BitVector &adj, BitVector &chromosome) {
    using std::begin; using std::end;

    static RNG rng;

    BitVector cover(order, 0);
    std::fill(begin(chromosome), end(chromosome), 0);
    auto uncovered_cnt = order;

    // Randomly select a set of vertices that completely cover the
    // graph
    while (uncovered_cnt > 0) {
        // Pick an uncovered vertex at random
        size_t skips = rng.random_index(0, uncovered_cnt - 1);
        size_t dist = 0;
        auto it = begin(cover);
        for (; it != end(cover); ++it)  {
            if (*it == 0 && skips-- == 0) {
                dist = std::distance(begin(cover), it);
                *it = 1;
                break;
            }
        }

        // Select it and mark all of its neighbors as covered
        chromosome[dist] = 1;
        --uncovered_cnt;
        for (auto i = begin(cover), j = begin(adj) + dist*order; i != end(cover); ++i, ++j) {
            if (*j != 0 && *i == 0) {
                *i = 1;
                if (--uncovered_cnt == 0) {
                    break;
                }
            }
        }
    }
}

BitVector
GeneticMaxisSolver::operator()() {
    using std::begin; using std::end;

    auto order = graph.order();
    auto adj = graph.adjacency_matrix();
    genetic::AlgorithmState state;

    // Keep a hash table to check for duplicate chromosomes
    auto hash_func = [](BitVector *chromosome) {
        return std::hash<std::vector<bool>>()(*chromosome);
    };
    auto eq_func = [](BitVector *lhs, BitVector *rhs) {
        return std::equal(begin(*lhs), end(*lhs), begin(*rhs));
    };
    std::unordered_set<BitVector*, decltype(hash_func), decltype(eq_func)> dupes(size, hash_func, eq_func);

    // Generate the initial population
    std::vector<BitVector> chromosomes;
    std::vector<genetic::Phenotype> pop;
    pop.reserve(size);
    chromosomes.reserve(size);

    // If the population size is larger than the set of possible
    // unique solutions, this will loop forever.
    for (decltype(size) i = 0; i < size; ++i) {
        chromosomes.emplace_back(order);
        pop.emplace_back(&chromosomes[i]);
        do {
            initialize_set(order, adj, chromosomes[i]);
        } while (dupes.insert(&chromosomes[i]).second == false);

        pop[i].fitness = graph.weighted_total(*pop[i].chromosome);
    }

    // Sort population by fitness and initialize state information
    std::sort(begin(pop), end(pop));
    state.min_fitness = begin(pop)->fitness;
    state.max_fitness = end(pop)->fitness;
    state.total_fitness = std::accumulate(
        begin(pop), end(pop), 0.0,
        [](double acc, genetic::Phenotype &ph) {
            return acc + ph.fitness;
        }
    );
    state.adjusted_fitness = state.total_fitness - state.min_fitness * size;

    // Start handling SIGINT
    std::signal(SIGINT, sigint_handler);

    while (state.max_fitness < constraint && !sigint_flag) {

        // Select the weakest member of the population to be replaced
        auto &child = *begin(pop);
        dupes.erase(child.chromosome);

        // Select two parents for breeding and store the result into the
        // child, while ensuring that the newly created child is unique.
        // If the selector returns the same result every time, and the
        // mutator does create a sufficiently unique solution, this
        // could loop forever.
        do {
            recombinator.breed(
                state,
                selector.select(state, begin(pop), end(pop)),
                selector.select(state, begin(pop), end(pop)),
                child
            );
            mutator.mutate(state, child);
            heuristic_feasibility(order, adj, *child.chromosome);

        } while (dupes.insert(child.chromosome).second == false);

        // Calculate fitness of the new phenotype and update the
        // total fitness
        state.total_fitness -= child.fitness;
        auto child_fitness = child.fitness = graph.weighted_total(*child.chromosome);
        state.total_fitness += child_fitness;

        // Log whenever we have an increase in the maximum fitness
        if (child_fitness > state.max_fitness) {
            state.max_fitness = child_fitness;
            std::cout << "Best independent set: " << state.max_fitness << std::endl;
        }

        // Use a single pass of bubble sort to put the new child in the
        // correct order. Phenotypes with the same fitness are ordered
        // by freshness.
        for (auto it = std::next(begin(pop)); it != end(pop) && it->fitness <= child_fitness; ++it) {
            std::swap(*it, *std::prev(it));
        }

        // Update the rest of the state information
        state.min_fitness = begin(pop)->fitness;
        state.adjusted_fitness = state.total_fitness - state.min_fitness * size;
        ++state.iterations;
        if ((state.iterations & 0x3ff) == 0) {
            std::cout << "Iterations: " << state.iterations
                      << "; Average Fitness: " << state.total_fitness / size << std::endl;
        }
    }

    auto max = std::prev(end(pop));

    if (sigint_flag) {
        std::cout << "Interrupted..." << std::endl;
    }

    // Rearrange the chromosome into the format of the original,
    // unsorted graph
    BitVector result(order);
    for (size_t i = 0; i < order; ++i) {
        result[permutation[i]] = (*max->chromosome)[i];
    }
    return result;
}

} // namespace maxis
<commit_msg>Initialize genetic solver members properly<commit_after>#include <algorithm>
#include <csignal>
#include <iostream>
#include <limits>
#include <unordered_set>

#include "maxis/graph.hpp"
#include "maxis/genetic.hpp"
#include "maxis/solver.hpp"
#include "maxis/rng.hpp"

static volatile bool sigint_flag;

void sigint_handler(int signum) {
    sigint_flag = true;
}

namespace maxis {

// Brute Force Solver
// ==================

BruteForceMaxisSolver::BruteForceMaxisSolver(const Graph &g) : graph{g} {}

// Ripple carry increment for a collection of bits.
// Returns false on overflow.
bool
increment_bit_vector(BitVector &bv) {
    using std::begin; using std::end;

    bool carry = true;
    for (auto it = begin(bv); it != end(bv); ++it) {
        if (*it == 0){
            *it = 1;
            carry = false;
            break;
        } else {
            *it = 0;
        }
    }

    return !carry;
}

BitVector
BruteForceMaxisSolver::operator()() {
    // max_weight tracks the current maximum weight
    int max_weight = 0;
    BitVector max_set;

    auto adjacency_matrix = graph.adjacency_matrix();

    BitVector bv(graph.order(), 0);
    do {
        if (graph.is_independent_set(bv)) {
            auto weight = graph.weighted_total(bv);
            if (weight > max_weight) {
                max_weight = weight;
                max_set = bv;
            }
        }
    } while (increment_bit_vector(bv));

    return max_set;
}

// Genetic Maxis Solver
// ====================

GeneticMaxisSolver::GeneticMaxisSolver(
            const Graph &g,
            genetic::Selector &sel,
            genetic::Recombinator &rec,
            genetic::Mutator &mut
        ) : constraint {std::numeric_limits<double>::infinity()},
            size{50},
            graph{g},
            selector{sel},
            recombinator{rec},
            mutator{mut} {

    using std::begin; using std::end;

    // Reorder graph by sorting vertices by their weight divided by
    // their degree. Vertices with a large weight and few neighbors have
    // the highest probability of appearing in the solution.
    permutation.resize(graph.order());
    std::iota(begin(permutation), end(permutation), 0);
    auto &weights = graph.weights;
    auto &adj = graph.adjacency_list;
    std::sort(begin(permutation), end(permutation), [&weights, &adj](size_t i, size_t j) {
        return weights[i] / adj[i].size() > weights[j] / adj[j].size();
    });

    graph.reorder(permutation);
}

// The heuristic feasability operator ensures that genotypes created
// from mutation and breeding are valid independent sets. It is
// essentially a greedy solver and was described by Beasley and Chu. It
// makes a higher mutation rate necessary because some of the mutated
// bits will be immediately cancelled out to make the set independent.
//
// Currently it is the bottleneck for a single iteration as it
// runs in O(|V|^2) time where |V| is the order of the graph.
void
GeneticMaxisSolver::heuristic_feasibility(size_t order, BitVector &adj, BitVector &chromosome) {
    using std::begin; using std::end;

    // Traverse the chromosome, marking vertices which are most likely
    // to appear in an optimal solution as kept. Delete all neighbors of
    // the kept vertices from the chromosome.
    for (auto it = std::make_pair(begin(adj), begin(chromosome)); it.second != end(chromosome); it.first += order, ++it.second) {
        // If vertex is not selected, there is no conflict
        if (!*it.second) {
            continue;
        }

        // Delete all neighbors of the vertex. Because we are assuming
        // that the graph is bidirectional, we only need to traverse
        // half of the adjacency matrix.
        for (auto jt = std::make_pair(std::next(it.first, std::distance(begin(chromosome), it.second)), it.second);
                    jt.second != chromosome.end(); ++jt.first, ++jt.second) {

            if (*jt.first) {
                *jt.second = 0;
            }
        }
    }

    // Add back vertices where we can
    for (auto it = std::make_pair(begin(adj), begin(chromosome)); it.second != end(chromosome); it.first += order, ++it.second) {
        if (*it.second) {
            continue;
        }
        if(std::inner_product(begin(chromosome), end(chromosome), it.first, 0) == 0) {
            *it.second = 1;
        }
    }
}

// initialize_set randomly creates an independent set on the graph with
// adjacency matrix adj
void
GeneticMaxisSolver::initialize_set(size_t order, BitVector &adj, BitVector &chromosome) {
    using std::begin; using std::end;

    static RNG rng;

    BitVector cover(order, 0);
    std::fill(begin(chromosome), end(chromosome), 0);
    auto uncovered_cnt = order;

    // Randomly select a set of vertices that completely cover the
    // graph
    while (uncovered_cnt > 0) {
        // Pick an uncovered vertex at random
        size_t skips = rng.random_index(0, uncovered_cnt - 1);
        size_t dist = 0;
        auto it = begin(cover);
        for (; it != end(cover); ++it)  {
            if (*it == 0 && skips-- == 0) {
                dist = std::distance(begin(cover), it);
                *it = 1;
                break;
            }
        }

        // Select it and mark all of its neighbors as covered
        chromosome[dist] = 1;
        --uncovered_cnt;
        for (auto i = begin(cover), j = begin(adj) + dist*order; i != end(cover); ++i, ++j) {
            if (*j != 0 && *i == 0) {
                *i = 1;
                if (--uncovered_cnt == 0) {
                    break;
                }
            }
        }
    }
}

BitVector
GeneticMaxisSolver::operator()() {
    using std::begin; using std::end;

    auto order = graph.order();
    auto adj = graph.adjacency_matrix();
    genetic::AlgorithmState state;

    // Keep a hash table to check for duplicate chromosomes
    auto hash_func = [](BitVector *chromosome) {
        return std::hash<std::vector<bool>>()(*chromosome);
    };
    auto eq_func = [](BitVector *lhs, BitVector *rhs) {
        return std::equal(begin(*lhs), end(*lhs), begin(*rhs));
    };
    std::unordered_set<BitVector*, decltype(hash_func), decltype(eq_func)> dupes(size, hash_func, eq_func);

    // Generate the initial population
    std::vector<BitVector> chromosomes;
    std::vector<genetic::Phenotype> pop;
    pop.reserve(size);
    chromosomes.reserve(size);

    // If the population size is larger than the set of possible
    // unique solutions, this will loop forever.
    for (decltype(size) i = 0; i < size; ++i) {
        chromosomes.emplace_back(order);
        pop.emplace_back(&chromosomes[i]);
        do {
            initialize_set(order, adj, chromosomes[i]);
        } while (dupes.insert(&chromosomes[i]).second == false);

        pop[i].fitness = graph.weighted_total(*pop[i].chromosome);
    }

    // Sort population by fitness and initialize state information
    std::sort(begin(pop), end(pop));
    state.min_fitness = begin(pop)->fitness;
    state.max_fitness = end(pop)->fitness;
    state.total_fitness = std::accumulate(
        begin(pop), end(pop), 0.0,
        [](double acc, genetic::Phenotype &ph) {
            return acc + ph.fitness;
        }
    );
    state.adjusted_fitness = state.total_fitness - state.min_fitness * size;

    // Start handling SIGINT
    std::signal(SIGINT, sigint_handler);

    while (state.max_fitness < constraint && !sigint_flag) {

        // Select the weakest member of the population to be replaced
        auto &child = *begin(pop);
        dupes.erase(child.chromosome);

        // Select two parents for breeding and store the result into the
        // child, while ensuring that the newly created child is unique.
        // If the selector returns the same result every time, and the
        // mutator does create a sufficiently unique solution, this
        // could loop forever.
        do {
            recombinator.breed(
                state,
                selector.select(state, begin(pop), end(pop)),
                selector.select(state, begin(pop), end(pop)),
                child
            );
            mutator.mutate(state, child);
            heuristic_feasibility(order, adj, *child.chromosome);

        } while (dupes.insert(child.chromosome).second == false);

        // Calculate fitness of the new phenotype and update the
        // total fitness
        state.total_fitness -= child.fitness;
        auto child_fitness = child.fitness = graph.weighted_total(*child.chromosome);
        state.total_fitness += child_fitness;

        // Log whenever we have an increase in the maximum fitness
        if (child_fitness > state.max_fitness) {
            state.max_fitness = child_fitness;
            std::cout << "Best independent set: " << state.max_fitness << std::endl;
        }

        // Use a single pass of bubble sort to put the new child in the
        // correct order. Phenotypes with the same fitness are ordered
        // by freshness.
        for (auto it = std::next(begin(pop)); it != end(pop) && it->fitness <= child_fitness; ++it) {
            std::swap(*it, *std::prev(it));
        }

        // Update the rest of the state information
        state.min_fitness = begin(pop)->fitness;
        state.adjusted_fitness = state.total_fitness - state.min_fitness * size;
        ++state.iterations;
        if ((state.iterations & 0x3ff) == 0) {
            std::cout << "Iterations: " << state.iterations
                      << "; Average Fitness: " << state.total_fitness / size << std::endl;
        }
    }

    auto max = std::prev(end(pop));

    if (sigint_flag) {
        std::cout << "Interrupted..." << std::endl;
    }

    // Rearrange the chromosome into the format of the original,
    // unsorted graph
    BitVector result(order);
    for (size_t i = 0; i < order; ++i) {
        result[permutation[i]] = (*max->chromosome)[i];
    }
    return result;
}

} // namespace maxis
<|endoftext|>


<commit_before>#include "core/log.h"
#include "client/state.h"
#include "client/app.h"
#include "interface/tcpsocket.h"
//#include <cereal/archives/binary.hpp>
//#include <cereal/types/string.hpp>
#include <cstring>
#include <deque>
#include <sys/socket.h>
#define MODULE "__state"

namespace client {

struct ClientPacketTypeRegistry
{
	sm_<ss_, PacketType> m_types;
	sm_<PacketType, ss_> m_names;

	void set(PacketType type, const ss_ &name){
		m_types[name] = type;
		m_names[type] = name;
	}
	PacketType get_type(const ss_ &name){
		auto it = m_types.find(name);
		if(it != m_types.end())
			return it->second;
		throw Exception(ss_()+"Packet not known: "+name);
	}
	ss_ get_name(PacketType type){
		auto it = m_names.find(type);
		if(it != m_names.end())
			return it->second;
		throw Exception(ss_()+"Packet not known: "+itos(type));
	}
};

struct CState: public State
{
	sp_<interface::TCPSocket> m_socket;
	std::deque<char> m_socket_buffer;
	ClientPacketTypeRegistry m_packet_types;
	sp_<app::App> m_app;

	CState(sp_<app::App> app):
		m_socket(interface::createTCPSocket()),
		m_app(app)
	{}

	bool connect(const ss_ &address, const ss_ &port)
	{
		bool ok = m_socket->connect_fd(address, port);
		if(ok)
			log_i(MODULE, "client::State: Connect succeeded (%s:%s)",
					cs(address), cs(port));
		else
			log_i(MODULE, "client::State: Connect failed (%s:%s)",
					cs(address), cs(port));
		return ok;
	}

	bool send(const ss_ &data)
	{
		return m_socket->send_fd(data);
	}

	void update()
	{
		if(m_socket->wait_data(0)){
			read_socket();
			handle_socket_buffer();
		}
	}

	void read_socket()
	{
		int fd = m_socket->fd();
		char buf[100000];
		ssize_t r = recv(fd, buf, 100000, 0);
		if(r == -1)
			throw Exception(ss_()+"Receive failed: "+strerror(errno));
		if(r == 0){
			log_i(MODULE, "Peer disconnected");
			return;
		}
		log_i(MODULE, "Received %zu bytes", r);
		m_socket_buffer.insert(m_socket_buffer.end(), buf, buf + r);
	}

	void handle_socket_buffer()
	{
		for(;;){
			if(m_socket_buffer.size() < 6)
				return;
			size_t type =
					(m_socket_buffer[0] & 0xff)<<0 |
					(m_socket_buffer[1] & 0xff)<<8;
			size_t size =
					(m_socket_buffer[2] & 0xff)<<0 |
					(m_socket_buffer[3] & 0xff)<<8 |
					(m_socket_buffer[4] & 0xff)<<16 |
					(m_socket_buffer[5] & 0xff)<<24;
			log_i(MODULE, "size=%zu", size);
			if(m_socket_buffer.size() < 6 + size)
				return;
			log_i(MODULE, "Received full packet; type=%zu, length=6+%zu",
					type, size);
			ss_ data(&m_socket_buffer[6], size);
			m_socket_buffer.erase(m_socket_buffer.begin(),
					m_socket_buffer.begin() + 6 + size);
			try {
				handle_packet(type, data);
			} catch(std::exception &e){
				log_w(MODULE, "Exception on handling packet: %s",
						e.what());
			}
		}
	}

	void handle_packet(size_t type, const ss_ &data)
	{
		// Type 0 is packet definition packet
		if(type == 0){
			PacketType type1 =
					data[0]<<0 |
					data[1]<<8;
			size_t name1_size =
					data[2]<<0 |
					data[3]<<8 |
					data[4]<<16 |
					data[5]<<24;
			if(data.size() < 6 + name1_size)
				return;
			ss_ name1(&data.c_str()[6], name1_size);
			log_i(MODULE, "Packet definition: %zu = %s", type1, cs(name1));
			m_packet_types.set(type1, name1);
			return;
		}

		ss_ packet_name = m_packet_types.get_name(type);
		log_i(MODULE, "Received packet name: %s", cs(packet_name));

		if(packet_name == "core:run_script"){
			log_i(MODULE, "Asked to run script:\n----\n%s\n----", cs(data));
			if(m_app)
				m_app->run_script(data);
			return;
		}
	}
};

State* createState(sp_<app::App> app)
{
	return new CState(app);
}
}
<commit_msg>client/state: Fix copying of packet data from deque to string<commit_after>#include "core/log.h"
#include "client/state.h"
#include "client/app.h"
#include "interface/tcpsocket.h"
//#include <cereal/archives/binary.hpp>
//#include <cereal/types/string.hpp>
#include <cstring>
#include <deque>
#include <sys/socket.h>
#define MODULE "__state"

namespace client {

struct ClientPacketTypeRegistry
{
	sm_<ss_, PacketType> m_types;
	sm_<PacketType, ss_> m_names;

	void set(PacketType type, const ss_ &name){
		m_types[name] = type;
		m_names[type] = name;
	}
	PacketType get_type(const ss_ &name){
		auto it = m_types.find(name);
		if(it != m_types.end())
			return it->second;
		throw Exception(ss_()+"Packet not known: "+name);
	}
	ss_ get_name(PacketType type){
		auto it = m_names.find(type);
		if(it != m_names.end())
			return it->second;
		throw Exception(ss_()+"Packet not known: "+itos(type));
	}
};

struct CState: public State
{
	sp_<interface::TCPSocket> m_socket;
	std::deque<char> m_socket_buffer;
	ClientPacketTypeRegistry m_packet_types;
	sp_<app::App> m_app;

	CState(sp_<app::App> app):
		m_socket(interface::createTCPSocket()),
		m_app(app)
	{}

	bool connect(const ss_ &address, const ss_ &port)
	{
		bool ok = m_socket->connect_fd(address, port);
		if(ok)
			log_i(MODULE, "client::State: Connect succeeded (%s:%s)",
					cs(address), cs(port));
		else
			log_i(MODULE, "client::State: Connect failed (%s:%s)",
					cs(address), cs(port));
		return ok;
	}

	bool send(const ss_ &data)
	{
		return m_socket->send_fd(data);
	}

	void update()
	{
		if(m_socket->wait_data(0)){
			read_socket();
			handle_socket_buffer();
		}
	}

	void read_socket()
	{
		int fd = m_socket->fd();
		char buf[100000];
		ssize_t r = recv(fd, buf, 100000, 0);
		if(r == -1)
			throw Exception(ss_()+"Receive failed: "+strerror(errno));
		if(r == 0){
			log_i(MODULE, "Peer disconnected");
			return;
		}
		log_i(MODULE, "Received %zu bytes", r);
		m_socket_buffer.insert(m_socket_buffer.end(), buf, buf + r);
	}

	void handle_socket_buffer()
	{
		for(;;){
			if(m_socket_buffer.size() < 6)
				return;
			size_t type =
					(m_socket_buffer[0] & 0xff)<<0 |
					(m_socket_buffer[1] & 0xff)<<8;
			size_t size =
					(m_socket_buffer[2] & 0xff)<<0 |
					(m_socket_buffer[3] & 0xff)<<8 |
					(m_socket_buffer[4] & 0xff)<<16 |
					(m_socket_buffer[5] & 0xff)<<24;
			log_i(MODULE, "size=%zu", size);
			if(m_socket_buffer.size() < 6 + size)
				return;
			log_i(MODULE, "Received full packet; type=%zu, length=6+%zu",
					type, size);
			ss_ data(m_socket_buffer.begin() + 6, m_socket_buffer.begin() + 6 + size);
			m_socket_buffer.erase(m_socket_buffer.begin(),
					m_socket_buffer.begin() + 6 + size);
			try {
				handle_packet(type, data);
			} catch(std::exception &e){
				log_w(MODULE, "Exception on handling packet: %s",
						e.what());
			}
		}
	}

	void handle_packet(size_t type, const ss_ &data)
	{
		// Type 0 is packet definition packet
		if(type == 0){
			PacketType type1 =
					data[0]<<0 |
					data[1]<<8;
			size_t name1_size =
					data[2]<<0 |
					data[3]<<8 |
					data[4]<<16 |
					data[5]<<24;
			if(data.size() < 6 + name1_size)
				return;
			ss_ name1(&data.c_str()[6], name1_size);
			log_i(MODULE, "Packet definition: %zu = %s", type1, cs(name1));
			m_packet_types.set(type1, name1);
			return;
		}

		ss_ packet_name = m_packet_types.get_name(type);
		log_i(MODULE, "Received packet name: %s", cs(packet_name));

		if(packet_name == "core:run_script"){
			log_i(MODULE, "Asked to run script:\n----\n%s\n----", cs(data));
			if(m_app)
				m_app->run_script(data);
			return;
		}
	}
};

State* createState(sp_<app::App> app)
{
	return new CState(app);
}
}
<|endoftext|>


<commit_before>/*
 * String hash map.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef BENG_PROXY_STRMAP_HXX
#define BENG_PROXY_STRMAP_HXX

#include <inline/compiler.h>

#include <boost/intrusive/set.hpp>

struct pool;

struct strmap_pair {
    const char *key, *value;

    constexpr strmap_pair(const char *_key, const char *_value)
        :key(_key), value(_value) {}
};

struct strmap {
    struct Item : strmap_pair {
        typedef boost::intrusive::set_member_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>> Hook;
        Hook hook;

        Item(const char *_key, const char *_value)
            :strmap_pair(_key, _value) {}

        class Compare {
            gcc_pure
            bool Less(const char *a, const char *b) const;

        public:
            gcc_pure
            bool operator()(const char *a, const Item &b) const {
                return Less(a, b.key);
            }

            gcc_pure
            bool operator()(const Item &a, const char *b) const {
                return Less(a.key, b);
            }

            gcc_pure
            bool operator()(const Item &a, const Item &b) const {
                return Less(a.key, b.key);
            }
        };
    };

    struct pool &pool;

    typedef boost::intrusive::multiset<Item,
                                       boost::intrusive::member_hook<Item, Item::Hook, &Item::hook>,
                                       boost::intrusive::compare<Item::Compare>,
                                       boost::intrusive::constant_time_size<false>> Map;

    typedef Map::const_iterator const_iterator;

    Map map;

    mutable Map::const_iterator cursor;

    explicit strmap(struct pool &_pool):pool(_pool) {}

    strmap(struct pool &_pool, const strmap &src);

    strmap(const strmap &) = delete;

    const_iterator begin() const {
        return map.begin();
    }

    const_iterator end() const {
        return map.end();
    }

    void Add(const char *key, const char *value);
    const char *Set(const char *key, const char *value);
    const char *Remove(const char *key);

    gcc_pure
    const char *Get(const char *key) const;

    gcc_pure
    const struct strmap_pair *LookupFirst(const char *key) const;

    gcc_pure
    const struct strmap_pair *LookupNext(const struct strmap_pair *pair) const;
};

struct strmap *gcc_malloc
strmap_new(struct pool *pool);

struct strmap *gcc_malloc
strmap_dup(struct pool *pool, struct strmap *src);

static inline const char *
strmap_get(const struct strmap *map, const char *key)
{
    return map->Get(key);
}

/**
 * @see hashmap_lookup_first()
 */
static inline const struct strmap_pair *
strmap_lookup_first(const struct strmap *map, const char *key)
{
    return map->LookupFirst(key);
}

/**
 * @see hashmap_lookup_next()
 */
static inline const struct strmap_pair *
strmap_lookup_next(const struct strmap *map, const struct strmap_pair *pair)
{
    return map->LookupNext(pair);
}

/**
 * This variation of strmap::Remove() allows the caller to pass
 * map=nullptr.
 */
static inline const char *
strmap_remove_checked(struct strmap *map, const char *key)
{
    return map != nullptr
        ? map->Remove(key)
        : nullptr;
}

/**
 * This variation of strmap_get() allows the caller to pass map=nullptr.
 */
gcc_pure
static inline const char *
strmap_get_checked(const struct strmap *map, const char *key)
{
    return map != nullptr
        ? strmap_get(map, key)
        : nullptr;
}

#endif
<commit_msg>strmap: remove unused attribute "cursor"<commit_after>/*
 * String hash map.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef BENG_PROXY_STRMAP_HXX
#define BENG_PROXY_STRMAP_HXX

#include <inline/compiler.h>

#include <boost/intrusive/set.hpp>

struct pool;

struct strmap_pair {
    const char *key, *value;

    constexpr strmap_pair(const char *_key, const char *_value)
        :key(_key), value(_value) {}
};

struct strmap {
    struct Item : strmap_pair {
        typedef boost::intrusive::set_member_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>> Hook;
        Hook hook;

        Item(const char *_key, const char *_value)
            :strmap_pair(_key, _value) {}

        class Compare {
            gcc_pure
            bool Less(const char *a, const char *b) const;

        public:
            gcc_pure
            bool operator()(const char *a, const Item &b) const {
                return Less(a, b.key);
            }

            gcc_pure
            bool operator()(const Item &a, const char *b) const {
                return Less(a.key, b);
            }

            gcc_pure
            bool operator()(const Item &a, const Item &b) const {
                return Less(a.key, b.key);
            }
        };
    };

    struct pool &pool;

    typedef boost::intrusive::multiset<Item,
                                       boost::intrusive::member_hook<Item, Item::Hook, &Item::hook>,
                                       boost::intrusive::compare<Item::Compare>,
                                       boost::intrusive::constant_time_size<false>> Map;

    typedef Map::const_iterator const_iterator;

    Map map;

    explicit strmap(struct pool &_pool):pool(_pool) {}

    strmap(struct pool &_pool, const strmap &src);

    strmap(const strmap &) = delete;

    const_iterator begin() const {
        return map.begin();
    }

    const_iterator end() const {
        return map.end();
    }

    void Add(const char *key, const char *value);
    const char *Set(const char *key, const char *value);
    const char *Remove(const char *key);

    gcc_pure
    const char *Get(const char *key) const;

    gcc_pure
    const struct strmap_pair *LookupFirst(const char *key) const;

    gcc_pure
    const struct strmap_pair *LookupNext(const struct strmap_pair *pair) const;
};

struct strmap *gcc_malloc
strmap_new(struct pool *pool);

struct strmap *gcc_malloc
strmap_dup(struct pool *pool, struct strmap *src);

static inline const char *
strmap_get(const struct strmap *map, const char *key)
{
    return map->Get(key);
}

/**
 * @see hashmap_lookup_first()
 */
static inline const struct strmap_pair *
strmap_lookup_first(const struct strmap *map, const char *key)
{
    return map->LookupFirst(key);
}

/**
 * @see hashmap_lookup_next()
 */
static inline const struct strmap_pair *
strmap_lookup_next(const struct strmap *map, const struct strmap_pair *pair)
{
    return map->LookupNext(pair);
}

/**
 * This variation of strmap::Remove() allows the caller to pass
 * map=nullptr.
 */
static inline const char *
strmap_remove_checked(struct strmap *map, const char *key)
{
    return map != nullptr
        ? map->Remove(key)
        : nullptr;
}

/**
 * This variation of strmap_get() allows the caller to pass map=nullptr.
 */
gcc_pure
static inline const char *
strmap_get_checked(const struct strmap *map, const char *key)
{
    return map != nullptr
        ? strmap_get(map, key)
        : nullptr;
}

#endif
<|endoftext|>


<commit_before>/*	Run a series of instruments as a group, chaining the output of each to the
    input of the next.

		p0 = output start time
		p1 = duration (-endtime)

*/
#include <unistd.h>
#include <stdio.h>
#include <ugens.h>
#include <Instrument.h>
#include <PField.h>
#include <PFieldSet.h>
#include <rt.h>
#include <rtdefs.h>
#include <Option.h>
#ifdef MAXMSP
#include <MMPrint.h>
#endif
#include "CHAIN.h"

CHAIN::CHAIN()
{
}

CHAIN::~CHAIN()
{
	for (std::vector<Instrument *>::iterator it = mInstVector.begin(); it != mInstVector.end(); ++it) {
		Instrument *inst = *it;
		inst->unref();
	}
}

int  CHAIN::setup(PFieldSet *inPFields)
{
	// Copy first 3 numeric args to new PFieldSet
	PFieldSet *newSet = new PFieldSet(3);
	for (int p = 0; p < 3; ++p)
		newSet->load(new ConstPField((*inPFields)[p].doubleValue(0.0)), p);
	// The remaining args are InstPFields
	int numChainedInstruments = (int)(*inPFields)[2].intValue(0.0);
	for (int p = 0; p < numChainedInstruments; ++p) {
		InstPField *ipf = (InstPField *) &(*inPFields)[p+3];
		Instrument *inst = ipf->instrument();
		mInstVector.push_back(inst);
		inst->ref();
	}
	if (Option::print() >= MMP_PRINTALL) {

#ifdef MAXMSP
		MMPrint::mm_print_ptr += sprintf(MMPrint::mm_print_ptr, "Instrument chain: ");
		for (std::vector<Instrument *>::iterator it = mInstVector.begin(); it != mInstVector.end(); ++it) {
			MMPrint::mm_print_ptr += sprintf(MMPrint::mm_print_ptr, "%s -> ", (*it)->name());
		}
		MMPrint::mm_print_ptr += sprintf(MMPrint::mm_print_ptr, "Out\n")+1;
#else

		printf("Instrument chain: ");
		for (std::vector<Instrument *>::iterator it = mInstVector.begin(); it != mInstVector.end(); ++it) {
			printf("%s -> ", (*it)->name());
		}
		printf("Out\n");
#endif
	}
	delete inPFields;
	return Instrument::setup(newSet);
}

int CHAIN::init(double p[], int n_args)
{
	const float outskip = p[0];
	float dur = p[1];

	if (rtsetoutput(outskip, dur, this) == -1)
		return DONT_SCHEDULE;

	return nSamps();
}

int CHAIN::configure()
{
	int status = -1;
	Instrument *previous = NULL;
	for (std::vector<Instrument *>::iterator it = mInstVector.begin(); it != mInstVector.end(); ++it) {
		Instrument *inst = *it;
		status = inst->configure(RTBUFSAMPS);
		if (status != 0)
			return status;
		if (previous != NULL) {
			status = inst->setChainedInputBuffer(previous->outbuf, previous->outputChannels());
			if (status != 0)
				return status;
		}
		previous = inst;
	}
	// For CHAIN itself, we override our (what should be zero) input channel count here.  This allows setChainedInputBuffer() to succeed
	// even though the counts don't seem to match.
	_input.inputchans = previous->outputChannels();
	status = setChainedInputBuffer(previous->outbuf, previous->outputChannels());
	return status;
}

int CHAIN::run()
{
	for (std::vector<Instrument *>::iterator it = mInstVector.begin(); it != mInstVector.end(); ++it) {
		Instrument *inst = *it;
		inst->setchunk(framesToRun());	// For outer instrument, this is done in inTraverse()
		inst->run(true);
		inst->addout(BUS_NONE_OUT, 0);		// Special bus type makes this a no-op
	}
	// Copy from inputChainedBuf, which points to the outbuf of the last instrument in the chain.
	unsigned copySize = framesToRun() * outputChannels() * sizeof(BUFTYPE);
	memcpy(outbuf, inputChainBuf, copySize);
	return framesToRun();
}

Instrument *makeCHAIN()
{
	CHAIN *inst = new CHAIN();
	inst->set_bus_config("CHAIN");

	return inst;
}

#ifndef MAXMSP
void rtprofile()
{
   RT_INTRO("CHAIN",makeCHAIN);
}
#endif
<commit_msg>fixed a minor MMPrint thing -- BGG<commit_after>/*	Run a series of instruments as a group, chaining the output of each to the
    input of the next.

		p0 = output start time
		p1 = duration (-endtime)

*/
#include <unistd.h>
#include <stdio.h>
#include <ugens.h>
#include <Instrument.h>
#include <PField.h>
#include <PFieldSet.h>
#include <rt.h>
#include <rtdefs.h>
#include <Option.h>
#include <MMPrint.h>
#include "CHAIN.h"

CHAIN::CHAIN()
{
}

CHAIN::~CHAIN()
{
	for (std::vector<Instrument *>::iterator it = mInstVector.begin(); it != mInstVector.end(); ++it) {
		Instrument *inst = *it;
		inst->unref();
	}
}

int  CHAIN::setup(PFieldSet *inPFields)
{
	// Copy first 3 numeric args to new PFieldSet
	PFieldSet *newSet = new PFieldSet(3);
	for (int p = 0; p < 3; ++p)
		newSet->load(new ConstPField((*inPFields)[p].doubleValue(0.0)), p);
	// The remaining args are InstPFields
	int numChainedInstruments = (int)(*inPFields)[2].intValue(0.0);
	for (int p = 0; p < numChainedInstruments; ++p) {
		InstPField *ipf = (InstPField *) &(*inPFields)[p+3];
		Instrument *inst = ipf->instrument();
		mInstVector.push_back(inst);
		inst->ref();
	}
	if (Option::print() >= MMP_PRINTALL) {

#ifdef MAXMSP
		MMPrint::mm_print_ptr += sprintf(MMPrint::mm_print_ptr, "Instrument chain: ");
		for (std::vector<Instrument *>::iterator it = mInstVector.begin(); it != mInstVector.end(); ++it) {
			MMPrint::mm_print_ptr += sprintf(MMPrint::mm_print_ptr, "%s -> ", (*it)->name());
		}
		MMPrint::mm_print_ptr += sprintf(MMPrint::mm_print_ptr, "Out\n")+1;
#else

		printf("Instrument chain: ");
		for (std::vector<Instrument *>::iterator it = mInstVector.begin(); it != mInstVector.end(); ++it) {
			printf("%s -> ", (*it)->name());
		}
		printf("Out\n");
#endif
	}
	delete inPFields;
	return Instrument::setup(newSet);
}

int CHAIN::init(double p[], int n_args)
{
	const float outskip = p[0];
	float dur = p[1];

	if (rtsetoutput(outskip, dur, this) == -1)
		return DONT_SCHEDULE;

	return nSamps();
}

int CHAIN::configure()
{
	int status = -1;
	Instrument *previous = NULL;
	for (std::vector<Instrument *>::iterator it = mInstVector.begin(); it != mInstVector.end(); ++it) {
		Instrument *inst = *it;
		status = inst->configure(RTBUFSAMPS);
		if (status != 0)
			return status;
		if (previous != NULL) {
			status = inst->setChainedInputBuffer(previous->outbuf, previous->outputChannels());
			if (status != 0)
				return status;
		}
		previous = inst;
	}
	// For CHAIN itself, we override our (what should be zero) input channel count here.  This allows setChainedInputBuffer() to succeed
	// even though the counts don't seem to match.
	_input.inputchans = previous->outputChannels();
	status = setChainedInputBuffer(previous->outbuf, previous->outputChannels());
	return status;
}

int CHAIN::run()
{
	for (std::vector<Instrument *>::iterator it = mInstVector.begin(); it != mInstVector.end(); ++it) {
		Instrument *inst = *it;
		inst->setchunk(framesToRun());	// For outer instrument, this is done in inTraverse()
		inst->run(true);
		inst->addout(BUS_NONE_OUT, 0);		// Special bus type makes this a no-op
	}
	// Copy from inputChainedBuf, which points to the outbuf of the last instrument in the chain.
	unsigned copySize = framesToRun() * outputChannels() * sizeof(BUFTYPE);
	memcpy(outbuf, inputChainBuf, copySize);
	return framesToRun();
}

Instrument *makeCHAIN()
{
	CHAIN *inst = new CHAIN();
	inst->set_bus_config("CHAIN");

	return inst;
}

#ifndef MAXMSP
void rtprofile()
{
   RT_INTRO("CHAIN",makeCHAIN);
}
#endif
<|endoftext|>


<commit_before>/*
 *  Software License Agreement (BSD License)
 *
 *  Copyright (c) 2016-2020, Natalnet Laboratory for Perceptual Robotics
 *  All rights reserved.
 *  Redistribution and use in source and binary forms, with or without modification, are permitted provided
 *  that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice, this list of conditions and
 *     the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
 *     the following disclaimer in the documentation and/or other materials provided with the distribution.
 *
 *  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or
 *     promote products derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  Authors:
 *
 *  Felipe Ferreira Barbosa
 *  Vanessa Dantas de Souto Costa
 *  Bruno Silva
 */

#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <string>
#include <iterator>
#include <algorithm>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

#include <event_logger.h>
#include <kitti_stereo_loader.h>

using namespace cv;
using namespace std;

void KITTIStereoLoader::loadStereoPair(const std::string& left_path, const std::string& right_path)
{
    left_image_ = imread(left_path);
    right_image_ = imread(right_path);
}

void KITTIStereoLoader::loadStereoSequence(const std::string& sequence_path, const int& sequence_num, const bool& use_color)
{
    int exec_status;
    char tmp[3];
    string seq_number_str, cmd, index_path;

    //set the root directory (common to all gray/color left/right images)
    root_path_ = sequence_path;

    //set the sequence number
    sprintf(tmp, "%02d", sequence_num);
    seq_number_str = string(tmp);

    //set the prefix path
    prefix_path_ = root_path_ + "sequences/" + seq_number_str;
    MLOG_INFO(EventLogger::M_IO, "Loading KITTI sequence: %s\n", prefix_path_.c_str());

    if(use_color)
    {
        cmd = "ls " + prefix_path_ + "/image_2 >"
                    + prefix_path_ + "/image_2/index.txt";
        index_path = prefix_path_ + "/image_2/index.txt";
    }
    else
    {
        cmd = "ls " + prefix_path_ + "/image_0 >"
                    + prefix_path_ + "/image_0/index.txt";
        index_path = prefix_path_ + "/image_0/index.txt";
    }

    //execute system command (to generate *.txt files)
    exec_status = system(cmd.c_str());

    if(exec_status == 0)
    {
        //Read file and insert each entry into the vector of strings
        ifstream myfile(index_path.c_str());
        copy(istream_iterator<string>(myfile),
             istream_iterator<string>(),
             back_inserter(left_image_names_));
        myfile.close();

        left_image_names_.erase(left_image_names_.begin() + left_image_names_.size() - 1);
        right_image_names_ = left_image_names_;

        num_pairs_ = left_image_names_.size();
    }
    else
    {
        MLOG_ERROR(EventLogger::M_IO, "@KITTIStereoLoader::loadStereoSequence: Could not create index file (error on system command).\n");
        exit(0);
    }
}

Mat KITTIStereoLoader::getLeftImage()
{
    return left_image_;
}

Mat KITTIStereoLoader::getRightImage()
{
    return right_image_;
}

Mat KITTIStereoLoader::getNextLeftImage(const bool& use_color)
{
    string path;
    Mat left_img;

    if(use_color)
    {
        path = prefix_path_ + "/image_2/" + left_image_names_[next_left_];
        left_img = imread(path, IMREAD_COLOR);
    }
    else
    {
        path = prefix_path_ + "/image_0/" + left_image_names_[next_left_];
        left_img = imread(path, CV_LOAD_IMAGE_UNCHANGED);
    }

    next_left_++;
    return left_img;
}

Mat KITTIStereoLoader::getNextRightImage(const bool& use_color)
{
    string path;
    Mat right_img;

    if(use_color)
    {
        path = prefix_path_ + "/image_3/" + right_image_names_[next_right_];
        right_img = imread(path, IMREAD_COLOR);
    }
    else
    {
        path = prefix_path_ + "/image_1/" + right_image_names_[next_right_];
        right_img = imread(path, CV_LOAD_IMAGE_UNCHANGED);
    }

    next_right_++;
    return right_img;
}

KITTIStereoLoader::~KITTIStereoLoader()
{
    left_image_.release();
    right_image_.release();
    root_path_.clear();
    left_image_names_.clear();
    right_image_names_.clear();
}
<commit_msg>Changed KITTIStereoLoader to no longer append 'sequences' to the path.<commit_after>/*
 *  Software License Agreement (BSD License)
 *
 *  Copyright (c) 2016-2020, Natalnet Laboratory for Perceptual Robotics
 *  All rights reserved.
 *  Redistribution and use in source and binary forms, with or without modification, are permitted provided
 *  that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice, this list of conditions and
 *     the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
 *     the following disclaimer in the documentation and/or other materials provided with the distribution.
 *
 *  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or
 *     promote products derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 *  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  Authors:
 *
 *  Felipe Ferreira Barbosa
 *  Vanessa Dantas de Souto Costa
 *  Bruno Silva
 */

#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <string>
#include <iterator>
#include <algorithm>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

#include <event_logger.h>
#include <kitti_stereo_loader.h>

using namespace cv;
using namespace std;

void KITTIStereoLoader::loadStereoPair(const std::string& left_path, const std::string& right_path)
{
    left_image_ = imread(left_path);
    right_image_ = imread(right_path);
}

void KITTIStereoLoader::loadStereoSequence(const std::string& sequence_path, const int& sequence_num, const bool& use_color)
{
    int exec_status;
    char tmp[3];
    string seq_number_str, cmd, index_path, needed_slash = "";

    //set the root directory (common to all gray/color left/right images)
    root_path_ = sequence_path;

    //set the sequence number
    sprintf(tmp, "%02d", sequence_num);
    seq_number_str = string(tmp);

    //add last '/'' if the path does not have one
    if(sequence_path.back() != '/')
    {
        needed_slash = "/";
    }

    //set the prefix path
    prefix_path_ = root_path_ + needed_slash + seq_number_str;
    MLOG_INFO(EventLogger::M_IO, "Loading KITTI sequence: %s\n", prefix_path_.c_str());

    if(use_color)
    {
        cmd = "ls " + prefix_path_ + "/image_2 >"
                    + prefix_path_ + "/image_2/index.txt";
        index_path = prefix_path_ + "/image_2/index.txt";
    }
    else
    {
        cmd = "ls " + prefix_path_ + "/image_0 >"
                    + prefix_path_ + "/image_0/index.txt";
        index_path = prefix_path_ + "/image_0/index.txt";
    }

    //execute system command (to generate *.txt files)
    exec_status = system(cmd.c_str());

    if(exec_status == 0)
    {
        //Read file and insert each entry into the vector of strings
        ifstream myfile(index_path.c_str());
        copy(istream_iterator<string>(myfile),
             istream_iterator<string>(),
             back_inserter(left_image_names_));
        myfile.close();

        left_image_names_.erase(left_image_names_.begin() + left_image_names_.size() - 1);
        right_image_names_ = left_image_names_;

        num_pairs_ = left_image_names_.size();
    }
    else
    {
        MLOG_ERROR(EventLogger::M_IO, "@KITTIStereoLoader::loadStereoSequence: Could not create index file (error on system command).\n");
        exit(0);
    }
}

Mat KITTIStereoLoader::getLeftImage()
{
    return left_image_;
}

Mat KITTIStereoLoader::getRightImage()
{
    return right_image_;
}

Mat KITTIStereoLoader::getNextLeftImage(const bool& use_color)
{
    string path;
    Mat left_img;

    if(use_color)
    {
        path = prefix_path_ + "/image_2/" + left_image_names_[next_left_];
        left_img = imread(path, IMREAD_COLOR);
    }
    else
    {
        path = prefix_path_ + "/image_0/" + left_image_names_[next_left_];
        left_img = imread(path, CV_LOAD_IMAGE_UNCHANGED);
    }

    next_left_++;
    return left_img;
}

Mat KITTIStereoLoader::getNextRightImage(const bool& use_color)
{
    string path;
    Mat right_img;

    if(use_color)
    {
        path = prefix_path_ + "/image_3/" + right_image_names_[next_right_];
        right_img = imread(path, IMREAD_COLOR);
    }
    else
    {
        path = prefix_path_ + "/image_1/" + right_image_names_[next_right_];
        right_img = imread(path, CV_LOAD_IMAGE_UNCHANGED);
    }

    next_right_++;
    return right_img;
}

KITTIStereoLoader::~KITTIStereoLoader()
{
    left_image_.release();
    right_image_.release();
    root_path_.clear();
    left_image_names_.clear();
    right_image_names_.clear();
}
<|endoftext|>


<commit_before>#include "scanscalar.h"
#include "scanner.h"
#include "exp.h"
#include "exceptions.h"
#include "token.h"

namespace YAML
{
	// ScanScalar
	// . This is where the scalar magic happens.
	//
	// . We do the scanning in three phases:
	//   1. Scan until newline
	//   2. Eat newline
	//   3. Scan leading blanks.
	//
	// . Depending on the parameters given, we store or stop
	//   and different places in the above flow.
	std::string ScanScalar(Stream& INPUT, ScanScalarParams& params)
	{
		bool foundNonEmptyLine = false;
		bool emptyLine = false, moreIndented = false;
		std::string scalar;
		params.leadingSpaces = false;

		while(INPUT) {
			// ********************************
			// Phase #1: scan until line ending
			while(!params.end.Matches(INPUT) && !Exp::Break.Matches(INPUT)) {
				if(INPUT.peek() == EOF)
					break;

				// document indicator?
				if(INPUT.column == 0 && Exp::DocIndicator.Matches(INPUT)) {
					if(params.onDocIndicator == BREAK)
						break;
					else if(params.onDocIndicator == THROW)
						throw IllegalDocIndicator();
				}

				foundNonEmptyLine = true;

				// escaped newline? (only if we're escaping on slash)
				if(params.escape == '\\' && Exp::EscBreak.Matches(INPUT)) {
					int n = Exp::EscBreak.Match(INPUT);
					INPUT.eat(n);
					continue;
				}

				// escape this?
				if(INPUT.peek() == params.escape) {
					scalar += Exp::Escape(INPUT);
					continue;
				}

				// otherwise, just add the damn character
				scalar += INPUT.get();
			}

			// eof? if we're looking to eat something, then we throw
			if(INPUT.peek() == EOF) {
				if(params.eatEnd)
					throw IllegalEOF();
				break;
			}

			// doc indicator?
			if(params.onDocIndicator == BREAK && INPUT.column == 0 && Exp::DocIndicator.Matches(INPUT))
				break;

			// are we done via character match?
			int n = params.end.Match(INPUT);
			if(n >= 0) {
				if(params.eatEnd)
					INPUT.eat(n);
				break;
			}

			// ********************************
			// Phase #2: eat line ending
			n = Exp::Break.Match(INPUT);
			INPUT.eat(n);

			// ********************************
			// Phase #3: scan initial spaces

			// first the required indentation
			while(INPUT.peek() == ' ' && (INPUT.column < params.indent || (params.detectIndent && !foundNonEmptyLine)))
				INPUT.eat(1);

			// update indent if we're auto-detecting
			if(params.detectIndent && !foundNonEmptyLine)
				params.indent = std::max(params.indent, INPUT.column);

			// and then the rest of the whitespace
			while(Exp::Blank.Matches(INPUT)) {
				// we check for tabs that masquerade as indentation
				if(INPUT.peek() == '\t'&& INPUT.column < params.indent && params.onTabInIndentation == THROW)
					throw IllegalTabInIndentation();

				if(!params.eatLeadingWhitespace)
					break;

				INPUT.eat(1);
			}

			// was this an empty line?
			bool nextEmptyLine = Exp::Break.Matches(INPUT);
			bool nextMoreIndented = (INPUT.peek() == ' ');

			// TODO: for block scalars, we always start with a newline, so we should fold OR keep that

			if(params.fold && !emptyLine && !nextEmptyLine && !moreIndented && !nextMoreIndented)
				scalar += " ";
			else
				scalar += "\n";

			emptyLine = nextEmptyLine;
			moreIndented = nextMoreIndented;

			// are we done via indentation?
			if(!emptyLine && INPUT.column < params.indent) {
				params.leadingSpaces = true;
				break;
			}
		}

		// post-processing
		if(params.trimTrailingSpaces) {
			unsigned pos = scalar.find_last_not_of(' ');
			if(pos < scalar.size())
				scalar.erase(pos + 1);
		}

		if(params.chomp <= 0) {
			unsigned pos = scalar.find_last_not_of('\n');
			if(params.chomp == 0 && pos + 1 < scalar.size())
				scalar.erase(pos + 2);
			else if(params.chomp == -1 && pos < scalar.size())
				scalar.erase(pos + 1);
		}

		return scalar;
	}
}
<commit_msg>Fixed opening newline bug for block scalars.<commit_after>#include "scanscalar.h"
#include "scanner.h"
#include "exp.h"
#include "exceptions.h"
#include "token.h"

namespace YAML
{
	// ScanScalar
	// . This is where the scalar magic happens.
	//
	// . We do the scanning in three phases:
	//   1. Scan until newline
	//   2. Eat newline
	//   3. Scan leading blanks.
	//
	// . Depending on the parameters given, we store or stop
	//   and different places in the above flow.
	std::string ScanScalar(Stream& INPUT, ScanScalarParams& params)
	{
		bool foundNonEmptyLine = false, pastOpeningBreak = false;
		bool emptyLine = false, moreIndented = false;
		std::string scalar;
		params.leadingSpaces = false;

		while(INPUT) {
			// ********************************
			// Phase #1: scan until line ending
			while(!params.end.Matches(INPUT) && !Exp::Break.Matches(INPUT)) {
				if(INPUT.peek() == EOF)
					break;

				// document indicator?
				if(INPUT.column == 0 && Exp::DocIndicator.Matches(INPUT)) {
					if(params.onDocIndicator == BREAK)
						break;
					else if(params.onDocIndicator == THROW)
						throw IllegalDocIndicator();
				}

				foundNonEmptyLine = true;
				pastOpeningBreak = true;

				// escaped newline? (only if we're escaping on slash)
				if(params.escape == '\\' && Exp::EscBreak.Matches(INPUT)) {
					int n = Exp::EscBreak.Match(INPUT);
					INPUT.eat(n);
					continue;
				}

				// escape this?
				if(INPUT.peek() == params.escape) {
					scalar += Exp::Escape(INPUT);
					continue;
				}

				// otherwise, just add the damn character
				scalar += INPUT.get();
			}

			// eof? if we're looking to eat something, then we throw
			if(INPUT.peek() == EOF) {
				if(params.eatEnd)
					throw IllegalEOF();
				break;
			}

			// doc indicator?
			if(params.onDocIndicator == BREAK && INPUT.column == 0 && Exp::DocIndicator.Matches(INPUT))
				break;

			// are we done via character match?
			int n = params.end.Match(INPUT);
			if(n >= 0) {
				if(params.eatEnd)
					INPUT.eat(n);
				break;
			}

			// ********************************
			// Phase #2: eat line ending
			n = Exp::Break.Match(INPUT);
			INPUT.eat(n);

			// ********************************
			// Phase #3: scan initial spaces

			// first the required indentation
			while(INPUT.peek() == ' ' && (INPUT.column < params.indent || (params.detectIndent && !foundNonEmptyLine)))
				INPUT.eat(1);

			// update indent if we're auto-detecting
			if(params.detectIndent && !foundNonEmptyLine)
				params.indent = std::max(params.indent, INPUT.column);

			// and then the rest of the whitespace
			while(Exp::Blank.Matches(INPUT)) {
				// we check for tabs that masquerade as indentation
				if(INPUT.peek() == '\t'&& INPUT.column < params.indent && params.onTabInIndentation == THROW)
					throw IllegalTabInIndentation();

				if(!params.eatLeadingWhitespace)
					break;

				INPUT.eat(1);
			}

			// was this an empty line?
			bool nextEmptyLine = Exp::Break.Matches(INPUT);
			bool nextMoreIndented = (INPUT.peek() == ' ');

			// for block scalars, we always start with a newline, so we should ignore it (not fold or keep)
			if(pastOpeningBreak) {
				if(params.fold && !emptyLine && !nextEmptyLine && !moreIndented && !nextMoreIndented)
					scalar += " ";
				else
					scalar += "\n";
			}

			emptyLine = nextEmptyLine;
			moreIndented = nextMoreIndented;
			pastOpeningBreak = true;

			// are we done via indentation?
			if(!emptyLine && INPUT.column < params.indent) {
				params.leadingSpaces = true;
				break;
			}
		}

		// post-processing
		if(params.trimTrailingSpaces) {
			unsigned pos = scalar.find_last_not_of(' ');
			if(pos < scalar.size())
				scalar.erase(pos + 1);
		}

		if(params.chomp <= 0) {
			unsigned pos = scalar.find_last_not_of('\n');
			if(params.chomp == 0 && pos + 1 < scalar.size())
				scalar.erase(pos + 2);
			else if(params.chomp == -1 && pos < scalar.size())
				scalar.erase(pos + 1);
		}

		return scalar;
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, Tim Vandermeersch
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the <organization> nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "tool.h"

#include <Helium/fingerprints/similarity.h>
#include <Helium/fileio/fingerprints.h>
#include <Helium/fileio/fps.h>
#include <Helium/smiles.h>

#ifdef HAVE_CPP11
#include <Helium/concurrent.h>
#endif

#include <json/json.h>

#include "args.h"

namespace Helium {

  Word* compute_fingerprint(const std::string &settings, const std::string &smiles)
  {
    Json::Reader reader;
    Json::Value data;

    if (!reader.parse(settings, data)) {
      std::cerr << reader.getFormattedErrorMessages() << std::endl;
      return 0;
    }

    HeMol mol;
    parse_smiles(smiles, mol);

    int bits = data["num_bits"].asInt();
    int words = bitvec_num_words_for_bits(bits);
    int k = data["fingerprint"]["k"].asInt();
    int prime = data["fingerprint"]["prime"].asInt();
    std::string type = data["fingerprint"]["type"].asString();

    Word *fingerprint = new Word[words];

    if (type == "Helium::paths_fingerprint") {
      path_fingerprint(mol, fingerprint, k, words, prime);
      return fingerprint;
    }

    if (type == "Helium::trees_fingerprint") {
      tree_fingerprint(mol, fingerprint, k, words, prime);
      return fingerprint;
    }

    if (type == "Helium::subgraph_fingerprint") {
      subgraph_fingerprint(mol, fingerprint, k, words, prime);
      return fingerprint;
    }

    std::cerr << "Fingerprint type \"" << type << "\" not recognised" << std::endl;

    delete [] fingerprint;
    return 0;
  }

  bool is_fps_file(const std::string &filename)
  {
    std::ifstream ifs(filename.c_str());
    if (!ifs)
      return false;
    std::string line;
    std::getline(ifs, line);
    return line == "#FPS1";
  }

  bool load_queries(const std::string &query, const std::string &storage_header, std::vector<Word*> &queries)
  {
    if (is_fps_file(query)) {
      // load fps file
      FpsFile fpsFile;
      fpsFile.load(query);

      // copy fingerprints
      int numWords = bitvec_num_words_for_bits(fpsFile.numBits());
      for (unsigned int i = 0; i < fpsFile.numFingerprints(); ++i)
        queries.push_back(bitvec_copy(fpsFile.fingerprint(i), numWords));
    } else {
      // query is SMILES
      Word *fingerprint = compute_fingerprint(storage_header, query);
      if (!fingerprint)
        return false;
      queries.push_back(fingerprint);
    }

    return queries.size();
  }

  void free_queries(std::vector<Word*> &queries)
  {
    for (std::size_t i = 0; i < queries.size(); ++i)
      delete [] queries[i];
  }

  template<typename FingerprintStorageType>
  struct RunSimilaritySearch
  {
    RunSimilaritySearch(const SimilaritySearchIndex<FingerprintStorageType> &index_, double Tmin_)
      : index(index_), Tmin(Tmin_)
    {
    }

    void operator()(const Word *query, std::vector<std::pair<unsigned int, double> > &result) const
    {
      result = index.search(query, Tmin);
    }

    const SimilaritySearchIndex<FingerprintStorageType> &index;
    const double Tmin;
  };

  class SimilarityTool : public HeliumTool
  {
    public:
      /**
       * Perform tool action.
       */
      int run(int argc, char **argv)
      {
        //
        // Argument handling
        //
        ParseArgs args(argc, argv, ParseArgs::Args("-Tmin(number)", "-brute",
#ifdef HAVE_CPP11
              "-brute-mt", "-mt",
#endif
              "-k(number)"), ParseArgs::Args("query", "fingerprint_file"));
        // optional arguments
        const double Tmin = args.IsArg("-Tmin") ? args.GetArgDouble("-Tmin", 0) - 10e-5 : 0.7 - 10e-5;
        bool brute = args.IsArg("-brute");
#ifdef HAVE_CPP11
        const bool brute_mt = args.IsArg("-brute-mt");
        const bool mt = args.IsArg("-mt");
#endif
        const int k = args.IsArg("-k") ? args.GetArgInt("-k", 0) : 3;
        // required arguments
        std::string query = args.GetArgString("query");
        std::string filename = args.GetArgString("fingerprint_file");

        //
        // check for incompatible arguments
        //
        if (brute && brute_mt) {
          std::cerr << "Options -brute and -brute-mt can not be used simultaneously, -brute will be ignored." << std::endl;
          brute = false;
        }
        if (brute && args.IsArg("-k"))
          std::cerr << "Option -k <n> has no effect when using option -brute, -k will be ignored." << std::endl;
        if (brute_mt && args.IsArg("-k"))
          std::cerr << "Option -k <n> has no effect when using option -brute-mt, -k will be ignored." << std::endl;
        if (brute && mt)
          std::cerr << "Option -mt has no effect when using option -brute, -mt will be ignored." << std::endl;
        if (brute_mt && mt)
          std::cerr << "Option -mt has no effect when using option -brute-mt, -mt will be ignored." << std::endl;

        //
        // open fingerprint file
        //
        InMemoryRowMajorFingerprintStorage storage;
        try {
          storage.load(filename);
        } catch (const std::exception &e) {
          std::cerr << e.what() << std::endl;
          return -1;
        }

        //
        // load queries
        //
        std::vector<Word*> queries;
        if (!load_queries(query, storage.header(), queries)) {
          std::cerr << "Could not load queries" << std::endl;
          return -1;
        }

        //
        // perform search
        //
        std::vector<std::vector<std::pair<unsigned int, double> > > result(queries.size());
#ifdef HAVE_CPP11
        if (brute_mt) {
          for (std::size_t i = 0; i < queries.size(); ++i)
            result[i] = brute_force_similarity_search_threaded(queries[i], storage, Tmin);
        } else
#endif
        if (brute) {
          for (std::size_t i = 0; i < queries.size(); ++i)
            result[i] = brute_force_similarity_search(queries[i], storage, Tmin);
        } else {
          SimilaritySearchIndex<InMemoryRowMajorFingerprintStorage> index(storage, k);
#ifdef HAVE_CPP11
          if (mt) {
            // run searches in concurrently using multiple threads
            typedef RunSimilaritySearch<InMemoryRowMajorFingerprintStorage> CallableType;
            typedef Word* TaskType;
            typedef std::vector<std::pair<unsigned int, double> > ResultType;

            Concurrent<const CallableType&, TaskType, ResultType> concurrent;
            concurrent.run(CallableType(index, Tmin), queries, result);
          } else {
            // run all searches sequentially using a single thread
            for (std::size_t i = 0; i < queries.size(); ++i)
              result[i] = index.search(queries[i], Tmin);
          }
#else
          // run all searches sequentially using a single thread
          for (std::size_t i = 0; i < queries.size(); ++i)
            result[i] = index.search(queries[i], Tmin);
#endif
        }

        // deallocate fingerprint
        free_queries(queries);

        // sort the results
        for (std::size_t i = 0; i < queries.size(); ++i)
          std::sort(result[i].begin(), result[i].end(), compare_first<unsigned int, double>());

        //
        // print results
        //
        Json::Value data;
        data["hits"] = Json::Value(Json::arrayValue);
        for (std::size_t i = 0; i < result.size(); ++i) {
          data["hits"][Json::ArrayIndex(i)] = Json::Value(Json::arrayValue);
          for (std::size_t j = 0; j < result[i].size(); ++j) {
            data["hits"][Json::ArrayIndex(i)][Json::ArrayIndex(j)] = Json::Value(Json::objectValue);
            Json::Value &obj = data["hits"][Json::ArrayIndex(i)][Json::ArrayIndex(j)];
            obj["index"] = result[i][j].first;
            obj["tanimoto"] = result[i][j].second;
          }
        }

        Json::StyledWriter writer;
        std::cout << writer.write(data);

        return 0;
      }

  };

  class SimilarityToolFactory : public HeliumToolFactory
  {
    public:
      HELIUM_TOOL("similarity", "Perform a similarity search on a fingerprint index file", 2, SimilarityTool);

      /**
       * Get usage information.
       */
      std::string usage(const std::string &command) const
      {
        std::stringstream ss;
        ss << "Usage: " << command << " [options] <query> <fingerprint_file>" << std::endl;
        ss << std::endl;
        ss << "Perform a similarity search on a fingerprint file. The fingerprint file must store the" << std::endl;
        ss << "fingerprints in row-major order. The query has to be a SMILES string." << std::endl;
        ss << std::endl;
        ss << "Optionally, the <query> can be replaced with 'interactive' to start an interactive session" << std::endl;
        ss << std::endl;
        ss << "Options:" << std::endl;
        ss << "    -Tmin <n>     The minimum tanimoto score (default is 0.7)" << std::endl;
        ss << "    -brute        Do brute force search (default is to use index)" << std::endl;
#ifdef HAVE_CPP11
        ss << "    -brute-mt     Do threaded brute force search (default is to use index)" << std::endl;
        ss << "    -mt           Do threaded index search (default is not to use threads)" << std::endl;
#endif
        ss << "    -k <n>        When using an index (i.e. no -brute), specify the dimension for the kD-grid (default is 3)" << std::endl;
        ss << std::endl;
        return ss.str();
      }
  };

  SimilarityToolFactory theSimilarityToolFactory;

}
<commit_msg>Fix compile error when c++11 is disabled<commit_after>/*
 * Copyright (c) 2013, Tim Vandermeersch
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the <organization> nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "tool.h"

#include <Helium/fingerprints/similarity.h>
#include <Helium/fileio/fingerprints.h>
#include <Helium/fileio/fps.h>
#include <Helium/smiles.h>

#ifdef HAVE_CPP11
#include <Helium/concurrent.h>
#endif

#include <json/json.h>

#include "args.h"

namespace Helium {

  Word* compute_fingerprint(const std::string &settings, const std::string &smiles)
  {
    Json::Reader reader;
    Json::Value data;

    if (!reader.parse(settings, data)) {
      std::cerr << reader.getFormattedErrorMessages() << std::endl;
      return 0;
    }

    HeMol mol;
    parse_smiles(smiles, mol);

    int bits = data["num_bits"].asInt();
    int words = bitvec_num_words_for_bits(bits);
    int k = data["fingerprint"]["k"].asInt();
    int prime = data["fingerprint"]["prime"].asInt();
    std::string type = data["fingerprint"]["type"].asString();

    Word *fingerprint = new Word[words];

    if (type == "Helium::paths_fingerprint") {
      path_fingerprint(mol, fingerprint, k, words, prime);
      return fingerprint;
    }

    if (type == "Helium::trees_fingerprint") {
      tree_fingerprint(mol, fingerprint, k, words, prime);
      return fingerprint;
    }

    if (type == "Helium::subgraph_fingerprint") {
      subgraph_fingerprint(mol, fingerprint, k, words, prime);
      return fingerprint;
    }

    std::cerr << "Fingerprint type \"" << type << "\" not recognised" << std::endl;

    delete [] fingerprint;
    return 0;
  }

  bool is_fps_file(const std::string &filename)
  {
    std::ifstream ifs(filename.c_str());
    if (!ifs)
      return false;
    std::string line;
    std::getline(ifs, line);
    return line == "#FPS1";
  }

  bool load_queries(const std::string &query, const std::string &storage_header, std::vector<Word*> &queries)
  {
    if (is_fps_file(query)) {
      // load fps file
      FpsFile fpsFile;
      fpsFile.load(query);

      // copy fingerprints
      int numWords = bitvec_num_words_for_bits(fpsFile.numBits());
      for (unsigned int i = 0; i < fpsFile.numFingerprints(); ++i)
        queries.push_back(bitvec_copy(fpsFile.fingerprint(i), numWords));
    } else {
      // query is SMILES
      Word *fingerprint = compute_fingerprint(storage_header, query);
      if (!fingerprint)
        return false;
      queries.push_back(fingerprint);
    }

    return queries.size();
  }

  void free_queries(std::vector<Word*> &queries)
  {
    for (std::size_t i = 0; i < queries.size(); ++i)
      delete [] queries[i];
  }

  template<typename FingerprintStorageType>
  struct RunSimilaritySearch
  {
    RunSimilaritySearch(const SimilaritySearchIndex<FingerprintStorageType> &index_, double Tmin_)
      : index(index_), Tmin(Tmin_)
    {
    }

    void operator()(const Word *query, std::vector<std::pair<unsigned int, double> > &result) const
    {
      result = index.search(query, Tmin);
    }

    const SimilaritySearchIndex<FingerprintStorageType> &index;
    const double Tmin;
  };

  class SimilarityTool : public HeliumTool
  {
    public:
      /**
       * Perform tool action.
       */
      int run(int argc, char **argv)
      {
        //
        // Argument handling
        //
        ParseArgs args(argc, argv, ParseArgs::Args("-Tmin(number)", "-brute",
#ifdef HAVE_CPP11
              "-brute-mt", "-mt",
#endif
              "-k(number)"), ParseArgs::Args("query", "fingerprint_file"));
        // optional arguments
        const double Tmin = args.IsArg("-Tmin") ? args.GetArgDouble("-Tmin", 0) - 10e-5 : 0.7 - 10e-5;
        bool brute = args.IsArg("-brute");
#ifdef HAVE_CPP11
        const bool brute_mt = args.IsArg("-brute-mt");
        const bool mt = args.IsArg("-mt");
#endif
        const int k = args.IsArg("-k") ? args.GetArgInt("-k", 0) : 3;
        // required arguments
        std::string query = args.GetArgString("query");
        std::string filename = args.GetArgString("fingerprint_file");

        //
        // check for incompatible arguments
        //
#ifdef HAVE_CPP11
        if (brute && brute_mt) {
          std::cerr << "Options -brute and -brute-mt can not be used simultaneously, -brute will be ignored." << std::endl;
          brute = false;
        }
        if (brute_mt && args.IsArg("-k"))
          std::cerr << "Option -k <n> has no effect when using option -brute-mt, -k will be ignored." << std::endl;
        if (brute && mt)
          std::cerr << "Option -mt has no effect when using option -brute, -mt will be ignored." << std::endl;
        if (brute_mt && mt)
          std::cerr << "Option -mt has no effect when using option -brute-mt, -mt will be ignored." << std::endl;
#endif
        if (brute && args.IsArg("-k"))
          std::cerr << "Option -k <n> has no effect when using option -brute, -k will be ignored." << std::endl;

        //
        // open fingerprint file
        //
        InMemoryRowMajorFingerprintStorage storage;
        try {
          storage.load(filename);
        } catch (const std::exception &e) {
          std::cerr << e.what() << std::endl;
          return -1;
        }

        //
        // load queries
        //
        std::vector<Word*> queries;
        if (!load_queries(query, storage.header(), queries)) {
          std::cerr << "Could not load queries" << std::endl;
          return -1;
        }

        //
        // perform search
        //
        std::vector<std::vector<std::pair<unsigned int, double> > > result(queries.size());
#ifdef HAVE_CPP11
        if (brute_mt) {
          for (std::size_t i = 0; i < queries.size(); ++i)
            result[i] = brute_force_similarity_search_threaded(queries[i], storage, Tmin);
        } else
#endif
        if (brute) {
          for (std::size_t i = 0; i < queries.size(); ++i)
            result[i] = brute_force_similarity_search(queries[i], storage, Tmin);
        } else {
          SimilaritySearchIndex<InMemoryRowMajorFingerprintStorage> index(storage, k);
#ifdef HAVE_CPP11
          if (mt) {
            // run searches in concurrently using multiple threads
            typedef RunSimilaritySearch<InMemoryRowMajorFingerprintStorage> CallableType;
            typedef Word* TaskType;
            typedef std::vector<std::pair<unsigned int, double> > ResultType;

            Concurrent<const CallableType&, TaskType, ResultType> concurrent;
            concurrent.run(CallableType(index, Tmin), queries, result);
          } else {
            // run all searches sequentially using a single thread
            for (std::size_t i = 0; i < queries.size(); ++i)
              result[i] = index.search(queries[i], Tmin);
          }
#else
          // run all searches sequentially using a single thread
          for (std::size_t i = 0; i < queries.size(); ++i)
            result[i] = index.search(queries[i], Tmin);
#endif
        }

        // deallocate fingerprint
        free_queries(queries);

        // sort the results
        for (std::size_t i = 0; i < queries.size(); ++i)
          std::sort(result[i].begin(), result[i].end(), compare_first<unsigned int, double>());

        //
        // print results
        //
        Json::Value data;
        data["hits"] = Json::Value(Json::arrayValue);
        for (std::size_t i = 0; i < result.size(); ++i) {
          data["hits"][Json::ArrayIndex(i)] = Json::Value(Json::arrayValue);
          for (std::size_t j = 0; j < result[i].size(); ++j) {
            data["hits"][Json::ArrayIndex(i)][Json::ArrayIndex(j)] = Json::Value(Json::objectValue);
            Json::Value &obj = data["hits"][Json::ArrayIndex(i)][Json::ArrayIndex(j)];
            obj["index"] = result[i][j].first;
            obj["tanimoto"] = result[i][j].second;
          }
        }

        Json::StyledWriter writer;
        std::cout << writer.write(data);

        return 0;
      }

  };

  class SimilarityToolFactory : public HeliumToolFactory
  {
    public:
      HELIUM_TOOL("similarity", "Perform a similarity search on a fingerprint index file", 2, SimilarityTool);

      /**
       * Get usage information.
       */
      std::string usage(const std::string &command) const
      {
        std::stringstream ss;
        ss << "Usage: " << command << " [options] <query> <fingerprint_file>" << std::endl;
        ss << std::endl;
        ss << "Perform a similarity search on a fingerprint file. The fingerprint file must store the" << std::endl;
        ss << "fingerprints in row-major order. The query has to be a SMILES string." << std::endl;
        ss << std::endl;
        ss << "Optionally, the <query> can be replaced with 'interactive' to start an interactive session" << std::endl;
        ss << std::endl;
        ss << "Options:" << std::endl;
        ss << "    -Tmin <n>     The minimum tanimoto score (default is 0.7)" << std::endl;
        ss << "    -brute        Do brute force search (default is to use index)" << std::endl;
#ifdef HAVE_CPP11
        ss << "    -brute-mt     Do threaded brute force search (default is to use index)" << std::endl;
        ss << "    -mt           Do threaded index search (default is not to use threads)" << std::endl;
#endif
        ss << "    -k <n>        When using an index (i.e. no -brute), specify the dimension for the kD-grid (default is 3)" << std::endl;
        ss << std::endl;
        return ss.str();
      }
  };

  SimilarityToolFactory theSimilarityToolFactory;

}
<|endoftext|>