<commit_before>/* featureSelector.C
 *
 * Copyright (C) 2009 Marcel Schumann
 *
 * This file is part of QuEasy -- A Toolbox for Automated QSAR Model
 * Construction and Validation.
 * QuEasy is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * QuEasy is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

#include <fstream>
#include <BALL/QSAR/registry.h>
#include <BALL/QSAR/featureSelection.h>
#include <BALL/QSAR/configIO.h>

using namespace BALL::QSAR;
using namespace BALL;

void startFeatureSelection(std::ifstream& in, QSARData* q, String* data_filename)
{
	FeatureSelectionConfiguration conf = ConfigIO::readFeatureSelectionConfiguration(&in);

	if(conf.done) return; // stop processing this section

	bool created_data_object=0;
	if(q==NULL || data_filename==NULL || conf.data_file!=*data_filename)
	{
		if(q==NULL)
		{
			q = new QSARData;
			created_data_object=1;
		}
		q->readFromFile(conf.data_file);
		if(data_filename) *data_filename = conf.data_file;
	}
	else
	{
		std::cout<<"[FeatureSelector debug-info:] QSARData object for file "<<conf.data_file<<" already in memory; not reading it again."<<std::endl;
	}

	Registry reg;
	Model* m;
	String model_type;

	std::ifstream model_input(conf.model.c_str()); // read model-abbreviation
	if(!model_input)
	{
		std::cout<<"Error: Model-file '"<<conf.model<<"' does not exist!!"<<std::endl;
		return;
	}
	std::getline(model_input,model_type);
	std::getline(model_input,model_type);
	model_type = model_type.getField(0,"\t");
	model_input.close();

	RegistryEntry* entry = reg.getEntry(model_type);
	bool regression = entry->regression;

	if(!entry->kernel)
	{
		m = (*entry->create)(*q);
	}
	else
	{
		// parameters irrelevant; will be overwritten by those read from file
		m = (*entry->createKernel1)(*q,1,1, -1);
	}

	std::cout<<"  using "<<conf.statistic_name<<" to assess qualitiy of the model ... "<<std::endl;
	m->model_val->selectStat(conf.statistic);

	m->readFromFile(conf.model.c_str());
	FeatureSelection fs(*m);
	if(conf.quality_increase_cutoff!=-1)
	{
		fs.setQualityIncreaseCutoff(conf.quality_increase_cutoff);
	}
	if(conf.remove_correlated || conf.feat_type==0)
	{
		fs.removeHighlyCorrelatedFeatures(conf.cor_threshold);
	}
	if(conf.feat_type==1)
	{
		fs.forwardSelection(conf.k_fold,conf.opt);
	}
	else if(conf.feat_type==2)
	{
		fs.backwardSelection(conf.k_fold,conf.opt);
	}
	else if(conf.feat_type==3)
	{
		fs.stepwiseSelection(conf.k_fold,conf.opt);
	}
	else if(conf.feat_type==4)
	{
		fs.removeLowResponseCorrelation(conf.cor_threshold);
	}
	else if(conf.feat_type==6)
	{
		fs.twinScan(conf.k_fold,conf.opt);
	}
	if(conf.opt_model_after_fs)
	{
		m->optimizeParameters(conf.opt_k_fold);
	}
	KernelModel* km = dynamic_cast<KernelModel*>(m);
	if(km && conf.opt_kernel_after_fs)
	{
		/// search locally around current kernel parameters
		try
		{
			// specifing start-values for grid search now obsolete; grid search will automatically search locally around current kernel parameter(s)
			km->kernel->gridSearch(conf.grid_search_stepwidth, conf.grid_search_steps,conf.grid_search_recursions,conf.opt_k_fold,conf.opt/*,start_par1,start_par2*/);
		}
		catch(BALL::Exception::GeneralException e)
		{
			std::cout<<e.getName()<<" : "<<e.getMessage()<<std::endl;
			return;
		}
	}

	m->readTrainingData();
	m->train();
	m->saveToFile(conf.output);

	if(created_data_object) delete q;
	delete m;
}

#ifndef EXT_MAIN
int main(int argc, char* argv[])
{
	if(argc<2)
	{
		std::cout<<"Please specify configuration file!"<<std::endl;
		return 0;
	}

	std::ifstream in(argv[1]);
	if(!in)
	{
		std::cout<<"Configuration file '"<<argv[1]<<"' not found!"<<std::endl;
		return 0;
	}
	String line;

	for(int i=0;!in.eof();i++) // read ALL FeatureSelector sections
	{
		for(int j=0;!in.eof();j++) // skip everthing until the beginning of the next FeatureSelector-section
		{
			std::getline(in,line);
			if(!line.hasPrefix("[FeatureSelector]")) continue;
			else break;
		}
		if(!line.hasPrefix("[FeatureSelector]")) break; // there are no (more) FS-sections!
		ConfigIO::putbackLine(&in,line);

		startFeatureSelection(in, NULL, NULL);
	}
}
#endif
<commit_msg>Clarified licence<commit_after>#include <fstream>
#include <BALL/QSAR/registry.h>
#include <BALL/QSAR/featureSelection.h>
#include <BALL/QSAR/configIO.h>

using namespace BALL::QSAR;
using namespace BALL;

void startFeatureSelection(std::ifstream& in, QSARData* q, String* data_filename)
{
	FeatureSelectionConfiguration conf = ConfigIO::readFeatureSelectionConfiguration(&in);

	if(conf.done) return; // stop processing this section

	bool created_data_object=0;
	if(q==NULL || data_filename==NULL || conf.data_file!=*data_filename)
	{
		if(q==NULL)
		{
			q = new QSARData;
			created_data_object=1;
		}
		q->readFromFile(conf.data_file);
		if(data_filename) *data_filename = conf.data_file;
	}
	else
	{
		std::cout<<"[FeatureSelector debug-info:] QSARData object for file "<<conf.data_file<<" already in memory; not reading it again."<<std::endl;
	}

	Registry reg;
	Model* m;
	String model_type;

	std::ifstream model_input(conf.model.c_str()); // read model-abbreviation
	if(!model_input)
	{
		std::cout<<"Error: Model-file '"<<conf.model<<"' does not exist!!"<<std::endl;
		return;
	}
	std::getline(model_input,model_type);
	std::getline(model_input,model_type);
	model_type = model_type.getField(0,"\t");
	model_input.close();

	RegistryEntry* entry = reg.getEntry(model_type);
	bool regression = entry->regression;

	if(!entry->kernel)
	{
		m = (*entry->create)(*q);
	}
	else
	{
		// parameters irrelevant; will be overwritten by those read from file
		m = (*entry->createKernel1)(*q,1,1, -1);
	}

	std::cout<<"  using "<<conf.statistic_name<<" to assess qualitiy of the model ... "<<std::endl;
	m->model_val->selectStat(conf.statistic);

	m->readFromFile(conf.model.c_str());
	FeatureSelection fs(*m);
	if(conf.quality_increase_cutoff!=-1)
	{
		fs.setQualityIncreaseCutoff(conf.quality_increase_cutoff);
	}
	if(conf.remove_correlated || conf.feat_type==0)
	{
		fs.removeHighlyCorrelatedFeatures(conf.cor_threshold);
	}
	if(conf.feat_type==1)
	{
		fs.forwardSelection(conf.k_fold,conf.opt);
	}
	else if(conf.feat_type==2)
	{
		fs.backwardSelection(conf.k_fold,conf.opt);
	}
	else if(conf.feat_type==3)
	{
		fs.stepwiseSelection(conf.k_fold,conf.opt);
	}
	else if(conf.feat_type==4)
	{
		fs.removeLowResponseCorrelation(conf.cor_threshold);
	}
	else if(conf.feat_type==6)
	{
		fs.twinScan(conf.k_fold,conf.opt);
	}
	if(conf.opt_model_after_fs)
	{
		m->optimizeParameters(conf.opt_k_fold);
	}
	KernelModel* km = dynamic_cast<KernelModel*>(m);
	if(km && conf.opt_kernel_after_fs)
	{
		/// search locally around current kernel parameters
		try
		{
			// specifing start-values for grid search now obsolete; grid search will automatically search locally around current kernel parameter(s)
			km->kernel->gridSearch(conf.grid_search_stepwidth, conf.grid_search_steps,conf.grid_search_recursions,conf.opt_k_fold,conf.opt/*,start_par1,start_par2*/);
		}
		catch(BALL::Exception::GeneralException e)
		{
			std::cout<<e.getName()<<" : "<<e.getMessage()<<std::endl;
			return;
		}
	}

	m->readTrainingData();
	m->train();
	m->saveToFile(conf.output);

	if(created_data_object) delete q;
	delete m;
}

#ifndef EXT_MAIN
int main(int argc, char* argv[])
{
	if(argc<2)
	{
		std::cout<<"Please specify configuration file!"<<std::endl;
		return 0;
	}

	std::ifstream in(argv[1]);
	if(!in)
	{
		std::cout<<"Configuration file '"<<argv[1]<<"' not found!"<<std::endl;
		return 0;
	}
	String line;

	for(int i=0;!in.eof();i++) // read ALL FeatureSelector sections
	{
		for(int j=0;!in.eof();j++) // skip everthing until the beginning of the next FeatureSelector-section
		{
			std::getline(in,line);
			if(!line.hasPrefix("[FeatureSelector]")) continue;
			else break;
		}
		if(!line.hasPrefix("[FeatureSelector]")) break; // there are no (more) FS-sections!
		ConfigIO::putbackLine(&in,line);

		startFeatureSelection(in, NULL, NULL);
	}
}
#endif
<|endoftext|>


<commit_before>#ifndef GNR_DBG_HPP
# define GNR_DBG_HPP
# pragma once

#include <cstdlib> // abort
#include <iostream>

namespace gnr
{

#ifdef NDEBUG
static struct
{
  template <typename U>
  auto& operator<<(U&&) const noexcept
  {
    return *this;
  }
} const dbg;

#else
static constexpr auto& dbg(std::cout);
#endif // NDEBUG

[[noreturn]] inline std::ostream& abort(std::ostream& os)
{
  os << std::endl;
  std::abort();
}

}

#endif // GNR_DBG_HPP
<commit_msg>some fixes<commit_after>#ifndef GNR_DBG_HPP
# define GNR_DBG_HPP
# pragma once

#include <cstdlib> // abort
#include <iostream>

namespace gnr
{

#ifdef NDEBUG
static struct
{
  template <typename U>
  auto& operator<<(U&&) const noexcept
  {
    return *this;
  }
} const dbg;

#else
static constexpr auto& dbg(std::cout);
#endif // NDEBUG

[[noreturn]] inline std::ostream& abort(std::ostream& os) noexcept
{
  std::abort();
}

}

#endif // GNR_DBG_HPP
<|endoftext|>


<commit_before>// Copyright 2022 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "smart_card_connector_app/src/application.h"

#include <functional>
#include <memory>
#include <mutex>
#include <queue>
#include <string>
#include <utility>
#include <vector>

#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <winscard.h>

#include <google_smart_card_common/messaging/typed_message_router.h>
#include <google_smart_card_common/multi_string.h>
#include <google_smart_card_common/unique_ptr_utils.h>
#include <google_smart_card_common/value.h>

#include "common/cpp/src/public/testing_global_context.h"
#include "smart_card_connector_app/src/testing_smart_card_simulation.h"

#ifdef __native_client__
#include <google_smart_card_common/nacl_io_utils.h>
#endif  // __native_client__

using testing::ElementsAre;

namespace google_smart_card {

namespace {

// Records reader_* messages sent to JS and allows to inspect them in tests.
class ReaderNotificationObserver final {
 public:
  void Init(TestingGlobalContext& global_context) {
    for (const auto* event_name : {"reader_init_add", "reader_finish_add"}) {
      global_context.RegisterMessageHandler(
          event_name,
          std::bind(&ReaderNotificationObserver::OnMessageToJs, this,
                    event_name, /*request_payload=*/std::placeholders::_1,
                    /*request_id=*/std::placeholders::_2));
    }
  }

  // Extracts the next recorded notification, in the format "<event>:<reader>"
  // (for simplifying test assertions).
  std::string Pop() {
    std::unique_lock<std::mutex> lock(mutex_);
    GOOGLE_SMART_CARD_CHECK(!recorded_notifications_.empty());
    std::string next = std::move(recorded_notifications_.front());
    recorded_notifications_.pop();
    return next;
  }

 private:
  void OnMessageToJs(const std::string& event_name,
                     Value message_data,
                     optional<RequestId> /*request_id*/) {
    std::string notification =
        event_name + ":" +
        message_data.GetDictionaryItem("readerName")->GetString();

    std::unique_lock<std::mutex> lock(mutex_);
    recorded_notifications_.push(notification);
  }

  std::mutex mutex_;
  std::queue<std::string> recorded_notifications_;
};

}  // namespace

class SmartCardConnectorApplicationTest : public ::testing::Test {
 protected:
  SmartCardConnectorApplicationTest() {
#ifdef __native_client__
    // Make resource files accessible.
    MountNaclIoFolders();
#endif  // __native_client__
    SetUpUsbSimulation();
    reader_notification_observer_.Init(global_context_);
  }

  ~SmartCardConnectorApplicationTest() { application_->ShutDownAndWait(); }

  void StartApplication() {
    // Set up the expectation on the first C++-to-JS message.
    auto pcsc_lite_ready_message_waiter = global_context_.CreateMessageWaiter(
        /*awaited_message_type=*/"pcsc_lite_ready");
    // Set up the expectation for the application to run the provided callback.
    ::testing::MockFunction<void()> background_initialization_callback;
    EXPECT_CALL(background_initialization_callback, Call());
    // Create the application, which spawns the background initialization
    // thread.
    application_ = MakeUnique<Application>(
        &global_context_, &typed_message_router_,
        background_initialization_callback.AsStdFunction());
    // Wait until the daemon's background thread completes the initialization
    // and notifies the JS side.
    pcsc_lite_ready_message_waiter->Wait();
    EXPECT_TRUE(pcsc_lite_ready_message_waiter->value().StrictlyEquals(
        Value(Value::Type::kDictionary)));
  }

  // Enables the specified fake USB devices.
  void SetUsbDevices(
      const std::vector<TestingSmartCardSimulation::Device>& devices) {
    smart_card_simulation_.SetDevices(devices);
  }

  ReaderNotificationObserver& reader_notification_observer() {
    return reader_notification_observer_;
  }

 private:
  void SetUpUsbSimulation() {
    global_context_.RegisterRequestHandler(
        TestingSmartCardSimulation::kRequesterName,
        std::bind(&TestingSmartCardSimulation::OnRequestToJs,
                  &smart_card_simulation_,
                  /*request_payload=*/std::placeholders::_1,
                  /*request_id=*/std::placeholders::_2));
  }

  TypedMessageRouter typed_message_router_;
  TestingSmartCardSimulation smart_card_simulation_{&typed_message_router_};
  ReaderNotificationObserver reader_notification_observer_;
  TestingGlobalContext global_context_{&typed_message_router_};
  std::unique_ptr<Application> application_;
};

TEST_F(SmartCardConnectorApplicationTest, SmokeTest) {
  StartApplication();
}

// Test a PC/SC-Lite context can be established and freed, via direct C function
// calls `SCardEstablishContext()` and `SCardReleaseContext()`.
// This is an extended version of the "smoke test" as it verifies the daemon
// successfully started and replies to calls sent over (fake) sockets.
TEST_F(SmartCardConnectorApplicationTest, InternalApiContextEstablishing) {
  // Arrange:

  StartApplication();

  // Act:

  SCARDCONTEXT scard_context = 0;
  EXPECT_EQ(SCardEstablishContext(SCARD_SCOPE_SYSTEM, /*pvReserved1=*/nullptr,
                                  /*pvReserved2=*/nullptr, &scard_context),
            SCARD_S_SUCCESS);
  EXPECT_NE(scard_context, 0);

  EXPECT_EQ(SCardReleaseContext(scard_context), SCARD_S_SUCCESS);
}

// Test a single reader is successfully initialized by PC/SC-Lite and is
// returned via the direct C function call `SCardListReaders()`.
TEST_F(SmartCardConnectorApplicationTest, InternalApiSingleDeviceListing) {
  // Arrange:

  TestingSmartCardSimulation::Device device;
  device.id = 123;
  device.type = TestingSmartCardSimulation::DeviceType::kGemaltoPcTwinReader;
  SetUsbDevices({device});

  StartApplication();
  EXPECT_EQ(reader_notification_observer().Pop(),
            "reader_init_add:Gemalto PC Twin Reader");
  EXPECT_EQ(reader_notification_observer().Pop(),
            "reader_finish_add:Gemalto PC Twin Reader");

  // Act:

  SCARDCONTEXT scard_context = 0;
  EXPECT_EQ(SCardEstablishContext(SCARD_SCOPE_SYSTEM, /*pvReserved1=*/nullptr,
                                  /*pvReserved2=*/nullptr, &scard_context),
            SCARD_S_SUCCESS);

  DWORD readers_size = 0;
  EXPECT_EQ(SCardListReaders(scard_context, /*mszGroups=*/nullptr,
                             /*mszReaders=*/nullptr, &readers_size),
            SCARD_S_SUCCESS);
  EXPECT_GT(readers_size, 0U);

  std::string readers_multistring;
  readers_multistring.resize(readers_size);
  EXPECT_EQ(SCardListReaders(scard_context, /*mszGroups=*/nullptr,
                             &readers_multistring[0], &readers_size),
            SCARD_S_SUCCESS);
  EXPECT_EQ(readers_size, readers_multistring.size());

  EXPECT_EQ(SCardReleaseContext(scard_context), SCARD_S_SUCCESS);

  // Assert:

  EXPECT_THAT(ExtractMultiStringElements(readers_multistring),
              ElementsAre("Gemalto PC Twin Reader 00 00"));
}

}  // namespace google_smart_card
<commit_msg>Tests  connector adding/removing reader (#638)<commit_after>// Copyright 2022 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "smart_card_connector_app/src/application.h"

#include <functional>
#include <memory>
#include <mutex>
#include <queue>
#include <string>
#include <utility>
#include <vector>

#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <winscard.h>

#include <google_smart_card_common/messaging/typed_message_router.h>
#include <google_smart_card_common/multi_string.h>
#include <google_smart_card_common/unique_ptr_utils.h>
#include <google_smart_card_common/value.h>

#include "common/cpp/src/public/testing_global_context.h"
#include "smart_card_connector_app/src/testing_smart_card_simulation.h"

#ifdef __native_client__
#include <google_smart_card_common/nacl_io_utils.h>
#endif  // __native_client__

using testing::ElementsAre;
using testing::IsEmpty;

namespace google_smart_card {

namespace {

// Records reader_* messages sent to JS and allows to inspect them in tests.
class ReaderNotificationObserver final {
 public:
  void Init(TestingGlobalContext& global_context) {
    for (const auto* event_name :
         {"reader_init_add", "reader_finish_add", "reader_remove"}) {
      global_context.RegisterMessageHandler(
          event_name,
          std::bind(&ReaderNotificationObserver::OnMessageToJs, this,
                    event_name, /*request_payload=*/std::placeholders::_1,
                    /*request_id=*/std::placeholders::_2));
    }
  }

  // Extracts the next recorded notification, in the format "<event>:<reader>"
  // (for simplifying test assertions).
  std::string Pop() {
    std::unique_lock<std::mutex> lock(mutex_);
    GOOGLE_SMART_CARD_CHECK(!recorded_notifications_.empty());
    std::string next = std::move(recorded_notifications_.front());
    recorded_notifications_.pop();
    return next;
  }

  // Same as `Pop()`, but waits if there's no notification to return.
  std::string WaitAndPop() {
    std::unique_lock<std::mutex> lock(mutex_);
    condition_.wait(lock, [&] {
      return !recorded_notifications_.empty();
    });
    std::string next = std::move(recorded_notifications_.front());
    recorded_notifications_.pop();
    return next;
  }

  // Returns whether there's a notification to return.
  bool Empty() const {
    std::unique_lock<std::mutex> lock(mutex_);
    return recorded_notifications_.empty();
  }

 private:
  void OnMessageToJs(const std::string& event_name,
                     Value message_data,
                     optional<RequestId> /*request_id*/) {
    std::string notification =
        event_name + ":" +
        message_data.GetDictionaryItem("readerName")->GetString();

    std::unique_lock<std::mutex> lock(mutex_);
    recorded_notifications_.push(notification);
    condition_.notify_one();
  }

  // Mutable to allow locking in const methods.
  mutable std::mutex mutex_;
  std::condition_variable condition_;
  std::queue<std::string> recorded_notifications_;
};

std::vector<std::string> DirectCallSCardListReaders(
    SCARDCONTEXT scard_context) {
  DWORD readers_size = 0;
  LONG return_code = SCardListReaders(scard_context, /*mszGroups=*/nullptr,
                                      /*mszReaders=*/nullptr, &readers_size);
  if (return_code == SCARD_E_NO_READERS_AVAILABLE)
    return {};
  EXPECT_EQ(return_code, SCARD_S_SUCCESS);
  EXPECT_GT(readers_size, 0U);

  std::string readers_multistring;
  readers_multistring.resize(readers_size);
  EXPECT_EQ(SCardListReaders(scard_context, /*mszGroups=*/nullptr,
                             &readers_multistring[0], &readers_size),
            SCARD_S_SUCCESS);
  EXPECT_EQ(readers_size, readers_multistring.size());

  return ExtractMultiStringElements(readers_multistring);
}

}  // namespace

class SmartCardConnectorApplicationTest : public ::testing::Test {
 protected:
  SmartCardConnectorApplicationTest() {
#ifdef __native_client__
    // Make resource files accessible.
    MountNaclIoFolders();
#endif  // __native_client__
    SetUpUsbSimulation();
    reader_notification_observer_.Init(global_context_);
  }

  ~SmartCardConnectorApplicationTest() { application_->ShutDownAndWait(); }

  void StartApplication() {
    // Set up the expectation on the first C++-to-JS message.
    auto pcsc_lite_ready_message_waiter = global_context_.CreateMessageWaiter(
        /*awaited_message_type=*/"pcsc_lite_ready");
    // Set up the expectation for the application to run the provided callback.
    ::testing::MockFunction<void()> background_initialization_callback;
    EXPECT_CALL(background_initialization_callback, Call());
    // Create the application, which spawns the background initialization
    // thread.
    application_ = MakeUnique<Application>(
        &global_context_, &typed_message_router_,
        background_initialization_callback.AsStdFunction());
    // Wait until the daemon's background thread completes the initialization
    // and notifies the JS side.
    pcsc_lite_ready_message_waiter->Wait();
    EXPECT_TRUE(pcsc_lite_ready_message_waiter->value().StrictlyEquals(
        Value(Value::Type::kDictionary)));
  }

  // Enables the specified fake USB devices.
  void SetUsbDevices(
      const std::vector<TestingSmartCardSimulation::Device>& devices) {
    smart_card_simulation_.SetDevices(devices);
  }

  ReaderNotificationObserver& reader_notification_observer() {
    return reader_notification_observer_;
  }

 private:
  void SetUpUsbSimulation() {
    global_context_.RegisterRequestHandler(
        TestingSmartCardSimulation::kRequesterName,
        std::bind(&TestingSmartCardSimulation::OnRequestToJs,
                  &smart_card_simulation_,
                  /*request_payload=*/std::placeholders::_1,
                  /*request_id=*/std::placeholders::_2));
  }

  TypedMessageRouter typed_message_router_;
  TestingSmartCardSimulation smart_card_simulation_{&typed_message_router_};
  ReaderNotificationObserver reader_notification_observer_;
  TestingGlobalContext global_context_{&typed_message_router_};
  std::unique_ptr<Application> application_;
};

TEST_F(SmartCardConnectorApplicationTest, SmokeTest) {
  StartApplication();
}

// Test a PC/SC-Lite context can be established and freed, via direct C function
// calls `SCardEstablishContext()` and `SCardReleaseContext()`.
// This is an extended version of the "smoke test" as it verifies the daemon
// successfully started and replies to calls sent over (fake) sockets.
TEST_F(SmartCardConnectorApplicationTest, InternalApiContextEstablishing) {
  // Arrange:

  StartApplication();

  // Act:

  SCARDCONTEXT scard_context = 0;
  EXPECT_EQ(SCardEstablishContext(SCARD_SCOPE_SYSTEM, /*pvReserved1=*/nullptr,
                                  /*pvReserved2=*/nullptr, &scard_context),
            SCARD_S_SUCCESS);
  EXPECT_NE(scard_context, 0);

  EXPECT_EQ(SCardReleaseContext(scard_context), SCARD_S_SUCCESS);
}

// Test a single reader is successfully initialized by PC/SC-Lite and is
// returned via the direct C function call `SCardListReaders()`.
TEST_F(SmartCardConnectorApplicationTest, InternalApiSingleDeviceListing) {
  // Arrange:

  TestingSmartCardSimulation::Device device;
  device.id = 123;
  device.type = TestingSmartCardSimulation::DeviceType::kGemaltoPcTwinReader;
  SetUsbDevices({device});

  StartApplication();
  // No need to wait here, since the notifications for the initially present
  // devices are sent during the startup.
  EXPECT_EQ(reader_notification_observer().Pop(),
            "reader_init_add:Gemalto PC Twin Reader");
  EXPECT_EQ(reader_notification_observer().Pop(),
            "reader_finish_add:Gemalto PC Twin Reader");
  EXPECT_TRUE(reader_notification_observer().Empty());

  // Act:

  SCARDCONTEXT scard_context = 0;
  EXPECT_EQ(SCardEstablishContext(SCARD_SCOPE_SYSTEM, /*pvReserved1=*/nullptr,
                                  /*pvReserved2=*/nullptr, &scard_context),
            SCARD_S_SUCCESS);
  std::vector<std::string> readers = DirectCallSCardListReaders(scard_context);
  EXPECT_EQ(SCardReleaseContext(scard_context), SCARD_S_SUCCESS);

  // Assert:

  EXPECT_THAT(readers, ElementsAre("Gemalto PC Twin Reader 00 00"));
}

TEST_F(SmartCardConnectorApplicationTest,
       InternalApiGetStatusChangeDeviceAppearing) {
  // Arrange:

  // Start with an empty list of readers.
  StartApplication();
  EXPECT_TRUE(reader_notification_observer().Empty());

  // Act:

  SCARDCONTEXT scard_context = 0;
  EXPECT_EQ(SCardEstablishContext(SCARD_SCOPE_SYSTEM, /*pvReserved1=*/nullptr,
                                  /*pvReserved2=*/nullptr, &scard_context),
            SCARD_S_SUCCESS);
  EXPECT_TRUE(DirectCallSCardListReaders(scard_context).empty());

  // Simulate connecting a reader.
  TestingSmartCardSimulation::Device device;
  device.id = 123;
  device.type = TestingSmartCardSimulation::DeviceType::kGemaltoPcTwinReader;
  SetUsbDevices({device});

  // Wait until PC/SC reports a change in the list of readers.
  std::vector<SCARD_READERSTATE> reader_states(1);
  reader_states[0].szReader = R"(\\?PnP?\Notification)";
  reader_states[0].dwCurrentState = SCARD_STATE_UNAWARE;
  EXPECT_EQ(SCardGetStatusChange(scard_context, /*dwTimeout=*/INFINITE,
                                 reader_states.data(), reader_states.size()),
            SCARD_S_SUCCESS);

  std::vector<std::string> readers = DirectCallSCardListReaders(scard_context);

  EXPECT_EQ(SCardReleaseContext(scard_context), SCARD_S_SUCCESS);

  // Assert:

  EXPECT_THAT(readers, ElementsAre("Gemalto PC Twin Reader 00 00"));
  EXPECT_EQ(reader_notification_observer().WaitAndPop(),
            "reader_init_add:Gemalto PC Twin Reader");
  EXPECT_EQ(reader_notification_observer().WaitAndPop(),
            "reader_finish_add:Gemalto PC Twin Reader");
  EXPECT_TRUE(reader_notification_observer().Empty());
}

TEST_F(SmartCardConnectorApplicationTest,
       InternalApiGetStatusChangeDeviceRemoving) {
  // Arrange:

  // Start with a single reader.
  TestingSmartCardSimulation::Device device;
  device.id = 123;
  device.type = TestingSmartCardSimulation::DeviceType::kGemaltoPcTwinReader;
  SetUsbDevices({device});

  StartApplication();
  // No need to wait here, since the notifications for the initially present
  // devices are sent during the startup.
  EXPECT_EQ(reader_notification_observer().Pop(),
            "reader_init_add:Gemalto PC Twin Reader");
  EXPECT_EQ(reader_notification_observer().Pop(),
            "reader_finish_add:Gemalto PC Twin Reader");
  EXPECT_TRUE(reader_notification_observer().Empty());

  // Act:

  SCARDCONTEXT scard_context = 0;
  EXPECT_EQ(SCardEstablishContext(SCARD_SCOPE_SYSTEM, /*pvReserved1=*/nullptr,
                                  /*pvReserved2=*/nullptr, &scard_context),
            SCARD_S_SUCCESS);

  EXPECT_THAT(DirectCallSCardListReaders(scard_context),
              ElementsAre("Gemalto PC Twin Reader 00 00"));

  // Simulate disconnecting the reader.
  SetUsbDevices({});

  // Wait until PC/SC reports a change in the list of readers.
  std::vector<SCARD_READERSTATE> reader_states(1);
  reader_states[0].szReader = R"(\\?PnP?\Notification)";
  reader_states[0].dwCurrentState = SCARD_STATE_UNAWARE;
  EXPECT_EQ(SCardGetStatusChange(scard_context, /*dwTimeout=*/INFINITE,
                                 reader_states.data(), reader_states.size()),
            SCARD_S_SUCCESS);

  std::vector<std::string> readers = DirectCallSCardListReaders(scard_context);

  EXPECT_EQ(SCardReleaseContext(scard_context), SCARD_S_SUCCESS);

  // Assert:

  EXPECT_THAT(readers, IsEmpty());
  EXPECT_EQ(reader_notification_observer().WaitAndPop(),
            "reader_remove:Gemalto PC Twin Reader");
  EXPECT_TRUE(reader_notification_observer().Empty());
}

}  // namespace google_smart_card
<|endoftext|>


<commit_before>#include "SegmentationCostFunction.h"
#include <imageprocessing/ConnectedComponent.h>
#include <sopnet/segments/EndSegment.h>
#include <sopnet/segments/ContinuationSegment.h>
#include <sopnet/segments/BranchSegment.h>
#include <sopnet/slices/Slice.h>
#include <util/ProgramOptions.h>

logger::LogChannel segmentationcostfunctionlog("segmentationcostfunctionlog", "[SegmentationCostFunction] ");

util::ProgramOption optionInvertMembraneMaps(
		util::_module           = "sopnet",
		util::_long_name        = "invertMembraneMaps",
		util::_description_text = "Invert the meaning of the membrane map. The default "
		                          "(not inverting) is: bright pixel = hight membrane probability.");

SegmentationCostFunction::SegmentationCostFunction() :
	_costFunction(boost::make_shared<costs_function_type>(boost::bind(&SegmentationCostFunction::costs, this, _1, _2, _3, _4))) {

	registerInput(_membranes, "membranes");
	registerInput(_parameters, "parameters");

	registerOutput(_costFunction, "cost function");
}

void
SegmentationCostFunction::updateOutputs() {

	// nothing to do here
}

void
SegmentationCostFunction::costs(
		const std::vector<boost::shared_ptr<EndSegment> >&          ends,
		const std::vector<boost::shared_ptr<ContinuationSegment> >& continuations,
		const std::vector<boost::shared_ptr<BranchSegment> >&       branches,
		std::vector<double>& segmentCosts) {

	segmentCosts.resize(ends.size() + continuations.size() + branches.size(), 0);

	if (_segmentationCosts.size() != segmentCosts.size() ||
	    _parameters->priorForeground != _prevParameters.priorForeground) {

		LOG_DEBUG(segmentationcostfunctionlog)
				<< "updating segmentation costs (number of segments: "
				<< segmentCosts.size() << ", number of cached values "
				<< _segmentationCosts.size() << ")" << std::endl;

		_segmentationCosts.clear();
		_segmentationCosts.reserve(ends.size() + continuations.size() + branches.size());

		computeSegmentationCosts(ends, continuations, branches);

		LOG_DEBUG(segmentationcostfunctionlog)
				<< "computed " << _segmentationCosts.size() << " segmentation cost values" << std::endl;
	}

	if (_boundaryLengths.size() != segmentCosts.size()) {

		LOG_DEBUG(segmentationcostfunctionlog) << "updating boundary lengths..." << std::endl;

		_boundaryLengths.clear();
		_boundaryLengths.reserve(ends.size() + continuations.size() + branches.size());

		computeBoundaryLengths(ends, continuations, branches);
	}

	_prevParameters = *_parameters;

	unsigned int i = 0;

	foreach (boost::shared_ptr<EndSegment> end, ends) {

		segmentCosts[i] += _parameters->weight*(_segmentationCosts[i] + _parameters->weightPotts*_boundaryLengths[i]);

		i++;
	}

	foreach (boost::shared_ptr<ContinuationSegment> continuation, continuations) {

		segmentCosts[i] += _parameters->weight*(_segmentationCosts[i] + _parameters->weightPotts*_boundaryLengths[i]);

		i++;
	}

	foreach (boost::shared_ptr<BranchSegment> branch, branches) {

		segmentCosts[i] += _parameters->weight*(_segmentationCosts[i] + _parameters->weightPotts*_boundaryLengths[i]);

		i++;
	}
}

void
SegmentationCostFunction::computeSegmentationCosts(
		const std::vector<boost::shared_ptr<EndSegment> >&          ends,
		const std::vector<boost::shared_ptr<ContinuationSegment> >& continuations,
		const std::vector<boost::shared_ptr<BranchSegment> >&       branches) {

	foreach (boost::shared_ptr<EndSegment> end, ends)
		computeSegmentationCost(*end);

	foreach (boost::shared_ptr<ContinuationSegment> continuation, continuations)
		computeSegmentationCost(*continuation);

	foreach (boost::shared_ptr<BranchSegment> branch, branches)
		computeSegmentationCost(*branch);
}

void
SegmentationCostFunction::computeBoundaryLengths(
		const std::vector<boost::shared_ptr<EndSegment> >&          ends,
		const std::vector<boost::shared_ptr<ContinuationSegment> >& continuations,
		const std::vector<boost::shared_ptr<BranchSegment> >&       branches) {

	foreach (boost::shared_ptr<EndSegment> end, ends)
		computeBoundaryLength(*end);

	foreach (boost::shared_ptr<ContinuationSegment> continuation, continuations)
		computeBoundaryLength(*continuation);

	foreach (boost::shared_ptr<BranchSegment> branch, branches)
		computeBoundaryLength(*branch);
}

void
SegmentationCostFunction::computeSegmentationCost(const EndSegment& end) {

	_segmentationCosts.push_back(computeSegmentationCost(*end.getSlice()));
}

void
SegmentationCostFunction::computeSegmentationCost(const ContinuationSegment& continuation) {

	_segmentationCosts.push_back(
			computeSegmentationCost(*continuation.getSourceSlice()) +
			computeSegmentationCost(*continuation.getTargetSlice()));
}

void
SegmentationCostFunction::computeSegmentationCost(const BranchSegment& branch) {

	_segmentationCosts.push_back(
			computeSegmentationCost(*branch.getSourceSlice()) +
			computeSegmentationCost(*branch.getTargetSlice1()) +
			computeSegmentationCost(*branch.getTargetSlice2()));
}

void
SegmentationCostFunction::computeBoundaryLength(const EndSegment& end) {

	_boundaryLengths.push_back(computeBoundaryLength(*end.getSlice()));
}

void
SegmentationCostFunction::computeBoundaryLength(const ContinuationSegment& continuation) {

	_boundaryLengths.push_back(
			computeBoundaryLength(*continuation.getSourceSlice()) +
			computeBoundaryLength(*continuation.getTargetSlice()));
}

void
SegmentationCostFunction::computeBoundaryLength(const BranchSegment& branch) {

	_boundaryLengths.push_back(
			computeBoundaryLength(*branch.getSourceSlice()) +
			computeBoundaryLength(*branch.getTargetSlice1()) +
			computeBoundaryLength(*branch.getTargetSlice2()));
}

double
SegmentationCostFunction::computeSegmentationCost(const Slice& slice) {

	if (_sliceSegmentationCosts.count(slice.getId()))
		return _sliceSegmentationCosts[slice.getId()];

	unsigned int section = slice.getSection();

	double costs = 0.0;

	// for each pixel in the slice
	foreach (const util::point<unsigned int>& pixel, slice.getComponent()->getPixels()) {

		// get the membrane data probability p(x|y=membrane)
		double probMembrane = (*(*_membranes)[section])(pixel.x, pixel.y)/255.0;

		if (optionInvertMembraneMaps)
			probMembrane = 1.0 - probMembrane;

		// get the neuron data probability p(x|y=neuron)
		double probNeuron = 1.0 - probMembrane;

		// multiply both with the respective prior p(y)
		probMembrane *= (1.0 - _parameters->priorForeground);
		probNeuron   *= _parameters->priorForeground;

		// normalize both probabilities, so that we get p(y|x)
		probMembrane /= probMembrane + probNeuron;
		probNeuron   /= probMembrane + probNeuron;

		// ensure numerical stability
		probMembrane = std::max(0.0001, std::min(0.9999, probMembrane));
		probNeuron   = std::max(0.0001, std::min(0.9999, probNeuron));

		// compute the corresponding costs
		double costsMembrane = -log(probMembrane);
		double costsNeuron   = -log(probNeuron);

		// costs for accepting the segmentation is the cost difference between
		// segmenting the region as background and segmenting the region as
		// foreground
		costs += costsNeuron - costsMembrane;
	}

	_sliceSegmentationCosts[slice.getId()] = costs;

	return costs;
}

unsigned int
SegmentationCostFunction::computeBoundaryLength(const Slice& slice) {

	if (_sliceBoundaryLengths.count(slice.getId()))
		return _sliceBoundaryLengths[slice.getId()];

	boost::shared_ptr<ConnectedComponent> component = slice.getComponent();

	unsigned int width   = (unsigned int)(component->getBoundingBox().width()  + 1);
	unsigned int height  = (unsigned int)(component->getBoundingBox().height() + 1);
	unsigned int offsetX = (unsigned int)component->getBoundingBox().minX;
	unsigned int offsetY = (unsigned int)component->getBoundingBox().minY;

	std::vector<bool> pixels(width*height, false);

	foreach (const util::point<unsigned int>& p, component->getPixels()) {

		unsigned int x = p.x - offsetX;
		unsigned int y = p.y - offsetY;

		pixels[x + y*width] = true;
	}

	unsigned int boundaryLength = 0;

	foreach (const util::point<unsigned int>& p, component->getPixels()) {

		// for neighborhood testing with offset
		unsigned int x = p.x - offsetX;
		unsigned int y = p.y - offsetY;

		// left
		if (x == 0 || !pixels[(x - 1) + y*width])
			boundaryLength++;

		// right
		if (x == width - 1 || !pixels[(x + 1) + y*width])
			boundaryLength++;

		// top
		if (y == 0 || !pixels[x + (y - 1)*width])
			boundaryLength++;

		// bottom
		if (y == height - 1 || !pixels[x + (y + 1)*width])
			boundaryLength++;
	}

	_sliceBoundaryLengths[slice.getId()] = boundaryLength;

	return boundaryLength;
}
<commit_msg>bugfix: segmentation costs assumed wrongly membrane intensities in [0, 255]<commit_after>#include "SegmentationCostFunction.h"
#include <imageprocessing/ConnectedComponent.h>
#include <sopnet/segments/EndSegment.h>
#include <sopnet/segments/ContinuationSegment.h>
#include <sopnet/segments/BranchSegment.h>
#include <sopnet/slices/Slice.h>
#include <util/ProgramOptions.h>

logger::LogChannel segmentationcostfunctionlog("segmentationcostfunctionlog", "[SegmentationCostFunction] ");

util::ProgramOption optionInvertMembraneMaps(
		util::_module           = "sopnet",
		util::_long_name        = "invertMembraneMaps",
		util::_description_text = "Invert the meaning of the membrane map. The default "
		                          "(not inverting) is: bright pixel = hight membrane probability.");

SegmentationCostFunction::SegmentationCostFunction() :
	_costFunction(boost::make_shared<costs_function_type>(boost::bind(&SegmentationCostFunction::costs, this, _1, _2, _3, _4))) {

	registerInput(_membranes, "membranes");
	registerInput(_parameters, "parameters");

	registerOutput(_costFunction, "cost function");
}

void
SegmentationCostFunction::updateOutputs() {

	// nothing to do here
}

void
SegmentationCostFunction::costs(
		const std::vector<boost::shared_ptr<EndSegment> >&          ends,
		const std::vector<boost::shared_ptr<ContinuationSegment> >& continuations,
		const std::vector<boost::shared_ptr<BranchSegment> >&       branches,
		std::vector<double>& segmentCosts) {

	segmentCosts.resize(ends.size() + continuations.size() + branches.size(), 0);

	if (_segmentationCosts.size() != segmentCosts.size() ||
	    _parameters->priorForeground != _prevParameters.priorForeground) {

		LOG_DEBUG(segmentationcostfunctionlog)
				<< "updating segmentation costs (number of segments: "
				<< segmentCosts.size() << ", number of cached values "
				<< _segmentationCosts.size() << ")" << std::endl;

		_sliceSegmentationCosts.clear();
		_segmentationCosts.clear();
		_segmentationCosts.reserve(ends.size() + continuations.size() + branches.size());

		computeSegmentationCosts(ends, continuations, branches);

		LOG_DEBUG(segmentationcostfunctionlog)
				<< "computed " << _segmentationCosts.size() << " segmentation cost values" << std::endl;
	}

	if (_boundaryLengths.size() != segmentCosts.size()) {

		LOG_DEBUG(segmentationcostfunctionlog) << "updating boundary lengths..." << std::endl;

		_boundaryLengths.clear();
		_boundaryLengths.reserve(ends.size() + continuations.size() + branches.size());

		computeBoundaryLengths(ends, continuations, branches);
	}

	_prevParameters = *_parameters;

	unsigned int i = 0;

	foreach (boost::shared_ptr<EndSegment> end, ends) {

		segmentCosts[i] += _parameters->weight*(_segmentationCosts[i] + _parameters->weightPotts*_boundaryLengths[i]);

		i++;
	}

	foreach (boost::shared_ptr<ContinuationSegment> continuation, continuations) {

		segmentCosts[i] += _parameters->weight*(_segmentationCosts[i] + _parameters->weightPotts*_boundaryLengths[i]);

		i++;
	}

	foreach (boost::shared_ptr<BranchSegment> branch, branches) {

		segmentCosts[i] += _parameters->weight*(_segmentationCosts[i] + _parameters->weightPotts*_boundaryLengths[i]);

		i++;
	}
}

void
SegmentationCostFunction::computeSegmentationCosts(
		const std::vector<boost::shared_ptr<EndSegment> >&          ends,
		const std::vector<boost::shared_ptr<ContinuationSegment> >& continuations,
		const std::vector<boost::shared_ptr<BranchSegment> >&       branches) {

	foreach (boost::shared_ptr<EndSegment> end, ends)
		computeSegmentationCost(*end);

	foreach (boost::shared_ptr<ContinuationSegment> continuation, continuations)
		computeSegmentationCost(*continuation);

	foreach (boost::shared_ptr<BranchSegment> branch, branches)
		computeSegmentationCost(*branch);
}

void
SegmentationCostFunction::computeBoundaryLengths(
		const std::vector<boost::shared_ptr<EndSegment> >&          ends,
		const std::vector<boost::shared_ptr<ContinuationSegment> >& continuations,
		const std::vector<boost::shared_ptr<BranchSegment> >&       branches) {

	foreach (boost::shared_ptr<EndSegment> end, ends)
		computeBoundaryLength(*end);

	foreach (boost::shared_ptr<ContinuationSegment> continuation, continuations)
		computeBoundaryLength(*continuation);

	foreach (boost::shared_ptr<BranchSegment> branch, branches)
		computeBoundaryLength(*branch);
}

void
SegmentationCostFunction::computeSegmentationCost(const EndSegment& end) {

	_segmentationCosts.push_back(computeSegmentationCost(*end.getSlice()));
}

void
SegmentationCostFunction::computeSegmentationCost(const ContinuationSegment& continuation) {

	_segmentationCosts.push_back(
			computeSegmentationCost(*continuation.getSourceSlice()) +
			computeSegmentationCost(*continuation.getTargetSlice()));
}

void
SegmentationCostFunction::computeSegmentationCost(const BranchSegment& branch) {

	_segmentationCosts.push_back(
			computeSegmentationCost(*branch.getSourceSlice()) +
			computeSegmentationCost(*branch.getTargetSlice1()) +
			computeSegmentationCost(*branch.getTargetSlice2()));
}

void
SegmentationCostFunction::computeBoundaryLength(const EndSegment& end) {

	_boundaryLengths.push_back(computeBoundaryLength(*end.getSlice()));
}

void
SegmentationCostFunction::computeBoundaryLength(const ContinuationSegment& continuation) {

	_boundaryLengths.push_back(
			computeBoundaryLength(*continuation.getSourceSlice()) +
			computeBoundaryLength(*continuation.getTargetSlice()));
}

void
SegmentationCostFunction::computeBoundaryLength(const BranchSegment& branch) {

	_boundaryLengths.push_back(
			computeBoundaryLength(*branch.getSourceSlice()) +
			computeBoundaryLength(*branch.getTargetSlice1()) +
			computeBoundaryLength(*branch.getTargetSlice2()));
}

double
SegmentationCostFunction::computeSegmentationCost(const Slice& slice) {

	if (_sliceSegmentationCosts.count(slice.getId()))
		return _sliceSegmentationCosts[slice.getId()];

	unsigned int section = slice.getSection();

	double costs = 0.0;

	// for each pixel in the slice
	foreach (const util::point<unsigned int>& pixel, slice.getComponent()->getPixels()) {

		// get the membrane data probability p(x|y=membrane)
		double probMembrane = (*(*_membranes)[section])(pixel.x, pixel.y);

		if (optionInvertMembraneMaps)
			probMembrane = 1.0 - probMembrane;

		// get the neuron data probability p(x|y=neuron)
		double probNeuron = 1.0 - probMembrane;

		// multiply both with the respective prior p(y)
		probMembrane *= (1.0 - _parameters->priorForeground);
		probNeuron   *= _parameters->priorForeground;

		// normalize both probabilities, so that we get p(y|x)
		probMembrane /= probMembrane + probNeuron;
		probNeuron   /= probMembrane + probNeuron;

		// ensure numerical stability
		probMembrane = std::max(0.0001, std::min(0.9999, probMembrane));
		probNeuron   = std::max(0.0001, std::min(0.9999, probNeuron));

		// compute the corresponding costs
		double costsMembrane = -log(probMembrane);
		double costsNeuron   = -log(probNeuron);

		// costs for accepting the segmentation is the cost difference between
		// segmenting the region as background and segmenting the region as
		// foreground
		costs += costsNeuron - costsMembrane;
	}

	_sliceSegmentationCosts[slice.getId()] = costs;

	return costs;
}

unsigned int
SegmentationCostFunction::computeBoundaryLength(const Slice& slice) {

	if (_sliceBoundaryLengths.count(slice.getId()))
		return _sliceBoundaryLengths[slice.getId()];

	boost::shared_ptr<ConnectedComponent> component = slice.getComponent();

	unsigned int width   = (unsigned int)(component->getBoundingBox().width()  + 1);
	unsigned int height  = (unsigned int)(component->getBoundingBox().height() + 1);
	unsigned int offsetX = (unsigned int)component->getBoundingBox().minX;
	unsigned int offsetY = (unsigned int)component->getBoundingBox().minY;

	std::vector<bool> pixels(width*height, false);

	foreach (const util::point<unsigned int>& p, component->getPixels()) {

		unsigned int x = p.x - offsetX;
		unsigned int y = p.y - offsetY;

		pixels[x + y*width] = true;
	}

	unsigned int boundaryLength = 0;

	foreach (const util::point<unsigned int>& p, component->getPixels()) {

		// for neighborhood testing with offset
		unsigned int x = p.x - offsetX;
		unsigned int y = p.y - offsetY;

		// left
		if (x == 0 || !pixels[(x - 1) + y*width])
			boundaryLength++;

		// right
		if (x == width - 1 || !pixels[(x + 1) + y*width])
			boundaryLength++;

		// top
		if (y == 0 || !pixels[x + (y - 1)*width])
			boundaryLength++;

		// bottom
		if (y == height - 1 || !pixels[x + (y + 1)*width])
			boundaryLength++;
	}

	_sliceBoundaryLengths[slice.getId()] = boundaryLength;

	return boundaryLength;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <vector>
#include <sstream>
#include <algorithm>
#include <chrono>
#include <thread>

#include <cfloat>

#include <SDL.h>

#include "compiler.h"
#include "TCPServer.hpp"

#ifdef _WINDOWS
#include <tchar.h>
int wmain(int argc, _TCHAR* argv[]) {
#else
int main(int argc, char** argv) {
#endif

	// defaults
	unsigned int port = 60601;

	// command line params
	if (argc >= 2) {
		port = atoi(argv[1]);
	}

	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_TIMER) != 0) {
		std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl;
		return 1;
	}

	std::cout << "hello sdl" << std::endl;

	SDL_Window* windowHandle = SDL_CreateWindow("Hello World!", 100, 100, 640, 480, SDL_WINDOW_SHOWN);

	if (windowHandle == nullptr) {
		std::cout << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl;
		return 1;
	}

	// setup joysticks
	SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");

    bool dirty = false;
    unsigned int numJoysticks = SDL_NumJoysticks();

	auto numAxes = new unsigned int[numJoysticks];
	auto numButtons = new unsigned int[numJoysticks];

    int activeJoystick = 0; // starting with 0
	std::cout << numJoysticks << " joystic(s) found." << std::endl;

	for (unsigned int i = 0; i < numJoysticks; i++)
	{
		numAxes[i] = 0;
		numButtons[i] = 0;

		SDL_Joystick* stick = SDL_JoystickOpen(i);

		if (stick) {
	        printf("Opened Joystick 0\n");
	        printf("Name: %s\n", SDL_JoystickNameForIndex(i));
	        // printf("Devise GUID: %s\n", SDL_JoystickGetGUIDString(i));
	        printf("Number of Axes: %d\n", SDL_JoystickNumAxes(stick));
	        printf("Number of Buttons: %d\n", SDL_JoystickNumButtons(stick));
	        printf("Number of Balls: %d\n", SDL_JoystickNumBalls(stick));

            numAxes[i] = SDL_JoystickNumAxes(stick);
            numButtons[i] = SDL_JoystickNumButtons(stick);
	    } else {
	        printf("Couldn't open Joystick 0\n");
	    }
	}

    auto axesBuffer = new float*[numJoysticks];
    auto buttonsBuffer = new float*[numJoysticks];

    for (unsigned int i = 0; i < numJoysticks; i++)
    {
    	axesBuffer[i] = new float[numAxes[i]];
    	for (unsigned int j = 0; j < numAxes[i]; j++)
	    {
	        axesBuffer[i][j] = 0;
	    }

	    buttonsBuffer[i] = new float[numButtons[i]];
	    for (unsigned int j = 0; j < numButtons[i]; j++)
	    {
			buttonsBuffer[i][j] = 0;
	    }
    }

    // setup networking
    boost::asio::io_service io_service;
    TCPServer server(io_service, 60601);

    auto lastFrame = 0.0f;
    auto minFrameDelta = FLT_MAX;
    auto maxFrameDelta = 0.0f;

	// run!
    std::cout << "entering main loop" << std::endl;
    std::cout << "active joystic is: " << activeJoystick << std::endl;
	bool running = true;
	while (running)
	{
        try
        {
            io_service.poll();
        }
        catch (std::exception& e)
        {
            std::cerr << e.what() << std::endl;
        }

		SDL_JoystickUpdate();

		SDL_Event e;
		while (SDL_PollEvent(&e))
        {
			if (e.type == SDL_QUIT)
			{
				running = false;
			}
            else if (e.type == SDL_KEYDOWN)
            {
             if (e.key.state == SDL_PRESSED && e.key.keysym.scancode == SDL_SCANCODE_LEFT)
                {
                    activeJoystick = (activeJoystick - 1) % numJoysticks;
                    std::cout << "active joystic is: " << activeJoystick << std::endl;
                }
                if (e.key.state == SDL_PRESSED && e.key.keysym.scancode == SDL_SCANCODE_RIGHT)
                {
                    activeJoystick = (activeJoystick + 1) % numJoysticks;
                    std::cout << "active joystic is: " << activeJoystick << std::endl;
                }
            }
			else if (e.type == SDL_JOYAXISMOTION)
			{
                if (e.jaxis.which == activeJoystick)
                {
					float oldValue = axesBuffer[activeJoystick][e.jaxis.axis];
					float newValue = (float)e.jaxis.value / (float)(0xffff / 2);

					dirty = dirty || (oldValue != newValue);

					axesBuffer[activeJoystick][e.jaxis.axis] = newValue;
				}
            }
            else if (e.type == SDL_JOYBUTTONDOWN)
			{
				if (e.jaxis.which == activeJoystick)
                {
					dirty = dirty || (buttonsBuffer[activeJoystick][e.jbutton.button] != 1.0f);
	                buttonsBuffer[activeJoystick][e.jbutton.button] = 1.0f;
	            }
			}
            else if (e.type == SDL_JOYBUTTONUP)
			{
                if (e.jaxis.which == activeJoystick)
                {
	                dirty = dirty || (buttonsBuffer[activeJoystick][e.jbutton.button] != 0.0f);
	                buttonsBuffer[activeJoystick][e.jbutton.button] = 0.0f;
	            }
			}
		}

        // transfer frame
        float time = (float)SDL_GetTicks() / 1000.0f;

        if (dirty)
        {
            for (unsigned int i = 0; i < numAxes[activeJoystick]; i++)
            {
                std::stringstream sstr;
                sstr << "axis-" << i;

                server.inputAnalog(sstr.str(), axesBuffer[activeJoystick][i], time, -1.0f, 1.0f);
            }

            for (unsigned int i = 0; i < numButtons[activeJoystick]; i++)
            {
                std::stringstream sstr;
                sstr << "button-" << i;

                server.inputDigital(sstr.str(), buttonsBuffer[activeJoystick][i], time);
            }

            auto dt = time - lastFrame;
            minFrameDelta = std::min(minFrameDelta, dt);
            maxFrameDelta = std::max(maxFrameDelta, dt);
            std::cout << "dt: " << dt * 1000 << "ms, min dt: " << minFrameDelta * 1000 << "ms, max dt: " << maxFrameDelta << std::endl;

			lastFrame = time;
        }

        dirty = false;

        // deschedule this thread to save some resources...
        // time passed to the function is lower bound
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
	}

	SDL_Quit();
	return 0;
}

<commit_msg>input-logger: quit if no controller found on startup<commit_after>#include <iostream>
#include <vector>
#include <sstream>
#include <algorithm>
#include <chrono>
#include <thread>

#include <cfloat>

#include <SDL.h>

#include "compiler.h"
#include "TCPServer.hpp"

#ifdef _WINDOWS
#include <tchar.h>
int wmain(int argc, _TCHAR* argv[]) {
#else
int main(int argc, char** argv) {
#endif

	// defaults
	unsigned int port = 60601;

	// command line params
	if (argc >= 2) {
		port = atoi(argv[1]);
	}

	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_TIMER) != 0) {
		std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl;
		return 1;
	}

	std::cout << "hello sdl" << std::endl;

	SDL_Window* windowHandle = SDL_CreateWindow("Hello World!", 100, 100, 640, 480, SDL_WINDOW_SHOWN);

	if (windowHandle == nullptr) {
		std::cout << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl;
		return 1;
	}

	// setup joysticks
	SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");

    bool dirty = false;
    unsigned int numJoysticks = SDL_NumJoysticks();

	auto numAxes = new unsigned int[numJoysticks];
	auto numButtons = new unsigned int[numJoysticks];

    int activeJoystick = 0; // starting with 0
	std::cout << numJoysticks << " joystic(s) found." << std::endl;

	if (numJoysticks == 0)
	{
		std::cout << "quitting..." << std::endl;
		return 1;
	}

	for (unsigned int i = 0; i < numJoysticks; i++)
	{
		numAxes[i] = 0;
		numButtons[i] = 0;

		SDL_Joystick* stick = SDL_JoystickOpen(i);

		if (stick) {
	        printf("Opened Joystick 0\n");
	        printf("Name: %s\n", SDL_JoystickNameForIndex(i));
	        // printf("Devise GUID: %s\n", SDL_JoystickGetGUIDString(i));
	        printf("Number of Axes: %d\n", SDL_JoystickNumAxes(stick));
	        printf("Number of Buttons: %d\n", SDL_JoystickNumButtons(stick));
	        printf("Number of Balls: %d\n", SDL_JoystickNumBalls(stick));

            numAxes[i] = SDL_JoystickNumAxes(stick);
            numButtons[i] = SDL_JoystickNumButtons(stick);
	    } else {
	        printf("Couldn't open Joystick 0\n");
	    }
	}

    auto axesBuffer = new float*[numJoysticks];
    auto buttonsBuffer = new float*[numJoysticks];

    for (unsigned int i = 0; i < numJoysticks; i++)
    {
    	axesBuffer[i] = new float[numAxes[i]];
    	for (unsigned int j = 0; j < numAxes[i]; j++)
	    {
	        axesBuffer[i][j] = 0;
	    }

	    buttonsBuffer[i] = new float[numButtons[i]];
	    for (unsigned int j = 0; j < numButtons[i]; j++)
	    {
			buttonsBuffer[i][j] = 0;
	    }
    }

    // setup networking
    boost::asio::io_service io_service;
    TCPServer server(io_service, 60601);

    auto lastFrame = 0.0f;
    auto minFrameDelta = FLT_MAX;
    auto maxFrameDelta = 0.0f;

	// run!
    std::cout << "entering main loop" << std::endl;
    std::cout << "active joystic is: " << activeJoystick << std::endl;
	bool running = true;
	while (running)
	{
        try
        {
            io_service.poll();
        }
        catch (std::exception& e)
        {
            std::cerr << e.what() << std::endl;
        }

		SDL_JoystickUpdate();

		SDL_Event e;
		while (SDL_PollEvent(&e))
        {
			if (e.type == SDL_QUIT)
			{
				running = false;
			}
            else if (e.type == SDL_KEYDOWN)
            {
             if (e.key.state == SDL_PRESSED && e.key.keysym.scancode == SDL_SCANCODE_LEFT)
                {
                    activeJoystick = (activeJoystick - 1) % numJoysticks;
                    std::cout << "active joystic is: " << activeJoystick << std::endl;
                }
                if (e.key.state == SDL_PRESSED && e.key.keysym.scancode == SDL_SCANCODE_RIGHT)
                {
                    activeJoystick = (activeJoystick + 1) % numJoysticks;
                    std::cout << "active joystic is: " << activeJoystick << std::endl;
                }
            }
			else if (e.type == SDL_JOYAXISMOTION)
			{
                if (e.jaxis.which == activeJoystick)
                {
					float oldValue = axesBuffer[activeJoystick][e.jaxis.axis];
					float newValue = (float)e.jaxis.value / (float)(0xffff / 2);

					dirty = dirty || (oldValue != newValue);

					axesBuffer[activeJoystick][e.jaxis.axis] = newValue;
				}
            }
            else if (e.type == SDL_JOYBUTTONDOWN)
			{
				if (e.jaxis.which == activeJoystick)
                {
					dirty = dirty || (buttonsBuffer[activeJoystick][e.jbutton.button] != 1.0f);
	                buttonsBuffer[activeJoystick][e.jbutton.button] = 1.0f;
	            }
			}
            else if (e.type == SDL_JOYBUTTONUP)
			{
                if (e.jaxis.which == activeJoystick)
                {
	                dirty = dirty || (buttonsBuffer[activeJoystick][e.jbutton.button] != 0.0f);
	                buttonsBuffer[activeJoystick][e.jbutton.button] = 0.0f;
	            }
			}
		}

        // transfer frame
        float time = (float)SDL_GetTicks() / 1000.0f;

        if (dirty)
        {
            for (unsigned int i = 0; i < numAxes[activeJoystick]; i++)
            {
                std::stringstream sstr;
                sstr << "axis-" << i;

                server.inputAnalog(sstr.str(), axesBuffer[activeJoystick][i], time, -1.0f, 1.0f);
            }

            for (unsigned int i = 0; i < numButtons[activeJoystick]; i++)
            {
                std::stringstream sstr;
                sstr << "button-" << i;

                server.inputDigital(sstr.str(), buttonsBuffer[activeJoystick][i], time);
            }

            auto dt = time - lastFrame;
            minFrameDelta = std::min(minFrameDelta, dt);
            maxFrameDelta = std::max(maxFrameDelta, dt);
            std::cout << "dt: " << dt * 1000 << "ms, min dt: " << minFrameDelta * 1000 << "ms, max dt: " << maxFrameDelta << std::endl;

			lastFrame = time;
        }

        dirty = false;

        // deschedule this thread to save some resources...
        // time passed to the function is lower bound
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
	}

	SDL_Quit();
	return 0;
}

<|endoftext|>


<commit_before>/*******************************************************************************
 * c7a/net/net_dispatcher.hpp
 *
 * Asynchronous callback wrapper around select(), epoll(), or other kernel-level
 * dispatchers.
 *
 * Part of Project c7a.
 *
 * Copyright (C) 2015 Timo Bingmann <tb@panthema.net>
 *
 * This file has no license. Only Chuck Norris can compile it.
 ******************************************************************************/

#pragma once
#ifndef C7A_NET_NET_DISPATCHER_HEADER
#define C7A_NET_NET_DISPATCHER_HEADER

#include <c7a/net/net_connection.hpp>
#include <c7a/net/buffer.hpp>
#include <c7a/net/lowlevel/socket.hpp>
#include <c7a/net/lowlevel/select_dispatcher.hpp>
//#include <c7a/net/lowlevel/epoll-dispatcher.hpp>

#include <string>
#include <deque>
#include <queue>
#include <ctime>

namespace c7a {
namespace net {
//! \addtogroup net Network Communication
//! \{

/**
 * NetDispatcher is a high level wrapper for asynchronous callback
 * processing.. One can register NetConnection objects for readability and
 * writability checks, buffered reads and writes with completion callbacks, and
 * also timer functions.
 */
class NetDispatcher {
    static const bool debug = false;

protected:
    //! switch between different low-level dispatchers
    typedef lowlevel::SelectDispatcher<NetConnection&> Dispatcher;
    //typedef lowlevel::EPollDispatcher Dispatcher;

    //! import into class namespace
    typedef lowlevel::Socket Socket;

public:
    //! \name Timeout Callbacks
    //! \{

    //! callback signature for timer events
    typedef std::function<bool ()> TimerCallback;

    //! Register a relative timeout callback
    void AddRelativeTimeout(double timeout, const TimerCallback& cb) {
        timer_pq_.emplace(GetClock() + timeout, timeout, cb);
    }

    //! \}

    //! \name NetConnection Callbacks
    //! \{

    //! callback signature for socket readable/writable events
    typedef std::function<bool (NetConnection&)> ConnectionCallback;

    //! Register a buffered read callback and a default exception callback.
    void AddRead(NetConnection& c, const ConnectionCallback& read_cb) {
        return dispatcher_.AddRead(c.GetSocket().fd(), c, read_cb);
    }

    //! Register a buffered write callback and a default exception callback.
    void AddWrite(NetConnection& c, const ConnectionCallback& write_cb) {
        return dispatcher_.AddWrite(c.GetSocket().fd(), c, write_cb);
    }

    //! Register a buffered write callback and a default exception callback.
    void AddReadWrite(
        NetConnection& c,
        const ConnectionCallback& read_cb, const ConnectionCallback& write_cb) {
        return dispatcher_.AddReadWrite(
            c.GetSocket().fd(), c, read_cb, write_cb);
    }

    //! \}

    //! \name Asynchronous Data Reader/Writer Callbacks
    //! \{

    //! callback signature for async read callbacks, they may acquire the buffer
    typedef std::function<void (NetConnection& c,
                                Buffer&& buffer)> AsyncReadCallback;

    //! asynchronously read n bytes and deliver them to the callback
    virtual void AsyncRead(NetConnection& c, size_t n,
                           AsyncReadCallback done_cb) {
        LOG << "async read on read dispatcher";
        if (n == 0) {
            if (done_cb) done_cb(c, Buffer());
            return;
        }

        // add new async reader object
        async_read_.emplace_back(n, done_cb);

        // register read callback
        AsyncReadBuffer& arb = async_read_.back();
        AddRead(c, [&arb](NetConnection& c) { return arb(c); });
    }

    //! callback signature for async write callbacks
    typedef std::function<void (NetConnection&)> AsyncWriteCallback;

    //! asynchronously write buffer and callback when delivered. The buffer is
    //! MOVED into the async writer.
    void AsyncWrite(NetConnection& c, Buffer&& buffer,
                    AsyncWriteCallback done_cb = nullptr) {
        if (buffer.size() == 0) {
            if (done_cb) done_cb(c);
            return;
        }

        // add new async writer object
        async_write_.emplace_back(std::move(buffer), done_cb);

        // register write callback
        AsyncWriteBuffer& awb = async_write_.back();
        AddWrite(c, [&awb](NetConnection& c) { return awb(c); });
    }

    //! asynchronously write buffer and callback when delivered. COPIES the data
    //! into a Buffer!
    void AsyncWriteCopy(NetConnection& c, const void* buffer, size_t size,
                        AsyncWriteCallback done_cb = NULL) {
        return AsyncWrite(c, Buffer(buffer, size), done_cb);
    }

    //! asynchronously write buffer and callback when delivered. COPIES the data
    //! into a Buffer!
    void AsyncWriteCopy(NetConnection& c, const std::string& str,
                        AsyncWriteCallback done_cb = NULL) {
        return AsyncWriteCopy(c, str.data(), str.size(), done_cb);
    }

    //! \}

    //! \name Dispatch
    //! \{

    //! dispatch one or more events
    void Dispatch(bool quit_when_empty = false) {
        // process timer events that lie in the past
        double now = GetClock();

        while (!timer_pq_.empty() && timer_pq_.top().next_timeout <= now)
        {
            const Timer& top = timer_pq_.top();
            if (top.cb()) {
                // requeue timeout event again.
                timer_pq_.emplace(top.next_timeout + top.timeout,
                                  top.timeout, top.cb);
            }
            timer_pq_.pop();
        }

        // calculate time until next timer event
        if (timer_pq_.empty()) {
            if (quit_when_empty) return;
            dispatcher_.Dispatch(INFINITY);
        }
        else {
            dispatcher_.Dispatch(timer_pq_.top().next_timeout - now);
        }
    }

    //! \}

protected:
    //! get a current monotonic clock
    static double GetClock() {
        struct timespec ts;
        ::clock_gettime(CLOCK_MONOTONIC, &ts);
        return ts.tv_sec + ts.tv_nsec * 1e-9;
    }

    //! low-level file descriptor async processing
    Dispatcher dispatcher_;

    //! struct for timer callbacks
    struct Timer
    {
        //! timepoint of next timeout
        double        next_timeout;
        //! relative timeout for restarting
        double        timeout;
        //! callback
        TimerCallback cb;

        Timer(double _next_timeout, double _timeout, const TimerCallback& _cb)
            : next_timeout(_next_timeout), timeout(_timeout), cb(_cb)
        { }

        bool operator < (const Timer& b) const
        { return next_timeout < b.next_timeout; }
    };

    //! priority queue of timer callbacks
    typedef std::priority_queue<Timer> TimerPQ;

    //! priority queue of timer callbacks, obviously kept in timeout
    //! order. Currently not addressable.
    TimerPQ timer_pq_;

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

    class AsyncReadBuffer {
    public:
        //! Construct buffered reader with callback
        AsyncReadBuffer(size_t buffer_size, const AsyncReadCallback& callback)
            : callback_(callback),
              buffer_(buffer_size)
        { }

        //! Should be called when the socket is readable
        bool operator () (NetConnection& c) {
            int r = c.GetSocket().recv_one(
                buffer_.data() + size_, buffer_.size() - size_);

            if (r < 0)
                throw Exception("AsyncReadBuffer() error in recv", errno);

            size_ += r;

            if (size_ == buffer_.size()) {
                if (callback_) callback_(c, std::move(buffer_));
                return false;
            }
            else {
                return true;
            }
        }

    private:
        //! total size currently read
        size_t size_ = 0;

        //! functional object to call once data is complete
        AsyncReadCallback callback_;

        //! Receive buffer
        Buffer buffer_;
    };

    //! deque of asynchronous readers
    std::deque<AsyncReadBuffer> async_read_;

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

    class AsyncWriteBuffer {
    public:
        //! Construct buffered writer with callback
        AsyncWriteBuffer(Buffer&& buffer,
                         const AsyncWriteCallback& callback)
            : callback_(callback),
              buffer_(std::move(buffer))
        { }

        //! Should be called when the socket is writable
        bool operator () (NetConnection& c) {
            int r = c.GetSocket().send_one(
                buffer_.data() + size_, buffer_.size() - size_);

            if (r < 0)
                throw Exception("AsyncWriteBuffer() error in send", errno);

            size_ += r;

            if (size_ == buffer_.size()) {
                if (callback_) callback_(c);
                return false;
            }
            else {
                return true;
            }
        }

    private:
        //! total size currently written
        size_t size_ = 0;

        //! functional object to call once data is complete
        AsyncWriteCallback callback_;

        //! Receive buffer
        Buffer buffer_;
    };

    //! deque of asynchronous writers
    std::deque<AsyncWriteBuffer> async_write_;

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

    //! Default exception handler
    static bool ExceptionCallback(NetConnection& s) {
        // exception on listen socket ?
        throw Exception(
                  "NetDispatcher() exception on socket fd "
                  + std::to_string(s.GetSocket().fd()) + "!", errno);
    }
};

//! \}
} // namespace net
} // namespace c7a

#endif // !C7A_NET_NET_DISPATCHER_HEADER

/******************************************************************************/
<commit_msg>remove nasty dispatcher exit hack<commit_after>/*******************************************************************************
 * c7a/net/net_dispatcher.hpp
 *
 * Asynchronous callback wrapper around select(), epoll(), or other kernel-level
 * dispatchers.
 *
 * Part of Project c7a.
 *
 * Copyright (C) 2015 Timo Bingmann <tb@panthema.net>
 *
 * This file has no license. Only Chuck Norris can compile it.
 ******************************************************************************/

#pragma once
#ifndef C7A_NET_NET_DISPATCHER_HEADER
#define C7A_NET_NET_DISPATCHER_HEADER

#include <c7a/net/net_connection.hpp>
#include <c7a/net/buffer.hpp>
#include <c7a/net/lowlevel/socket.hpp>
#include <c7a/net/lowlevel/select_dispatcher.hpp>
//#include <c7a/net/lowlevel/epoll-dispatcher.hpp>

#include <string>
#include <deque>
#include <queue>
#include <ctime>

namespace c7a {
namespace net {
//! \addtogroup net Network Communication
//! \{

/**
 * NetDispatcher is a high level wrapper for asynchronous callback
 * processing.. One can register NetConnection objects for readability and
 * writability checks, buffered reads and writes with completion callbacks, and
 * also timer functions.
 */
class NetDispatcher {
    static const bool debug = false;

protected:
    //! switch between different low-level dispatchers
    typedef lowlevel::SelectDispatcher<NetConnection&> Dispatcher;
    //typedef lowlevel::EPollDispatcher Dispatcher;

    //! import into class namespace
    typedef lowlevel::Socket Socket;

public:
    //! \name Timeout Callbacks
    //! \{

    //! callback signature for timer events
    typedef std::function<bool ()> TimerCallback;

    //! Register a relative timeout callback
    void AddRelativeTimeout(double timeout, const TimerCallback& cb) {
        timer_pq_.emplace(GetClock() + timeout, timeout, cb);
    }

    //! \}

    //! \name NetConnection Callbacks
    //! \{

    //! callback signature for socket readable/writable events
    typedef std::function<bool (NetConnection&)> ConnectionCallback;

    //! Register a buffered read callback and a default exception callback.
    void AddRead(NetConnection& c, const ConnectionCallback& read_cb) {
        return dispatcher_.AddRead(c.GetSocket().fd(), c, read_cb);
    }

    //! Register a buffered write callback and a default exception callback.
    void AddWrite(NetConnection& c, const ConnectionCallback& write_cb) {
        return dispatcher_.AddWrite(c.GetSocket().fd(), c, write_cb);
    }

    //! Register a buffered write callback and a default exception callback.
    void AddReadWrite(
        NetConnection& c,
        const ConnectionCallback& read_cb, const ConnectionCallback& write_cb) {
        return dispatcher_.AddReadWrite(
            c.GetSocket().fd(), c, read_cb, write_cb);
    }

    //! \}

    //! \name Asynchronous Data Reader/Writer Callbacks
    //! \{

    //! callback signature for async read callbacks, they may acquire the buffer
    typedef std::function<void (NetConnection& c,
                                Buffer&& buffer)> AsyncReadCallback;

    //! asynchronously read n bytes and deliver them to the callback
    virtual void AsyncRead(NetConnection& c, size_t n,
                           AsyncReadCallback done_cb) {
        LOG << "async read on read dispatcher";
        if (n == 0) {
            if (done_cb) done_cb(c, Buffer());
            return;
        }

        // add new async reader object
        async_read_.emplace_back(n, done_cb);

        // register read callback
        AsyncReadBuffer& arb = async_read_.back();
        AddRead(c, [&arb](NetConnection& c) { return arb(c); });
    }

    //! callback signature for async write callbacks
    typedef std::function<void (NetConnection&)> AsyncWriteCallback;

    //! asynchronously write buffer and callback when delivered. The buffer is
    //! MOVED into the async writer.
    void AsyncWrite(NetConnection& c, Buffer&& buffer,
                    AsyncWriteCallback done_cb = nullptr) {
        if (buffer.size() == 0) {
            if (done_cb) done_cb(c);
            return;
        }

        // add new async writer object
        async_write_.emplace_back(std::move(buffer), done_cb);

        // register write callback
        AsyncWriteBuffer& awb = async_write_.back();
        AddWrite(c, [&awb](NetConnection& c) { return awb(c); });
    }

    //! asynchronously write buffer and callback when delivered. COPIES the data
    //! into a Buffer!
    void AsyncWriteCopy(NetConnection& c, const void* buffer, size_t size,
                        AsyncWriteCallback done_cb = NULL) {
        return AsyncWrite(c, Buffer(buffer, size), done_cb);
    }

    //! asynchronously write buffer and callback when delivered. COPIES the data
    //! into a Buffer!
    void AsyncWriteCopy(NetConnection& c, const std::string& str,
                        AsyncWriteCallback done_cb = NULL) {
        return AsyncWriteCopy(c, str.data(), str.size(), done_cb);
    }

    //! \}

    //! \name Dispatch
    //! \{

    //! dispatch one or more events
    void Dispatch() {
        // process timer events that lie in the past
        double now = GetClock();

        while (!timer_pq_.empty() && timer_pq_.top().next_timeout <= now)
        {
            const Timer& top = timer_pq_.top();
            if (top.cb()) {
                // requeue timeout event again.
                timer_pq_.emplace(top.next_timeout + top.timeout,
                                  top.timeout, top.cb);
            }
            timer_pq_.pop();
        }

        // calculate time until next timer event
        if (timer_pq_.empty()) {
            dispatcher_.Dispatch(INFINITY);
        }
        else {
            dispatcher_.Dispatch(timer_pq_.top().next_timeout - now);
        }
    }

    //! \}

protected:
    //! get a current monotonic clock
    static double GetClock() {
        struct timespec ts;
        ::clock_gettime(CLOCK_MONOTONIC, &ts);
        return ts.tv_sec + ts.tv_nsec * 1e-9;
    }

    //! low-level file descriptor async processing
    Dispatcher dispatcher_;

    //! struct for timer callbacks
    struct Timer
    {
        //! timepoint of next timeout
        double        next_timeout;
        //! relative timeout for restarting
        double        timeout;
        //! callback
        TimerCallback cb;

        Timer(double _next_timeout, double _timeout, const TimerCallback& _cb)
            : next_timeout(_next_timeout), timeout(_timeout), cb(_cb)
        { }

        bool operator < (const Timer& b) const
        { return next_timeout < b.next_timeout; }
    };

    //! priority queue of timer callbacks
    typedef std::priority_queue<Timer> TimerPQ;

    //! priority queue of timer callbacks, obviously kept in timeout
    //! order. Currently not addressable.
    TimerPQ timer_pq_;

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

    class AsyncReadBuffer {
    public:
        //! Construct buffered reader with callback
        AsyncReadBuffer(size_t buffer_size, const AsyncReadCallback& callback)
            : callback_(callback),
              buffer_(buffer_size)
        { }

        //! Should be called when the socket is readable
        bool operator () (NetConnection& c) {
            int r = c.GetSocket().recv_one(
                buffer_.data() + size_, buffer_.size() - size_);

            if (r < 0)
                throw Exception("AsyncReadBuffer() error in recv", errno);

            size_ += r;

            if (size_ == buffer_.size()) {
                if (callback_) callback_(c, std::move(buffer_));
                return false;
            }
            else {
                return true;
            }
        }

    private:
        //! total size currently read
        size_t size_ = 0;

        //! functional object to call once data is complete
        AsyncReadCallback callback_;

        //! Receive buffer
        Buffer buffer_;
    };

    //! deque of asynchronous readers
    std::deque<AsyncReadBuffer> async_read_;

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

    class AsyncWriteBuffer {
    public:
        //! Construct buffered writer with callback
        AsyncWriteBuffer(Buffer&& buffer,
                         const AsyncWriteCallback& callback)
            : callback_(callback),
              buffer_(std::move(buffer))
        { }

        //! Should be called when the socket is writable
        bool operator () (NetConnection& c) {
            int r = c.GetSocket().send_one(
                buffer_.data() + size_, buffer_.size() - size_);

            if (r < 0)
                throw Exception("AsyncWriteBuffer() error in send", errno);

            size_ += r;

            if (size_ == buffer_.size()) {
                if (callback_) callback_(c);
                return false;
            }
            else {
                return true;
            }
        }

    private:
        //! total size currently written
        size_t size_ = 0;

        //! functional object to call once data is complete
        AsyncWriteCallback callback_;

        //! Receive buffer
        Buffer buffer_;
    };

    //! deque of asynchronous writers
    std::deque<AsyncWriteBuffer> async_write_;

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

    //! Default exception handler
    static bool ExceptionCallback(NetConnection& s) {
        // exception on listen socket ?
        throw Exception(
                  "NetDispatcher() exception on socket fd "
                  + std::to_string(s.GetSocket().fd()) + "!", errno);
    }
};

//! \}
} // namespace net
} // namespace c7a

#endif // !C7A_NET_NET_DISPATCHER_HEADER

/******************************************************************************/
<|endoftext|>


<commit_before>#include <set>
#include <algorithm>
#include <numeric>

#include "Mesh2D.hh"
#include "Mesh3D.hh"

namespace Amanzi {
namespace AmanziGeometry {

Mesh3D::Mesh3D(const Mesh2D * const m_, int n_layers) :
    m(m_),
    current_layer(0),
    total_layers(n_layers),
    datum(m_->datum)
{
  // reserve/allocate space
  Point d(3);
  int n_nodes = m->nnodes*(n_layers+1);
  coords.reserve(n_nodes);

  int n_cells = n_layers * m->ncells;
  cell2face.reserve(n_cells);

  int n_faces = n_layers * m->nfaces
      + (n_layers+1)*m->ncells;
  face2node.reserve(n_faces);

  // copy the top surface coords
  coords.insert(coords.end(), m->coords.begin(), m->coords.end());

  // create the top layer of faces
  face2node.insert(face2node.end(), m->cell2node.begin(),
                   m->cell2node.end());
  up_faces.resize(face2node.size());
  std::iota(up_faces.begin(), up_faces.end(), 0);
  up_nodes.resize(coords.size());
  std::iota(up_nodes.begin(), up_nodes.end(), 0);
  dn_nodes = up_nodes;
  dn_faces = up_faces;

  // create the "bottom" sideset
  std::vector<int> bottom_c(m->ncells, -1);
  std::vector<int> bottom_f(m->ncells, 1);
  side_sets.emplace_back(std::make_pair(bottom_c,bottom_f));
  side_sets_id.push_back(1);

  // create the "surface" sideset
  side_sets.emplace_back(std::piecewise_construct,
                         std::forward_as_tuple(m->ncells, -1),
                         std::forward_as_tuple(m->ncells, 0));
  side_sets_id.push_back(2);

  // create an empty "sides" sideset
  side_sets.emplace_back(std::make_pair(std::vector<int>(), std::vector<int>()));
  side_sets_id.push_back(3);
}

void
Mesh3D::extrude(const std::vector<double>& dz,
                const std::vector<int>& block_ids_) {
  ASSERT(dz.size() == m->coords.size());
  ASSERT(block_ids_.size() == m->cell2node.size());

  auto this_layer_sides = std::vector<int>(m->face2node.size(), -1);
  auto node_differs = [this](int n) {return this->dn_nodes[n] != this->up_nodes[n];};

  // shift the up-node coordinates by dz
  for (int n=0; n!=dz.size(); ++n) {
    if (dz[n] > 0.) {
      Point nc(coords[up_nodes[n]]);
      nc[2] -= dz[n];
      coords.emplace_back(std::move(nc));
      dn_nodes[n] = coords.size()-1;
    }
  }

  // add cells, faces
  for (int c=0; c!=m->ncells; ++c) {
    if (std::any_of(m->cell2node[c].begin(), m->cell2node[c].end(),
                    node_differs)) {
      // add the bottom face
      std::vector<int> dn_face;
      for (auto n : m->cell2node[c]) dn_face.emplace_back(dn_nodes[n]);
      int my_dn_f = face2node.size();
      face2node.emplace_back(std::move(dn_face));
      dn_faces[c] = my_dn_f;

      // push back a cell containing the up, dn faces
      auto cell_faces = std::vector<int>{up_faces[c], dn_faces[c]};
      int my_c = cell2face.size();

      // if this is the top cell, put it into the surface side set
      if (side_sets[1].first[c] < 0) side_sets[1].first[c] = my_c;
      // put this cell into the bottom side set -- will be overwritten if any lower
      side_sets[0].first[c] = my_c;

      // add faces for the sides as needed
      for (auto sf : m->cell2face[c]) {
        int my_f = this_layer_sides[sf];

        if (std::any_of(m->face2node[sf].begin(), m->face2node[sf].end(),
                        node_differs)) {
          if (my_f < 0) {
            // may need to create the face
            my_f = face2node.size();
            auto side_nodes = std::vector<int>{ up_nodes[m->face2node[sf][0]],
                                                up_nodes[m->face2node[sf][1]] };
            if (node_differs(m->face2node[sf][1])) side_nodes.push_back(dn_nodes[m->face2node[sf][1]]);
            if (node_differs(m->face2node[sf][0])) side_nodes.push_back(dn_nodes[m->face2node[sf][0]]);
            face2node.emplace_back(std::move(side_nodes));
            cell_faces.push_back(my_f);
            this_layer_sides[sf] = my_f;

            // check if this is a boundary side, and add it to the side_set if so
            if (m->side_face_counts[sf] == 1) {
              side_sets[2].first.push_back(my_c);
              side_sets[2].second.push_back(cell_faces.size() - 1);
            }

          } else {
            // no need to create the face, but the cell still needs to know it
            cell_faces.push_back(my_f);
          }

        }
      }

      // finally add the cell, including a block id
      cell2face.emplace_back(std::move(cell_faces));
      block_ids.push_back(block_ids_[c]);
    }
  }

  // increment the layer metadata
  current_layer++;
  up_nodes = dn_nodes;
  up_faces = dn_faces;

  ASSERT(block_ids.size() == cell2face.size());
  std::cout << "POST-Extruding: currently " << cell2face.size() << " cells and " << face2node.size() << " faces." << std::endl;

}

void
Mesh3D::finish() {
  // flip the bottom faces for proper outward orientation
  for (auto f : dn_faces)
    std::reverse(face2node[f].begin(), face2node[f].end());

  // move the 2d cell sets to face sets on the surface
  std::set<int> set_ids;
  for (auto& part : m->cell_sets) {
    set_ids.insert(part.begin(), part.end());
  }

  for (int sid : set_ids) {
    std::vector<int> set_cells;
    for (auto& part : m->cell_sets) {
      for (int c=0; c!=part.size(); ++c) {
        if (part[c] == sid) {
          set_cells.push_back(side_sets[1].first[c]);
        }
      }
    }
    std::vector<int> set_faces(set_cells.size(), 0);
    side_sets.emplace_back(std::make_pair(std::move(set_cells),
            std::move(set_faces)));
    side_sets_id.push_back(sid);
  }

  // check side sets
  std::vector<int> side_face_counts(face2node.size(), 0);
  for (auto& c : cell2face)
    for (auto& f : c)
      side_face_counts[f]++;

  for (int lcv_s=0; lcv_s!=side_sets.size(); ++lcv_s) {
    auto& fs = side_sets[lcv_s];
    for (int i=0; i!=side_sets[lcv_s].first.size(); ++i) {
      int c = fs.first[i];
      int fi = fs.second[i];
      int f = cell2face[c][fi];
      if (side_face_counts[f] != 1) {
        std::cout << "Face Set " << side_sets_id[lcv_s] << ": face = " << f << " (" << c << "," << fi << ") has been counted " << side_face_counts[f] << " times (should be 1)!" << std::endl;
      }
    }
  }
}

}
}
<commit_msg>extra assertions for error checking<commit_after>#include <set>
#include <algorithm>
#include <numeric>

#include "Mesh2D.hh"
#include "Mesh3D.hh"

namespace Amanzi {
namespace AmanziGeometry {

Mesh3D::Mesh3D(const Mesh2D * const m_, int n_layers) :
    m(m_),
    current_layer(0),
    total_layers(n_layers),
    datum(m_->datum)
{
  // reserve/allocate space
  Point d(3);
  int n_nodes = m->nnodes*(n_layers+1);
  coords.reserve(n_nodes);

  int n_cells = n_layers * m->ncells;
  cell2face.reserve(n_cells);

  int n_faces = n_layers * m->nfaces
      + (n_layers+1)*m->ncells;
  face2node.reserve(n_faces);

  // copy the top surface coords
  coords.insert(coords.end(), m->coords.begin(), m->coords.end());

  // create the top layer of faces
  face2node.insert(face2node.end(), m->cell2node.begin(),
                   m->cell2node.end());
  up_faces.resize(face2node.size());
  std::iota(up_faces.begin(), up_faces.end(), 0);
  up_nodes.resize(coords.size());
  std::iota(up_nodes.begin(), up_nodes.end(), 0);
  dn_nodes = up_nodes;
  dn_faces = up_faces;

  // create the "bottom" sideset
  std::vector<int> bottom_c(m->ncells, -1);
  std::vector<int> bottom_f(m->ncells, 1);
  side_sets.emplace_back(std::make_pair(bottom_c,bottom_f));
  side_sets_id.push_back(1);

  // create the "surface" sideset
  side_sets.emplace_back(std::piecewise_construct,
                         std::forward_as_tuple(m->ncells, -1),
                         std::forward_as_tuple(m->ncells, 0));
  side_sets_id.push_back(2);

  // create an empty "sides" sideset
  side_sets.emplace_back(std::make_pair(std::vector<int>(), std::vector<int>()));
  side_sets_id.push_back(3);
}

void
Mesh3D::extrude(const std::vector<double>& dz,
                const std::vector<int>& block_ids_) {
  ASSERT(dz.size() == m->coords.size());
  ASSERT(block_ids_.size() == m->cell2node.size());

  auto this_layer_sides = std::vector<int>(m->face2node.size(), -1);
  auto node_differs = [this](int n) {return this->dn_nodes[n] != this->up_nodes[n];};
  auto node_same_horiz = [this](int n) {return coords[this->dn_nodes[n]][0] == coords[this->up_nodes[n]][0]
                                        && coords[this->dn_nodes[n]][1] == coords[this->up_nodes[n]][1];};

  // shift the up-node coordinates by dz
  for (int n=0; n!=dz.size(); ++n) {
    if (dz[n] > 0.) {
      Point nc(coords[up_nodes[n]]);
      nc[2] -= dz[n];
      coords.emplace_back(std::move(nc));
      dn_nodes[n] = coords.size()-1;
    }
  }

  // add cells, faces
  for (int c=0; c!=m->ncells; ++c) {
    if (std::any_of(m->cell2node[c].begin(), m->cell2node[c].end(),
                    node_differs)) {
      // add the bottom face
      std::vector<int> dn_face;
      for (auto n : m->cell2node[c]) dn_face.emplace_back(dn_nodes[n]);
      int my_dn_f = face2node.size();
      face2node.emplace_back(std::move(dn_face));
      dn_faces[c] = my_dn_f;

      // push back a cell containing the up, dn faces
      auto cell_faces = std::vector<int>{up_faces[c], dn_faces[c]};
      int my_c = cell2face.size();

      // if this is the top cell, put it into the surface side set
      if (side_sets[1].first[c] < 0) side_sets[1].first[c] = my_c;
      // put this cell into the bottom side set -- will be overwritten if any lower
      side_sets[0].first[c] = my_c;

      // add faces for the sides as needed
      for (auto sf : m->cell2face[c]) {
        int my_f = this_layer_sides[sf];

        ASSERT(node_same_horiz(m->face2node[sf][0]));
        ASSERT(node_same_horiz(m->face2node[sf][1]));

        if (std::any_of(m->face2node[sf].begin(), m->face2node[sf].end(),
                        node_differs)) {
          if (my_f < 0) {
            // may need to create the face
            my_f = face2node.size();
            auto side_nodes = std::vector<int>{ up_nodes[m->face2node[sf][0]],
                                                up_nodes[m->face2node[sf][1]] };
            if (node_differs(m->face2node[sf][1])) side_nodes.push_back(dn_nodes[m->face2node[sf][1]]);
            if (node_differs(m->face2node[sf][0])) side_nodes.push_back(dn_nodes[m->face2node[sf][0]]);
            face2node.emplace_back(std::move(side_nodes));
            cell_faces.push_back(my_f);
            this_layer_sides[sf] = my_f;

            // check if this is a boundary side, and add it to the side_set if so
            if (m->side_face_counts[sf] == 1) {
              side_sets[2].first.push_back(my_c);
              side_sets[2].second.push_back(cell_faces.size() - 1);
            }

          } else {
            // no need to create the face, but the cell still needs to know it
            cell_faces.push_back(my_f);
          }

        }
      }

      // finally add the cell, including a block id
      cell2face.emplace_back(std::move(cell_faces));
      block_ids.push_back(block_ids_[c]);
    }
  }

  // increment the layer metadata
  current_layer++;
  up_nodes = dn_nodes;
  up_faces = dn_faces;

  ASSERT(block_ids.size() == cell2face.size());
  std::cout << "POST-Extruding: currently " << cell2face.size() << " cells and " << face2node.size() << " faces." << std::endl;

}

void
Mesh3D::finish() {
  // flip the bottom faces for proper outward orientation
  for (auto f : dn_faces)
    std::reverse(face2node[f].begin(), face2node[f].end());

  // move the 2d cell sets to face sets on the surface
  std::set<int> set_ids;
  for (auto& part : m->cell_sets) {
    set_ids.insert(part.begin(), part.end());
  }

  for (int sid : set_ids) {
    std::vector<int> set_cells;
    for (auto& part : m->cell_sets) {
      for (int c=0; c!=part.size(); ++c) {
        if (part[c] == sid) {
          set_cells.push_back(side_sets[1].first[c]);
        }
      }
    }
    std::vector<int> set_faces(set_cells.size(), 0);
    side_sets.emplace_back(std::make_pair(std::move(set_cells),
            std::move(set_faces)));
    side_sets_id.push_back(sid);
  }

  // check side sets
  std::vector<int> side_face_counts(face2node.size(), 0);
  for (auto& c : cell2face)
    for (auto& f : c)
      side_face_counts[f]++;

  for (int lcv_s=0; lcv_s!=side_sets.size(); ++lcv_s) {
    auto& fs = side_sets[lcv_s];
    for (int i=0; i!=side_sets[lcv_s].first.size(); ++i) {
      int c = fs.first[i];
      int fi = fs.second[i];
      int f = cell2face[c][fi];
      if (side_face_counts[f] != 1) {
        std::cout << "Face Set " << side_sets_id[lcv_s] << ": face = " << f << " (" << c << "," << fi << ") has been counted " << side_face_counts[f] << " times (should be 1)!" << std::endl;
      }
    }
  }
}

}
}
<|endoftext|>