<commit_before>/* Copyright 2018 The TensorFlow Authors. 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 "tensorflow/lite/micro/examples/micro_speech/audio_provider.h"

#include <AudioToolbox/AudioToolbox.h>

#include "tensorflow/lite/micro/examples/micro_speech/simple_features/simple_model_settings.h"

namespace {

constexpr int kNumberRecordBuffers = 3;
bool g_is_audio_initialized = false;
constexpr int kAudioCaptureBufferSize = kAudioSampleFrequency * 0.5;
int16_t g_audio_capture_buffer[kAudioCaptureBufferSize];
int16_t g_audio_output_buffer[kMaxAudioSampleSize];
int32_t g_latest_audio_timestamp = 0;

// Checks for MacOS errors, prints information and returns a TF Lite version.
#define RETURN_IF_OS_ERROR(error, error_reporter)                           \
  do {                                                                      \
    if (error != noErr) {                                                   \
      TF_LITE_REPORT_ERROR(error_reporter, "Error: %s:%d (%d)\n", __FILE__, \
                           __LINE__, error);                                \
      return kTfLiteError;                                                  \
    }                                                                       \
  } while (0);

// Called when an audio input buffer has been filled.
void OnAudioBufferFilledCallback(
    void* user_data, AudioQueueRef queue, AudioQueueBufferRef buffer,
    const AudioTimeStamp* start_time, UInt32 num_packets,
    const AudioStreamPacketDescription* packet_description) {
  const int sample_size = buffer->mAudioDataByteSize / sizeof(float);
  const int64_t sample_offset = start_time->mSampleTime;
  const int32_t time_in_ms =
      (sample_offset + sample_size) / (kAudioSampleFrequency / 1000);
  const float* float_samples = static_cast<const float*>(buffer->mAudioData);
  for (int i = 0; i < sample_size; ++i) {
    const int capture_index = (sample_offset + i) % kAudioCaptureBufferSize;
    g_audio_capture_buffer[capture_index] = float_samples[i] * ((1 << 15) - 1);
  }
  // This is how we let the outside world know that new audio data has arrived.
  g_latest_audio_timestamp = time_in_ms;
  AudioQueueEnqueueBuffer(queue, buffer, 0, nullptr);
}

// Set up everything we need to capture audio samples from the default recording
// device on MacOS.
TfLiteStatus InitAudioRecording(tflite::ErrorReporter* error_reporter) {
  // Set up the format of the audio - single channel, 32-bit float at 16KHz.
  AudioStreamBasicDescription recordFormat = { };
  recordFormat.mSampleRate = kAudioSampleFrequency;
  recordFormat.mFormatID = kAudioFormatLinearPCM;
  recordFormat.mFormatFlags =
      kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked;
  recordFormat.mBitsPerChannel = 8 * sizeof(float);
  recordFormat.mChannelsPerFrame = 1;
  recordFormat.mBytesPerFrame = sizeof(float) * recordFormat.mChannelsPerFrame;
  recordFormat.mFramesPerPacket = 1;
  recordFormat.mBytesPerPacket =
      recordFormat.mBytesPerFrame * recordFormat.mFramesPerPacket;
  recordFormat.mReserved = 0;

  UInt32 propSize = sizeof(recordFormat);
  RETURN_IF_OS_ERROR(AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0,
                                            NULL, &propSize, &recordFormat),
                     error_reporter);

  // Create a recording queue.
  AudioQueueRef queue;
  RETURN_IF_OS_ERROR(
      AudioQueueNewInput(&recordFormat, OnAudioBufferFilledCallback,
                         error_reporter, nullptr, nullptr, 0, &queue),
      error_reporter);

  // Set up the buffers we'll need.
  int buffer_bytes = 512 * sizeof(float);
  for (int i = 0; i < kNumberRecordBuffers; ++i) {
    AudioQueueBufferRef buffer;
    RETURN_IF_OS_ERROR(AudioQueueAllocateBuffer(queue, buffer_bytes, &buffer),
                       error_reporter);
    RETURN_IF_OS_ERROR(AudioQueueEnqueueBuffer(queue, buffer, 0, nullptr),
                       error_reporter);
  }

  // Start capturing audio.
  RETURN_IF_OS_ERROR(AudioQueueStart(queue, nullptr), error_reporter);

  return kTfLiteOk;
}

}  // namespace

TfLiteStatus GetAudioSamples(tflite::ErrorReporter* error_reporter,
                             int start_ms, int duration_ms,
                             int* audio_samples_size, int16_t** audio_samples) {
  if (!g_is_audio_initialized) {
    TfLiteStatus init_status = InitAudioRecording(error_reporter);
    if (init_status != kTfLiteOk) {
      return init_status;
    }
    for (int i = 0; i < kMaxAudioSampleSize; ++i) {
      g_audio_output_buffer[i] = 0;
    }
    g_is_audio_initialized = true;
  }
  // This should only be called when the main thread notices that the latest
  // audio sample data timestamp has changed, so that there's new data in the
  // capture ring buffer. The ring buffer will eventually wrap around and
  // overwrite the data, but the assumption is that the main thread is checking
  // often enough and the buffer is large enough that this call will be made
  // before that happens.
  const int start_offset = start_ms * (kAudioSampleFrequency / 1000);
  const int duration_sample_count =
      duration_ms * (kAudioSampleFrequency / 1000);
  for (int i = 0; i < duration_sample_count; ++i) {
    const int capture_index = (start_offset + i) % kAudioCaptureBufferSize;
    g_audio_output_buffer[i] = g_audio_capture_buffer[capture_index];
  }

  *audio_samples_size = kMaxAudioSampleSize;
  *audio_samples = g_audio_output_buffer;
  return kTfLiteOk;
}

int32_t LatestAudioTimestamp() { return g_latest_audio_timestamp; }
<commit_msg>fixed syntax<commit_after>/* Copyright 2018 The TensorFlow Authors. 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 "tensorflow/lite/micro/examples/micro_speech/audio_provider.h"

#include <AudioToolbox/AudioToolbox.h>

#include "tensorflow/lite/micro/examples/micro_speech/simple_features/simple_model_settings.h"

namespace {

constexpr int kNumberRecordBuffers = 3;
bool g_is_audio_initialized = false;
constexpr int kAudioCaptureBufferSize = kAudioSampleFrequency * 0.5;
int16_t g_audio_capture_buffer[kAudioCaptureBufferSize];
int16_t g_audio_output_buffer[kMaxAudioSampleSize];
int32_t g_latest_audio_timestamp = 0;

// Checks for MacOS errors, prints information and returns a TF Lite version.
#define RETURN_IF_OS_ERROR(error, error_reporter)                           \
  do {                                                                      \
    if (error != noErr) {                                                   \
      TF_LITE_REPORT_ERROR(error_reporter, "Error: %s:%d (%d)\n", __FILE__, \
                           __LINE__, error);                                \
      return kTfLiteError;                                                  \
    }                                                                       \
  } while (0);

// Called when an audio input buffer has been filled.
void OnAudioBufferFilledCallback(
    void* user_data, AudioQueueRef queue, AudioQueueBufferRef buffer,
    const AudioTimeStamp* start_time, UInt32 num_packets,
    const AudioStreamPacketDescription* packet_description) {
  const int sample_size = buffer->mAudioDataByteSize / sizeof(float);
  const int64_t sample_offset = start_time->mSampleTime;
  const int32_t time_in_ms =
      (sample_offset + sample_size) / (kAudioSampleFrequency / 1000);
  const float* float_samples = static_cast<const float*>(buffer->mAudioData);
  for (int i = 0; i < sample_size; ++i) {
    const int capture_index = (sample_offset + i) % kAudioCaptureBufferSize;
    g_audio_capture_buffer[capture_index] = float_samples[i] * ((1 << 15) - 1);
  }
  // This is how we let the outside world know that new audio data has arrived.
  g_latest_audio_timestamp = time_in_ms;
  AudioQueueEnqueueBuffer(queue, buffer, 0, nullptr);
}

// Set up everything we need to capture audio samples from the default recording
// device on MacOS.
TfLiteStatus InitAudioRecording(tflite::ErrorReporter* error_reporter) {
  // Set up the format of the audio - single channel, 32-bit float at 16KHz.
  AudioStreamBasicDescription recordFormat = {};
  recordFormat.mSampleRate = kAudioSampleFrequency;
  recordFormat.mFormatID = kAudioFormatLinearPCM;
  recordFormat.mFormatFlags =
      kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked;
  recordFormat.mBitsPerChannel = 8 * sizeof(float);
  recordFormat.mChannelsPerFrame = 1;
  recordFormat.mBytesPerFrame = sizeof(float) * recordFormat.mChannelsPerFrame;
  recordFormat.mFramesPerPacket = 1;
  recordFormat.mBytesPerPacket =
      recordFormat.mBytesPerFrame * recordFormat.mFramesPerPacket;
  recordFormat.mReserved = 0;

  UInt32 propSize = sizeof(recordFormat);
  RETURN_IF_OS_ERROR(AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0,
                                            NULL, &propSize, &recordFormat),
                     error_reporter);

  // Create a recording queue.
  AudioQueueRef queue;
  RETURN_IF_OS_ERROR(
      AudioQueueNewInput(&recordFormat, OnAudioBufferFilledCallback,
                         error_reporter, nullptr, nullptr, 0, &queue),
      error_reporter);

  // Set up the buffers we'll need.
  int buffer_bytes = 512 * sizeof(float);
  for (int i = 0; i < kNumberRecordBuffers; ++i) {
    AudioQueueBufferRef buffer;
    RETURN_IF_OS_ERROR(AudioQueueAllocateBuffer(queue, buffer_bytes, &buffer),
                       error_reporter);
    RETURN_IF_OS_ERROR(AudioQueueEnqueueBuffer(queue, buffer, 0, nullptr),
                       error_reporter);
  }

  // Start capturing audio.
  RETURN_IF_OS_ERROR(AudioQueueStart(queue, nullptr), error_reporter);

  return kTfLiteOk;
}

}  // namespace

TfLiteStatus GetAudioSamples(tflite::ErrorReporter* error_reporter,
                             int start_ms, int duration_ms,
                             int* audio_samples_size, int16_t** audio_samples) {
  if (!g_is_audio_initialized) {
    TfLiteStatus init_status = InitAudioRecording(error_reporter);
    if (init_status != kTfLiteOk) {
      return init_status;
    }
    for (int i = 0; i < kMaxAudioSampleSize; ++i) {
      g_audio_output_buffer[i] = 0;
    }
    g_is_audio_initialized = true;
  }
  // This should only be called when the main thread notices that the latest
  // audio sample data timestamp has changed, so that there's new data in the
  // capture ring buffer. The ring buffer will eventually wrap around and
  // overwrite the data, but the assumption is that the main thread is checking
  // often enough and the buffer is large enough that this call will be made
  // before that happens.
  const int start_offset = start_ms * (kAudioSampleFrequency / 1000);
  const int duration_sample_count =
      duration_ms * (kAudioSampleFrequency / 1000);
  for (int i = 0; i < duration_sample_count; ++i) {
    const int capture_index = (start_offset + i) % kAudioCaptureBufferSize;
    g_audio_output_buffer[i] = g_audio_capture_buffer[capture_index];
  }

  *audio_samples_size = kMaxAudioSampleSize;
  *audio_samples = g_audio_output_buffer;
  return kTfLiteOk;
}

int32_t LatestAudioTimestamp() { return g_latest_audio_timestamp; }
<|endoftext|>


<commit_before>#include <vector>

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include "caffe/layers/multiscale_accumulator_loss_layer.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/util/rng.hpp"

namespace caffe {

template <typename Dtype>
MultiscaleAccumulatorLossLayer<Dtype>::MultiscaleAccumulatorLossLayer(const LayerParameter &param)
    : LossLayer<Dtype>(param)
{
    CHECK(param.has_accumulator_loss_param()) << "AccumulatorLossParameter is mandatory!";
    CHECK(param.accumulator_loss_param().has_radius()) << "Radius is mandatory!";
    CHECK(param.accumulator_loss_param().has_circle_ratio()) << "Circle ratio is mandatory!";
    CHECK(param.accumulator_loss_param().has_downsampling()) << "Downsampling is mandatory!";
}

template <typename Dtype>
void MultiscaleAccumulatorLossLayer<Dtype>::LayerSetUp (const vector<Blob<Dtype>*> &bottom,
                                                        const vector<Blob<Dtype>*> &top)
{
    // IMPORTANT: The label blob is first and the accumulators follow

    LossLayer<Dtype>::LayerSetUp(bottom, top);

    const int num_accumulators = bottom.size() - 1;
    int scale = this->layer_param_.accumulator_loss_param().downsampling();

    this->_accumulators.clear();
    this->_diffs.clear();
    this->_scales.clear();
    this->_bb_bounds.clear();

    this->_sigmoid_layers.clear();
    this->_sigmoid_outputs.clear();
    this->_sigmoid_bottom_vecs.clear();
    this->_sigmoid_top_vecs.clear();

    for (int i = 0; i < num_accumulators; ++i)
    {
        this->_accumulators.push_back(std::make_shared<Blob<Dtype>>());
        this->_diffs.push_back(std::make_shared<Blob<Dtype>>());
        // Compute the scales of the accumulators
        this->_scales.push_back(scale);
        // Compute the bounds for bounding boxes, which should be included in this accumulator
        this->_bb_bounds.push_back(this->_computeSizeBounds(i, num_accumulators, scale));
        scale *= 2;

        // Initialize the sigmoid layers - see sigmoid_cross_entropy_loss_layer for details
        this->_sigmoid_layers.emplace_back(new SigmoidLayer<Dtype>(this->layer_param()));
        this->_sigmoid_outputs.emplace_back(new Blob<Dtype>());
        this->_sigmoid_bottom_vecs.emplace_back(); // This creates an empty vector
        this->_sigmoid_bottom_vecs[i].push_back(bottom[i+1]);
        this->_sigmoid_top_vecs.emplace_back(); // This creates an empty vector
        this->_sigmoid_top_vecs[i].push_back(this->_sigmoid_outputs[i].get());
        this->_sigmoid_layers[i]->SetUp(this->_sigmoid_bottom_vecs[i], this->_sigmoid_top_vecs[i]);
    }
}

template <typename Dtype>
void MultiscaleAccumulatorLossLayer<Dtype>::Reshape (const vector<Blob<Dtype>*> &bottom,
                                                     const vector<Blob<Dtype>*> &top)
{
    // IMPORTANT: The label blob is first and the accumulators follow

    LossLayer<Dtype>::Reshape(bottom, top);

    const int num_accumulators = bottom.size() - 1;
    for (int i = 0; i < num_accumulators; ++i)
    {
        this->_accumulators[i]->ReshapeLike(*bottom[i+1]);
        this->_diffs[i]->ReshapeLike(*bottom[i+1]);

        this->_sigmoid_layers[i]->Reshape(this->_sigmoid_bottom_vecs[i], this->_sigmoid_top_vecs[i]);

        CHECK_EQ(this->_sigmoid_top_vecs[i][0]->count(), bottom[i+1]->count())
                << "Sigmoid layer must have the same size as accumulator!";
    }
}

template <typename Dtype>
void MultiscaleAccumulatorLossLayer<Dtype>::Forward_cpu (const vector<Blob<Dtype>*> &bottom,
                                                         const vector<Blob<Dtype>*> &top)
{
    // Create the accumulators from the labels
    this->_buildAccumulators(bottom[0]);

    Dtype loss_total  = Dtype(0.0f);
    const int num_acc = bottom.size() - 1;

    // Now it is a simple squared Euclidean distance of the net outputs and the accumulators
    for (int i = 0; i < num_acc; ++i)
    {
        // Compute the sigmoid outputs - they are actually not used here, but in the backward pass to compute
        // the diffs
        this->_sigmoid_bottom_vecs[i][0] = bottom[i+1];
        this->_sigmoid_layers[i]->Forward(this->_sigmoid_bottom_vecs[i], this->_sigmoid_top_vecs[i]);

        const int count = bottom[i+1]->count();
        Dtype loss      = Dtype(0.0f);

        // Compute the loss (negative log likelihood)
        const Dtype* output = bottom[i+1]->cpu_data();
        const Dtype* target = this->_accumulators[i]->cpu_data();
        for (int j = 0; j < count; ++j)
        {
            loss -= output[j] * (target[j] - (output[j] >= 0)) -
                    log(1 + exp(output[j] - 2 * output[j] * (output[j] >= 0)));
        }

        loss_total += loss / count;
    }

    // Write the loss to the output blob
    top[0]->mutable_cpu_data()[0] = loss_total / num_acc;
}

template <typename Dtype>
void MultiscaleAccumulatorLossLayer<Dtype>::Backward_cpu (const vector<Blob<Dtype>*> &top,
                                                          const vector<bool> &propagate_down,
                                                          const vector<Blob<Dtype>*> &bottom)
{
    const int num_acc = bottom.size() - 1;
    for (int i = 0; i < num_acc; ++i)
    {
        // Fill in the diff for each output accumulator
        if (propagate_down[i+1])
        {
            const int count = bottom[i+1]->count();

            caffe_sub(count, this->_sigmoid_outputs[i]->cpu_data(), this->_accumulators[i]->cpu_data(),
                      bottom[i+1]->mutable_cpu_diff());

            // Apply weight on the diffs to compensate for the smaller amount of positive pixels in
            // the target accumulator
            this->_applyDiffWeights(i, bottom[i+1]);

            // Scale the gradient
            const Dtype loss_weight = top[0]->cpu_diff()[0] / count;
            caffe_scal(count, loss_weight, bottom[i+1]->mutable_cpu_diff());
        }
    }
}

// -----------------------------------------  PROTECTED METHODS  ----------------------------------------- //

template <typename Dtype>
std::pair<float, float>
MultiscaleAccumulatorLossLayer<Dtype>::_computeSizeBounds (const int i, const int num_accumulators,
                                                           const int scale) const
{
    // The bounding boxes that should be detected by a given accumulator are determined by their size. Each
    // accumulator detects bounding boxes with size in some interval <min bound, max bound>. Here we compute
    // these bounds. These bounds are also not sharp, but they overlap so some objects will be detected by
    // multiple accumulators

    const int radius = this->layer_param_.accumulator_loss_param().radius();
    const float cr   = this->layer_param_.accumulator_loss_param().circle_ratio();
    const float bo   = this->layer_param_.accumulator_loss_param().bounds_overlap();

    auto bounds = std::make_pair(0.0f, 99999.9f);

    // The "ideal" size of a bounding box that should be detected by this accumulator
    const float ideal_size = float(2*radius+1) / cr * scale;

    if (i < num_accumulators-1 && num_accumulators != 1)
    {
        // Everything but the last accumulator will have a bound above
        const float ext_above  = ((1-bo)*ideal_size)/2 + bo*ideal_size;
        bounds.second = ideal_size + ext_above;
    }
    if (i > 0 && num_accumulators != 1)
    {
        // Everything but the first accumulator will have a bound below
        const float difference = ideal_size / 2.0f;
        const float ext_below  = ((1-bo)*difference)/2 + bo*difference;
        bounds.first = ideal_size - ext_below;
    }

    LOG(INFO) << "[" << i << "] Scale: x" << scale << ", bbs: " << bounds.first << " to "
              << bounds.second << " px, ideal: " << ideal_size << " px";

    return bounds;
}

template <typename Dtype>
void MultiscaleAccumulatorLossLayer<Dtype>::_buildAccumulators (const Blob<Dtype> *labels)
{
    // This is because of the cv::Mat type - I don't want to program other types now except CV_32FC1
    CHECK((std::is_same<Dtype,float>::value)) << "MultiscaleAccumulatorLossLayer supports only float!";

    // Radius of the circle drawn in the center of the bounding box
    const int radius = this->layer_param_.accumulator_loss_param().radius();

    // Fill accumulators of all scales
    for (int a = 0; a < this->_accumulators.size(); ++a)
    {
        std::shared_ptr<Blob<Dtype>> accumulator = this->_accumulators[a];

        const int height = accumulator->shape(2);
        const int width  = accumulator->shape(3);

        Dtype *accumulator_data = accumulator->mutable_cpu_data();

        const double scaling_ratio = 1.0 / this->_scales[a];

        // Go through all images on the output and for each of them create accumulators
        for (int b = 0; b < labels->shape(0); ++b)
        {
            // Create a cv::Mat wrapper for the accumulator - this way we can now use OpenCV drawing functions
            // to create circles in the accumulator
            cv::Mat acc(height, width, CV_32FC1, accumulator_data + accumulator->offset(b));
            acc.setTo(cv::Scalar(0));

            // Draw circles in the center of the bounding boxes
            for (int i = 0; i < labels->shape(1); ++i)
            {
                // Data are stored like this [label, xmin, ymin, xmax, ymax]
                const Dtype *data = labels->cpu_data() + labels->offset(b, i);

                // If everything is 0, there are no more bounding boxes
                if (data[0] == 0 && data[1] == 0 && data[2] == 0 && data[3] == 0 && data[4] == 0) break;

                // Check if the size of the bounding box fits within the bounds of this accumulator - the
                // largest dimension of the bounding box has to by within the bounds
                Dtype largest_dim = std::max(data[3]-data[1], data[4]-data[2]);
                if (largest_dim > this->_bb_bounds[a].first && largest_dim < this->_bb_bounds[a].second)
                {
                    cv::circle(acc, cv::Point(scaling_ratio*(data[1]+data[3])/2,
                                              scaling_ratio*(data[2]+data[4])/2), radius, cv::Scalar(1), -1);
                }
            }
        }
    }
}

template <typename Dtype>
void MultiscaleAccumulatorLossLayer<Dtype>::_applyDiffWeights (int i, Blob<Dtype> *bottom)
{
    // Applies weights on the diffs in order to even the impact of the positive and negative samples on
    // the gradient

    const std::shared_ptr<Blob<Dtype>> accumulator = this->_accumulators[i];
    CHECK_EQ(accumulator->count(), bottom->count()) << "Accumulator and diff size is not the same!";

    Dtype* data_diff              = bottom->mutable_cpu_data();
    const Dtype* data_accumulator = accumulator->cpu_data();

    const float pn = caffe_cpu_asum(accumulator->count(), accumulator->cpu_data()) / bottom->shape(0);
    const float nr = this->layer_param().accumulator_loss_param().negative_ratio();
    float neg_diff_weight = 1.0f / (accumulator->count() / nr / bottom->shape(0) / pn);

    for (int j = 0; j < accumulator->count(); ++j)
    {
        if (*data_accumulator == Dtype(0.0f))
        {
            *data_diff *= neg_diff_weight;
        }

        data_diff++;
        data_accumulator++;
    }
}

// ----------------------------------------  LAYER INSTANTIATION  ---------------------------------------- //

#ifdef CPU_ONLY
//STUB_GPU(MultiscaleAccumulatorLossLayer);
#endif

INSTANTIATE_CLASS(MultiscaleAccumulatorLossLayer);
REGISTER_LAYER_CLASS(MultiscaleAccumulatorLoss);

}  // namespace caffe
<commit_msg>Reduced scaling of the gradient - the net had problems to learn because the gradient was too small<commit_after>#include <vector>

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include "caffe/layers/multiscale_accumulator_loss_layer.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/util/rng.hpp"

namespace caffe {

template <typename Dtype>
MultiscaleAccumulatorLossLayer<Dtype>::MultiscaleAccumulatorLossLayer(const LayerParameter &param)
    : LossLayer<Dtype>(param)
{
    CHECK(param.has_accumulator_loss_param()) << "AccumulatorLossParameter is mandatory!";
    CHECK(param.accumulator_loss_param().has_radius()) << "Radius is mandatory!";
    CHECK(param.accumulator_loss_param().has_circle_ratio()) << "Circle ratio is mandatory!";
    CHECK(param.accumulator_loss_param().has_downsampling()) << "Downsampling is mandatory!";
}

template <typename Dtype>
void MultiscaleAccumulatorLossLayer<Dtype>::LayerSetUp (const vector<Blob<Dtype>*> &bottom,
                                                        const vector<Blob<Dtype>*> &top)
{
    // IMPORTANT: The label blob is first and the accumulators follow

    LossLayer<Dtype>::LayerSetUp(bottom, top);

    const int num_accumulators = bottom.size() - 1;
    int scale = this->layer_param_.accumulator_loss_param().downsampling();

    this->_accumulators.clear();
    this->_diffs.clear();
    this->_scales.clear();
    this->_bb_bounds.clear();

    this->_sigmoid_layers.clear();
    this->_sigmoid_outputs.clear();
    this->_sigmoid_bottom_vecs.clear();
    this->_sigmoid_top_vecs.clear();

    for (int i = 0; i < num_accumulators; ++i)
    {
        this->_accumulators.push_back(std::make_shared<Blob<Dtype>>());
        this->_diffs.push_back(std::make_shared<Blob<Dtype>>());
        // Compute the scales of the accumulators
        this->_scales.push_back(scale);
        // Compute the bounds for bounding boxes, which should be included in this accumulator
        this->_bb_bounds.push_back(this->_computeSizeBounds(i, num_accumulators, scale));
        scale *= 2;

        // Initialize the sigmoid layers - see sigmoid_cross_entropy_loss_layer for details
        this->_sigmoid_layers.emplace_back(new SigmoidLayer<Dtype>(this->layer_param()));
        this->_sigmoid_outputs.emplace_back(new Blob<Dtype>());
        this->_sigmoid_bottom_vecs.emplace_back(); // This creates an empty vector
        this->_sigmoid_bottom_vecs[i].push_back(bottom[i+1]);
        this->_sigmoid_top_vecs.emplace_back(); // This creates an empty vector
        this->_sigmoid_top_vecs[i].push_back(this->_sigmoid_outputs[i].get());
        this->_sigmoid_layers[i]->SetUp(this->_sigmoid_bottom_vecs[i], this->_sigmoid_top_vecs[i]);
    }
}

template <typename Dtype>
void MultiscaleAccumulatorLossLayer<Dtype>::Reshape (const vector<Blob<Dtype>*> &bottom,
                                                     const vector<Blob<Dtype>*> &top)
{
    // IMPORTANT: The label blob is first and the accumulators follow

    LossLayer<Dtype>::Reshape(bottom, top);

    const int num_accumulators = bottom.size() - 1;
    for (int i = 0; i < num_accumulators; ++i)
    {
        this->_accumulators[i]->ReshapeLike(*bottom[i+1]);
        this->_diffs[i]->ReshapeLike(*bottom[i+1]);

        this->_sigmoid_layers[i]->Reshape(this->_sigmoid_bottom_vecs[i], this->_sigmoid_top_vecs[i]);

        CHECK_EQ(this->_sigmoid_top_vecs[i][0]->count(), bottom[i+1]->count())
                << "Sigmoid layer must have the same size as accumulator!";
    }
}

template <typename Dtype>
void MultiscaleAccumulatorLossLayer<Dtype>::Forward_cpu (const vector<Blob<Dtype>*> &bottom,
                                                         const vector<Blob<Dtype>*> &top)
{
    // Create the accumulators from the labels
    this->_buildAccumulators(bottom[0]);

    Dtype loss_total  = Dtype(0.0f);
    const int num_acc = bottom.size() - 1;

    // Now it is a simple squared Euclidean distance of the net outputs and the accumulators
    for (int i = 0; i < num_acc; ++i)
    {
        // Compute the sigmoid outputs - they are actually not used here, but in the backward pass to compute
        // the diffs
        this->_sigmoid_bottom_vecs[i][0] = bottom[i+1];
        this->_sigmoid_layers[i]->Forward(this->_sigmoid_bottom_vecs[i], this->_sigmoid_top_vecs[i]);

        const int count = bottom[i+1]->count();
        Dtype loss      = Dtype(0.0f);

        // Compute the loss (negative log likelihood)
        const Dtype* output = bottom[i+1]->cpu_data();
        const Dtype* target = this->_accumulators[i]->cpu_data();
        for (int j = 0; j < count; ++j)
        {
            loss -= output[j] * (target[j] - (output[j] >= 0)) -
                    log(1 + exp(output[j] - 2 * output[j] * (output[j] >= 0)));
        }

        loss_total += loss / count;
    }

    // Write the loss to the output blob
    top[0]->mutable_cpu_data()[0] = loss_total / num_acc;
}

template <typename Dtype>
void MultiscaleAccumulatorLossLayer<Dtype>::Backward_cpu (const vector<Blob<Dtype>*> &top,
                                                          const vector<bool> &propagate_down,
                                                          const vector<Blob<Dtype>*> &bottom)
{
    const int num_acc = bottom.size() - 1;
    for (int i = 0; i < num_acc; ++i)
    {
        // Fill in the diff for each output accumulator
        if (propagate_down[i+1])
        {
            const int count = bottom[i+1]->count();

            caffe_sub(count, this->_sigmoid_outputs[i]->cpu_data(), this->_accumulators[i]->cpu_data(),
                      bottom[i+1]->mutable_cpu_diff());

            // Apply weight on the diffs to compensate for the smaller amount of positive pixels in
            // the target accumulator
            this->_applyDiffWeights(i, bottom[i+1]);

            // Scale the gradient
            // CAREFUL HERE! We cannot scale it too much otherwise the net will basically have such small
            // gradients that it won't learn anything!
            const Dtype loss_weight = top[0]->cpu_diff()[0] / (count / bottom[i+1]->shape(0));
            caffe_scal(count, loss_weight, bottom[i+1]->mutable_cpu_diff());
        }
    }
}

// -----------------------------------------  PROTECTED METHODS  ----------------------------------------- //

template <typename Dtype>
std::pair<float, float>
MultiscaleAccumulatorLossLayer<Dtype>::_computeSizeBounds (const int i, const int num_accumulators,
                                                           const int scale) const
{
    // The bounding boxes that should be detected by a given accumulator are determined by their size. Each
    // accumulator detects bounding boxes with size in some interval <min bound, max bound>. Here we compute
    // these bounds. These bounds are also not sharp, but they overlap so some objects will be detected by
    // multiple accumulators

    const int radius = this->layer_param_.accumulator_loss_param().radius();
    const float cr   = this->layer_param_.accumulator_loss_param().circle_ratio();
    const float bo   = this->layer_param_.accumulator_loss_param().bounds_overlap();

    auto bounds = std::make_pair(0.0f, 99999.9f);

    // The "ideal" size of a bounding box that should be detected by this accumulator
    const float ideal_size = float(2*radius+1) / cr * scale;

    if (i < num_accumulators-1 && num_accumulators != 1)
    {
        // Everything but the last accumulator will have a bound above
        const float ext_above  = ((1-bo)*ideal_size)/2 + bo*ideal_size;
        bounds.second = ideal_size + ext_above;
    }
    if (i > 0 && num_accumulators != 1)
    {
        // Everything but the first accumulator will have a bound below
        const float difference = ideal_size / 2.0f;
        const float ext_below  = ((1-bo)*difference)/2 + bo*difference;
        bounds.first = ideal_size - ext_below;
    }

    LOG(INFO) << "[" << i << "] Scale: x" << scale << ", bbs: " << bounds.first << " to "
              << bounds.second << " px, ideal: " << ideal_size << " px";

    return bounds;
}

template <typename Dtype>
void MultiscaleAccumulatorLossLayer<Dtype>::_buildAccumulators (const Blob<Dtype> *labels)
{
    // This is because of the cv::Mat type - I don't want to program other types now except CV_32FC1
    CHECK((std::is_same<Dtype,float>::value)) << "MultiscaleAccumulatorLossLayer supports only float!";

    // Radius of the circle drawn in the center of the bounding box
    const int radius = this->layer_param_.accumulator_loss_param().radius();

    // Fill accumulators of all scales
    for (int a = 0; a < this->_accumulators.size(); ++a)
    {
        std::shared_ptr<Blob<Dtype>> accumulator = this->_accumulators[a];

        const int height = accumulator->shape(2);
        const int width  = accumulator->shape(3);

        Dtype *accumulator_data = accumulator->mutable_cpu_data();

        const double scaling_ratio = 1.0 / this->_scales[a];

        // Go through all images on the output and for each of them create accumulators
        for (int b = 0; b < labels->shape(0); ++b)
        {
            // Create a cv::Mat wrapper for the accumulator - this way we can now use OpenCV drawing functions
            // to create circles in the accumulator
            cv::Mat acc(height, width, CV_32FC1, accumulator_data + accumulator->offset(b));
            acc.setTo(cv::Scalar(0));

            // Draw circles in the center of the bounding boxes
            for (int i = 0; i < labels->shape(1); ++i)
            {
                // Data are stored like this [label, xmin, ymin, xmax, ymax]
                const Dtype *data = labels->cpu_data() + labels->offset(b, i);

                // If everything is 0, there are no more bounding boxes
                if (data[0] == 0 && data[1] == 0 && data[2] == 0 && data[3] == 0 && data[4] == 0) break;

                // Check if the size of the bounding box fits within the bounds of this accumulator - the
                // largest dimension of the bounding box has to by within the bounds
                Dtype largest_dim = std::max(data[3]-data[1], data[4]-data[2]);
                if (largest_dim > this->_bb_bounds[a].first && largest_dim < this->_bb_bounds[a].second)
                {
                    cv::circle(acc, cv::Point(scaling_ratio*(data[1]+data[3])/2,
                                              scaling_ratio*(data[2]+data[4])/2), radius, cv::Scalar(1), -1);
                }
            }
        }
    }
}

template <typename Dtype>
void MultiscaleAccumulatorLossLayer<Dtype>::_applyDiffWeights (int i, Blob<Dtype> *bottom)
{
    // Applies weights on the diffs in order to even the impact of the positive and negative samples on
    // the gradient

    const std::shared_ptr<Blob<Dtype>> accumulator = this->_accumulators[i];
    CHECK_EQ(accumulator->count(), bottom->count()) << "Accumulator and diff size is not the same!";

    Dtype* data_diff              = bottom->mutable_cpu_data();
    const Dtype* data_accumulator = accumulator->cpu_data();

    const float pn = caffe_cpu_asum(accumulator->count(), accumulator->cpu_data()) / bottom->shape(0);
    const float nr = this->layer_param().accumulator_loss_param().negative_ratio();
    float pos_diff_weight = accumulator->count() / nr / bottom->shape(0) / pn;

    for (int j = 0; j < accumulator->count(); ++j)
    {
        if (*data_accumulator > Dtype(0.0f))
        {
            *data_diff *= pos_diff_weight;
        }

        data_diff++;
        data_accumulator++;
    }
}

// ----------------------------------------  LAYER INSTANTIATION  ---------------------------------------- //

#ifdef CPU_ONLY
//STUB_GPU(MultiscaleAccumulatorLossLayer);
#endif

INSTANTIATE_CLASS(MultiscaleAccumulatorLossLayer);
REGISTER_LAYER_CLASS(MultiscaleAccumulatorLoss);

}  // namespace caffe
<|endoftext|>


<commit_before>#include "torch/utils.h"
#include "THCl.h"
//#include "THFile.h"
#include "luaT.h"

extern "C" {
  void cltorch_ClStorage_init(lua_State* L);
}

/* everything is as the generic Storage.c, except few things (see below) */

#define real float
#define Real Cl
#define TH_GENERIC_FILE "generic/Storage.c"

#define torch_Storage_(NAME) TH_CONCAT_4(torch_,Real,Storage_,NAME)

#define THFile_readRealRaw(file, data, size)                            \
  {                                                                     \
    float *fdata = (float*)THAlloc(sizeof(float)*size);                 \
    THError("Not implemented"); \
    THFile_readFloatRaw(file, fdata, size);                             \
/*    THClCheck(clMemcpy(data, fdata, size * sizeof(float), clMemcpyHostToDevice));*/ \
    THFree(fdata);                                                      \
  }

#define THFile_writeRealRaw(file, data, size)                           \
  {                                                                     \
    float *fdata = (float*)THAlloc(sizeof(float)*size);                 \
    THError("Not implemented"); \
/*    THClCheck(clMemcpy(fdata, data, size * sizeof(float), clMemcpyDeviceToHost));*/ \
    THFile_writeFloatRaw(file, fdata, size);                            \
    THFree(fdata);                                                      \
  }

#define torch_Storage TH_CONCAT_STRING_3(torch.,Real,Storage)

#include "generic/Storage.cpp"

#undef real
#undef Real
#undef TH_GENERIC_FILE

/* now we overwrite some methods specific to ClStorage */

static int cltorch_ClStorage_copy(lua_State *L)
{
  THClState *state = cltorch_getstate(L);
  THClStorage *storage = static_cast<THClStorage *>(luaT_checkudata(L, 1, "torch.ClStorage"));
  void *src;
  if( (src = luaT_toudata(L, 2, "torch.ClStorage")) )
    THClStorage_copy(state, storage, static_cast<THClStorage *>(src));
  else if( (src = luaT_toudata(L, 2, "torch.ByteStorage")) )
    THClStorage_copyByte(state, storage, static_cast<THByteStorage *>(src));
  else if( (src = luaT_toudata(L, 2, "torch.CharStorage")) )
    THClStorage_copyChar(state, storage, static_cast<THCharStorage *>(src));
  else if( (src = luaT_toudata(L, 2, "torch.ShortStorage")) )
    THClStorage_copyShort(state, storage, static_cast<THShortStorage *>(src));
  else if( (src = luaT_toudata(L, 2, "torch.IntStorage")) )
    THClStorage_copyInt(state, storage, static_cast<THIntStorage *>(src));
  else if( (src = luaT_toudata(L, 2, "torch.LongStorage")) )
    THClStorage_copyLong(state, storage, static_cast<THLongStorage *>(src));
  else if( (src = luaT_toudata(L, 2, "torch.FloatStorage")) )
    THClStorage_copyFloat(state, storage, static_cast<THFloatStorage *>(src));
  else if( (src = luaT_toudata(L, 2, "torch.DoubleStorage")) )
    THClStorage_copyDouble(state, storage, static_cast<THDoubleStorage *>(src));
  else if( (src = luaT_toudata(L, 2, "torch.ClStorage")) )
    THClStorage_copyCl(state, storage, static_cast<THClStorage *>(src));
  else
    luaL_typerror(L, 2, "torch.*Storage");

  lua_settop(L, 1);
  return 1;
}

#define CL_IMPLEMENT_STORAGE_COPY(TYPEC)                              \
  static int cltorch_##TYPEC##Storage_copy(lua_State *L)                \
  {                                                                     \
    TH##TYPEC##Storage *storage = static_cast<TH##TYPEC##Storage *>(luaT_checkudata(L, 1, "torch." #TYPEC "Storage")); \
    void *src;                                                          \
    if( (src = luaT_toudata(L, 2, "torch." #TYPEC "Storage")) )         \
      TH##TYPEC##Storage_copy(storage, static_cast<TH##TYPEC##Storage *>(src));                            \
    else if( (src = luaT_toudata(L, 2, "torch.ByteStorage")) )          \
      TH##TYPEC##Storage_copyByte(storage, static_cast<THByteStorage *>(src));                        \
    else if( (src = luaT_toudata(L, 2, "torch.CharStorage")) )          \
      TH##TYPEC##Storage_copyChar(storage, static_cast<THCharStorage *>(src));                        \
    else if( (src = luaT_toudata(L, 2, "torch.ShortStorage")) )         \
      TH##TYPEC##Storage_copyShort(storage, static_cast<THShortStorage *>(src));                       \
    else if( (src = luaT_toudata(L, 2, "torch.IntStorage")) )           \
      TH##TYPEC##Storage_copyInt(storage, static_cast<THIntStorage *>(src));                         \
    else if( (src = luaT_toudata(L, 2, "torch.LongStorage")) )          \
      TH##TYPEC##Storage_copyLong(storage, static_cast<THLongStorage *>(src));                        \
    else if( (src = luaT_toudata(L, 2, "torch.FloatStorage")) )         \
      TH##TYPEC##Storage_copyFloat(storage, static_cast<THFloatStorage *>(src));                       \
    else if( (src = luaT_toudata(L, 2, "torch.DoubleStorage")) )        \
      TH##TYPEC##Storage_copyDouble(storage, static_cast<THDoubleStorage *>(src));                      \
    else if( (src = luaT_toudata(L, 2, "torch.ClStorage")) )          \
      TH##TYPEC##Storage_copyCl(cltorch_getstate(L), storage, static_cast<THClStorage *>(src));   \
    else                                                                \
      luaL_typerror(L, 2, "torch.*Storage");                            \
                                                                        \
    lua_settop(L, 1);                                                   \
    return 1;                                                           \
}

CL_IMPLEMENT_STORAGE_COPY(Byte)
CL_IMPLEMENT_STORAGE_COPY(Char)
CL_IMPLEMENT_STORAGE_COPY(Short)
CL_IMPLEMENT_STORAGE_COPY(Int)
CL_IMPLEMENT_STORAGE_COPY(Long)
CL_IMPLEMENT_STORAGE_COPY(Float)
CL_IMPLEMENT_STORAGE_COPY(Double)

void cltorch_ClStorage_init(lua_State* L)
{
  /* the standard stuff */
  torch_ClStorage_init(L);

  /* the copy methods */
  {
    int i;

    const char* tnames[8] = {"torch.ByteStorage",
                             "torch.CharStorage",
                             "torch.ShortStorage",
                             "torch.IntStorage",
                             "torch.LongStorage",
                             "torch.FloatStorage",
                             "torch.DoubleStorage",
                             "torch.ClStorage"};

    static int (*funcs[8])(lua_State*) = {cltorch_ByteStorage_copy,
                                          cltorch_CharStorage_copy,
                                          cltorch_ShortStorage_copy,
                                          cltorch_IntStorage_copy,
                                          cltorch_LongStorage_copy,
                                          cltorch_FloatStorage_copy,
                                          cltorch_DoubleStorage_copy,
                                          cltorch_ClStorage_copy};

    for(i = 0; i < 8; i++)
    {
      luaT_pushmetatable(L, tnames[i]);
      lua_pushcfunction(L, funcs[i]);
      lua_setfield(L, -2, "copy");
      lua_pop(L, 1);
    }
  }
}
<commit_msg>convert some more out of memory excptions to torch errors<commit_after>#include "torch/utils.h"
#include "THCl.h"
//#include "THFile.h"
#include "luaT.h"

extern "C" {
  void cltorch_ClStorage_init(lua_State* L);
}

#define EXCEPT_TO_THERROR(method) \
try { \
  method; \
} catch(exception &e) { \
  THError("Something went wrong: %s", e.what()); \
}

/* everything is as the generic Storage.c, except few things (see below) */

#define real float
#define Real Cl
#define TH_GENERIC_FILE "generic/Storage.c"

#define torch_Storage_(NAME) TH_CONCAT_4(torch_,Real,Storage_,NAME)

#define THFile_readRealRaw(file, data, size)                            \
  {                                                                     \
    float *fdata = (float*)THAlloc(sizeof(float)*size);                 \
    THError("Not implemented"); \
    THFile_readFloatRaw(file, fdata, size);                             \
/*    THClCheck(clMemcpy(data, fdata, size * sizeof(float), clMemcpyHostToDevice));*/ \
    THFree(fdata);                                                      \
  }

#define THFile_writeRealRaw(file, data, size)                           \
  {                                                                     \
    float *fdata = (float*)THAlloc(sizeof(float)*size);                 \
    THError("Not implemented"); \
/*    THClCheck(clMemcpy(fdata, data, size * sizeof(float), clMemcpyDeviceToHost));*/ \
    THFile_writeFloatRaw(file, fdata, size);                            \
    THFree(fdata);                                                      \
  }

#define torch_Storage TH_CONCAT_STRING_3(torch.,Real,Storage)

#include "generic/Storage.cpp"

#undef real
#undef Real
#undef TH_GENERIC_FILE

/* now we overwrite some methods specific to ClStorage */

static int cltorch_ClStorage_copy(lua_State *L)
{
  THClState *state = cltorch_getstate(L);
  THClStorage *storage = static_cast<THClStorage *>(luaT_checkudata(L, 1, "torch.ClStorage"));
  void *src;
  if( (src = luaT_toudata(L, 2, "torch.ClStorage")) ) {
    EXCEPT_TO_THERROR(THClStorage_copy(state, storage, static_cast<THClStorage *>(src)));
  } else if( (src = luaT_toudata(L, 2, "torch.ByteStorage")) ) {
    EXCEPT_TO_THERROR(THClStorage_copyByte(state, storage, static_cast<THByteStorage *>(src)));
  } else if( (src = luaT_toudata(L, 2, "torch.CharStorage")) ) {
    EXCEPT_TO_THERROR(THClStorage_copyChar(state, storage, static_cast<THCharStorage *>(src)));
  } else if( (src = luaT_toudata(L, 2, "torch.ShortStorage")) ) {
    EXCEPT_TO_THERROR(THClStorage_copyShort(state, storage, static_cast<THShortStorage *>(src)));
  } else if( (src = luaT_toudata(L, 2, "torch.IntStorage")) ) {
    EXCEPT_TO_THERROR(THClStorage_copyInt(state, storage, static_cast<THIntStorage *>(src)));
  } else if( (src = luaT_toudata(L, 2, "torch.LongStorage")) ) {
    EXCEPT_TO_THERROR(THClStorage_copyLong(state, storage, static_cast<THLongStorage *>(src)));
  } else if( (src = luaT_toudata(L, 2, "torch.FloatStorage")) ) {
    EXCEPT_TO_THERROR(THClStorage_copyFloat(state, storage, static_cast<THFloatStorage *>(src)));
  } else if( (src = luaT_toudata(L, 2, "torch.DoubleStorage")) ) {
    EXCEPT_TO_THERROR(THClStorage_copyDouble(state, storage, static_cast<THDoubleStorage *>(src)));
  } else if( (src = luaT_toudata(L, 2, "torch.ClStorage")) ) {
    EXCEPT_TO_THERROR(THClStorage_copyCl(state, storage, static_cast<THClStorage *>(src)));
  } else
    luaL_typerror(L, 2, "torch.*Storage");

  lua_settop(L, 1);
  return 1;
}

#define CL_IMPLEMENT_STORAGE_COPY(TYPEC)                              \
  static int cltorch_##TYPEC##Storage_copy(lua_State *L)                \
  {                                                                     \
    TH##TYPEC##Storage *storage = static_cast<TH##TYPEC##Storage *>(luaT_checkudata(L, 1, "torch." #TYPEC "Storage")); \
    void *src;                                                          \
    if( (src = luaT_toudata(L, 2, "torch." #TYPEC "Storage")) )         \
      TH##TYPEC##Storage_copy(storage, static_cast<TH##TYPEC##Storage *>(src));                            \
    else if( (src = luaT_toudata(L, 2, "torch.ByteStorage")) )          \
      TH##TYPEC##Storage_copyByte(storage, static_cast<THByteStorage *>(src));                        \
    else if( (src = luaT_toudata(L, 2, "torch.CharStorage")) )          \
      TH##TYPEC##Storage_copyChar(storage, static_cast<THCharStorage *>(src));                        \
    else if( (src = luaT_toudata(L, 2, "torch.ShortStorage")) )         \
      TH##TYPEC##Storage_copyShort(storage, static_cast<THShortStorage *>(src));                       \
    else if( (src = luaT_toudata(L, 2, "torch.IntStorage")) )           \
      TH##TYPEC##Storage_copyInt(storage, static_cast<THIntStorage *>(src));                         \
    else if( (src = luaT_toudata(L, 2, "torch.LongStorage")) )          \
      TH##TYPEC##Storage_copyLong(storage, static_cast<THLongStorage *>(src));                        \
    else if( (src = luaT_toudata(L, 2, "torch.FloatStorage")) )         \
      TH##TYPEC##Storage_copyFloat(storage, static_cast<THFloatStorage *>(src));                       \
    else if( (src = luaT_toudata(L, 2, "torch.DoubleStorage")) )        \
      TH##TYPEC##Storage_copyDouble(storage, static_cast<THDoubleStorage *>(src));                      \
    else if( (src = luaT_toudata(L, 2, "torch.ClStorage")) )          \
      TH##TYPEC##Storage_copyCl(cltorch_getstate(L), storage, static_cast<THClStorage *>(src));   \
    else                                                                \
      luaL_typerror(L, 2, "torch.*Storage");                            \
                                                                        \
    lua_settop(L, 1);                                                   \
    return 1;                                                           \
}

CL_IMPLEMENT_STORAGE_COPY(Byte)
CL_IMPLEMENT_STORAGE_COPY(Char)
CL_IMPLEMENT_STORAGE_COPY(Short)
CL_IMPLEMENT_STORAGE_COPY(Int)
CL_IMPLEMENT_STORAGE_COPY(Long)
CL_IMPLEMENT_STORAGE_COPY(Float)
CL_IMPLEMENT_STORAGE_COPY(Double)

void cltorch_ClStorage_init(lua_State* L)
{
  /* the standard stuff */
  torch_ClStorage_init(L);

  /* the copy methods */
  {
    int i;

    const char* tnames[8] = {"torch.ByteStorage",
                             "torch.CharStorage",
                             "torch.ShortStorage",
                             "torch.IntStorage",
                             "torch.LongStorage",
                             "torch.FloatStorage",
                             "torch.DoubleStorage",
                             "torch.ClStorage"};

    static int (*funcs[8])(lua_State*) = {cltorch_ByteStorage_copy,
                                          cltorch_CharStorage_copy,
                                          cltorch_ShortStorage_copy,
                                          cltorch_IntStorage_copy,
                                          cltorch_LongStorage_copy,
                                          cltorch_FloatStorage_copy,
                                          cltorch_DoubleStorage_copy,
                                          cltorch_ClStorage_copy};

    for(i = 0; i < 8; i++)
    {
      luaT_pushmetatable(L, tnames[i]);
      lua_pushcfunction(L, funcs[i]);
      lua_setfield(L, -2, "copy");
      lua_pop(L, 1);
    }
  }
}
<|endoftext|>


<commit_before>/***********************************************************************
    filename:   CEGUIIrrlichtRenderer.cpp
    created:    Tue Mar 3 2009
    author:     Paul D Turner (parts based on original code by Thomas Suter)
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2009 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#include "CEGUIIrrlichtRenderer.h"
#include "CEGUIIrrlichtGeometryBuffer.h"
#include "CEGUIIrrlichtWindowTarget.h"
#include "CEGUIIrrlichtTextureTarget.h"
#include "CEGUIIrrlichtTexture.h"
#include "CEGUIIrrlichtResourceProvider.h"
#include "CEGUIRenderingRoot.h"
#include "CEGUIIrrlichtEventPusher.h"

#include <irrlicht.h>

// Start of CEGUI namespace section
namespace CEGUI
{
String IrrlichtRenderer::d_rendererID("CEGUI::IrrlichtRenderer "
"- Official Irrlicht based 2nd generation renderer module.");

//----------------------------------------------------------------------------//
IrrlichtRenderer& IrrlichtRenderer::create(irr::IrrlichtDevice& device)
{
    return *new IrrlichtRenderer(device);
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::destroy(IrrlichtRenderer& renderer)
{
    delete &renderer;
}

//----------------------------------------------------------------------------//
IrrlichtResourceProvider&
IrrlichtRenderer::createIrrlichtResourceProvider(irr::io::IFileSystem& fs)
{
    return *new IrrlichtResourceProvider(fs);
}

//----------------------------------------------------------------------------//
void
IrrlichtRenderer::destroyIrrlichtResourceProvider(IrrlichtResourceProvider& rp)
{
    delete &rp;
}

//----------------------------------------------------------------------------//
bool IrrlichtRenderer::injectEvent(const irr::SEvent& event)
{
    return d_eventPusher->OnEvent(event);
}

//----------------------------------------------------------------------------//
RenderingRoot& IrrlichtRenderer::getDefaultRenderingRoot()
{
    return *d_defaultRoot;
}

//----------------------------------------------------------------------------//
GeometryBuffer& IrrlichtRenderer::createGeometryBuffer()
{
    IrrlichtGeometryBuffer* gb =
        new IrrlichtGeometryBuffer(*d_driver);

    d_geometryBuffers.push_back(gb);
    return *gb;
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::destroyGeometryBuffer(const GeometryBuffer& buffer)
{
    GeometryBufferList::iterator i = std::find(d_geometryBuffers.begin(),
                                               d_geometryBuffers.end(),
                                               &buffer);

    if (d_geometryBuffers.end() != i)
    {
        d_geometryBuffers.erase(i);
        delete &buffer;
    }
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::destroyAllGeometryBuffers()
{
    while (!d_geometryBuffers.empty())
        destroyGeometryBuffer(**d_geometryBuffers.begin());
}

//----------------------------------------------------------------------------//
TextureTarget* IrrlichtRenderer::createTextureTarget()
{
    if (!d_driver->queryFeature(irr::video::EVDF_RENDER_TO_TARGET))
        return 0;

    TextureTarget* tt = new IrrlichtTextureTarget(*this, *d_driver);
    d_textureTargets.push_back(tt);
    return tt;
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::destroyTextureTarget(TextureTarget* target)
{
    TextureTargetList::iterator i = std::find(d_textureTargets.begin(),
                                              d_textureTargets.end(),
                                              target);

    if (d_textureTargets.end() != i)
    {
        d_textureTargets.erase(i);
        delete target;
    }
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::destroyAllTextureTargets()
{
    while (!d_textureTargets.empty())
        destroyTextureTarget(*d_textureTargets.begin());
}

//----------------------------------------------------------------------------//
Texture& IrrlichtRenderer::createTexture()
{
    IrrlichtTexture* t = new IrrlichtTexture(*d_driver);
    d_textures.push_back(t);
    return *t;
}

//----------------------------------------------------------------------------//
Texture& IrrlichtRenderer::createTexture(const String& filename,
                                         const String& resourceGroup)
{
    IrrlichtTexture* t = new IrrlichtTexture(*d_driver, filename,
                                             resourceGroup);
    d_textures.push_back(t);
    return *t;
}

//----------------------------------------------------------------------------//
Texture& IrrlichtRenderer::createTexture(const Size& size)
{
    IrrlichtTexture* t = new IrrlichtTexture(*d_driver, size);
    d_textures.push_back(t);
    return *t;
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::destroyTexture(Texture& texture)
{
    TextureList::iterator i = std::find(d_textures.begin(),
                                        d_textures.end(),
                                        &texture);

    if (d_textures.end() != i)
    {
        d_textures.erase(i);
        delete &static_cast<IrrlichtTexture&>(texture);
    }
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::destroyAllTextures()
{
    while (!d_textures.empty())
        destroyTexture(**d_textures.begin());
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::beginRendering()
{
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::endRendering()
{
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::setDisplaySize(const Size& sz)
{
    if (sz != d_displaySize)
    {
        d_displaySize = sz;

        // FIXME: This is probably not the right thing to do in all cases.
        Rect area(d_defaultTarget->getArea());
        area.setSize(sz);
        d_defaultTarget->setArea(area);
    }

}

//----------------------------------------------------------------------------//
const Size& IrrlichtRenderer::getDisplaySize() const
{
    return d_displaySize;
}

//----------------------------------------------------------------------------//
const Vector2& IrrlichtRenderer::getDisplayDPI() const
{
    return d_displayDPI;
}

//----------------------------------------------------------------------------//
uint IrrlichtRenderer::getMaxTextureSize() const
{
    return d_maxTextureSize;
}

//----------------------------------------------------------------------------//
const String& IrrlichtRenderer::getIdentifierString() const
{
    return d_rendererID;
}

//----------------------------------------------------------------------------//
IrrlichtRenderer::IrrlichtRenderer(irr::IrrlichtDevice& device) :
    d_device(device),
    d_driver(d_device.getVideoDriver()),
    d_displaySize(d_driver->getScreenSize().Width,
                  d_driver->getScreenSize().Height),
    d_displayDPI(96, 96),
    d_defaultTarget(new IrrlichtWindowTarget(*this, *d_driver)),
    d_defaultRoot(new RenderingRoot(*d_defaultTarget)),
    d_maxTextureSize(2048),
    d_eventPusher(new IrrlichtEventPusher(d_device.getCursorControl()))
{
}

//----------------------------------------------------------------------------//
IrrlichtRenderer::~IrrlichtRenderer()
{
    destroyAllGeometryBuffers();
    destroyAllTextureTargets();
    destroyAllTextures();

    delete d_eventPusher;
    delete d_defaultRoot;
    delete d_defaultTarget;
}

//----------------------------------------------------------------------------//

} // End of  CEGUI namespace section
<commit_msg>Fix issue with missing <algorithm> header.<commit_after>/***********************************************************************
    filename:   CEGUIIrrlichtRenderer.cpp
    created:    Tue Mar 3 2009
    author:     Paul D Turner (parts based on original code by Thomas Suter)
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2009 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#include "CEGUIIrrlichtRenderer.h"
#include "CEGUIIrrlichtGeometryBuffer.h"
#include "CEGUIIrrlichtWindowTarget.h"
#include "CEGUIIrrlichtTextureTarget.h"
#include "CEGUIIrrlichtTexture.h"
#include "CEGUIIrrlichtResourceProvider.h"
#include "CEGUIRenderingRoot.h"
#include "CEGUIIrrlichtEventPusher.h"

#include <irrlicht.h>
#include <algorithm>

// Start of CEGUI namespace section
namespace CEGUI
{
String IrrlichtRenderer::d_rendererID("CEGUI::IrrlichtRenderer "
"- Official Irrlicht based 2nd generation renderer module.");

//----------------------------------------------------------------------------//
IrrlichtRenderer& IrrlichtRenderer::create(irr::IrrlichtDevice& device)
{
    return *new IrrlichtRenderer(device);
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::destroy(IrrlichtRenderer& renderer)
{
    delete &renderer;
}

//----------------------------------------------------------------------------//
IrrlichtResourceProvider&
IrrlichtRenderer::createIrrlichtResourceProvider(irr::io::IFileSystem& fs)
{
    return *new IrrlichtResourceProvider(fs);
}

//----------------------------------------------------------------------------//
void
IrrlichtRenderer::destroyIrrlichtResourceProvider(IrrlichtResourceProvider& rp)
{
    delete &rp;
}

//----------------------------------------------------------------------------//
bool IrrlichtRenderer::injectEvent(const irr::SEvent& event)
{
    return d_eventPusher->OnEvent(event);
}

//----------------------------------------------------------------------------//
RenderingRoot& IrrlichtRenderer::getDefaultRenderingRoot()
{
    return *d_defaultRoot;
}

//----------------------------------------------------------------------------//
GeometryBuffer& IrrlichtRenderer::createGeometryBuffer()
{
    IrrlichtGeometryBuffer* gb =
        new IrrlichtGeometryBuffer(*d_driver);

    d_geometryBuffers.push_back(gb);
    return *gb;
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::destroyGeometryBuffer(const GeometryBuffer& buffer)
{
    GeometryBufferList::iterator i = std::find(d_geometryBuffers.begin(),
                                               d_geometryBuffers.end(),
                                               &buffer);

    if (d_geometryBuffers.end() != i)
    {
        d_geometryBuffers.erase(i);
        delete &buffer;
    }
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::destroyAllGeometryBuffers()
{
    while (!d_geometryBuffers.empty())
        destroyGeometryBuffer(**d_geometryBuffers.begin());
}

//----------------------------------------------------------------------------//
TextureTarget* IrrlichtRenderer::createTextureTarget()
{
    if (!d_driver->queryFeature(irr::video::EVDF_RENDER_TO_TARGET))
        return 0;

    TextureTarget* tt = new IrrlichtTextureTarget(*this, *d_driver);
    d_textureTargets.push_back(tt);
    return tt;
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::destroyTextureTarget(TextureTarget* target)
{
    TextureTargetList::iterator i = std::find(d_textureTargets.begin(),
                                              d_textureTargets.end(),
                                              target);

    if (d_textureTargets.end() != i)
    {
        d_textureTargets.erase(i);
        delete target;
    }
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::destroyAllTextureTargets()
{
    while (!d_textureTargets.empty())
        destroyTextureTarget(*d_textureTargets.begin());
}

//----------------------------------------------------------------------------//
Texture& IrrlichtRenderer::createTexture()
{
    IrrlichtTexture* t = new IrrlichtTexture(*d_driver);
    d_textures.push_back(t);
    return *t;
}

//----------------------------------------------------------------------------//
Texture& IrrlichtRenderer::createTexture(const String& filename,
                                         const String& resourceGroup)
{
    IrrlichtTexture* t = new IrrlichtTexture(*d_driver, filename,
                                             resourceGroup);
    d_textures.push_back(t);
    return *t;
}

//----------------------------------------------------------------------------//
Texture& IrrlichtRenderer::createTexture(const Size& size)
{
    IrrlichtTexture* t = new IrrlichtTexture(*d_driver, size);
    d_textures.push_back(t);
    return *t;
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::destroyTexture(Texture& texture)
{
    TextureList::iterator i = std::find(d_textures.begin(),
                                        d_textures.end(),
                                        &texture);

    if (d_textures.end() != i)
    {
        d_textures.erase(i);
        delete &static_cast<IrrlichtTexture&>(texture);
    }
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::destroyAllTextures()
{
    while (!d_textures.empty())
        destroyTexture(**d_textures.begin());
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::beginRendering()
{
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::endRendering()
{
}

//----------------------------------------------------------------------------//
void IrrlichtRenderer::setDisplaySize(const Size& sz)
{
    if (sz != d_displaySize)
    {
        d_displaySize = sz;

        // FIXME: This is probably not the right thing to do in all cases.
        Rect area(d_defaultTarget->getArea());
        area.setSize(sz);
        d_defaultTarget->setArea(area);
    }

}

//----------------------------------------------------------------------------//
const Size& IrrlichtRenderer::getDisplaySize() const
{
    return d_displaySize;
}

//----------------------------------------------------------------------------//
const Vector2& IrrlichtRenderer::getDisplayDPI() const
{
    return d_displayDPI;
}

//----------------------------------------------------------------------------//
uint IrrlichtRenderer::getMaxTextureSize() const
{
    return d_maxTextureSize;
}

//----------------------------------------------------------------------------//
const String& IrrlichtRenderer::getIdentifierString() const
{
    return d_rendererID;
}

//----------------------------------------------------------------------------//
IrrlichtRenderer::IrrlichtRenderer(irr::IrrlichtDevice& device) :
    d_device(device),
    d_driver(d_device.getVideoDriver()),
    d_displaySize(d_driver->getScreenSize().Width,
                  d_driver->getScreenSize().Height),
    d_displayDPI(96, 96),
    d_defaultTarget(new IrrlichtWindowTarget(*this, *d_driver)),
    d_defaultRoot(new RenderingRoot(*d_defaultTarget)),
    d_maxTextureSize(2048),
    d_eventPusher(new IrrlichtEventPusher(d_device.getCursorControl()))
{
}

//----------------------------------------------------------------------------//
IrrlichtRenderer::~IrrlichtRenderer()
{
    destroyAllGeometryBuffers();
    destroyAllTextureTargets();
    destroyAllTextures();

    delete d_eventPusher;
    delete d_defaultRoot;
    delete d_defaultTarget;
}

//----------------------------------------------------------------------------//

} // End of  CEGUI namespace section
<|endoftext|>


<commit_before>#include "TaskIcon.h"
#include <cmath>

const UINT icon_id = 100;
const UINT id_exit = 101;

static const wchar_t *const icon_label = L"Xbox Controller Battery Level";
static const wchar_t *const task_class = L"xinput-batlev_task-icon";
static const wchar_t icon_emoji[] = L"🎮";
static std::atomic<size_t> task_refcount = 0;

TaskIcon::TaskIcon()
    : icon_(128, 128, 2)
{
    RegClass();
    hwnd_ = CreateWindowExW(
        0, task_class, icon_label,
        0, 0, 0, 0, 0, nullptr,
        nullptr, hinstance, this
        );

    menu_ = CreatePopupMenu();
    if (!menu_) CheckLastError();

    InsertMenuW(menu_, 0, MF_BYPOSITION, id_exit, L"Exit");

    CheckHR(DWriteCreateFactory(
        DWRITE_FACTORY_TYPE_SHARED,
        __uuidof(*dwrite_),
        (IUnknown **)&dwrite_)
        );

    CheckHR(dwrite_->CreateTextFormat(
        L"Segoe UI Emoji",
        nullptr,
        DWRITE_FONT_WEIGHT_NORMAL,
        DWRITE_FONT_STYLE_NORMAL,
        DWRITE_FONT_STRETCH_NORMAL,
        42,
        L"en-US",
        &font_
        ));

    NOTIFYICONDATAW idata = { sizeof(idata) };
    idata.hWnd = hwnd_;
    idata.uID = icon_id;
    idata.uVersion = NOTIFYICON_VERSION_4;
    idata.uCallbackMessage = WM_TASKICON;
    idata.hIcon = MakeIcon(false);
    idata.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
    wcscpy_s(idata.szTip, icon_label);
    Shell_NotifyIconW(NIM_ADD, &idata);
}

TaskIcon::~TaskIcon()
{
    NOTIFYICONDATAW idata = { sizeof(idata) };
    idata.hWnd = hwnd_;
    idata.uID = icon_id;
    Shell_NotifyIconW(NIM_DELETE, &idata);

    if (menu_)
        DestroyMenu(menu_);
    if (hwnd_)
        DestroyWindow(hwnd_);
    UnregClass();
}

void TaskIcon::MessageLoop()
{
    // Recheck once every 5 seconds
    SetTimer(hwnd_, icon_id, 5 * 1000, nullptr);

    MSG msg;
    while (GetMessageW(&msg, hwnd_, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);

        if (stop_)
        {
            break;
        }
    }
}

void TaskIcon::Stop()
{
    stop_ = true;
}

LRESULT TaskIcon::StaticWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    if (msg == WM_CREATE)
    {
        auto cdata = (CREATESTRUCTW *)lparam;
        SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)cdata->lpCreateParams);
        return 0;
    }

    auto task_ptr = (TaskIcon *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
    if (task_ptr)
    {
        return task_ptr->WndProc(msg, wparam, lparam);
    }
    return DefWindowProcW(hwnd, msg, wparam, lparam);
}

LRESULT TaskIcon::WndProc(UINT msg, WPARAM wparam, LPARAM lparam)
{
    switch (msg)
    {
        case WM_TIMER:
        {
            UpdateIcon();
            return 0;
        }
        case WM_TASKICON:
        {
            if (LOWORD(lparam) == WM_LBUTTONUP || LOWORD(lparam) == WM_RBUTTONUP)
            {
                POINT cpos;
                GetCursorPos(&cpos);
                TrackPopupMenu(menu_, TPM_BOTTOMALIGN | TPM_LEFTALIGN, cpos.x, cpos.y, 0, hwnd_, nullptr);
            }
            return 0;
        }
        case WM_COMMAND:
        {
            if (wparam == id_exit)
            {
                stop_ = true;
            }
            return 0;
        }
    }

    return DefWindowProcW(hwnd_, msg, wparam, lparam);
}

void TaskIcon::UpdateIcon()
{
    XINPUT_BATTERY_INFORMATION info;
    if (XInputGetBatteryInformation(0, BATTERY_DEVTYPE_GAMEPAD, &info) != ERROR_SUCCESS)
    {
        info.BatteryType = BATTERY_TYPE_DISCONNECTED;
    }

    switch (info.BatteryType)
    {
        case BATTERY_TYPE_WIRED:
            batlevel_ = 1;
            nocontroller_ = false;
            unknown_ = false;
            break;

        case BATTERY_TYPE_ALKALINE:
        case BATTERY_TYPE_NIMH:
            switch (info.BatteryLevel)
            {
                case BATTERY_LEVEL_EMPTY:
                    batlevel_ = 0;
                    break;
                case BATTERY_LEVEL_LOW:
                    batlevel_ = 0.3f;
                    break;
                case BATTERY_LEVEL_MEDIUM:
                    batlevel_ = 0.6f;
                    break;
                case BATTERY_LEVEL_FULL:
                    batlevel_ = 1;
                    break;
            }
            nocontroller_ = false;
            unknown_ = false;
            break;

        case BATTERY_TYPE_UNKNOWN:
            batlevel_ = 0;
            nocontroller_ = false;
            unknown_ = true;

        case BATTERY_TYPE_DISCONNECTED:
        default:
            batlevel_ = 0;
            nocontroller_ = true;
            unknown_ = false;
            break;
    }

    NOTIFYICONDATAW idata = { sizeof(idata) };
    idata.hWnd = hwnd_;
    idata.uID = icon_id;
    idata.hIcon = MakeIcon(true);
    idata.uFlags = NIF_ICON;
    Shell_NotifyIconW(NIM_MODIFY, &idata);
}

HICON TaskIcon::MakeIcon(bool redraw)
{
    auto icon = icon_.GetIcon();
    if (!icon || redraw)
    {
        ComPtr<ID2D1RenderTarget> target;
        icon_.Lock(&target);

        target->BeginDraw();
        target->Clear();

        auto size = target->GetSize();
        auto batrect = D2D1::RectF(5, size.height / 2 - 16, size.width - 8, size.height / 2 + 16);
        auto posend = D2D1::RectF(size.width - 8, size.height / 2 - 8, size.width, size.height / 2 + 8);
        auto fillrect = D2D1::RectF(batrect.left + 5, batrect.top + 5, batrect.right - 5, batrect.bottom - 5);
        fillrect.right = (1 - batlevel_) * fillrect.left + batlevel_ * fillrect.right;

        ComPtr<ID2D1SolidColorBrush> brush;
        CheckHR(target->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &brush));

        target->DrawRectangle(batrect, brush, 5);
        target->FillRectangle(posend, brush);
        target->FillRectangle(fillrect, brush);

        target->DrawText(
            icon_emoji,
            ARRAYSIZE(icon_emoji) - 1,
            font_,
            D2D1::RectF(
                size.width / 3,
                size.height / 4.f,
                size.width,
                size.height
                ),
            brush,
            D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT
            );

        if (nocontroller_)
        {
            ComPtr<ID2D1SolidColorBrush> errbrush;
            CheckHR(target->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Red), &errbrush));
            auto center = D2D1::Point2F(16, size.height / 2 + 16);
            auto radiusx = std::sqrtf(2) * 7;
            auto tl = D2D1::Point2F(center.x - radiusx, center.y - radiusx);
            auto tr = D2D1::Point2F(center.x + radiusx, center.y - radiusx);
            auto bl = D2D1::Point2F(center.x - radiusx, center.y + radiusx);
            auto br = D2D1::Point2F(center.x + radiusx, center.y + radiusx);

            target->FillEllipse(
                D2D1::Ellipse(
                    center,
                    16, 16
                    ),
                errbrush
                );
            target->DrawLine(tl, br, brush, 5);
            target->DrawLine(tr, bl, brush, 5);
        }

        CheckHR(target->EndDraw());

        icon_.Unlock();
        icon = icon_.GetIcon();
    }
    return icon;
}

void TaskIcon::RegClass()
{
    if (task_refcount++)
        return;

    WNDCLASSEXW wndc = { sizeof(wndc) };
    wndc.cbWndExtra = sizeof(TaskIcon *);
    wndc.hInstance = hinstance;
    wndc.lpfnWndProc = TaskIcon::StaticWndProc;
    wndc.lpszClassName = task_class;
    RegisterClassExW(&wndc);
}

void TaskIcon::UnregClass()
{
    if (--task_refcount)
        return;

    UnregisterClassW(task_class, hinstance);
}
<commit_msg>Fix context menu<commit_after>#include "TaskIcon.h"
#include <cmath>

const UINT icon_id = 100;
const UINT id_exit = 101;

static const wchar_t *const icon_label = L"Xbox Controller Battery Level";
static const wchar_t *const task_class = L"xinput-batlev_task-icon";
static const wchar_t icon_emoji[] = L"🎮";
static std::atomic<size_t> task_refcount = 0;

TaskIcon::TaskIcon()
    : icon_(128, 128, 2)
{
    RegClass();
    hwnd_ = CreateWindowExW(
        0, task_class, icon_label,
        0, 0, 0, 0, 0, nullptr,
        nullptr, hinstance, this
        );

    menu_ = CreatePopupMenu();
    if (!menu_) CheckLastError();

    MENUINFO menuinfo = { sizeof(menuinfo) };
    menuinfo.fMask = MIM_STYLE;
    menuinfo.dwStyle = MNS_AUTODISMISS;
    SetMenuInfo(menu_, &menuinfo);

    InsertMenuW(menu_, 0, MF_BYPOSITION, id_exit, L"Exit");

    CheckHR(DWriteCreateFactory(
        DWRITE_FACTORY_TYPE_SHARED,
        __uuidof(*dwrite_),
        (IUnknown **)&dwrite_)
        );

    CheckHR(dwrite_->CreateTextFormat(
        L"Segoe UI Emoji",
        nullptr,
        DWRITE_FONT_WEIGHT_NORMAL,
        DWRITE_FONT_STYLE_NORMAL,
        DWRITE_FONT_STRETCH_NORMAL,
        42,
        L"en-US",
        &font_
        ));

    NOTIFYICONDATAW idata = { sizeof(idata) };
    idata.hWnd = hwnd_;
    idata.uID = icon_id;
    idata.uVersion = NOTIFYICON_VERSION_4;
    idata.uCallbackMessage = WM_TASKICON;
    idata.hIcon = MakeIcon(false);
    idata.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
    wcscpy_s(idata.szTip, icon_label);
    Shell_NotifyIconW(NIM_ADD, &idata);
}

TaskIcon::~TaskIcon()
{
    NOTIFYICONDATAW idata = { sizeof(idata) };
    idata.hWnd = hwnd_;
    idata.uID = icon_id;
    Shell_NotifyIconW(NIM_DELETE, &idata);

    if (menu_)
        DestroyMenu(menu_);
    if (hwnd_)
        DestroyWindow(hwnd_);
    UnregClass();
}

void TaskIcon::MessageLoop()
{
    // Recheck once every 5 seconds
    SetTimer(hwnd_, icon_id, 5 * 1000, nullptr);

    MSG msg;
    while (GetMessageW(&msg, hwnd_, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);

        if (stop_)
        {
            break;
        }
    }
}

void TaskIcon::Stop()
{
    stop_ = true;
}

LRESULT TaskIcon::StaticWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    if (msg == WM_CREATE)
    {
        auto cdata = (CREATESTRUCTW *)lparam;
        SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)cdata->lpCreateParams);
        return 0;
    }

    auto task_ptr = (TaskIcon *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
    if (task_ptr)
    {
        return task_ptr->WndProc(msg, wparam, lparam);
    }
    return DefWindowProcW(hwnd, msg, wparam, lparam);
}

LRESULT TaskIcon::WndProc(UINT msg, WPARAM wparam, LPARAM lparam)
{
    switch (msg)
    {
        case WM_TIMER:
        {
            UpdateIcon();
            return 0;
        }
        case WM_TASKICON:
        {
            if (LOWORD(lparam) == WM_RBUTTONUP)
            {
                POINT cpos;
                GetCursorPos(&cpos);

                SetForegroundWindow(hwnd_);
                TrackPopupMenuEx(
                    menu_,
                    TPM_BOTTOMALIGN | TPM_LEFTALIGN,
                    cpos.x, cpos.y,
                    hwnd_,
                    nullptr
                    );
            }
            return 0;
        }
        case WM_COMMAND:
        {
            if (wparam == id_exit)
            {
                stop_ = true;
            }
            return 0;
        }
    }

    return DefWindowProcW(hwnd_, msg, wparam, lparam);
}

void TaskIcon::UpdateIcon()
{
    XINPUT_BATTERY_INFORMATION info;
    if (XInputGetBatteryInformation(0, BATTERY_DEVTYPE_GAMEPAD, &info) != ERROR_SUCCESS)
    {
        info.BatteryType = BATTERY_TYPE_DISCONNECTED;
    }

    switch (info.BatteryType)
    {
        case BATTERY_TYPE_WIRED:
            batlevel_ = 1;
            nocontroller_ = false;
            unknown_ = false;
            break;

        case BATTERY_TYPE_ALKALINE:
        case BATTERY_TYPE_NIMH:
            switch (info.BatteryLevel)
            {
                case BATTERY_LEVEL_EMPTY:
                    batlevel_ = 0;
                    break;
                case BATTERY_LEVEL_LOW:
                    batlevel_ = 0.3f;
                    break;
                case BATTERY_LEVEL_MEDIUM:
                    batlevel_ = 0.6f;
                    break;
                case BATTERY_LEVEL_FULL:
                    batlevel_ = 1;
                    break;
            }
            nocontroller_ = false;
            unknown_ = false;
            break;

        case BATTERY_TYPE_UNKNOWN:
            batlevel_ = 0;
            nocontroller_ = false;
            unknown_ = true;

        case BATTERY_TYPE_DISCONNECTED:
        default:
            batlevel_ = 0;
            nocontroller_ = true;
            unknown_ = false;
            break;
    }

    NOTIFYICONDATAW idata = { sizeof(idata) };
    idata.hWnd = hwnd_;
    idata.uID = icon_id;
    idata.hIcon = MakeIcon(true);
    idata.uFlags = NIF_ICON;
    Shell_NotifyIconW(NIM_MODIFY, &idata);
}

HICON TaskIcon::MakeIcon(bool redraw)
{
    auto icon = icon_.GetIcon();
    if (!icon || redraw)
    {
        ComPtr<ID2D1RenderTarget> target;
        icon_.Lock(&target);

        target->BeginDraw();
        target->Clear();

        auto size = target->GetSize();
        auto batrect = D2D1::RectF(5, size.height / 2 - 16, size.width - 8, size.height / 2 + 16);
        auto posend = D2D1::RectF(size.width - 8, size.height / 2 - 8, size.width, size.height / 2 + 8);
        auto fillrect = D2D1::RectF(batrect.left + 5, batrect.top + 5, batrect.right - 5, batrect.bottom - 5);
        fillrect.right = (1 - batlevel_) * fillrect.left + batlevel_ * fillrect.right;

        ComPtr<ID2D1SolidColorBrush> brush;
        CheckHR(target->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &brush));

        target->DrawRectangle(batrect, brush, 5);
        target->FillRectangle(posend, brush);
        target->FillRectangle(fillrect, brush);

        target->DrawText(
            icon_emoji,
            ARRAYSIZE(icon_emoji) - 1,
            font_,
            D2D1::RectF(
                size.width / 3,
                size.height / 4.f,
                size.width,
                size.height
                ),
            brush,
            D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT
            );

        if (nocontroller_)
        {
            ComPtr<ID2D1SolidColorBrush> errbrush;
            CheckHR(target->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Red), &errbrush));
            auto center = D2D1::Point2F(16, size.height / 2 + 16);
            auto radiusx = std::sqrtf(2) * 7;
            auto tl = D2D1::Point2F(center.x - radiusx, center.y - radiusx);
            auto tr = D2D1::Point2F(center.x + radiusx, center.y - radiusx);
            auto bl = D2D1::Point2F(center.x - radiusx, center.y + radiusx);
            auto br = D2D1::Point2F(center.x + radiusx, center.y + radiusx);

            target->FillEllipse(
                D2D1::Ellipse(
                    center,
                    16, 16
                    ),
                errbrush
                );
            target->DrawLine(tl, br, brush, 5);
            target->DrawLine(tr, bl, brush, 5);
        }

        CheckHR(target->EndDraw());

        icon_.Unlock();
        icon = icon_.GetIcon();
    }
    return icon;
}

void TaskIcon::RegClass()
{
    if (task_refcount++)
        return;

    WNDCLASSEXW wndc = { sizeof(wndc) };
    wndc.cbWndExtra = sizeof(TaskIcon *);
    wndc.hInstance = hinstance;
    wndc.lpfnWndProc = TaskIcon::StaticWndProc;
    wndc.lpszClassName = task_class;
    RegisterClassExW(&wndc);
}

void TaskIcon::UnregClass()
{
    if (--task_refcount)
        return;

    UnregisterClassW(task_class, hinstance);
}
<|endoftext|>


<commit_before>#include <cassert>
#include <chrono>
#include <iostream>
#include <set>
#include <utility> // pair

#include <sdd/sdd.hh>

#include "mc/live.hh"
#include "mc/post.hh"
#include "mc/pre.hh"
#include "mc/work.hh"

namespace pnmc { namespace mc {

namespace chrono = std::chrono;

typedef sdd::conf1 sdd_conf;
typedef sdd::SDD<sdd_conf> SDD;
typedef sdd::homomorphism<sdd_conf> homomorphism;

using sdd::Composition;
using sdd::Fixpoint;
using sdd::Sum;
using sdd::ValuesFunction;

/*------------------------------------------------------------------------------------------------*/

struct mk_order_visitor
  : public boost::static_visitor<std::pair<std::string, sdd::order_builder<sdd_conf>>>
{
  // Place: base case of the recursion, there's no more possible nested hierarchies.
  std::pair<std::string, sdd::order_builder<sdd_conf>>
  operator()(const pn::place* p)
  const noexcept
  {
    return make_pair(p->id, sdd::order_builder<sdd_conf>());
  }

  // Hierarchy.
  std::pair<std::string, sdd::order_builder<sdd_conf>>
  operator()(const pn::module_node& m)
  const noexcept
  {
    sdd::order_builder<sdd_conf> ob;
    for (const auto& h : m.nested)
    {
      const auto res = boost::apply_visitor(mk_order_visitor(), *h);
      ob.push(res.first, res.second);
    }
    return make_pair(m.id , ob);
  }
};

/*------------------------------------------------------------------------------------------------*/

sdd::order<sdd_conf>
mk_order(const conf::pnmc_configuration& conf, const pn::net& net)
{
  if (not conf.order_force_flat and net.modules)
  {
    return sdd::order<sdd_conf>(boost::apply_visitor(mk_order_visitor(), *net.modules).second);
  }
  else
  {
    sdd::order_builder<sdd_conf> ob;
    for (const auto& place : net.places())
    {
      ob.push(place.id);
    }
    return sdd::order<sdd_conf>(ob);
  }
}

/*------------------------------------------------------------------------------------------------*/

SDD
initial_state(const sdd::order<sdd_conf>& order, const pn::net& net)
{
  return SDD(order, [&](const std::string& id)
                        -> sdd::values::flat_set<unsigned int>
                       {
                         return {net.places_by_id().find(id)->marking};
                       });
}

/*------------------------------------------------------------------------------------------------*/

homomorphism
transition_relation( const conf::pnmc_configuration& conf, const sdd::order<sdd_conf>& o
                   , const pn::net& net, boost::dynamic_bitset<>& transitions_bitset)
{
  chrono::time_point<chrono::system_clock> start;
  chrono::time_point<chrono::system_clock> end;
  std::size_t elapsed;

  start = chrono::system_clock::now();
  std::set<homomorphism> operands;
  operands.insert(sdd::Id<sdd_conf>());

  for (const auto& transition : net.transitions())
  {
    homomorphism h_t = sdd::Id<sdd_conf>();
    if (conf.compute_dead_transitions)
    {
      if (not transition.post.empty())
      {
        const auto f = ValuesFunction<sdd_conf>( o, transition.post.begin()->first
                                               , live(transition.index, transitions_bitset));
        h_t = sdd::carrier(o, transition.post.begin()->first, f);
      }
    }
    // post actions.
    for (const auto& arc : transition.post)
    {
      homomorphism f = ValuesFunction<sdd_conf>(o, arc.first, post(arc.second));
      h_t = Composition(h_t, sdd::carrier(o, arc.first, f));
    }
    // pre actions.
    for (const auto& arc : transition.pre)
    {
      homomorphism f = ValuesFunction<sdd_conf>(o, arc.first, pre(arc.second));
      h_t = Composition(h_t, sdd::carrier(o, arc.first, f));
    }

    operands.insert(h_t);
  }
  end = chrono::system_clock::now();
  elapsed = chrono::duration_cast<chrono::seconds>(end-start).count();

  if (conf.show_time)
  {
    std::cout << "Transition relation time: " << elapsed << "s" << std::endl;
  }

  start = chrono::system_clock::now();
  const auto res = sdd::rewrite(Fixpoint(Sum<sdd_conf>(o, operands.cbegin(), operands.cend())), o);
  end = chrono::system_clock::now();
  elapsed = chrono::duration_cast<chrono::seconds>(end-start).count();

  if (conf.show_time)
  {
    std::cout << "Rewrite time: " << elapsed << "s" << std::endl;
  }

  return res;
}

/*------------------------------------------------------------------------------------------------*/

SDD
state_space( const conf::pnmc_configuration& conf, const sdd::order<sdd_conf>& o, SDD m
           , homomorphism h)
{
  chrono::time_point<chrono::system_clock> start = chrono::system_clock::now();
  const auto res = h(o, m);
  chrono::time_point<chrono::system_clock> end = chrono::system_clock::now();
  const std::size_t elapsed = chrono::duration_cast<chrono::seconds>(end-start).count();
  if (conf.show_time)
  {
    std::cout << "State space computation time: " << elapsed << "s" << std::endl;
  }
  return res;
}

/*------------------------------------------------------------------------------------------------*/

void
work(const conf::pnmc_configuration& conf, const pn::net& net)
{
  auto manager = sdd::manager<sdd_conf>::init();

  boost::dynamic_bitset<> transitions_bitset(net.transitions().size());

  const sdd::order<sdd_conf>& o = mk_order(conf, net);
  if (conf.order_show)
  {
    std::cout << o << std::endl;
  }
  const SDD m0 = initial_state(o, net);

  const homomorphism h = transition_relation(conf, o, net, transitions_bitset);
  if (conf.show_relation)
  {
    std::cout << h << std::endl;
  }

  const SDD m = state_space(conf, o, m0, h);

  const auto n = sdd::count_combinations(m);
  std::cout << n.template convert_to<long double>() << " states" << std::endl;

  if (conf.compute_dead_transitions)
  {
    std::deque<std::string> dead_transitions;
    for (std::size_t i = 0; i < net.transitions().size(); ++i)
    {
      if (not transitions_bitset[i])
      {
        dead_transitions.push_back(net.get_transition_by_index(i).id);
      }
    }

    if (not dead_transitions.empty())
    {
      std::cout << dead_transitions.size() << " dead transition(s): ";
      std::copy( dead_transitions.cbegin(), std::prev(dead_transitions.cend())
               , std::ostream_iterator<std::string>(std::cout, ","));
      std::cout << *std::prev(dead_transitions.cend()) << std::endl;
    }
    else
    {
      std::cout << "No dead transitions" << std::endl;
    }
  }

  if (conf.show_hash_tables_stats)
  {
    std::cout << manager << std::endl;
  }
}

/*------------------------------------------------------------------------------------------------*/

}} // namespace pnmc::mc
<commit_msg>Assert that a constructed order is never empty.<commit_after>#include <cassert>
#include <chrono>
#include <iostream>
#include <set>
#include <utility> // pair

#include <sdd/sdd.hh>

#include "mc/live.hh"
#include "mc/post.hh"
#include "mc/pre.hh"
#include "mc/work.hh"

namespace pnmc { namespace mc {

namespace chrono = std::chrono;

typedef sdd::conf1 sdd_conf;
typedef sdd::SDD<sdd_conf> SDD;
typedef sdd::homomorphism<sdd_conf> homomorphism;

using sdd::Composition;
using sdd::Fixpoint;
using sdd::Sum;
using sdd::ValuesFunction;

/*------------------------------------------------------------------------------------------------*/

struct mk_order_visitor
  : public boost::static_visitor<std::pair<std::string, sdd::order_builder<sdd_conf>>>
{
  // Place: base case of the recursion, there's no more possible nested hierarchies.
  std::pair<std::string, sdd::order_builder<sdd_conf>>
  operator()(const pn::place* p)
  const noexcept
  {
    return make_pair(p->id, sdd::order_builder<sdd_conf>());
  }

  // Hierarchy.
  std::pair<std::string, sdd::order_builder<sdd_conf>>
  operator()(const pn::module_node& m)
  const noexcept
  {
    sdd::order_builder<sdd_conf> ob;
    for (const auto& h : m.nested)
    {
      const auto res = boost::apply_visitor(mk_order_visitor(), *h);
      ob.push(res.first, res.second);
    }
    return make_pair(m.id , ob);
  }
};

/*------------------------------------------------------------------------------------------------*/

sdd::order<sdd_conf>
mk_order(const conf::pnmc_configuration& conf, const pn::net& net)
{
  if (not conf.order_force_flat and net.modules)
  {
    return sdd::order<sdd_conf>(boost::apply_visitor(mk_order_visitor(), *net.modules).second);
  }
  else
  {
    sdd::order_builder<sdd_conf> ob;
    for (const auto& place : net.places())
    {
      ob.push(place.id);
    }
    return sdd::order<sdd_conf>(ob);
  }
}

/*------------------------------------------------------------------------------------------------*/

SDD
initial_state(const sdd::order<sdd_conf>& order, const pn::net& net)
{
  return SDD(order, [&](const std::string& id)
                        -> sdd::values::flat_set<unsigned int>
                       {
                         return {net.places_by_id().find(id)->marking};
                       });
}

/*------------------------------------------------------------------------------------------------*/

homomorphism
transition_relation( const conf::pnmc_configuration& conf, const sdd::order<sdd_conf>& o
                   , const pn::net& net, boost::dynamic_bitset<>& transitions_bitset)
{
  chrono::time_point<chrono::system_clock> start;
  chrono::time_point<chrono::system_clock> end;
  std::size_t elapsed;

  start = chrono::system_clock::now();
  std::set<homomorphism> operands;
  operands.insert(sdd::Id<sdd_conf>());

  for (const auto& transition : net.transitions())
  {
    homomorphism h_t = sdd::Id<sdd_conf>();
    if (conf.compute_dead_transitions)
    {
      if (not transition.post.empty())
      {
        const auto f = ValuesFunction<sdd_conf>( o, transition.post.begin()->first
                                               , live(transition.index, transitions_bitset));
        h_t = sdd::carrier(o, transition.post.begin()->first, f);
      }
    }
    // post actions.
    for (const auto& arc : transition.post)
    {
      homomorphism f = ValuesFunction<sdd_conf>(o, arc.first, post(arc.second));
      h_t = Composition(h_t, sdd::carrier(o, arc.first, f));
    }
    // pre actions.
    for (const auto& arc : transition.pre)
    {
      homomorphism f = ValuesFunction<sdd_conf>(o, arc.first, pre(arc.second));
      h_t = Composition(h_t, sdd::carrier(o, arc.first, f));
    }

    operands.insert(h_t);
  }
  end = chrono::system_clock::now();
  elapsed = chrono::duration_cast<chrono::seconds>(end-start).count();

  if (conf.show_time)
  {
    std::cout << "Transition relation time: " << elapsed << "s" << std::endl;
  }

  start = chrono::system_clock::now();
  const auto res = sdd::rewrite(Fixpoint(Sum<sdd_conf>(o, operands.cbegin(), operands.cend())), o);
  end = chrono::system_clock::now();
  elapsed = chrono::duration_cast<chrono::seconds>(end-start).count();

  if (conf.show_time)
  {
    std::cout << "Rewrite time: " << elapsed << "s" << std::endl;
  }

  return res;
}

/*------------------------------------------------------------------------------------------------*/

SDD
state_space( const conf::pnmc_configuration& conf, const sdd::order<sdd_conf>& o, SDD m
           , homomorphism h)
{
  chrono::time_point<chrono::system_clock> start = chrono::system_clock::now();
  const auto res = h(o, m);
  chrono::time_point<chrono::system_clock> end = chrono::system_clock::now();
  const std::size_t elapsed = chrono::duration_cast<chrono::seconds>(end-start).count();
  if (conf.show_time)
  {
    std::cout << "State space computation time: " << elapsed << "s" << std::endl;
  }
  return res;
}

/*------------------------------------------------------------------------------------------------*/

void
work(const conf::pnmc_configuration& conf, const pn::net& net)
{
  auto manager = sdd::manager<sdd_conf>::init();

  boost::dynamic_bitset<> transitions_bitset(net.transitions().size());

  const sdd::order<sdd_conf>& o = mk_order(conf, net);
  assert(not o.empty() && "Empty order");

  if (conf.order_show)
  {
    std::cout << o << std::endl;
  }
  const SDD m0 = initial_state(o, net);

  const homomorphism h = transition_relation(conf, o, net, transitions_bitset);
  if (conf.show_relation)
  {
    std::cout << h << std::endl;
  }

  const SDD m = state_space(conf, o, m0, h);

  const auto n = sdd::count_combinations(m);
  std::cout << n.template convert_to<long double>() << " states" << std::endl;

  if (conf.compute_dead_transitions)
  {
    std::deque<std::string> dead_transitions;
    for (std::size_t i = 0; i < net.transitions().size(); ++i)
    {
      if (not transitions_bitset[i])
      {
        dead_transitions.push_back(net.get_transition_by_index(i).id);
      }
    }

    if (not dead_transitions.empty())
    {
      std::cout << dead_transitions.size() << " dead transition(s): ";
      std::copy( dead_transitions.cbegin(), std::prev(dead_transitions.cend())
               , std::ostream_iterator<std::string>(std::cout, ","));
      std::cout << *std::prev(dead_transitions.cend()) << std::endl;
    }
    else
    {
      std::cout << "No dead transitions" << std::endl;
    }
  }

  if (conf.show_hash_tables_stats)
  {
    std::cout << manager << std::endl;
  }
}

/*------------------------------------------------------------------------------------------------*/

}} // namespace pnmc::mc
<|endoftext|>