<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/generic/memory/lib/utils/freq/mss_freq_scoreboard.C $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2018,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file mss_freq_scoreboard.C
/// @brief Frequency scoreboard class definitions
///
// *HWP HWP Owner: Stephen Glancy <sglancy@us.ibm.com>
// *HWP HWP Backup: Louis Stermole <stermole@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 3
// *HWP Consumed by: HB:FSP

#include <vector>
#include <fapi2.H>
#include <generic/memory/lib/utils/freq/mss_freq_scoreboard.H>
#include <generic/memory/lib/utils/c_str.H>
#include <generic/memory/lib/utils/find.H>
#include <generic/memory/lib/utils/shared/mss_generic_consts.H>

namespace mss
{

///
/// @brief Remove frequencies above a limit from the scoreboard
/// @param[in] i_port_pos position index of port within parent MCBIST
/// @param[in] i_freq_limit upper limit for frequency
/// @return FAPI2_RC_SUCCESS if successful
///
fapi2::ReturnCode freq_scoreboard::remove_freqs_above_limit(const uint64_t i_port_pos,
        const uint32_t i_freq_limit)
{
    FAPI_TRY( check_port_position(i_port_pos,
                                  generic_ffdc_codes::FREQ_SCOREBOARD_REMOVE_FREQS_ABOVE_LIMIT),
              "Invalid port index passed to remove_freqs_above_limit (%d)",
              i_port_pos);

    {
        auto& l_port_supported_freqs = iv_supported_port_freqs[i_port_pos];

        // Can't do a ranged for loop here because we need the index to get the frequency out of iv_freq_values
        for ( size_t l_index = 0; l_index < l_port_supported_freqs.size(); ++l_index )
        {
            const auto l_scoreboard_freq = iv_freq_values[l_index];

            if ( l_scoreboard_freq > i_freq_limit )
            {
                FAPI_INF("Removing freq %d on port %d since it's above the limit %d", l_scoreboard_freq, i_port_pos, i_freq_limit);
                l_port_supported_freqs[l_index] = false;
            }
        }
    }

    return fapi2::FAPI2_RC_SUCCESS;

fapi_try_exit:
    return fapi2::current_err;
}

///
/// @brief Remove frequencies above a limit from the scoreboard
/// @param[in] i_port_pos position index of port within parent MCBIST
/// @param[in] i_freq_limits reference to vector of upper limits for frequency per port
/// @return FAPI2_RC_SUCCESS if successful
///
fapi2::ReturnCode freq_scoreboard::remove_freqs_above_limit(const uint64_t i_port_pos,
        const std::vector<uint32_t> i_freq_limits)
{
    FAPI_TRY( check_port_position(i_port_pos,
                                  generic_ffdc_codes::FREQ_SCOREBOARD_REMOVE_FREQS_ABOVE_LIMIT_VECTOR),
              "Invalid port index passed to remove_freqs_above_limit (%d)",
              i_port_pos);

    FAPI_ASSERT(i_freq_limits.size() == iv_num_ports,
                fapi2::MSS_INVALID_FREQ_LIST_PASSED()
                .set_SIZE(i_freq_limits.size())
                .set_EXPECTED(iv_num_ports),
                "Invalid frequency list passed to remove_freqs_above_limit (size should be %d but got %d)",
                iv_num_ports, i_freq_limits.size());

    {
        const auto l_freq_limit = i_freq_limits[i_port_pos];
        FAPI_TRY( this->remove_freqs_above_limit(i_port_pos, l_freq_limit) );
    }

    return fapi2::FAPI2_RC_SUCCESS;

fapi_try_exit:
    return fapi2::current_err;
}

///
/// @brief Return the maximum supported frequency for a given port
/// @param[in] i_port_pos position index of port within parent MCBIST
/// @param[out] o_freq max supported frequency
/// @return FAPI2_RC_SUCCESS if successful
///
fapi2::ReturnCode freq_scoreboard::max_supported_freq(const uint64_t i_port_pos,
        uint32_t& o_freq) const
{
    FAPI_TRY( this->check_port_position(i_port_pos,
                                        generic_ffdc_codes::FREQ_SCOREBOARD_MAX_SUPPORTED_FREQ),
              "Invalid port index passed to max_supported_freq (%d)",
              i_port_pos);

    {
        std::vector<uint32_t> l_supported_freqs;
        FAPI_TRY( this->supported_freqs(i_port_pos, l_supported_freqs) );

        o_freq = l_supported_freqs.empty() ? 0 : l_supported_freqs.back();
    }

    return fapi2::FAPI2_RC_SUCCESS;

fapi_try_exit:
    return fapi2::current_err;
}

///
/// @brief Return a list of supported frequencies for a given port
/// @param[in] i_port_pos position index of port within parent MCBIST
/// @param[out] o_freq vector of supported frequencies
/// @return FAPI2_RC_SUCCESS if successful
///
fapi2::ReturnCode freq_scoreboard::supported_freqs(const uint64_t i_port_pos,
        std::vector<uint32_t>& o_freqs) const
{
    FAPI_TRY( check_port_position(i_port_pos,
                                  generic_ffdc_codes::FREQ_SCOREBOARD_SUPPORTED_FREQS),
              "Invalid port index passed to supported_freqs (%d)",
              i_port_pos);

    {
        o_freqs.clear();
        auto& l_port_supported_freqs = iv_supported_port_freqs[i_port_pos];

        for ( size_t l_index = 0; l_index < iv_freq_values.size(); ++l_index )
        {
            if (l_port_supported_freqs[l_index])
            {
                o_freqs.push_back(iv_freq_values[l_index]);
            }
        }
    }

    return fapi2::FAPI2_RC_SUCCESS;

fapi_try_exit:
    return fapi2::current_err;
}

} // ns mss
<commit_msg>Add snapshot of generic/memory for master-p10<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/generic/memory/lib/utils/freq/mss_freq_scoreboard.C $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2018,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
<|endoftext|>


<commit_before><commit_msg>CImgSmooth: fix<commit_after><|endoftext|>


<commit_before><commit_msg>Use a switch. NFC.<commit_after><|endoftext|>


<commit_before>// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2022, Benoit BLANCHON
// MIT License

#pragma once

#include <ArduinoJson/Memory/Alignment.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
#include <ArduinoJson/Polyfills/mpl/max.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <ArduinoJson/Variant/VariantSlot.hpp>

#include <string.h>  // memmove

#define JSON_STRING_SIZE(SIZE) (SIZE + 1)

namespace ARDUINOJSON_NAMESPACE {

// _begin                                   _end
// v                                           v
// +-------------+--------------+--------------+
// | strings...  |   (free)     |  ...variants |
// +-------------+--------------+--------------+
//               ^              ^
//             _left          _right

class MemoryPool {
 public:
  MemoryPool(char* buf, size_t capa)
      : _begin(buf),
        _left(buf),
        _right(buf ? buf + capa : 0),
        _end(buf ? buf + capa : 0),
        _overflowed(false) {
    ARDUINOJSON_ASSERT(isAligned(_begin));
    ARDUINOJSON_ASSERT(isAligned(_right));
    ARDUINOJSON_ASSERT(isAligned(_end));
  }

  void* buffer() {
    return _begin;  // NOLINT(clang-analyzer-unix.Malloc)
                    // movePointers() alters this pointer
  }

  // Gets the capacity of the memoryPool in bytes
  size_t capacity() const {
    return size_t(_end - _begin);
  }

  size_t size() const {
    return size_t(_left - _begin + _end - _right);
  }

  bool overflowed() const {
    return _overflowed;
  }

  VariantSlot* allocVariant() {
    return allocRight<VariantSlot>();
  }

  template <typename TAdaptedString>
  const char* saveString(TAdaptedString str) {
    if (str.isNull())
      return CopiedString();

#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
    const char* existingCopy = findString(str);
    if (existingCopy)
      return CopiedString(existingCopy, str.size());
#endif

    size_t n = str.size();

    char* newCopy = allocString(n + 1);
    if (newCopy) {
      stringGetChars(str, newCopy, n);
      newCopy[n] = 0;  // force null-terminator
    }
    return CopiedString(newCopy, n);
  }

  void getFreeZone(char** zoneStart, size_t* zoneSize) const {
    *zoneStart = _left;
    *zoneSize = size_t(_right - _left);
  }

  const char* saveStringFromFreeZone(size_t len) {
#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
    const char* dup = findString(adaptString(_left, len));
    if (dup)
      return CopiedString(dup, len);
#endif

    const char* str = _left;
    _left += len;
    *_left++ = 0;
    checkInvariants();
    return CopiedString(str, len);
  }

  void markAsOverflowed() {
    _overflowed = true;
  }

  void clear() {
    _left = _begin;
    _right = _end;
    _overflowed = false;
  }

  bool canAlloc(size_t bytes) const {
    return _left + bytes <= _right;
  }

  bool owns(void* p) const {
    return _begin <= p && p < _end;
  }

  // Workaround for missing placement new
  void* operator new(size_t, void* p) {
    return p;
  }

  // Squash the free space between strings and variants
  //
  // _begin                    _end
  // v                            v
  // +-------------+--------------+
  // | strings...  |  ...variants |
  // +-------------+--------------+
  //               ^
  //          _left _right
  //
  // This funcion is called before a realloc.
  ptrdiff_t squash() {
    char* new_right = addPadding(_left);
    if (new_right >= _right)
      return 0;

    size_t right_size = static_cast<size_t>(_end - _right);
    memmove(new_right, _right, right_size);

    ptrdiff_t bytes_reclaimed = _right - new_right;
    _right = new_right;
    _end = new_right + right_size;
    return bytes_reclaimed;
  }

  // Move all pointers together
  // This funcion is called after a realloc.
  void movePointers(ptrdiff_t offset) {
    _begin += offset;
    _left += offset;
    _right += offset;
    _end += offset;
  }

 private:
  void checkInvariants() {
    ARDUINOJSON_ASSERT(_begin <= _left);
    ARDUINOJSON_ASSERT(_left <= _right);
    ARDUINOJSON_ASSERT(_right <= _end);
    ARDUINOJSON_ASSERT(isAligned(_right));
  }

#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
  template <typename TAdaptedString>
  const char* findString(const TAdaptedString& str) const {
    size_t n = str.size();
    for (char* next = _begin; next + n < _left; ++next) {
      if (next[n] == '\0' && stringEquals(str, adaptString(next, n)))
        return next;

      // jump to next terminator
      while (*next) ++next;
    }
    return 0;
  }
#endif

  char* allocString(size_t n) {
    if (!canAlloc(n)) {
      _overflowed = true;
      return 0;
    }
    char* s = _left;
    _left += n;
    checkInvariants();
    return s;
  }

  template <typename T>
  T* allocRight() {
    return reinterpret_cast<T*>(allocRight(sizeof(T)));
  }

  void* allocRight(size_t bytes) {
    if (!canAlloc(bytes)) {
      _overflowed = true;
      return 0;
    }
    _right -= bytes;
    return _right;
  }

  char *_begin, *_left, *_right, *_end;
  bool _overflowed;
};

}  // namespace ARDUINOJSON_NAMESPACE
<commit_msg>Remove unused `CopiedString` in `MemoryPool`<commit_after>// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2022, Benoit BLANCHON
// MIT License

#pragma once

#include <ArduinoJson/Memory/Alignment.hpp>
#include <ArduinoJson/Polyfills/assert.hpp>
#include <ArduinoJson/Polyfills/mpl/max.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <ArduinoJson/Variant/VariantSlot.hpp>

#include <string.h>  // memmove

#define JSON_STRING_SIZE(SIZE) (SIZE + 1)

namespace ARDUINOJSON_NAMESPACE {

// _begin                                   _end
// v                                           v
// +-------------+--------------+--------------+
// | strings...  |   (free)     |  ...variants |
// +-------------+--------------+--------------+
//               ^              ^
//             _left          _right

class MemoryPool {
 public:
  MemoryPool(char* buf, size_t capa)
      : _begin(buf),
        _left(buf),
        _right(buf ? buf + capa : 0),
        _end(buf ? buf + capa : 0),
        _overflowed(false) {
    ARDUINOJSON_ASSERT(isAligned(_begin));
    ARDUINOJSON_ASSERT(isAligned(_right));
    ARDUINOJSON_ASSERT(isAligned(_end));
  }

  void* buffer() {
    return _begin;  // NOLINT(clang-analyzer-unix.Malloc)
                    // movePointers() alters this pointer
  }

  // Gets the capacity of the memoryPool in bytes
  size_t capacity() const {
    return size_t(_end - _begin);
  }

  size_t size() const {
    return size_t(_left - _begin + _end - _right);
  }

  bool overflowed() const {
    return _overflowed;
  }

  VariantSlot* allocVariant() {
    return allocRight<VariantSlot>();
  }

  template <typename TAdaptedString>
  const char* saveString(TAdaptedString str) {
    if (str.isNull())
      return 0;

#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
    const char* existingCopy = findString(str);
    if (existingCopy)
      return existingCopy;
#endif

    size_t n = str.size();

    char* newCopy = allocString(n + 1);
    if (newCopy) {
      stringGetChars(str, newCopy, n);
      newCopy[n] = 0;  // force null-terminator
    }
    return newCopy;
  }

  void getFreeZone(char** zoneStart, size_t* zoneSize) const {
    *zoneStart = _left;
    *zoneSize = size_t(_right - _left);
  }

  const char* saveStringFromFreeZone(size_t len) {
#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
    const char* dup = findString(adaptString(_left, len));
    if (dup)
      return dup;
#endif

    const char* str = _left;
    _left += len;
    *_left++ = 0;
    checkInvariants();
    return str;
  }

  void markAsOverflowed() {
    _overflowed = true;
  }

  void clear() {
    _left = _begin;
    _right = _end;
    _overflowed = false;
  }

  bool canAlloc(size_t bytes) const {
    return _left + bytes <= _right;
  }

  bool owns(void* p) const {
    return _begin <= p && p < _end;
  }

  // Workaround for missing placement new
  void* operator new(size_t, void* p) {
    return p;
  }

  // Squash the free space between strings and variants
  //
  // _begin                    _end
  // v                            v
  // +-------------+--------------+
  // | strings...  |  ...variants |
  // +-------------+--------------+
  //               ^
  //          _left _right
  //
  // This funcion is called before a realloc.
  ptrdiff_t squash() {
    char* new_right = addPadding(_left);
    if (new_right >= _right)
      return 0;

    size_t right_size = static_cast<size_t>(_end - _right);
    memmove(new_right, _right, right_size);

    ptrdiff_t bytes_reclaimed = _right - new_right;
    _right = new_right;
    _end = new_right + right_size;
    return bytes_reclaimed;
  }

  // Move all pointers together
  // This funcion is called after a realloc.
  void movePointers(ptrdiff_t offset) {
    _begin += offset;
    _left += offset;
    _right += offset;
    _end += offset;
  }

 private:
  void checkInvariants() {
    ARDUINOJSON_ASSERT(_begin <= _left);
    ARDUINOJSON_ASSERT(_left <= _right);
    ARDUINOJSON_ASSERT(_right <= _end);
    ARDUINOJSON_ASSERT(isAligned(_right));
  }

#if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
  template <typename TAdaptedString>
  const char* findString(const TAdaptedString& str) const {
    size_t n = str.size();
    for (char* next = _begin; next + n < _left; ++next) {
      if (next[n] == '\0' && stringEquals(str, adaptString(next, n)))
        return next;

      // jump to next terminator
      while (*next) ++next;
    }
    return 0;
  }
#endif

  char* allocString(size_t n) {
    if (!canAlloc(n)) {
      _overflowed = true;
      return 0;
    }
    char* s = _left;
    _left += n;
    checkInvariants();
    return s;
  }

  template <typename T>
  T* allocRight() {
    return reinterpret_cast<T*>(allocRight(sizeof(T)));
  }

  void* allocRight(size_t bytes) {
    if (!canAlloc(bytes)) {
      _overflowed = true;
      return 0;
    }
    _right -= bytes;
    return _right;
  }

  char *_begin, *_left, *_right, *_end;
  bool _overflowed;
};

}  // namespace ARDUINOJSON_NAMESPACE
<|endoftext|>


<commit_before>
#undef NDEBUG

#include "ospray/common/ospcommon.h"
#include "apps/common/xml/xml.h"
#include "model.h"

#include "common/sys/filename.h"

namespace ospray {
  namespace particle {
    struct Particle {
      double x,y,z;
    };
    // struct MPM {
    //   std::vector<vec3f> pos;
    // };
    // MPM mpm;

    void readParticles(Model *model,
                       size_t numParticles, const std::string &fn, size_t begin, size_t end)
    {
      // std::cout << "#mpm: reading " << numParticles << " particles... " << std::flush;
      // printf("#mpm: reading%7ld particles...",numParticles); fflush(0);
      FILE *file = fopen(fn.c_str(),"rb");
      assert(file);

      fseek(file,begin,SEEK_SET);
      size_t len = end-begin;
      // PRINT(len);
      // PRINT(len/numParticles);

      for (int i=0;i<numParticles;i++) {
        Particle p;
        fread(&p,1,sizeof(p),file);

        Model::Atom a;
        a.position = vec3f(p.x,p.y,p.z);
        a.type = model->getAtomType("<unnamed>");
        model->atom.push_back(a);
      }
      // std::cout << "done (total " << model->atom.size() << ")" << std::endl;

      // Particle *particle = new Particle[numParticles];
      // fread(particle,numParticles,sizeof(Particle),file);
      // for (int i=0;i
      // for (int i=0;i<100;i++)
      //   printf("particle %5i: %lf %lf %lf\n",particle[i].x,particle[i].y,particle[i].z);
      fclose(file);
    }

    void parse__Variable(Model *model,
                       const std::string &basePath, xml::Node *var)
    {
      size_t index = -1;
      size_t start = -1;
      size_t end = -1;
      size_t patch = -1;
      size_t numParticles = -1;
      std::string variable;
      std::string filename;
      for (int i=0;i<var->child.size();i++) {
        xml::Node *n = var->child[i];
        if (n->name == "index") {
          index = atoi(n->content.c_str());
        } else if (n->name == "variable") {
          variable = n->content;
        } else if (n->name == "numParticles") {
          numParticles = atol(n->content.c_str());
        } else if (n->name == "patch") {
          patch = atol(n->content.c_str());
        } else if (n->name == "filename") {
          filename = n->content;
        } else if (n->name == "start") {
          start = atol(n->content.c_str());
        } else if (n->name == "end") {
          end = atol(n->content.c_str());
        }
      }

      if (numParticles
          && variable == "p.x"
          /* && index == .... */
          ) {
        readParticles(model,numParticles,basePath+"/"+filename,start,end);
      }
      // PRINT(patch);
      // PRINT(numParticles);
      // PRINT(start);
      // PRINT(filename);
    }

    // pase a "Uintah_Output" node
    // void parse__Uintah_Output(const std::string &basePath, xml::Node *node)
    // {
    //   assert(node->name == "Uintah_Output");
    //   for (int i=0;i<node->child.size();i++) {
    //     xml::Node *c = node->child[i];
    //     assert(c->name == "Variable");
    //     parse__Variable(basePath,c);
    //   }
    // }
    void parse__Uintah_Datafile(Model *model,
                       const std::string &fileName)
    {
      std::string basePath = embree::FileName(fileName).path();

      xml::XMLDoc *doc = xml::readXML(fileName);
      assert(doc);
      assert(doc->child.size() == 1);
      xml::Node *node = doc->child[0];
      assert(node->name == "Uintah_Output");
      for (int i=0;i<node->child.size();i++) {
        xml::Node *c = node->child[i];
        assert(c->name == "Variable");
        parse__Variable(model,basePath,c);
      }
    }
    void parse__Uintah_TimeStep_Data(Model *model,
                       const std::string &basePath, xml::Node *node)
    {
      assert(node->name == "Data");
      for (int i=0;i<node->child.size();i++) {
        xml::Node *c = node->child[i];
        assert(c->name == "Datafile");
        for (int j=0;j<c->prop.size();j++) {
          xml::Prop *p = c->prop[j];
          if (p->name == "href") {
            parse__Uintah_Datafile(model,basePath+"/"+p->value);
          }
        }
      }
    }
    void parse__Uintah_timestep(Model *model,
                                const std::string &basePath, xml::Node *node)
    {
      assert(node->name == "Uintah_timestep");
      for (int i=0;i<node->child.size();i++) {
        xml::Node *c = node->child[i];
        if (c->name == "Data") {
          parse__Uintah_TimeStep_Data(model,basePath,c);
        }
      }
    }
    Model *parse__Uintah_timestep_xml(const std::string &s)
    {
      Model *model = new Model;
      Ref<xml::XMLDoc> doc = xml::readXML(s);
      assert(doc);
      assert(doc->child.size() == 1);
      assert(doc->child[0]->name == "Uintah_timestep");
      std::string basePath = embree::FileName(s).path();
      parse__Uintah_timestep(model, basePath, doc->child[0]);
      std::cout << "#osp:mpm: read " << s << " : "
                << model->atom.size() << " particles" << std::endl;
      model->radius = .002f;
      return model;
    }
  }
}

<commit_msg>added support for detecting big-endian data format in uintah particle parser<commit_after>
#undef NDEBUG

#include "ospray/common/ospcommon.h"
#include "apps/common/xml/xml.h"
#include "model.h"

#include "common/sys/filename.h"

namespace ospray {
  namespace particle {
    bool big_endian = false;

    struct Particle {
      double x,y,z;
    };
    // struct MPM {
    //   std::vector<vec3f> pos;
    // };
    // MPM mpm;

    double htonlf(double f)
    {
      double ret;
      char *in = (char*)&f;
      char *out = (char*)&ret;
      for (int i=0;i<8;i++)
        out[i] = in[7-i];
      return ret;
    }
    void readParticles(Model *model,
                       size_t numParticles, const std::string &fn, size_t begin, size_t end)
    {
      // std::cout << "#mpm: reading " << numParticles << " particles... " << std::flush;
      // printf("#mpm: reading%7ld particles...",numParticles); fflush(0);
      FILE *file = fopen(fn.c_str(),"rb");
      assert(file);

      fseek(file,begin,SEEK_SET);
      size_t len = end-begin;

      if (len != numParticles*sizeof(Particle)) {
        PING;
        PRINT(len);
        PRINT(numParticles);
        PRINT(len/numParticles);
      }
      // PRINT(len);

      for (int i=0;i<numParticles;i++) {
        Particle p;
        fread(&p,1,sizeof(p),file);
#if 1
        if (big_endian) {
          p.x = htonlf(p.x);
          p.y = htonlf(p.y);
          p.z = htonlf(p.z);
        }
#endif
        Model::Atom a;
        a.position = vec3f(p.x,p.y,p.z);
        a.type = model->getAtomType("<unnamed>");
        model->atom.push_back(a);
      }
      // std::cout << "done (total " << model->atom.size() << ")" << std::endl;

      // Particle *particle = new Particle[numParticles];
      // fread(particle,numParticles,sizeof(Particle),file);
      // for (int i=0;i
      // for (int i=0;i<100;i++)
      //   printf("particle %5i: %lf %lf %lf\n",particle[i].x,particle[i].y,particle[i].z);
      fclose(file);
    }

    void parse__Variable(Model *model,
                       const std::string &basePath, xml::Node *var)
    {
      size_t index = -1;
      size_t start = -1;
      size_t end = -1;
      size_t patch = -1;
      size_t numParticles = -1;
      std::string variable;
      std::string filename;
      for (int i=0;i<var->child.size();i++) {
        xml::Node *n = var->child[i];
        if (n->name == "index") {
          index = atoi(n->content.c_str());
        } else if (n->name == "variable") {
          variable = n->content;
        } else if (n->name == "numParticles") {
          numParticles = atol(n->content.c_str());
        } else if (n->name == "patch") {
          patch = atol(n->content.c_str());
        } else if (n->name == "filename") {
          filename = n->content;
        } else if (n->name == "start") {
          start = atol(n->content.c_str());
        } else if (n->name == "end") {
          end = atol(n->content.c_str());
        }
      }

      if (numParticles
          && variable == "p.x"
          /* && index == .... */
          ) {
        readParticles(model,numParticles,basePath+"/"+filename,start,end);
      }
      // PRINT(patch);
      // PRINT(numParticles);
      // PRINT(start);
      // PRINT(filename);
    }

    // pase a "Uintah_Output" node
    // void parse__Uintah_Output(const std::string &basePath, xml::Node *node)
    // {
    //   assert(node->name == "Uintah_Output");
    //   for (int i=0;i<node->child.size();i++) {
    //     xml::Node *c = node->child[i];
    //     assert(c->name == "Variable");
    //     parse__Variable(basePath,c);
    //   }
    // }
    void parse__Uintah_Datafile(Model *model,
                       const std::string &fileName)
    {
      std::string basePath = embree::FileName(fileName).path();

      xml::XMLDoc *doc = xml::readXML(fileName);
      assert(doc);
      assert(doc->child.size() == 1);
      xml::Node *node = doc->child[0];
      assert(node->name == "Uintah_Output");
      for (int i=0;i<node->child.size();i++) {
        xml::Node *c = node->child[i];
        assert(c->name == "Variable");
        parse__Variable(model,basePath,c);
      }
    }
    void parse__Uintah_TimeStep_Data(Model *model,
                       const std::string &basePath, xml::Node *node)
    {
      assert(node->name == "Data");
      for (int i=0;i<node->child.size();i++) {
        xml::Node *c = node->child[i];
        assert(c->name == "Datafile");
        for (int j=0;j<c->prop.size();j++) {
          xml::Prop *p = c->prop[j];
          if (p->name == "href") {
            parse__Uintah_Datafile(model,basePath+"/"+p->value);
          }
        }
      }
    }
    void parse__Uintah_TimeStep_Meta(Model *model,
                                     const std::string &basePath, xml::Node *node)
    {
      assert(node->name == "Meta");
      for (int i=0;i<node->child.size();i++) {
        xml::Node *c = node->child[i];
        if (c->name == "endianness") {
          if (c->content == "big_endian") {
            std::cout << "#osp:uintah: SWITCHING TO BIG_ENDIANNESS" << std::endl;
            big_endian = true;
          }
        }
      }
    }
    void parse__Uintah_timestep(Model *model,
                                const std::string &basePath, xml::Node *node)
    {
      assert(node->name == "Uintah_timestep");
      for (int i=0;i<node->child.size();i++) {
        xml::Node *c = node->child[i];
        if (c->name == "Meta") {
          parse__Uintah_TimeStep_Meta(model,basePath,c);
        }
        if (c->name == "Data") {
          parse__Uintah_TimeStep_Data(model,basePath,c);
        }
      }
    }
    Model *parse__Uintah_timestep_xml(const std::string &s)
    {
      Model *model = new Model;
      Ref<xml::XMLDoc> doc = xml::readXML(s);
      assert(doc);
      assert(doc->child.size() == 1);
      assert(doc->child[0]->name == "Uintah_timestep");
      std::string basePath = embree::FileName(s).path();
      parse__Uintah_timestep(model, basePath, doc->child[0]);
      std::cout << "#osp:mpm: read " << s << " : "
                << model->atom.size() << " particles" << std::endl;

      box3f bounds = embree::empty;
      for (int i=0;i<model->atom.size();i++) {
        bounds.extend(model->atom[i].position);
      }
      std::cout << "#osp:mpm: bounds of particle centers: " << bounds << std::endl;
      model->radius = .002f;
      return model;
    }
  }
}

<|endoftext|>


<commit_before>
#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "DispenserEntity.h"
#include "../Chunk.h"
#include "../BlockInfo.h"
#include "../Defines.h"
#include "../World.h"
#include "../Entities/Boat.h"
#include "../Entities/ProjectileEntity.h"
#include "../Simulator/FluidSimulator.h"
#include "../Items/ItemSpawnEgg.h"

cDispenserEntity::cDispenserEntity(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Vector3i a_Pos, cWorld * a_World):
	Super(a_BlockType, a_BlockMeta, a_Pos, a_World)
{
	ASSERT(a_BlockType == E_BLOCK_DISPENSER);
}

void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
{
	Vector3i dispRelCoord(GetRelPos());
	auto meta = a_Chunk.GetMeta(dispRelCoord);
	AddDropSpenserDir(dispRelCoord, meta);
	auto dispChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(dispRelCoord);
	if (dispChunk == nullptr)
	{
		// Would dispense into / interact with a non-loaded chunk, ignore the tick
		return;
	}

	BLOCKTYPE dispBlock = dispChunk->GetBlock(dispRelCoord);
	auto dispAbsCoord = dispChunk->RelativeToAbsolute(dispRelCoord);

	// Dispense the item:
	const cItem & SlotItem = m_Contents.GetSlot(a_SlotNum);
	if (ItemCategory::IsMinecart(SlotItem.m_ItemType) && IsBlockRail(dispBlock))  // only actually place the minecart if there are rails!
	{
		if (m_World->SpawnMinecart(dispAbsCoord.x + 0.5, dispAbsCoord.y + 0.5, dispAbsCoord.z + 0.5, SlotItem.m_ItemType) != cEntity::INVALID_ID)
		{
			m_Contents.ChangeSlotCount(a_SlotNum, -1);
		}
		return;
	}
	switch (SlotItem.m_ItemType)
	{
		case E_ITEM_BUCKET:
		{
			LOGD("Dispensing empty bucket in slot %d; dispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(dispBlock).c_str(), dispBlock);
			switch (dispBlock)
			{
				case E_BLOCK_STATIONARY_WATER:
				case E_BLOCK_WATER:
				{
					if (ScoopUpLiquid(a_SlotNum, E_ITEM_WATER_BUCKET))
					{
						dispChunk->SetBlock(dispRelCoord, E_BLOCK_AIR, 0);
					}
					break;
				}
				case E_BLOCK_STATIONARY_LAVA:
				case E_BLOCK_LAVA:
				{
					if (ScoopUpLiquid(a_SlotNum, E_ITEM_LAVA_BUCKET))
					{
						dispChunk->SetBlock(dispRelCoord, E_BLOCK_AIR, 0);
					}
					break;
				}
				default:
				{
					DropFromSlot(a_Chunk, a_SlotNum);
					break;
				}
			}
			break;
		}  // E_ITEM_BUCKET

		case E_ITEM_WATER_BUCKET:
		{
			LOGD("Dispensing water bucket in slot %d; dispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(dispBlock).c_str(), dispBlock);
			if (EmptyLiquidBucket(dispBlock, a_SlotNum))
			{
				dispChunk->SetBlock(dispRelCoord, E_BLOCK_WATER, 0);
			}
			else
			{
				DropFromSlot(a_Chunk, a_SlotNum);
			}
			break;
		}

		case E_ITEM_LAVA_BUCKET:
		{
			LOGD("Dispensing lava bucket in slot %d; dispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(dispBlock).c_str(), dispBlock);
			if (EmptyLiquidBucket(dispBlock, a_SlotNum))
			{
				dispChunk->SetBlock(dispRelCoord, E_BLOCK_LAVA, 0);
			}
			else
			{
				DropFromSlot(a_Chunk, a_SlotNum);
			}
			break;
		}

		case E_ITEM_SPAWN_EGG:
		{
			double MobX = 0.5 + dispAbsCoord.x;
			double MobZ = 0.5 + dispAbsCoord.z;
			auto MonsterType = cItemSpawnEggHandler::ItemDamageToMonsterType(m_Contents.GetSlot(a_SlotNum).m_ItemDamage);
			if (m_World->SpawnMob(MobX, dispAbsCoord.y, MobZ, static_cast<eMonsterType>(MonsterType), false) != cEntity::INVALID_ID)
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_BLOCK_TNT:
		{
			// Spawn a primed TNT entity, if space allows:
			if (!cBlockInfo::IsSolid(dispBlock))
			{
				m_World->SpawnPrimedTNT(Vector3d(0.5, 0.5, 0.5) + dispAbsCoord, 80, 0);  // 80 ticks fuse, no initial velocity
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_FLINT_AND_STEEL:
		{
			// Spawn fire if the block in front is air.
			if (dispBlock == E_BLOCK_AIR)
			{
				dispChunk->SetBlock(dispRelCoord, E_BLOCK_FIRE, 0);

				bool ItemBroke = m_Contents.DamageItem(a_SlotNum, 1);

				if (ItemBroke)
				{
					m_Contents.ChangeSlotCount(a_SlotNum, -1);
				}
			}
			break;
		}

		case E_ITEM_FIRE_CHARGE:
		{
			if (SpawnProjectileFromDispenser(dispAbsCoord, cProjectileEntity::pkFireCharge, GetShootVector(meta) * 20) != cEntity::INVALID_ID)
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_ARROW:
		{
			if (SpawnProjectileFromDispenser(dispAbsCoord, cProjectileEntity::pkArrow, GetShootVector(meta) * 30 + Vector3d(0, 1, 0)) != cEntity::INVALID_ID)
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_SNOWBALL:
		{
			if (SpawnProjectileFromDispenser(dispAbsCoord, cProjectileEntity::pkSnowball, GetShootVector(meta) * 20 + Vector3d(0, 1, 0)) != cEntity::INVALID_ID)
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_EGG:
		{
			if (SpawnProjectileFromDispenser(dispAbsCoord, cProjectileEntity::pkEgg, GetShootVector(meta) * 20 + Vector3d(0, 1, 0)) != cEntity::INVALID_ID)
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_BOTTLE_O_ENCHANTING:
		{
			if (SpawnProjectileFromDispenser(dispAbsCoord, cProjectileEntity::pkExpBottle, GetShootVector(meta) * 20 + Vector3d(0, 1, 0)) != cEntity::INVALID_ID)
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_POTION:
		{
			if (SpawnProjectileFromDispenser(dispAbsCoord, cProjectileEntity::pkSplashPotion, GetShootVector(meta) * 20 + Vector3d(0, 1, 0), &SlotItem) != cEntity::INVALID_ID)
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_DYE:
		{
			if (SlotItem.m_ItemDamage != E_META_DYE_WHITE)
			{
				DropFromSlot(a_Chunk, a_SlotNum);
				break;
			}
			if (m_World->GrowRipePlant(dispAbsCoord.x, dispAbsCoord.y, dispAbsCoord.z, true))
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_BOAT:
		case E_ITEM_SPRUCE_BOAT:
		case E_ITEM_BIRCH_BOAT:
		case E_ITEM_JUNGLE_BOAT:
		case E_ITEM_ACACIA_BOAT:
		case E_ITEM_DARK_OAK_BOAT:
		{
			Vector3d spawnPos = dispAbsCoord;
			if (IsBlockWater(dispBlock))
			{
				// Water next to the dispenser, spawn a boat above the water block
				spawnPos.y += 1;
			}
			else if (IsBlockWater(dispChunk->GetBlock(dispRelCoord.addedY(-1))))
			{
				// Water one block below the dispenser, spawn a boat at the dispenser's Y level
				// No adjustment needed
			}
			else
			{
				// There's no eligible water block, drop the boat as a pickup
				DropFromSlot(a_Chunk, a_SlotNum);
				break;
			}

			spawnPos += GetShootVector(meta) * 0.8;  // A boat is bigger than one block. Add the shoot vector to put it outside the dispenser.
			spawnPos += Vector3d(0.5, 0.5, 0.5);

			if (m_World->SpawnBoat(spawnPos, cBoat::ItemToMaterial(SlotItem)))
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_FIREWORK_ROCKET:
		{
			if (SpawnProjectileFromDispenser(dispAbsCoord, cProjectileEntity::pkFirework, GetShootVector(meta) * 20 + Vector3d(0, 1, 0), &SlotItem) != cEntity::INVALID_ID)
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		default:
		{
			DropFromSlot(a_Chunk, a_SlotNum);
			break;
		}
	}  // switch (SlotItem.m_ItemType)
}

UInt32 cDispenserEntity::SpawnProjectileFromDispenser(Vector3i a_BlockPos, cProjectileEntity::eKind a_Kind, const Vector3d & a_ShootVector, const cItem * a_Item)
{
	return m_World->CreateProjectile(Vector3d(0.5, 0.5, 0.5) + a_BlockPos,
		a_Kind, nullptr, a_Item, &a_ShootVector
	);
}

Vector3d cDispenserEntity::GetShootVector(NIBBLETYPE a_Meta)
{
	switch (a_Meta & E_META_DROPSPENSER_FACING_MASK)
	{
		case E_META_DROPSPENSER_FACING_YP: return Vector3d( 0,  1,  0);
		case E_META_DROPSPENSER_FACING_YM: return Vector3d( 0, -1,  0);
		case E_META_DROPSPENSER_FACING_XM: return Vector3d(-1,  0,  0);
		case E_META_DROPSPENSER_FACING_XP: return Vector3d( 1,  0,  0);
		case E_META_DROPSPENSER_FACING_ZM: return Vector3d( 0,  0, -1);
		case E_META_DROPSPENSER_FACING_ZP: return Vector3d( 0,  0,  1);
	}
	LOGWARNING("Unhandled dispenser meta: %d", a_Meta);
	ASSERT(!"Unhandled dispenser facing");
	return Vector3d(0, 1, 0);
}

bool cDispenserEntity::ScoopUpLiquid(int a_SlotNum, short a_ResultingBucketItemType)
{
	cItem LiquidBucket(a_ResultingBucketItemType);
	if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1)
	{
		// Special case: replacing one empty bucket with one full bucket
		m_Contents.SetSlot(a_SlotNum, LiquidBucket);
		return true;
	}

	// There are stacked buckets at the selected slot, see if a full bucket will fit somewhere else
	if (m_Contents.HowManyCanFit(LiquidBucket) < 1)
	{
		// Cannot fit into m_Contents
		return false;
	}

	m_Contents.ChangeSlotCount(a_SlotNum, -1);
	m_Contents.AddItem(LiquidBucket);
	return true;
}

bool cDispenserEntity::EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum)
{
	if (
		(a_BlockInFront != E_BLOCK_AIR) &&
		!IsBlockLiquid(a_BlockInFront) &&
		!cFluidSimulator::CanWashAway(a_BlockInFront)
	)
	{
		// Not a suitable block in front
		return false;
	}

	cItem EmptyBucket(E_ITEM_BUCKET, 1);
	if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1)
	{
		// Change the single full bucket present into a single empty bucket
		m_Contents.SetSlot(a_SlotNum, EmptyBucket);
		return true;
	}

	// There are full buckets stacked at this slot, check if we can fit in the empty bucket
	if (m_Contents.HowManyCanFit(EmptyBucket) < 1)
	{
		// The empty bucket wouldn't fit into m_Contents
		return false;
	}

	// The empty bucket fits in, remove one full bucket and add the empty one
	m_Contents.ChangeSlotCount(a_SlotNum, -1);
	m_Contents.AddItem(EmptyBucket);
	return true;
}
<commit_msg>Update DispenserEntity.cpp<commit_after>
#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "DispenserEntity.h"
#include "../Chunk.h"
#include "../BlockInfo.h"
#include "../Defines.h"
#include "../World.h"
#include "../Entities/Boat.h"
#include "../Entities/ProjectileEntity.h"
#include "../Simulator/FluidSimulator.h"
#include "../Items/ItemSpawnEgg.h"

cDispenserEntity::cDispenserEntity(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Vector3i a_Pos, cWorld * a_World):
	Super(a_BlockType, a_BlockMeta, a_Pos, a_World)
{
	ASSERT(a_BlockType == E_BLOCK_DISPENSER);
}

void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
{
	Vector3i dispRelCoord(GetRelPos());
	auto meta = a_Chunk.GetMeta(dispRelCoord);
	AddDropSpenserDir(dispRelCoord, meta);
	auto dispChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(dispRelCoord);
	if (dispChunk == nullptr)
	{
		// Would dispense into / interact with a non-loaded chunk, ignore the tick
		return;
	}

	BLOCKTYPE dispBlock = dispChunk->GetBlock(dispRelCoord);
	auto dispAbsCoord = dispChunk->RelativeToAbsolute(dispRelCoord);

	// Dispense the item:
	const cItem & SlotItem = m_Contents.GetSlot(a_SlotNum);
	if (ItemCategory::IsMinecart(SlotItem.m_ItemType) && IsBlockRail(dispBlock))  // only actually place the minecart if there are rails!
	{
		if (m_World->SpawnMinecart(dispAbsCoord.x + 0.5, dispAbsCoord.y + 0.5, dispAbsCoord.z + 0.5, SlotItem.m_ItemType) != cEntity::INVALID_ID)
		{
			m_Contents.ChangeSlotCount(a_SlotNum, -1);
		}
		return;
	}
	switch (SlotItem.m_ItemType)
	{
		case E_ITEM_BUCKET:
		{
			LOGD("Dispensing empty bucket in slot %d; dispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(dispBlock).c_str(), dispBlock);
			switch (dispBlock)
			{
				case E_BLOCK_STATIONARY_WATER:
				case E_BLOCK_WATER:
				{
					if (ScoopUpLiquid(a_SlotNum, E_ITEM_WATER_BUCKET))
					{
						dispChunk->SetBlock(dispRelCoord, E_BLOCK_AIR, 0);
					}
					break;
				}
				case E_BLOCK_STATIONARY_LAVA:
				case E_BLOCK_LAVA:
				{
					if (ScoopUpLiquid(a_SlotNum, E_ITEM_LAVA_BUCKET))
					{
						dispChunk->SetBlock(dispRelCoord, E_BLOCK_AIR, 0);
					}
					break;
				}
				default:
				{
					DropFromSlot(a_Chunk, a_SlotNum);
					break;
				}
			}
			break;
		}  // E_ITEM_BUCKET

		case E_ITEM_WATER_BUCKET:
		{
			LOGD("Dispensing water bucket in slot %d; dispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(dispBlock).c_str(), dispBlock);
			if (EmptyLiquidBucket(dispBlock, a_SlotNum))
			{
				dispChunk->SetBlock(dispRelCoord, E_BLOCK_WATER, 0);
			}
			else
			{
				DropFromSlot(a_Chunk, a_SlotNum);
			}
			break;
		}

		case E_ITEM_LAVA_BUCKET:
		{
			LOGD("Dispensing lava bucket in slot %d; dispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(dispBlock).c_str(), dispBlock);
			if (EmptyLiquidBucket(dispBlock, a_SlotNum))
			{
				dispChunk->SetBlock(dispRelCoord, E_BLOCK_LAVA, 0);
			}
			else
			{
				DropFromSlot(a_Chunk, a_SlotNum);
			}
			break;
		}

		case E_ITEM_SPAWN_EGG:
		{
			double MobX = 0.5 + dispAbsCoord.x;
			double MobZ = 0.5 + dispAbsCoord.z;
			auto MonsterType = cItemSpawnEggHandler::ItemDamageToMonsterType(m_Contents.GetSlot(a_SlotNum).m_ItemDamage);
			if (m_World->SpawnMob(MobX, dispAbsCoord.y, MobZ, MonsterType, false) != cEntity::INVALID_ID)
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_BLOCK_TNT:
		{
			// Spawn a primed TNT entity, if space allows:
			if (!cBlockInfo::IsSolid(dispBlock))
			{
				m_World->SpawnPrimedTNT(Vector3d(0.5, 0.5, 0.5) + dispAbsCoord, 80, 0);  // 80 ticks fuse, no initial velocity
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_FLINT_AND_STEEL:
		{
			// Spawn fire if the block in front is air.
			if (dispBlock == E_BLOCK_AIR)
			{
				dispChunk->SetBlock(dispRelCoord, E_BLOCK_FIRE, 0);

				bool ItemBroke = m_Contents.DamageItem(a_SlotNum, 1);

				if (ItemBroke)
				{
					m_Contents.ChangeSlotCount(a_SlotNum, -1);
				}
			}
			break;
		}

		case E_ITEM_FIRE_CHARGE:
		{
			if (SpawnProjectileFromDispenser(dispAbsCoord, cProjectileEntity::pkFireCharge, GetShootVector(meta) * 20) != cEntity::INVALID_ID)
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_ARROW:
		{
			if (SpawnProjectileFromDispenser(dispAbsCoord, cProjectileEntity::pkArrow, GetShootVector(meta) * 30 + Vector3d(0, 1, 0)) != cEntity::INVALID_ID)
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_SNOWBALL:
		{
			if (SpawnProjectileFromDispenser(dispAbsCoord, cProjectileEntity::pkSnowball, GetShootVector(meta) * 20 + Vector3d(0, 1, 0)) != cEntity::INVALID_ID)
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_EGG:
		{
			if (SpawnProjectileFromDispenser(dispAbsCoord, cProjectileEntity::pkEgg, GetShootVector(meta) * 20 + Vector3d(0, 1, 0)) != cEntity::INVALID_ID)
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_BOTTLE_O_ENCHANTING:
		{
			if (SpawnProjectileFromDispenser(dispAbsCoord, cProjectileEntity::pkExpBottle, GetShootVector(meta) * 20 + Vector3d(0, 1, 0)) != cEntity::INVALID_ID)
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_POTION:
		{
			if (SpawnProjectileFromDispenser(dispAbsCoord, cProjectileEntity::pkSplashPotion, GetShootVector(meta) * 20 + Vector3d(0, 1, 0), &SlotItem) != cEntity::INVALID_ID)
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_DYE:
		{
			if (SlotItem.m_ItemDamage != E_META_DYE_WHITE)
			{
				DropFromSlot(a_Chunk, a_SlotNum);
				break;
			}
			if (m_World->GrowRipePlant(dispAbsCoord.x, dispAbsCoord.y, dispAbsCoord.z, true))
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_BOAT:
		case E_ITEM_SPRUCE_BOAT:
		case E_ITEM_BIRCH_BOAT:
		case E_ITEM_JUNGLE_BOAT:
		case E_ITEM_ACACIA_BOAT:
		case E_ITEM_DARK_OAK_BOAT:
		{
			Vector3d spawnPos = dispAbsCoord;
			if (IsBlockWater(dispBlock))
			{
				// Water next to the dispenser, spawn a boat above the water block
				spawnPos.y += 1;
			}
			else if (IsBlockWater(dispChunk->GetBlock(dispRelCoord.addedY(-1))))
			{
				// Water one block below the dispenser, spawn a boat at the dispenser's Y level
				// No adjustment needed
			}
			else
			{
				// There's no eligible water block, drop the boat as a pickup
				DropFromSlot(a_Chunk, a_SlotNum);
				break;
			}

			spawnPos += GetShootVector(meta) * 0.8;  // A boat is bigger than one block. Add the shoot vector to put it outside the dispenser.
			spawnPos += Vector3d(0.5, 0.5, 0.5);

			if (m_World->SpawnBoat(spawnPos, cBoat::ItemToMaterial(SlotItem)))
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		case E_ITEM_FIREWORK_ROCKET:
		{
			if (SpawnProjectileFromDispenser(dispAbsCoord, cProjectileEntity::pkFirework, GetShootVector(meta) * 20 + Vector3d(0, 1, 0), &SlotItem) != cEntity::INVALID_ID)
			{
				m_Contents.ChangeSlotCount(a_SlotNum, -1);
			}
			break;
		}

		default:
		{
			DropFromSlot(a_Chunk, a_SlotNum);
			break;
		}
	}  // switch (SlotItem.m_ItemType)
}

UInt32 cDispenserEntity::SpawnProjectileFromDispenser(Vector3i a_BlockPos, cProjectileEntity::eKind a_Kind, const Vector3d & a_ShootVector, const cItem * a_Item)
{
	return m_World->CreateProjectile(Vector3d(0.5, 0.5, 0.5) + a_BlockPos,
		a_Kind, nullptr, a_Item, &a_ShootVector
	);
}

Vector3d cDispenserEntity::GetShootVector(NIBBLETYPE a_Meta)
{
	switch (a_Meta & E_META_DROPSPENSER_FACING_MASK)
	{
		case E_META_DROPSPENSER_FACING_YP: return Vector3d( 0,  1,  0);
		case E_META_DROPSPENSER_FACING_YM: return Vector3d( 0, -1,  0);
		case E_META_DROPSPENSER_FACING_XM: return Vector3d(-1,  0,  0);
		case E_META_DROPSPENSER_FACING_XP: return Vector3d( 1,  0,  0);
		case E_META_DROPSPENSER_FACING_ZM: return Vector3d( 0,  0, -1);
		case E_META_DROPSPENSER_FACING_ZP: return Vector3d( 0,  0,  1);
	}
	LOGWARNING("Unhandled dispenser meta: %d", a_Meta);
	ASSERT(!"Unhandled dispenser facing");
	return Vector3d(0, 1, 0);
}

bool cDispenserEntity::ScoopUpLiquid(int a_SlotNum, short a_ResultingBucketItemType)
{
	cItem LiquidBucket(a_ResultingBucketItemType);
	if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1)
	{
		// Special case: replacing one empty bucket with one full bucket
		m_Contents.SetSlot(a_SlotNum, LiquidBucket);
		return true;
	}

	// There are stacked buckets at the selected slot, see if a full bucket will fit somewhere else
	if (m_Contents.HowManyCanFit(LiquidBucket) < 1)
	{
		// Cannot fit into m_Contents
		return false;
	}

	m_Contents.ChangeSlotCount(a_SlotNum, -1);
	m_Contents.AddItem(LiquidBucket);
	return true;
}

bool cDispenserEntity::EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum)
{
	if (
		(a_BlockInFront != E_BLOCK_AIR) &&
		!IsBlockLiquid(a_BlockInFront) &&
		!cFluidSimulator::CanWashAway(a_BlockInFront)
	)
	{
		// Not a suitable block in front
		return false;
	}

	cItem EmptyBucket(E_ITEM_BUCKET, 1);
	if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1)
	{
		// Change the single full bucket present into a single empty bucket
		m_Contents.SetSlot(a_SlotNum, EmptyBucket);
		return true;
	}

	// There are full buckets stacked at this slot, check if we can fit in the empty bucket
	if (m_Contents.HowManyCanFit(EmptyBucket) < 1)
	{
		// The empty bucket wouldn't fit into m_Contents
		return false;
	}

	// The empty bucket fits in, remove one full bucket and add the empty one
	m_Contents.ChangeSlotCount(a_SlotNum, -1);
	m_Contents.AddItem(EmptyBucket);
	return true;
}
<|endoftext|>


<commit_before>#ifndef DUNE_STUFF_FUNCTION_AFFINEPARAMETRIC_DEFAULT_HH
#define DUNE_STUFF_FUNCTION_AFFINEPARAMETRIC_DEFAULT_HH

#include <memory>
#include <vector>

#include <dune/common/shared_ptr.hh>

#include <dune/stuff/common/parameter.hh>
#include <dune/stuff/common/string.hh>
#include <dune/stuff/common/color.hh>
#include <dune/stuff/common/parameter/tree.hh>

#include <dune/stuff/function.hh>
#include "coefficient.hh"

namespace Dune {
namespace Stuff {

template< class DomainFieldImp, int domainDim, class RangeFieldImp, int rangeDim >
class FunctionAffineParametricDefault
  : public FunctionInterface< DomainFieldImp, domainDim, RangeFieldImp, rangeDim >
{
public:
  typedef FunctionAffineParametricDefault< DomainFieldImp, domainDim, RangeFieldImp, rangeDim > ThisType;
  typedef FunctionInterface< DomainFieldImp, domainDim, RangeFieldImp, rangeDim >               BaseType;

  typedef typename BaseType::DomainFieldType  DomainFieldType;
  static const int                            dimDomain = BaseType::dimDomain;
  typedef typename BaseType::DomainType       DomainType;
  typedef typename BaseType::RangeFieldType   RangeFieldType;
  static const int                            dimRange = BaseType::dimRange;
  typedef typename BaseType::RangeType        RangeType;
  typedef typename BaseType::ParamFieldType   ParamFieldType;
  typedef typename BaseType::ParamType        ParamType;

  typedef typename BaseType::ComponentType    ComponentType;
  typedef typename BaseType::CoefficientType  CoefficientType;

  static const std::string id()
  {
    return BaseType::id() + ".affineparametric.default";
  }

  FunctionAffineParametricDefault(const size_t _paramSize,
                                  const std::vector< ParamType > _paramRange,
                                  const std::vector< std::shared_ptr< const ComponentType > > _components,
                                  const std::vector< std::shared_ptr< const CoefficientType > > _coefficients,
                                  const std::vector< std::string > _parameterExplanation = std::vector< std::string >(),
                                  const int _order = -1,
                                  const std::string _name = id())
    : paramSize_(_paramSize)
    , paramRange_(_paramRange)
    , components_(_components)
    , coefficients_(_coefficients)
    , order_(_order)
    , name_(_name)
  {
    // some checks
    assert(paramSize_ > 0);
    assert(components_.size() > 0);
    assert(coefficients_.size() > 0);
    assert((components_.size() == coefficients_.size())
           || (components_.size() == coefficients_.size() + 1));
    assert(paramRange_.size() == 2 && "Vector has wrong size!");
    assert(paramRange_[0].size() == paramSize_ && "Vector has wrong size!");
    assert(paramRange_[1].size() == paramSize_ && "Vector has wrong size!");
    for (size_t qq = 0; qq < paramSize_; ++qq) {
      assert(paramRange_[0][qq] <= paramRange_[1][qq]
          && "Given minimum parameter has to be piecewise <= maximum parameter!");
    }
    for (size_t qq = 0; qq < components_.size(); ++qq) {
      if (components_[qq]->parametric())
        DUNE_THROW(Dune::RangeError,
                   "\n" << DSC::colorStringRed("ERROR:")
                   << " component " << qq << " is parametric!");
    }
    // process parameter explanation
    for (size_t qq = 0; qq < std::min(paramSize_, _parameterExplanation.size()); ++qq)
      parameterExplanation_.push_back(_parameterExplanation[qq]);
    for (size_t qq = std::min(paramSize_, _parameterExplanation.size()); qq < paramSize_; ++qq)
      parameterExplanation_.push_back("parameter_component_" + DSC::toString(qq));
  } // FunctionAffineParametricDefault(...)

  FunctionAffineParametricDefault(const ThisType& other)
    : paramSize_(other.paramSize_)
    , paramRange_(other.paramRange_)
    , components_(other.components_)
    , coefficients_(other.coefficients_)
    , order_(other.order_)
    , name_(other.name_)
    , parameterExplanation_(other.parameterExplanation_)
  {}

  ThisType& operator=(const ThisType& other)
  {
    if (this != &other) {
      paramSize_ = other.paramSize();
      paramRange_ = other.paramRange();
      components_ = other.components();
      coefficients_ = other.coefficients();
      order_ = other.order();
      name_ = other.name();
      parameterExplanation_ = other.parameterExplanation();
    }
    return this;
  }

  static Dune::ParameterTree createDefaultSettings(const std::string subName = "")
  {
    Dune::ParameterTree description;
    description["name"] = id();
    description["order"] = "1";
    description["component.0"] = "2.0 * x[0]";
    description["component.1"] = "sin(x[0])";
    description["coefficient.0"] = "mu[0] + mu[1]";
    description["paramSize"] = "2";
    description["paramMin"] = "[0.1; 0.1]";
    description["paramMax"] = "[1.0; 1.0]";
    description["paramExplanation"] = "[first_parameter; second_parameter]";
    if (subName.empty())
      return description;

    DSC::ExtendedParameterTree extendedDescription;
    extendedDescription.add(description, subName);
    return extendedDescription;
  } // ... createDefaultSettings(...)

  static ThisType* create(const DSC::ExtendedParameterTree settings)
  {
    // first of all, read the optional stuff
    const std::string _name = settings.get< std::string >("name", id());
    const int _order = settings.get< int >("order", -1);
    // then, read what has to exist
    // * components
    std::vector< std::shared_ptr< const ComponentType > > _components;
    bool continue_search = true;
    while (continue_search) {
      // lets see if there is a component with this index
      const std::string key = "component." + DSC::toString(_components.size());
      if (settings.hasSub(key)) {
        // there is a sub
        Dune::ParameterTree componentSettings = settings.sub(key);
        // lets see if it has a type
        if (componentSettings.hasKey("type")) {
          const std::string type = componentSettings.get< std::string >("type");
          if (type == "function.affineparametric.default")
            DUNE_THROW(Dune::IOError,
                       "\n" << DSC::colorStringRed("ERROR:")
                       << " can not create a 'function.affineparametric.default' inside itself!");
          _components.emplace_back(Functions< DomainFieldType, dimDomain,
                                              RangeFieldType, dimRange >::create(type, componentSettings));
        } else {
          // since it does not have a type, treat it as an expression function
          if (!(componentSettings.hasKey("expression") || componentSettings.hasSub("expression")))
            DUNE_THROW(Dune::IOError,
                       "\n" << DSC::colorStringRed("ERROR:")
                       << " could not interpret component." << _components.size()
                       << "in the following Dune::ParameterTree:\n"
                       << settings.reportString("  "));
          if (!componentSettings.hasKey("variable"))
            componentSettings["variable"] = "x";
          if (!componentSettings.hasKey("name"))
            componentSettings["name"] = _name;
          if (!componentSettings.hasKey("order"))
            componentSettings["order"] = DSC::toString(_order);
          _components.emplace_back(Functions< DomainFieldType, dimDomain,
                                              RangeFieldType, dimRange >::create("function.expression",
                                                                               componentSettings));
        } // if (componentSettings.hasKey("type"))
      } else if (settings.hasKey(key)) {
        // there is only one key, interpret it as an entry for an expression function with variable x
        Dune::ParameterTree componentSettings;
        componentSettings["name"] = _name;
        componentSettings["order"] = DSC::toString(_order);
        componentSettings["variable"] = "x";
        componentSettings["expression"] = settings.get< std::string >(key);
        _components.emplace_back(Functions< DomainFieldType, dimDomain,
                                            RangeFieldType, dimRange >::create("function.expression",
                                                                             componentSettings));
      } else {
        // stop the search
        continue_search = false;
      }
    } // while (continue_search)
    if (_components.size() == 0)
      DUNE_THROW(Dune::IOError,
                 "\n" << DSC::colorStringRed("ERROR:")
                 << " no 'component' found in the following settings:\n" << settings.reportString("  "));
    // check that all components are nonparametric
    for (size_t qq = 0; qq < _components.size(); ++qq)
      if (_components[qq]->parametric())
        DUNE_THROW(Dune::RangeError,
                   "\n" << DSC::colorStringRed("ERROR:")
                   << " component " << qq << " is parametric!");
    // * coefficients
    std::vector< std::shared_ptr< const CoefficientType > > _coefficients;
    continue_search = true;
    while (continue_search) {
      // lets see if there is a coefficient with this index
      const std::string key = "coefficient." + DSC::toString(_coefficients.size());
      if (settings.hasKey(key)) {
        _coefficients.emplace_back(new CoefficientType(settings.get< std::string >(key)));
      } else
        continue_search = false;
    } // while (continue_search)
    if (_coefficients.size() == 0)
      DUNE_THROW(Dune::IOError,
                 "\n" << DSC::colorStringRed("ERROR:")
                 << " no 'coefficient' found in the following settings:\n" << settings.reportString("  "));
    else if (!(_coefficients.size() == _components.size()
             || _coefficients.size() == (_components.size() - 1)))
      DUNE_THROW(Dune::IOError,
                 "\n" << DSC::colorStringRed("ERROR:")
                 << " wrong number of 'coefficient' found in the following settings:\n"
                 << settings.reportString("  "));
    // * paramSize
    const size_t _paramSize = settings.get< size_t >("paramSize");
    // * paramRange
    const std::vector< ParamFieldType > _paramMins = settings.getVector< ParamFieldType >("paramMin", _paramSize);
    const std::vector< ParamFieldType > _paramMaxs = settings.getVector< ParamFieldType >("paramMax", _paramSize);
    ParamType _paramMin(_paramSize);
    ParamType _paramMax(_paramSize);
    for (size_t pp = 0; pp < _paramSize; ++pp) {
      _paramMin[pp] = _paramMins[pp];
      _paramMax[pp] = _paramMaxs[pp];
    }
    std::vector< ParamType > _paramRange;
    _paramRange.push_back(_paramMin);
    _paramRange.push_back(_paramMax);
    // * paramExplanation
    std::vector< std::string > _paramExplanation;
    if (settings.hasVector("paramExplanation"))
      _paramExplanation = settings.getVector< std::string >("paramExplanation", 1);
    else if (settings.hasKey("paramExplanation"))
      _paramExplanation.push_back(settings.get< std::string >("paramExplanation"));
    return new ThisType(_paramSize, _paramRange, _components, _coefficients, _paramExplanation, _order, _name);
  } // ... create(...)

  virtual bool parametric() const
  {
    return true;
  }

  virtual bool affineparametric() const
  {
    return true;
  }

  virtual int order() const
  {
    return order_;
  }

  virtual std::string name() const
  {
    return name_;
  }

  virtual size_t paramSize() const
  {
    return paramSize_;
  }

  virtual const std::vector< ParamType >& paramRange() const
  {
    return paramRange_;
  }

  virtual const std::vector< std::string >& paramExplanation() const
  {
    return parameterExplanation_;
  }

  virtual size_t numComponents() const
  {
    return components_.size();
  }

  virtual size_t numCoefficients() const
  {
    return coefficients_.size();
  }

  virtual const std::vector< std::shared_ptr< const ComponentType > >& components() const
  {
    return components_;
  }

  virtual const std::vector< std::shared_ptr< const CoefficientType > >& coefficients() const
  {
    return coefficients_;
  }

  virtual void evaluate(const DomainType& _x, const ParamType& _mu, RangeType& _ret) const
  {
    assert(_mu.size() == paramSize_);
    _ret = RangeFieldType(0);
    RangeType tmpComponentValue;
    for (size_t qq = 0; qq < numCoefficients(); ++qq) {
      components_[qq]->evaluate(_x, tmpComponentValue);
      tmpComponentValue *= coefficients_[qq]->evaluate(_mu);
      _ret += tmpComponentValue;
    }
    if (numComponents() > numCoefficients()) {
      components_[numCoefficients()]->evaluate(_x, tmpComponentValue);
      _ret += tmpComponentValue;
    }
  } // virtual void evaluate(const DomainType& _x, const ParamType& _mu, RangeType& _ret) const

private:
  size_t paramSize_;
  std::vector< ParamType > paramRange_;
  std::vector< std::shared_ptr< const ComponentType > > components_;
  std::vector< std::shared_ptr< const CoefficientType > > coefficients_;
  const int order_;
  const std::string name_;
  std::vector< std::string > parameterExplanation_;
}; // class FunctionAffineParametricDefault

} // namespace Stuff
} // namespace Dune

#endif // DUNE_STUFF_FUNCTION_AFFINEPARAMETRIC_DEFAULT_HH
<commit_msg>[function.affineparametric.default] updated to new layout<commit_after>#ifndef DUNE_STUFF_FUNCTION_AFFINEPARAMETRIC_DEFAULT_HH
#define DUNE_STUFF_FUNCTION_AFFINEPARAMETRIC_DEFAULT_HH

#include <memory>
#include <vector>

#include <dune/common/shared_ptr.hh>

#include <dune/stuff/common/parameter.hh>
#include <dune/stuff/common/string.hh>
#include <dune/stuff/common/color.hh>
#include <dune/stuff/common/parameter/tree.hh>

#include <dune/stuff/function.hh>
#include "coefficient.hh"

namespace Dune {
namespace Stuff {

// forward to allow for specialization
template< class DomainFieldImp, int domainDim, class RangeFieldImp, int rangeDimRows, int rangeDimCols = 1>
class AffineParametricFunctionDefault;

template< class DomainFieldImp, int domainDim, class RangeFieldImp, int rangeDim >
class AffineParametricFunctionDefault< DomainFieldImp, domainDim, RangeFieldImp, rangeDim, 1 >
  : public AffineParametricFunctionInterface< DomainFieldImp, domainDim, RangeFieldImp, rangeDim, 1 >
{
  typedef AffineParametricFunctionInterface< DomainFieldImp, domainDim, RangeFieldImp, rangeDim, 1 >  BaseType;
public:
  typedef AffineParametricFunctionDefault< DomainFieldImp, domainDim, RangeFieldImp, rangeDim, 1 >    ThisType;

  typedef typename BaseType::DomainFieldType  DomainFieldType;
  static const int                            dimDomain = BaseType::dimDomain;
  typedef typename BaseType::DomainType       DomainType;
  typedef typename BaseType::RangeFieldType   RangeFieldType;
  static const int                            dimRange = BaseType::dimRange;
  typedef typename BaseType::RangeType        RangeType;
  typedef typename BaseType::ParamFieldType   ParamFieldType;
  typedef typename BaseType::ParamType        ParamType;

  typedef typename BaseType::ComponentType    ComponentType;
  typedef typename BaseType::CoefficientType  CoefficientType;

  static const std::string id()
  {
    return BaseType::id() + ".default";
  }

  AffineParametricFunctionDefault(const size_t _paramSize,
                                  const std::vector< ParamType > _paramRange,
                                  const std::vector< std::shared_ptr< const ComponentType > > _components,
                                  const std::vector< std::shared_ptr< const CoefficientType > > _coefficients,
                                  const std::vector< std::string > _parameterExplanation = std::vector< std::string >(),
                                  const int _order = -1,
                                  const std::string _name = id())
    : paramSize_(_paramSize)
    , paramRange_(_paramRange)
    , components_(_components)
    , coefficients_(_coefficients)
    , hasAffinePart_(false)
    , order_(_order)
    , name_(_name)
  {
    // some checks
    assert(paramSize_ > 0);
    assert(components_.size() > 0);
    assert(coefficients_.size() > 0);
    assert(components_.size() == coefficients_.size());
    assert(paramRange_.size() == 2 && "Vector has wrong size!");
    assert(paramRange_[0].size() == paramSize_ && "Vector has wrong size!");
    assert(paramRange_[1].size() == paramSize_ && "Vector has wrong size!");
    for (size_t qq = 0; qq < paramSize_; ++qq) {
      assert(paramRange_[0][qq] <= paramRange_[1][qq]
          && "Given minimum parameter has to be piecewise <= maximum parameter!");
    }
    for (size_t qq = 0; qq < components_.size(); ++qq) {
      if (components_[qq]->parametric())
        DUNE_THROW(Dune::RangeError,
                   "\n" << DSC::colorStringRed("ERROR:")
                   << " component " << qq << " is parametric!");
    }
    // process parameter explanation
    for (size_t qq = 0; qq < std::min(paramSize_, _parameterExplanation.size()); ++qq)
      parameterExplanation_.push_back(_parameterExplanation[qq]);
    for (size_t qq = std::min(paramSize_, _parameterExplanation.size()); qq < paramSize_; ++qq)
      parameterExplanation_.push_back("parameter_component_" + DSC::toString(qq));
  } // FunctionAffineParametricDefault(...)

  AffineParametricFunctionDefault(const size_t _paramSize,
                                  const std::vector< ParamType > _paramRange,
                                  const std::vector< std::shared_ptr< const ComponentType > > _components,
                                  const std::vector< std::shared_ptr< const CoefficientType > > _coefficients,
                                  const std::shared_ptr< const ComponentType > _affinePart,
                                  const std::vector< std::string > _parameterExplanation = std::vector< std::string >(),
                                  const int _order = -1,
                                  const std::string _name = id())
    : paramSize_(_paramSize)
    , paramRange_(_paramRange)
    , components_(_components)
    , coefficients_(_coefficients)
    , hasAffinePart_(true)
    , order_(_order)
    , name_(_name)
    , affinePart_(_affinePart)

  {
    // some checks
    assert(paramSize_ > 0);
    assert(components_.size() > 0);
    assert(coefficients_.size() > 0);
    assert(components_.size() == coefficients_.size());
    assert(paramRange_.size() == 2 && "Vector has wrong size!");
    assert(paramRange_[0].size() == paramSize_ && "Vector has wrong size!");
    assert(paramRange_[1].size() == paramSize_ && "Vector has wrong size!");
    for (size_t qq = 0; qq < paramSize_; ++qq) {
      assert(paramRange_[0][qq] <= paramRange_[1][qq]
          && "Given minimum parameter has to be piecewise <= maximum parameter!");
    }
    for (size_t qq = 0; qq < components_.size(); ++qq) {
      if (components_[qq]->parametric())
        DUNE_THROW(Dune::RangeError,
                   "\n" << DSC::colorStringRed("ERROR:")
                   << " component " << qq << " is parametric!");
    }
    if (affinePart_->parametric())
      DUNE_THROW(Dune::RangeError,
                 "\n" << DSC::colorStringRed("ERROR:")
                 << " affinePart must not be parametric!");
    // process parameter explanation
    for (size_t qq = 0; qq < std::min(paramSize_, _parameterExplanation.size()); ++qq)
      parameterExplanation_.push_back(_parameterExplanation[qq]);
    for (size_t qq = std::min(paramSize_, _parameterExplanation.size()); qq < paramSize_; ++qq)
      parameterExplanation_.push_back("parameter_component_" + DSC::toString(qq));
  } // FunctionAffineParametricDefault(...)

  static Dune::ParameterTree defaultSettings(const std::string subName = "")
  {
    Dune::ParameterTree description;
    description["name"] = id();
    description["order"] = "1";
    description["component.0"] = "2.0 * x[0]";
    description["coefficient.0"] = "mu[0] + mu[1]";
    description["affinePart"] = "sin(x[0])";
    description["paramSize"] = "2";
    description["paramMin"] = "[0.1; 0.1]";
    description["paramMax"] = "[1.0; 1.0]";
    description["paramExplanation"] = "[first_parameter; second_parameter]";
    if (subName.empty())
      return description;
    DSC::ExtendedParameterTree extendedDescription;
    extendedDescription.add(description, subName);
    return extendedDescription;
  } // ... defaultSettings(...)

  static ThisType* create(const DSC::ExtendedParameterTree settings)
  {
    // first of all, read the optional stuff
    const std::string _name = settings.get< std::string >("name", id());
    const int _order = settings.get< int >("order", -1);
    // then, read what has to exist
    // * components
    std::vector< std::shared_ptr< const ComponentType > > _components;
    bool continue_search = true;
    while (continue_search) {
      // lets see if there is a component with this index
      const std::string key = "component." + DSC::toString(_components.size());
      if (settings.hasSub(key)) {
        // there is a sub
        Dune::ParameterTree componentSettings = settings.sub(key);
        // lets see if it has a type
        if (componentSettings.hasKey("type")) {
          const std::string type = componentSettings.get< std::string >("type");
          if (type == "function.affineparametric.default")
            DUNE_THROW(Dune::IOError,
                       "\n" << DSC::colorStringRed("ERROR:")
                       << " can not create a 'function.affineparametric.default' inside itself!");
          _components.emplace_back(Functions< DomainFieldType, dimDomain,
                                              RangeFieldType, dimRange >::create(type, componentSettings));
        } else {
          // since it does not have a type, treat it as an expression function
          if (!(componentSettings.hasKey("expression") || componentSettings.hasSub("expression")))
            DUNE_THROW(Dune::IOError,
                       "\n" << DSC::colorStringRed("ERROR:")
                       << " could not interpret component." << _components.size()
                       << "in the following settings:\n"
                       << settings.reportString("  "));
          if (!componentSettings.hasKey("variable"))
            componentSettings["variable"] = "x";
          if (!componentSettings.hasKey("name"))
            componentSettings["name"] = _name;
          if (!componentSettings.hasKey("order"))
            componentSettings["order"] = DSC::toString(_order);
          _components.emplace_back(Functions< DomainFieldType, dimDomain,
                                              RangeFieldType, dimRange >::create("function.expression",
                                                                               componentSettings));
        } // if (componentSettings.hasKey("type"))
      } else if (settings.hasKey(key)) {
        // there is only one key, interpret it as an entry for an expression function with variable x
        Dune::ParameterTree componentSettings;
        componentSettings["name"] = _name;
        componentSettings["order"] = DSC::toString(_order);
        componentSettings["variable"] = "x";
        componentSettings["expression"] = settings.get< std::string >(key);
        _components.emplace_back(Functions< DomainFieldType, dimDomain,
                                            RangeFieldType, dimRange >::create("function.expression",
                                                                             componentSettings));
      } else {
        // stop the search
        continue_search = false;
      }
    } // while (continue_search)
    if (_components.size() == 0)
      DUNE_THROW(Dune::IOError,
                 "\n" << DSC::colorStringRed("ERROR:")
                 << " no 'component' found in the following settings:\n" << settings.reportString("  "));
    // check that all components are nonparametric
    for (size_t qq = 0; qq < _components.size(); ++qq)
      if (_components[qq]->parametric())
        DUNE_THROW(Dune::RangeError,
                   "\n" << DSC::colorStringRed("ERROR:")
                   << " component " << qq << " is parametric!");
    // * coefficients
    std::vector< std::shared_ptr< const CoefficientType > > _coefficients;
    continue_search = true;
    while (continue_search) {
      // lets see if there is a coefficient with this index
      const std::string key = "coefficient." + DSC::toString(_coefficients.size());
      if (settings.hasKey(key)) {
        _coefficients.emplace_back(new CoefficientType(settings.get< std::string >(key)));
      } else
        continue_search = false;
    } // while (continue_search)
    if (_coefficients.size() == 0)
      DUNE_THROW(Dune::IOError,
                 "\n" << DSC::colorStringRed("ERROR:")
                 << " no 'coefficient' found in the following settings:\n" << settings.reportString("  "));
    else if (!(_coefficients.size() == _components.size()))
      DUNE_THROW(Dune::IOError,
                 "\n" << DSC::colorStringRed("ERROR:")
                 << " wrong number of 'coefficient' found in the following settings:\n"
                 << settings.reportString("  "));
    // the affine part
    std::shared_ptr< const ComponentType > _affinePart;
    bool _hasAffinePart = false;
    if (settings.hasSub("affinePart")) {
      _hasAffinePart = true;
      // there is a sub
      Dune::ParameterTree affinePartSettings = settings.sub("affinePart");
      // lets see if it has a type
      if (affinePartSettings.hasKey("type")) {
        const std::string type = affinePartSettings.get< std::string >("type");
        if (type == "function.affineparametric.default")
          DUNE_THROW(Dune::IOError,
                     "\n" << DSC::colorStringRed("ERROR:")
                     << " can not create a 'function.affineparametric.default' inside itself!");
        _affinePart = std::shared_ptr< ComponentType >(Functions< DomainFieldType, dimDomain,
                                                                  RangeFieldType, dimRange >::create(type, affinePartSettings));
      } else {
        // since it does not have a type, treat it as an expression function
        if (!(affinePartSettings.hasKey("expression") || affinePartSettings.hasSub("expression")))
          DUNE_THROW(Dune::IOError,
                     "\n" << DSC::colorStringRed("ERROR:")
                     << " could not interpret affinePart in the following settings:\n"
                     << settings.reportString("  "));
        if (!affinePartSettings.hasKey("variable"))
          affinePartSettings["variable"] = "x";
        if (!affinePartSettings.hasKey("name"))
          affinePartSettings["name"] = _name;
        if (!affinePartSettings.hasKey("order"))
          affinePartSettings["order"] = DSC::toString(_order);
        _affinePart = std::shared_ptr< ComponentType >(Functions< DomainFieldType, dimDomain,
                                                                  RangeFieldType, dimRange >::create("function.expression", affinePartSettings));
      } // if (componentSettings.hasKey("type"))
    } else if (settings.hasKey("affinePart")) {
      _hasAffinePart = true;
      // there is only one key, interpret it as an entry for an expression function with variable x
      Dune::ParameterTree affinePartSettings;
      affinePartSettings["name"] = _name;
      affinePartSettings["order"] = DSC::toString(_order);
      affinePartSettings["variable"] = "x";
      affinePartSettings["expression"] = settings.get< std::string >("affinePart");
      _affinePart = std::shared_ptr< ComponentType >(Functions< DomainFieldType, dimDomain,
                                                                RangeFieldType, dimRange >::create("function.expression", affinePartSettings));
    }
    // * paramSize
    const size_t _paramSize = settings.get< size_t >("paramSize");
    // * paramRange
    const std::vector< ParamFieldType > _paramMins = settings.getVector< ParamFieldType >("paramMin", _paramSize);
    const std::vector< ParamFieldType > _paramMaxs = settings.getVector< ParamFieldType >("paramMax", _paramSize);
    ParamType _paramMin(_paramSize);
    ParamType _paramMax(_paramSize);
    for (size_t pp = 0; pp < _paramSize; ++pp) {
      _paramMin[pp] = _paramMins[pp];
      _paramMax[pp] = _paramMaxs[pp];
    }
    std::vector< ParamType > _paramRange;
    _paramRange.push_back(_paramMin);
    _paramRange.push_back(_paramMax);
    // * paramExplanation
    std::vector< std::string > _paramExplanation;
    if (settings.hasVector("paramExplanation"))
      _paramExplanation = settings.getVector< std::string >("paramExplanation", 1);
    else if (settings.hasKey("paramExplanation"))
      _paramExplanation.push_back(settings.get< std::string >("paramExplanation"));
    if (_hasAffinePart)
      return new ThisType(_paramSize, _paramRange, _components, _coefficients, _affinePart, _paramExplanation, _order, _name);
    else
      return new ThisType(_paramSize, _paramRange, _components, _coefficients, _paramExplanation, _order, _name);
  } // ... create(...)

  virtual bool parametric() const
  {
    return true;
  }

  virtual bool affineparametric() const
  {
    return true;
  }

  virtual int order() const
  {
    return order_;
  }

  virtual std::string name() const
  {
    return name_;
  }

  virtual size_t paramSize() const
  {
    return paramSize_;
  }

  virtual const std::vector< ParamType >& paramRange() const
  {
    return paramRange_;
  }

  virtual const std::vector< std::string >& paramExplanation() const
  {
    return parameterExplanation_;
  }

  virtual const std::vector< std::shared_ptr< const ComponentType > >& components() const
  {
    return components_;
  }

  virtual const std::vector< std::shared_ptr< const CoefficientType > >& coefficients() const
  {
    return coefficients_;
  }

  virtual bool hasAffinePart() const
  {
    return hasAffinePart_;
  }

  virtual const std::shared_ptr< const ComponentType > & affinePart() const
  {
    DUNE_THROW(Dune::InvalidStateException,
               "\n" << Dune::Stuff::Common::colorStringRed("ERROR:")
               << " do not call affinePart() if hasAffinePart() is false!");
    return affinePart_;
  }

  virtual void evaluate(const DomainType& _x, const ParamType& _mu, RangeType& _ret) const
  {
    assert(_mu.size() == paramSize_);
    _ret = RangeFieldType(0);
    RangeType tmpComponentValue;
    RangeFieldType tmpCoefficientValue;
    for (size_t qq = 0; qq < coefficients_.size(); ++qq) {
      components_[qq]->evaluate(_x, tmpComponentValue);
      coefficients_[qq]->evaluate(_mu, tmpCoefficientValue);
      tmpComponentValue *= tmpCoefficientValue;
      _ret += tmpComponentValue;
    }
    if (hasAffinePart_) {
      affinePart_->evaluate(_x, tmpComponentValue);
      _ret += tmpComponentValue;
    }
  } // virtual void evaluate(const DomainType& _x, const ParamType& _mu, RangeType& _ret) const

private:
  size_t paramSize_;
  std::vector< ParamType > paramRange_;
  std::vector< std::shared_ptr< const ComponentType > > components_;
  std::vector< std::shared_ptr< const CoefficientType > > coefficients_;
  bool hasAffinePart_;
  const int order_;
  const std::string name_;
  std::shared_ptr< const ComponentType > affinePart_;
  std::vector< std::string > parameterExplanation_;
}; // class AffineParametricFunctionDefault

} // namespace Stuff
} // namespace Dune

#endif // DUNE_STUFF_FUNCTION_AFFINEPARAMETRIC_DEFAULT_HH
<|endoftext|>


<commit_before>// $Id: AST.hh,v 1.3 2004/01/25 21:21:54 stefan Exp $
//
// Copyright (C) 2004 Stefan Seefeld
// All rights reserved.
// Licensed to the public under the terms of the GNU LGPL (>= 2),
// see the file COPYING for details.
//

#ifndef _Synopsis_AST_AST_hh
#define _Synopsis_AST_AST_hh

#include <Synopsis/Object.hh>
#include <Synopsis/AST/Declaration.hh>

namespace Synopsis
{
namespace AST
{

class AST : public Object
{
public:
  AST() {}
  AST(const Object &o) throw(TypeError) : Object(o) { assert_type();}

  Dict files() { return Dict(attr("files")());}
  List declarations() { return List(attr("declarations")());}
  void assert_type() throw(TypeError) { Object::assert_type("Synopsis.AST", "AST");}
};

}
}

#endif
<commit_msg>add 'types()' method from python API<commit_after>//
// Copyright (C) 2004 Stefan Seefeld
// All rights reserved.
// Licensed to the public under the terms of the GNU LGPL (>= 2),
// see the file COPYING for details.
//

#ifndef _Synopsis_AST_AST_hh
#define _Synopsis_AST_AST_hh

#include <Synopsis/Object.hh>
#include <Synopsis/AST/Declaration.hh>

namespace Synopsis
{
namespace AST
{

class AST : public Object
{
public:
  AST() {}
  AST(const Object &o) throw(TypeError) : Object(o) { assert_type();}

  Dict files() { return Dict(attr("files")());}
  Object types() { return attr("types")();}
  List declarations() { return List(attr("declarations")());}
  void assert_type() throw(TypeError) { Object::assert_type("Synopsis.AST", "AST");}
};

}
}

#endif
<|endoftext|>


<commit_before>#ifndef THREAD_POOL_HPP_INCLUDED
#define THREAD_POOL_HPP_INCLUDED

/**
 @file thread_pool.hpp
 @author François Becker

MIT License

Copyright (c) 2015-2018 François Becker

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 OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <thread>
#include <vector>
#include <queue>
#include <sstream>
#if __APPLE__
#include <pthread.h>
#endif
#include <fbu/lang_utils.hpp>

namespace fbu
{

/**
 @class ThreadPool
 @brief A simple ThreadPool class that is instantiated with a fixed number of
 threads, a function allows to add jobs, a function allows to wait for jobs
 termination.
 */
class ThreadPool
{
    std::vector<std::thread> mThreads;
    std::atomic_bool mTerminate{false};
    std::atomic_int mNumBusyThreads{0};
    std::condition_variable mJobAvailableCV;
    std::condition_variable mCompletionCV;
    std::queue< std::function<void(void)> > mJobsQueue;
    std::mutex mJobsQueueMutex;

public:
    /**
     Constructor.
     @param pNumThreads The number of threads in the ThreadPool. Use 0 for an
     automatic guess of the number of concurrent threads supported by the
     hardware platform. If it can't be determined at runtime, a default number
     of 2 threads is considered.
     @param pName The name to give to the threads, as a prefix followed by a number.
     */
    ThreadPool(int pNumThreads = 0, const std::string& pName = "fbu::ThreadPool")
    : mThreads(pNumThreads != 0
               ? (unsigned)pNumThreads
               : (std::thread::hardware_concurrency() != 0
                  ? std::thread::hardware_concurrency()
                  : 2u))
    {
        int i = 0;
        for (std::thread& t : mThreads)
        {
            std::string lThreadName = (std::ostringstream() << pName << " " << i).str();
            t = std::thread([this, lThreadName]{
#if __APPLE__
                pthread_setname_np(lThreadName.c_str());
#else
#pragma message("Thread name not implemented on this platform yet")
#endif
                this->threadExecLoop();
            });
            ++i;
        }
    }

    /**
     Destructor.
     Note: call waitForCompletion() beforehand, otherwise the ~ThreadPool will
     not wait for all the jobs to be processed.
     */
    ~ThreadPool()
    {
        mTerminate = true;
        mJobAvailableCV.notify_all();
        for (std::thread& t : mThreads)
        {
            t.join();
        }
        assert(mNumBusyThreads == 0);
    }

    /**
     Add a job.
     */
    void addJob(std::function<void(void)> pJob)
    {
        std::lock_guard<std::mutex> lGuard(mJobsQueueMutex);
        mJobsQueue.push(pJob);
        mJobAvailableCV.notify_one();
    }

    /**
     Wait for the jobs to be completed. Call this method prior to destroying the
     ThreadPool.
     */
    void waitForCompletion()
    {
        std::unique_lock<std::mutex> lLock(mJobsQueueMutex);
        while (mJobsQueue.size() > 0 || mNumBusyThreads > 0)
        {
            mCompletionCV.wait(lLock);
        }
    }

    size_t getNumThreads() const
    {
        return mThreads.size();
    }

    int getNumBusyThreads() const
    {
        return mNumBusyThreads;
    }

private:
    void threadExecLoop()
    {
        while (!mTerminate)
        {
            auto lJob = grabNextJob();
            lJob();
            {
                std::unique_lock<std::mutex> lLock(mJobsQueueMutex);
                --mNumBusyThreads;
                mCompletionCV.notify_all();
            }
        }
    }

    std::function<void(void)> grabNextJob()
    {
        std::function<void(void)> lJob;
        std::unique_lock<std::mutex> lLock(mJobsQueueMutex);

        mJobAvailableCV.wait(lLock, [this]() -> bool { return (mJobsQueue.size() > 0) || mTerminate; });

        ++mNumBusyThreads;
        if (!mTerminate)
        {
            assert(!mJobsQueue.empty());
            lJob = mJobsQueue.front();
            mJobsQueue.pop();
        }
        else
        {
            lJob = []{};
        }
        return lJob;
    }
};

/**
 @class ThreadPoolJobsExecutor
 @brief Manages a list of jobs given to a ThreadPool while allowing to wait for
        all jobs that have been passed through this object to be completed.
 @todo deprecate, use JobCounter instead.
 */
class ThreadPoolJobsExecutor
{
public:
    /**
     Constructor.
     @param pTP The ThreadPool to use.
     */
    ThreadPoolJobsExecutor(ThreadPool& pTP)
    : mTP(pTP)
    {
    }

    /**
     Add a job.
     */
    void addJob(std::function<void(void)> pJob)
    {
        {
            std::unique_lock<std::mutex>(mMutex);
            ++mNumJobs;
        }
        mTP.addJob([this, pJob](){
            pJob();
            {
                std::unique_lock<std::mutex>(mMutex);
                --mNumJobs;
                mCompletion.notify_all();
            }
        });
    }

    /**
     Wait for all the jobs given to the ThreadPool through this object to be completed.
     */
    void waitForCompletion()
    {
        std::unique_lock<std::mutex> lLock(mMutex);
        mCompletion.wait(lLock, [&](){ return mNumJobs == 0; });
    }

private:
    ThreadPool& mTP;
    std::mutex mMutex;
    std::condition_variable mCompletion;
    std::atomic_int mNumJobs{0};
};

/**
 @class JobCounter
 A job counter with a completion waiting method.
 Increment prior to submitting a job to a ThreadPool.
 Decrement at the end of the jobs after unlocking all mutexes on shared data.
 Call waitForCompletion() for waiting for all the jobs to be finished.
 @todo unit test.
 */
class JobCounter : public fbu::lang::NonCopyable
{
public:
    JobCounter() {}

    void increment()
    {
        std::unique_lock<std::mutex> lLock(mMutex);
        ++mNumJobs;
    }

    void decrement()
    {
        std::unique_lock<std::mutex> lLock(mMutex);
        assert(mNumJobs > 0);
        --mNumJobs;
        mCompletion.notify_all();
    }

    void waitForCompletion()
    {
        std::unique_lock<std::mutex> lLock(mMutex);
        mCompletion.wait(lLock, [&]{ return mNumJobs == 0; });
    }

private:
    std::mutex mMutex;
    std::condition_variable mCompletion;
    std::atomic_int mNumJobs{0};
};

}

#endif
<commit_msg>fbu::ThreadPool: variable names given to the locks.<commit_after>#ifndef THREAD_POOL_HPP_INCLUDED
#define THREAD_POOL_HPP_INCLUDED

/**
 @file thread_pool.hpp
 @author François Becker

MIT License

Copyright (c) 2015-2018 François Becker

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 OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <thread>
#include <vector>
#include <queue>
#include <sstream>
#if __APPLE__
#include <pthread.h>
#endif
#include <fbu/lang_utils.hpp>

namespace fbu
{

/**
 @class ThreadPool
 @brief A simple ThreadPool class that is instantiated with a fixed number of
 threads, a function allows to add jobs, a function allows to wait for jobs
 termination.
 */
class ThreadPool
{
    std::vector<std::thread> mThreads;
    std::atomic_bool mTerminate{false};
    std::atomic_int mNumBusyThreads{0};
    std::condition_variable mJobAvailableCV;
    std::condition_variable mCompletionCV;
    std::queue< std::function<void(void)> > mJobsQueue;
    std::mutex mJobsQueueMutex;

public:
    /**
     Constructor.
     @param pNumThreads The number of threads in the ThreadPool. Use 0 for an
     automatic guess of the number of concurrent threads supported by the
     hardware platform. If it can't be determined at runtime, a default number
     of 2 threads is considered.
     @param pName The name to give to the threads, as a prefix followed by a number.
     */
    ThreadPool(int pNumThreads = 0, const std::string& pName = "fbu::ThreadPool")
    : mThreads(pNumThreads != 0
               ? (unsigned)pNumThreads
               : (std::thread::hardware_concurrency() != 0
                  ? std::thread::hardware_concurrency()
                  : 2u))
    {
        int i = 0;
        for (std::thread& t : mThreads)
        {
            std::string lThreadName = (std::ostringstream() << pName << " " << i).str();
            t = std::thread([this, lThreadName]{
#if __APPLE__
                pthread_setname_np(lThreadName.c_str());
#else
#pragma message("Thread name not implemented on this platform yet")
#endif
                this->threadExecLoop();
            });
            ++i;
        }
    }

    /**
     Destructor.
     Note: call waitForCompletion() beforehand, otherwise the ~ThreadPool will
     not wait for all the jobs to be processed.
     */
    ~ThreadPool()
    {
        mTerminate = true;
        mJobAvailableCV.notify_all();
        for (std::thread& t : mThreads)
        {
            t.join();
        }
        assert(mNumBusyThreads == 0);
    }

    /**
     Add a job.
     */
    void addJob(std::function<void(void)> pJob)
    {
        std::lock_guard<std::mutex> lGuard(mJobsQueueMutex);
        mJobsQueue.push(pJob);
        mJobAvailableCV.notify_one();
    }

    /**
     Wait for the jobs to be completed. Call this method prior to destroying the
     ThreadPool.
     */
    void waitForCompletion()
    {
        std::unique_lock<std::mutex> lLock(mJobsQueueMutex);
        while (mJobsQueue.size() > 0 || mNumBusyThreads > 0)
        {
            mCompletionCV.wait(lLock);
        }
    }

    size_t getNumThreads() const
    {
        return mThreads.size();
    }

    int getNumBusyThreads() const
    {
        return mNumBusyThreads;
    }

private:
    void threadExecLoop()
    {
        while (!mTerminate)
        {
            auto lJob = grabNextJob();
            lJob();
            {
                std::unique_lock<std::mutex> lLock(mJobsQueueMutex);
                --mNumBusyThreads;
                mCompletionCV.notify_all();
            }
        }
    }

    std::function<void(void)> grabNextJob()
    {
        std::function<void(void)> lJob;
        std::unique_lock<std::mutex> lLock(mJobsQueueMutex);

        mJobAvailableCV.wait(lLock, [this]() -> bool { return (mJobsQueue.size() > 0) || mTerminate; });

        ++mNumBusyThreads;
        if (!mTerminate)
        {
            assert(!mJobsQueue.empty());
            lJob = mJobsQueue.front();
            mJobsQueue.pop();
        }
        else
        {
            lJob = []{};
        }
        return lJob;
    }
};

/**
 @class ThreadPoolJobsExecutor
 @brief Manages a list of jobs given to a ThreadPool while allowing to wait for
        all jobs that have been passed through this object to be completed.
 @todo deprecate, use JobCounter instead.
 */
class ThreadPoolJobsExecutor
{
public:
    /**
     Constructor.
     @param pTP The ThreadPool to use.
     */
    ThreadPoolJobsExecutor(ThreadPool& pTP)
    : mTP(pTP)
    {
    }

    /**
     Add a job.
     */
    void addJob(std::function<void(void)> pJob)
    {
        {
            std::unique_lock<std::mutex> lLock(mMutex);
            ++mNumJobs;
        }
        mTP.addJob([this, pJob](){
            pJob();
            {
                std::unique_lock<std::mutex> lLock(mMutex);
                --mNumJobs;
                mCompletion.notify_all();
            }
        });
    }

    /**
     Wait for all the jobs given to the ThreadPool through this object to be completed.
     */
    void waitForCompletion()
    {
        std::unique_lock<std::mutex> lLock(mMutex);
        mCompletion.wait(lLock, [&](){ return mNumJobs == 0; });
    }

private:
    ThreadPool& mTP;
    std::mutex mMutex;
    std::condition_variable mCompletion;
    std::atomic_int mNumJobs{0};
};

/**
 @class JobCounter
 A job counter with a completion waiting method.
 Increment prior to submitting a job to a ThreadPool.
 Decrement at the end of the jobs after unlocking all mutexes on shared data.
 Call waitForCompletion() for waiting for all the jobs to be finished.
 @todo unit test.
 */
class JobCounter : public fbu::lang::NonCopyable
{
public:
    JobCounter() {}

    void increment()
    {
        std::unique_lock<std::mutex> lLock(mMutex);
        ++mNumJobs;
    }

    void decrement()
    {
        std::unique_lock<std::mutex> lLock(mMutex);
        assert(mNumJobs > 0);
        --mNumJobs;
        mCompletion.notify_all();
    }

    void waitForCompletion()
    {
        std::unique_lock<std::mutex> lLock(mMutex);
        mCompletion.wait(lLock, [&]{ return mNumJobs == 0; });
    }

private:
    std::mutex mMutex;
    std::condition_variable mCompletion;
    std::atomic_int mNumJobs{0};
};

}

#endif
<|endoftext|>