<commit_before>// This file is part of the HörTech Open Master Hearing Aid (openMHA)
// Copyright © 2018 2019 HörTech gGmbH
//
// openMHA is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// openMHA is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License, version 3 for more details.
//
// You should have received a copy of the GNU Affero General Public License,
// version 3 along with openMHA.  If not, see <http://www.gnu.org/licenses/>.

#include "mha_tcp_server.hh"
#include <asio/read_until.hpp>
#include <asio/write.hpp>

namespace mha_tcp {
    server_t::server_t(const std::string & interface,
                       uint16_t port)
    {
        // A single hostname + port may resolve to multiple ip addresses
        const auto endpoints = asio::ip::tcp::resolver(io_context).
            resolve(interface, std::to_string(port));
        acceptor = std::
            make_shared<asio::ip::tcp::acceptor>(io_context,
                                                 *endpoints.begin(), // use 1st
                                                 true); // reuse-port early
        acceptor->listen();
    }

    asio::ip::tcp::endpoint server_t::get_endpoint() const {
        return acceptor->local_endpoint();
    }

    uint16_t server_t::get_port() const {
        return acceptor->local_endpoint().port();
    }

    asio::ip::address server_t::get_address() const {
        return acceptor->local_endpoint().address();
    }

    size_t server_t::get_num_accepted_connections() const {
        // Total number of completed accept operations. Only used for tests.
        return num_accepted_connections;
    }

    void server_t::run() {
        trigger_accept();
        get_context().run();
    }

    bool server_t::on_received_line(std::shared_ptr<buffered_socket_t> c,
                                    const std::string & l)
    {
        // default implementation. To be overriden by derived classes.
        return false;
        // return false means: read no more lines. Will cause this connection
        // to be closed because the shared pointer holding it in memory goes
        // out of scope and no new shared_ptr is registered anywhere.
        // Derived classes will usually return true.
    }

    void server_t::trigger_accept() {
        if (async_accept_has_been_triggered) {
            // we can only accept one connection at a time.
            throw std::logic_error("network acceptance cannot be triggered"
                                   " while another acceptance is still in"
                                   " progress");
        }
        // We need to allocate the socket to be used for the future accepted
        // connection in advance
        std::shared_ptr<buffered_socket_t> connection =
            std::make_shared<buffered_socket_t>(get_context());

        // Set flag that we have a pending acceptance job in asio
        async_accept_has_been_triggered = true;
        acceptor->async_accept(*connection,
                               // The acceptance handler (lambda) keeps the
                               // connection (socket) object in memory by
                               // capturing the shared_ptr by value.
                               [this,connection](const asio::error_code& ec) {
                                   // When this handler is called, then asio
                                   // has finished the async_accept request
                                   async_accept_has_been_triggered = false;
                                   if (!ec) {
                                       // in the absence of errors we have a new
                                       // connection
                                       trigger_read_line(connection);
                                       ++num_accepted_connections;
                                       add_connection(connection);
                                   }
                                   // Trigger acceptance of the next connection
                                   trigger_accept();
                               });
    }

    void server_t::post_trigger_read_line(std::shared_ptr<buffered_socket_t> c)
    {
        // Call trigger_read_line with a detour through asio's scheduler
        // to avoid starving other connections when one connection floods us
        asio::post(get_context(),[this,c](){trigger_read_line(c);});
    }

    void server_t::trigger_read_line(std::shared_ptr<buffered_socket_t> c)
    {
        using asio::async_read_until;
        async_read_until(*c, c->get_buffer(), '\n',
                         // The read handler (lambda) keeps the connection
                         // object (socket + buffers) in memory by
                         // capturing the shared_ptr by value.
                         [this,c](const asio::error_code& ec, std::size_t) {
                             // When this handler is called, then asio has
                             // received a line of text in the input buffer
                             if (ec) {
                                 // There was an error, probably end-of-input.
                                 // We will not reregister for reading.
                                 // Already queued write requests will still be
                                 // served unless they also cause errors.
                                 // Not re-registering the shared_ptr
                                 // will cause it to be closed and destructed,
                                 // either now or after any pending write
                                 // requests.
                             } else {
                                 // get the received line from the input buffer
                                 std::string line;
                                 std::istream istream(&c->get_buffer());
                                 std::getline(istream, line);
                                 // remove end-of-line
                                 while (line.size() && (line.back() == '\r' ||
                                                        line.back() == '\n')) {
                                     line.resize(line.size()-1);
                                 }
                                 // let client handle the received line
                                 if (on_received_line(c, line)) {
                                     // client returned true, reregister for
                                     // reading next line
                                     post_trigger_read_line(c);
                                 }
                             }
                         });
    }

    asio::io_context & server_t::get_context()
    {
        return io_context;
    }

    void server_t::shutdown()
    {
        // refuse any new connections
        acceptor->close();
        // close the input data side on all connections.  No new commands
        // will be received.
        for (auto weak_connection : connections) {
            if (weak_connection.use_count()) {
                auto connection = weak_connection.lock();
                try {
                    connection->shutdown(asio::ip::tcp::socket::shutdown_receive);
                }
                catch(asio::system_error& e){
                    // Errors during connection shutdown can be safely ignored as we are shutting
                    // down anyway
                }
            }
        }
        // in 1 second, terminate the event loop. This should give us enough
        // time to respond to the cmd=quit command with a "success" message.
        std::shared_ptr<asio::steady_timer> t =
            std::make_shared<asio::steady_timer>(get_context(),
                                                 std::chrono::seconds(1));
        t->async_wait([this,t](std::error_code){get_context().stop();});
    }

    void buffered_socket_t::queue_write(const std::string & message) {
        std::shared_ptr<buffered_socket_t> self = this->shared_from_this();
        next_message += message;
        if (next_message.size() && current_message.empty()) {
            current_message.swap(next_message);
            using asio::async_write;
            async_write(*this, asio::buffer(current_message),
                        // The lambda keeps this socket and the buffers
                        // in memory through copy-capturing the shared_ptr.
                        [self,this](const asio::error_code & ec, std::size_t) {
                            if (ec) {
                                // There was an error sending the last message.
                                // Connection closed?  Do not re-register for
                                // writing.
                            } else {
                                current_message.clear();
                                if (next_message.size()) {
                                    // We already have a next message, send it
                                    queue_write("");
                                }
                            }
                        });
        }
    }
}

/*
 * Local Variables:
 * compile-command: "make -C .."
 * c-basic-offset: 4
 * indent-tabs-mode: nil
 * coding: utf-8-unix
 * End:
 */
<commit_msg>Revert "mha_tcp_server: Ignore all asio system errors on shutdown"<commit_after>// This file is part of the HörTech Open Master Hearing Aid (openMHA)
// Copyright © 2018 2019 HörTech gGmbH
//
// openMHA is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// openMHA is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License, version 3 for more details.
//
// You should have received a copy of the GNU Affero General Public License,
// version 3 along with openMHA.  If not, see <http://www.gnu.org/licenses/>.

#include "mha_tcp_server.hh"
#include <asio/read_until.hpp>
#include <asio/write.hpp>

namespace mha_tcp {
    server_t::server_t(const std::string & interface,
                       uint16_t port)
    {
        // A single hostname + port may resolve to multiple ip addresses
        const auto endpoints = asio::ip::tcp::resolver(io_context).
            resolve(interface, std::to_string(port));
        acceptor = std::
            make_shared<asio::ip::tcp::acceptor>(io_context,
                                                 *endpoints.begin(), // use 1st
                                                 true); // reuse-port early
        acceptor->listen();
    }

    asio::ip::tcp::endpoint server_t::get_endpoint() const {
        return acceptor->local_endpoint();
    }

    uint16_t server_t::get_port() const {
        return acceptor->local_endpoint().port();
    }

    asio::ip::address server_t::get_address() const {
        return acceptor->local_endpoint().address();
    }

    size_t server_t::get_num_accepted_connections() const {
        // Total number of completed accept operations. Only used for tests.
        return num_accepted_connections;
    }

    void server_t::run() {
        trigger_accept();
        get_context().run();
    }

    bool server_t::on_received_line(std::shared_ptr<buffered_socket_t> c,
                                    const std::string & l)
    {
        // default implementation. To be overriden by derived classes.
        return false;
        // return false means: read no more lines. Will cause this connection
        // to be closed because the shared pointer holding it in memory goes
        // out of scope and no new shared_ptr is registered anywhere.
        // Derived classes will usually return true.
    }

    void server_t::trigger_accept() {
        if (async_accept_has_been_triggered) {
            // we can only accept one connection at a time.
            throw std::logic_error("network acceptance cannot be triggered"
                                   " while another acceptance is still in"
                                   " progress");
        }
        // We need to allocate the socket to be used for the future accepted
        // connection in advance
        std::shared_ptr<buffered_socket_t> connection =
            std::make_shared<buffered_socket_t>(get_context());

        // Set flag that we have a pending acceptance job in asio
        async_accept_has_been_triggered = true;
        acceptor->async_accept(*connection,
                               // The acceptance handler (lambda) keeps the
                               // connection (socket) object in memory by
                               // capturing the shared_ptr by value.
                               [this,connection](const asio::error_code& ec) {
                                   // When this handler is called, then asio
                                   // has finished the async_accept request
                                   async_accept_has_been_triggered = false;
                                   if (!ec) {
                                       // in the absence of errors we have a new
                                       // connection
                                       trigger_read_line(connection);
                                       ++num_accepted_connections;
                                       add_connection(connection);
                                   }
                                   // Trigger acceptance of the next connection
                                   trigger_accept();
                               });
    }

    void server_t::post_trigger_read_line(std::shared_ptr<buffered_socket_t> c)
    {
        // Call trigger_read_line with a detour through asio's scheduler
        // to avoid starving other connections when one connection floods us
        asio::post(get_context(),[this,c](){trigger_read_line(c);});
    }

    void server_t::trigger_read_line(std::shared_ptr<buffered_socket_t> c)
    {
        using asio::async_read_until;
        async_read_until(*c, c->get_buffer(), '\n',
                         // The read handler (lambda) keeps the connection
                         // object (socket + buffers) in memory by
                         // capturing the shared_ptr by value.
                         [this,c](const asio::error_code& ec, std::size_t) {
                             // When this handler is called, then asio has
                             // received a line of text in the input buffer
                             if (ec) {
                                 // There was an error, probably end-of-input.
                                 // We will not reregister for reading.
                                 // Already queued write requests will still be
                                 // served unless they also cause errors.
                                 // Not re-registering the shared_ptr
                                 // will cause it to be closed and destructed,
                                 // either now or after any pending write
                                 // requests.
                             } else {
                                 // get the received line from the input buffer
                                 std::string line;
                                 std::istream istream(&c->get_buffer());
                                 std::getline(istream, line);
                                 // remove end-of-line
                                 while (line.size() && (line.back() == '\r' ||
                                                        line.back() == '\n')) {
                                     line.resize(line.size()-1);
                                 }
                                 // let client handle the received line
                                 if (on_received_line(c, line)) {
                                     // client returned true, reregister for
                                     // reading next line
                                     post_trigger_read_line(c);
                                 }
                             }
                         });
    }

    asio::io_context & server_t::get_context()
    {
        return io_context;
    }

    void server_t::shutdown()
    {
        // refuse any new connections
        acceptor->close();
        // close the input data side on all connections.  No new commands
        // will be received.
        for (auto weak_connection : connections) {
            if (weak_connection.use_count()) {
                auto connection = weak_connection.lock();
                try {
                    connection->shutdown(asio::ip::tcp::socket::shutdown_receive);
                }
                catch(asio::system_error& e){
                    if(e.what()==std::string("shutdown: Socket is not connected")){}
                    else throw;
                }
            }
        }
        // in 1 second, terminate the event loop. This should give us enough
        // time to respond to the cmd=quit command with a "success" message.
        std::shared_ptr<asio::steady_timer> t =
            std::make_shared<asio::steady_timer>(get_context(),
                                                 std::chrono::seconds(1));
        t->async_wait([this,t](std::error_code){get_context().stop();});
    }

    void buffered_socket_t::queue_write(const std::string & message) {
        std::shared_ptr<buffered_socket_t> self = this->shared_from_this();
        next_message += message;
        if (next_message.size() && current_message.empty()) {
            current_message.swap(next_message);
            using asio::async_write;
            async_write(*this, asio::buffer(current_message),
                        // The lambda keeps this socket and the buffers
                        // in memory through copy-capturing the shared_ptr.
                        [self,this](const asio::error_code & ec, std::size_t) {
                            if (ec) {
                                // There was an error sending the last message.
                                // Connection closed?  Do not re-register for
                                // writing.
                            } else {
                                current_message.clear();
                                if (next_message.size()) {
                                    // We already have a next message, send it
                                    queue_write("");
                                }
                            }
                        });
        }
    }
}

/*
 * Local Variables:
 * compile-command: "make -C .."
 * c-basic-offset: 4
 * indent-tabs-mode: nil
 * coding: utf-8-unix
 * End:
 */
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////////
// This file is part of the "NcML Module" project, a BES module designed
// to allow NcML files to be used to be used as a wrapper to add
// AIS to existing datasets of any format.
//
// Copyright (c) 2010 OPeNDAP, Inc.
// Author: Michael Johnson  <m.johnson@opendap.org>
//
// For more information, please also see the main website: http://opendap.org/
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// Please see the files COPYING and COPYRIGHT for more information on the GLPL.
//
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
/////////////////////////////////////////////////////////////////////////////
#include "AggMemberDatasetWithDimensionCacheBase.h"

#include <string.h>
#include <errno.h>

#include <sys/stat.h>

#include <sstream>
#include <algorithm>
#include <fstream>

#include "Array.h" // libdap
#include "BaseType.h" // libdap
#include "Constructor.h" // libdap
#include "DataDDS.h" // libdap
#include "DDS.h" // libdap

#include "AggregationException.h" // agg_util
#include "AggMemberDatasetDimensionCache.h"
#include "NCMLDebug.h"
#include "TheBESKeys.h"

using std::string;
using libdap::BaseType;
using libdap::Constructor;
using libdap::DataDDS;
using libdap::DDS;

#if 0
static const string BES_DATA_ROOT("BES.Data.RootDirectory");
static const string BES_CATALOG_ROOT("BES.Catalog.catalog.RootDirectory");
#endif

#define MAX_DIMENSION_COUNT_KEY "NCML.DimensionCache.maxDimensions"
#define DEFAULT_MAX_DIMENSIONS 100

static const string DEBUG_CHANNEL("agg_util");

namespace agg_util {

// Used to init the DimensionCache below with an estimated number of dimensions
static const unsigned int DIMENSION_CACHE_INITIAL_SIZE = 0;

AggMemberDatasetWithDimensionCacheBase::AggMemberDatasetWithDimensionCacheBase(const std::string& location) :
    AggMemberDataset(location), _dimensionCache(DIMENSION_CACHE_INITIAL_SIZE)
{
}

/* virtual */
AggMemberDatasetWithDimensionCacheBase::~AggMemberDatasetWithDimensionCacheBase()
{
    _dimensionCache.clear();
    _dimensionCache.resize(0);
}

AggMemberDatasetWithDimensionCacheBase::AggMemberDatasetWithDimensionCacheBase(
    const AggMemberDatasetWithDimensionCacheBase& proto) :
    RCObjectInterface(), AggMemberDataset(proto), _dimensionCache(proto._dimensionCache)
{
}

AggMemberDatasetWithDimensionCacheBase&
AggMemberDatasetWithDimensionCacheBase::operator=(const AggMemberDatasetWithDimensionCacheBase& rhs)
{
    if (&rhs != this) {
        AggMemberDataset::operator=(rhs);
        _dimensionCache.clear();
        _dimensionCache = rhs._dimensionCache;
    }
    return *this;
}

/* virtual */
unsigned int AggMemberDatasetWithDimensionCacheBase::getCachedDimensionSize(const std::string& dimName) const
{
    Dimension* pDim = const_cast<AggMemberDatasetWithDimensionCacheBase*>(this)->findDimension(dimName);
    if (pDim) {
        return pDim->size;
    }
    else {
        std::ostringstream oss;
        oss << __PRETTY_FUNCTION__ << " Dimension " << dimName << " was not found in the cache!";
        throw DimensionNotFoundException(oss.str());
    }
}

/* virtual */
bool AggMemberDatasetWithDimensionCacheBase::isDimensionCached(const std::string& dimName) const
{
    return bool(const_cast<AggMemberDatasetWithDimensionCacheBase*>(this)->findDimension(dimName));
}

/* virtual */
void AggMemberDatasetWithDimensionCacheBase::setDimensionCacheFor(const Dimension& dim, bool throwIfFound)
{
    Dimension* pExistingDim = findDimension(dim.name);
    if (pExistingDim) {
        if (!throwIfFound) {
            // This discards the object that was in the vector with this name
            // and replaces it with the information passed in via 'dim'. NB:
            // the values of the object referenced by 'dim' are copied into
            // the object pointed to by 'pExistingDim'.
            *pExistingDim = dim;
        }
        else {
            std::ostringstream msg;
            msg << __PRETTY_FUNCTION__ << " Dimension name=" << dim.name
                << " already exists and we were asked to set uniquely!";
            throw AggregationException(msg.str());
        }
    }
    else {
        _dimensionCache.push_back(dim);
    }
}

/* virtual */
void AggMemberDatasetWithDimensionCacheBase::fillDimensionCacheByUsingDataDDS()
{
    // Get the dds
    DataDDS* pDDS = const_cast<DataDDS*>(getDataDDS());
    VALID_PTR(pDDS);

    // Recursive add on all of them
    for (DataDDS::Vars_iter it = pDDS->var_begin(); it != pDDS->var_end(); ++it) {
        BaseType* pBT = *it;
        VALID_PTR(pBT);
        addDimensionsForVariableRecursive(*pBT);
    }
}

/* virtual */
void AggMemberDatasetWithDimensionCacheBase::flushDimensionCache()
{
    _dimensionCache.clear();
}

/* virtual */
void AggMemberDatasetWithDimensionCacheBase::saveDimensionCache(std::ostream& ostr)
{
    saveDimensionCacheInternal(ostr);
}

/* virtual */
void AggMemberDatasetWithDimensionCacheBase::loadDimensionCache(std::istream& istr)
{
    loadDimensionCacheInternal(istr);
}

Dimension*
AggMemberDatasetWithDimensionCacheBase::findDimension(const std::string& dimName)
{
    Dimension* ret = 0;
    for (vector<Dimension>::iterator it = _dimensionCache.begin(); it != _dimensionCache.end(); ++it) {
        if (it->name == dimName) {
            ret = &(*it);
        }
    }
    BESDEBUG(DEBUG_CHANNEL,"AggMemberDatasetWithDimensionCacheBase::findDimension(dimName="<<dimName<<") -  " << (ret?"Found " + ret->name:"Dimension Not Found") << endl);

    return ret;
}

void AggMemberDatasetWithDimensionCacheBase::addDimensionsForVariableRecursive(libdap::BaseType& var)
{
    BESDEBUG_FUNC(DEBUG_CHANNEL, "Adding dimensions for variable name=" << var.name() << endl);

    if (var.type() == libdap::dods_array_c) {
        BESDEBUG(DEBUG_CHANNEL, " Adding dimensions for array variable name = " << var.name() << endl);

        libdap::Array& arrVar = dynamic_cast<libdap::Array&>(var);
        libdap::Array::Dim_iter it;
        for (it = arrVar.dim_begin(); it != arrVar.dim_end(); ++it) {
            libdap::Array::dimension& dim = *it;
            if (!isDimensionCached(dim.name)) {
                Dimension newDim(dim.name, dim.size);
                setDimensionCacheFor(newDim, false);

                BESDEBUG(DEBUG_CHANNEL,
                    " Adding dimension: " << newDim.toString() << " to the dataset granule cache..." << endl);
            }
        }
    }

    else if (var.is_constructor_type()) // then recurse
    {
        BESDEBUG(DEBUG_CHANNEL, " Recursing on all variables for constructor variable name = " << var.name() << endl);

        libdap::Constructor& containerVar = dynamic_cast<libdap::Constructor&>(var);
        libdap::Constructor::Vars_iter it;
        for (it = containerVar.var_begin(); it != containerVar.var_end(); ++it) {
            BESDEBUG(DEBUG_CHANNEL, " Recursing on variable name=" << (*it)->name() << endl);

            addDimensionsForVariableRecursive(*(*it));
        }
    }
}

// Sort function
static bool sIsDimNameLessThan(const Dimension& lhs, const Dimension& rhs)
{
    return (lhs.name < rhs.name);
}

void AggMemberDatasetWithDimensionCacheBase::saveDimensionCacheInternal(std::ostream& ostr)
{
    BESDEBUG("ncml", "Saving dimension cache for dataset location = " << getLocation() << " ..." << endl);

    // Not really necessary, but might help with trying to read output
    std::sort(_dimensionCache.begin(), _dimensionCache.end(), sIsDimNameLessThan);

    // Save out the location first, ASSUMES \n is NOT in the location for read back
    const std::string& loc = getLocation();
    ostr << loc << '\n';

    // Now save each dimension
    unsigned int n = _dimensionCache.size();
    ostr << n << '\n';
    for (unsigned int i = 0; i < n; ++i) {
        const Dimension& dim = _dimensionCache.at(i);
        // @TODO This assumes the dimension names don't contain spaces. We should fix this, and the loader, to work with any name.
        ostr << dim.name << '\n' << dim.size << '\n';
    }
}

void AggMemberDatasetWithDimensionCacheBase::loadDimensionCacheInternal(std::istream& istr)
{
    BESDEBUG("ncml", "Loading dimension cache for dataset location = " << getLocation() << endl);

    string maxDimsStr;
    unsigned long maxDims;
    bool found;
    TheBESKeys::TheKeys()->get_value(MAX_DIMENSION_COUNT_KEY,maxDimsStr, found);
    if(found){
        maxDims = strtoul(maxDimsStr.c_str(), 0, 0);
        if (maxDims == 0)
            throw BESError(string("The value '") + maxDimsStr + "' is not valid: " + strerror(errno),
                BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
        // Replace 2011 string function for Debian (and CentOS?).
        // jhrg 10/27/15
        // maxDims = stoul(maxDimsStr,0);
    }
    else {
        maxDims = DEFAULT_MAX_DIMENSIONS;
    }

    // Read in the location string
    std::string loc;
    getline(istr, loc, '\n');

    // Make sure the location we read is the same as the location
    // for this AMD or there's an unrecoverable serialization bug
    if (loc != getLocation()) {
        stringstream ss;
        ss << "Serialization error: the location loaded from the "
            "dimensions cache was: \"" << loc << "\" but we expected it to be " << getLocation()
            << "\".  Unrecoverable!";
        THROW_NCML_INTERNAL_ERROR(ss.str());
    }

#if 0
    unsigned int n = 0;
    istr >> n >> ws;
    BESDEBUG("ncml", "AggMemberDatasetWithDimensionCacheBase::loadDimensionCacheInternal() - n: " << n << endl);
    for (unsigned int i = 0; i < n; ++i) {
        Dimension newDim;
        istr >> newDim.name >> ws;
        BESDEBUG("ncml", "AggMemberDatasetWithDimensionCacheBase::loadDimensionCacheInternal() - newDim.name: " << newDim.name << endl);
        istr >> newDim.size >> ws;
        BESDEBUG("ncml", "AggMemberDatasetWithDimensionCacheBase::loadDimensionCacheInternal() - newDim.size: " << newDim.size << endl);
        if (istr.bad()) {
            // Best we can do is throw an internal error for now.
            // Perhaps later throw something else that causes a
            // recreation of the cache
            THROW_NCML_INTERNAL_ERROR("Parsing dimension cache failed to deserialize from stream.");
        }
        _dimensionCache.push_back(newDim);
    }
#endif

    unsigned long numDims = 0;
    unsigned int dimCount = 0;
    string dimName;

    istr >> numDims >> ws;
    if(istr.fail()){
        THROW_NCML_INTERNAL_ERROR("Parsing dimension cache FAIL. Unable to read number of dimensions from cache file.");
    }
    if(numDims>maxDims){
        stringstream msg;
        msg << "Parsing dimension cache FAIL. Dimension count exceeds limits. Changing value of the ncml module configuration "
            "key " << MAX_DIMENSION_COUNT_KEY << " may help. numDims: "<< numDims << "  maxDims: "<< maxDims;
        THROW_NCML_INTERNAL_ERROR(msg.str());
    }

    while(istr.peek()!=EOF){
        Dimension newDim;
        istr >> newDim.name >> ws;
        if(istr.fail()){
            THROW_NCML_INTERNAL_ERROR("Parsing dimension cache FAIL. Unable to read dimension name from cache.");
        }
        BESDEBUG("ncml", "AggMemberDatasetWithDimensionCacheBase::loadDimensionCacheInternal() - newDim.name: " << newDim.name << endl);

        if(istr.peek()==EOF){
            THROW_NCML_INTERNAL_ERROR("Parsing dimension cache FAIL. Unexpected EOF. Expected to find dimension size value.");
        }

        istr >> newDim.size >> ws;
        if(istr.fail()){
            THROW_NCML_INTERNAL_ERROR("Parsing dimension cache FAIL. Unable to read dimension size from cache.");
        }
        BESDEBUG("ncml", "AggMemberDatasetWithDimensionCacheBase::loadDimensionCacheInternal() - newDim.size: " << newDim.size << endl);

        dimCount++;

        if(dimCount > numDims){
            stringstream msg;
            msg << "Parsing the dimension cache failed because the number of dimensions found in the cache did "
                "not match the number indicated in the cache header. Expected " << numDims << " Found: " << dimCount;
            BESDEBUG("ncml", "AggMemberDatasetWithDimensionCacheBase::loadDimensionCacheInternal() - " << msg.str() << endl);
            THROW_NCML_INTERNAL_ERROR(msg.str());
        }
        _dimensionCache.push_back(newDim);
    }

    if(dimCount != numDims){
        stringstream msg;
        msg << "Parsing the dimension cache failed because the number of dimensions found in the cache did "
            "not match the number indicated in the cache header. Expected " << numDims << " Found: " << dimCount;
        BESDEBUG("ncml", "AggMemberDatasetWithDimensionCacheBase::loadDimensionCacheInternal() - " << msg.str() << endl);
        THROW_NCML_INTERNAL_ERROR(msg.str());
    }

    BESDEBUG("ncml", "Loaded dimension cache ("<< numDims << " dimensions) for dataset location = " << getLocation() << endl);

}

}
<commit_msg>Instrumentation for ncml_handler<commit_after>//////////////////////////////////////////////////////////////////////////////
// This file is part of the "NcML Module" project, a BES module designed
// to allow NcML files to be used to be used as a wrapper to add
// AIS to existing datasets of any format.
//
// Copyright (c) 2010 OPeNDAP, Inc.
// Author: Michael Johnson  <m.johnson@opendap.org>
//
// For more information, please also see the main website: http://opendap.org/
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// Please see the files COPYING and COPYRIGHT for more information on the GLPL.
//
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
/////////////////////////////////////////////////////////////////////////////
#include "AggMemberDatasetWithDimensionCacheBase.h"

#include <string.h>
#include <errno.h>

#include <sys/stat.h>

#include <sstream>
#include <algorithm>
#include <fstream>

#include "Array.h" // libdap
#include "BaseType.h" // libdap
#include "Constructor.h" // libdap
#include "DataDDS.h" // libdap
#include "DDS.h" // libdap

#include "AggregationException.h" // agg_util
#include "AggMemberDatasetDimensionCache.h"
#include "NCMLDebug.h"
#include "TheBESKeys.h"

using std::string;
using libdap::BaseType;
using libdap::Constructor;
using libdap::DataDDS;
using libdap::DDS;

#if 0
static const string BES_DATA_ROOT("BES.Data.RootDirectory");
static const string BES_CATALOG_ROOT("BES.Catalog.catalog.RootDirectory");
#endif

#define MAX_DIMENSION_COUNT_KEY "NCML.DimensionCache.maxDimensions"
#define DEFAULT_MAX_DIMENSIONS 100

static const string DEBUG_CHANNEL("agg_util");

namespace agg_util {

// Used to init the DimensionCache below with an estimated number of dimensions
static const unsigned int DIMENSION_CACHE_INITIAL_SIZE = 0;

AggMemberDatasetWithDimensionCacheBase::AggMemberDatasetWithDimensionCacheBase(const std::string& location) :
    AggMemberDataset(location), _dimensionCache(DIMENSION_CACHE_INITIAL_SIZE)
{
}

/* virtual */
AggMemberDatasetWithDimensionCacheBase::~AggMemberDatasetWithDimensionCacheBase()
{
    _dimensionCache.clear();
    _dimensionCache.resize(0);
}

AggMemberDatasetWithDimensionCacheBase::AggMemberDatasetWithDimensionCacheBase(
    const AggMemberDatasetWithDimensionCacheBase& proto) :
    RCObjectInterface(), AggMemberDataset(proto), _dimensionCache(proto._dimensionCache)
{
}

AggMemberDatasetWithDimensionCacheBase&
AggMemberDatasetWithDimensionCacheBase::operator=(const AggMemberDatasetWithDimensionCacheBase& rhs)
{
    if (&rhs != this) {
        AggMemberDataset::operator=(rhs);
        _dimensionCache.clear();
        _dimensionCache = rhs._dimensionCache;
    }
    return *this;
}

/* virtual */
unsigned int AggMemberDatasetWithDimensionCacheBase::getCachedDimensionSize(const std::string& dimName) const
{
    Dimension* pDim = const_cast<AggMemberDatasetWithDimensionCacheBase*>(this)->findDimension(dimName);
    if (pDim) {
        return pDim->size;
    }
    else {
        std::ostringstream oss;
        oss << __PRETTY_FUNCTION__ << " Dimension " << dimName << " was not found in the cache!";
        throw DimensionNotFoundException(oss.str());
    }
}

/* virtual */
bool AggMemberDatasetWithDimensionCacheBase::isDimensionCached(const std::string& dimName) const
{
    return bool(const_cast<AggMemberDatasetWithDimensionCacheBase*>(this)->findDimension(dimName));
}

/* virtual */
void AggMemberDatasetWithDimensionCacheBase::setDimensionCacheFor(const Dimension& dim, bool throwIfFound)
{
    Dimension* pExistingDim = findDimension(dim.name);
    if (pExistingDim) {
        if (!throwIfFound) {
            // This discards the object that was in the vector with this name
            // and replaces it with the information passed in via 'dim'. NB:
            // the values of the object referenced by 'dim' are copied into
            // the object pointed to by 'pExistingDim'.
            *pExistingDim = dim;
        }
        else {
            std::ostringstream msg;
            msg << __PRETTY_FUNCTION__ << " Dimension name=" << dim.name
                << " already exists and we were asked to set uniquely!";
            throw AggregationException(msg.str());
        }
    }
    else {
        _dimensionCache.push_back(dim);
    }
}

/* virtual */
void AggMemberDatasetWithDimensionCacheBase::fillDimensionCacheByUsingDataDDS()
{
    // Get the dds
    DataDDS* pDDS = const_cast<DataDDS*>(getDataDDS());
    VALID_PTR(pDDS);

    // Recursive add on all of them
    for (DataDDS::Vars_iter it = pDDS->var_begin(); it != pDDS->var_end(); ++it) {
        BaseType* pBT = *it;
        VALID_PTR(pBT);
        addDimensionsForVariableRecursive(*pBT);
    }
}

/* virtual */
void AggMemberDatasetWithDimensionCacheBase::flushDimensionCache()
{
    _dimensionCache.clear();
}

/* virtual */
void AggMemberDatasetWithDimensionCacheBase::saveDimensionCache(std::ostream& ostr)
{
    saveDimensionCacheInternal(ostr);
}

/* virtual */
void AggMemberDatasetWithDimensionCacheBase::loadDimensionCache(std::istream& istr)
{
    loadDimensionCacheInternal(istr);
}

Dimension*
AggMemberDatasetWithDimensionCacheBase::findDimension(const std::string& dimName)
{
    Dimension* ret = 0;
    for (vector<Dimension>::iterator it = _dimensionCache.begin(); it != _dimensionCache.end(); ++it) {
        if (it->name == dimName) {
            ret = &(*it);
        }
    }
    BESDEBUG(DEBUG_CHANNEL,"AggMemberDatasetWithDimensionCacheBase::findDimension(dimName='"<<dimName<<"') -  " << (ret?"Found " + ret->name:"Dimension Not Found") << endl);

    return ret;
}

void AggMemberDatasetWithDimensionCacheBase::addDimensionsForVariableRecursive(libdap::BaseType& var)
{
    BESDEBUG_FUNC(DEBUG_CHANNEL, "Adding dimensions for variable name=" << var.name() << endl);

    if (var.type() == libdap::dods_array_c) {
        BESDEBUG(DEBUG_CHANNEL, " Adding dimensions for array variable name = " << var.name() << endl);

        libdap::Array& arrVar = dynamic_cast<libdap::Array&>(var);
        libdap::Array::Dim_iter it;
        for (it = arrVar.dim_begin(); it != arrVar.dim_end(); ++it) {
            libdap::Array::dimension& dim = *it;
            if (!isDimensionCached(dim.name)) {
                Dimension newDim(dim.name, dim.size);
                setDimensionCacheFor(newDim, false);

                BESDEBUG(DEBUG_CHANNEL,
                    " Adding dimension: " << newDim.toString() << " to the dataset granule cache..." << endl);
            }
        }
    }

    else if (var.is_constructor_type()) // then recurse
    {
        BESDEBUG(DEBUG_CHANNEL, " Recursing on all variables for constructor variable name = " << var.name() << endl);

        libdap::Constructor& containerVar = dynamic_cast<libdap::Constructor&>(var);
        libdap::Constructor::Vars_iter it;
        for (it = containerVar.var_begin(); it != containerVar.var_end(); ++it) {
            BESDEBUG(DEBUG_CHANNEL, " Recursing on variable name=" << (*it)->name() << endl);

            addDimensionsForVariableRecursive(*(*it));
        }
    }
}

// Sort function
static bool sIsDimNameLessThan(const Dimension& lhs, const Dimension& rhs)
{
    return (lhs.name < rhs.name);
}

void AggMemberDatasetWithDimensionCacheBase::saveDimensionCacheInternal(std::ostream& ostr)
{
    BESDEBUG("ncml", "Saving dimension cache for dataset location = " << getLocation() << " ..." << endl);

    // Not really necessary, but might help with trying to read output
    std::sort(_dimensionCache.begin(), _dimensionCache.end(), sIsDimNameLessThan);

    // Save out the location first, ASSUMES \n is NOT in the location for read back
    const std::string& loc = getLocation();
    ostr << loc << '\n';

    // Now save each dimension
    unsigned int n = _dimensionCache.size();
    ostr << n << '\n';
    for (unsigned int i = 0; i < n; ++i) {
        const Dimension& dim = _dimensionCache.at(i);
        // @TODO This assumes the dimension names don't contain spaces. We should fix this, and the loader, to work with any name.
        ostr << dim.name << '\n' << dim.size << '\n';
    }
}

void AggMemberDatasetWithDimensionCacheBase::loadDimensionCacheInternal(std::istream& istr)
{
    BESDEBUG("ncml", "Loading dimension cache for dataset location = " << getLocation() << endl);

    string maxDimsStr;
    unsigned long maxDims;
    bool found;
    TheBESKeys::TheKeys()->get_value(MAX_DIMENSION_COUNT_KEY,maxDimsStr, found);
    if(found){
        maxDims = strtoul(maxDimsStr.c_str(), 0, 0);
        if (maxDims == 0)
            throw BESError(string("The value '") + maxDimsStr + "' is not valid: " + strerror(errno),
                BES_SYNTAX_USER_ERROR, __FILE__, __LINE__);
        // Replace 2011 string function for Debian (and CentOS?).
        // jhrg 10/27/15
        // maxDims = stoul(maxDimsStr,0);
    }
    else {
        maxDims = DEFAULT_MAX_DIMENSIONS;
    }

    // Read in the location string
    std::string loc;
    getline(istr, loc, '\n');

    // Make sure the location we read is the same as the location
    // for this AMD or there's an unrecoverable serialization bug
    if (loc != getLocation()) {
        stringstream ss;
        ss << "Serialization error: the location loaded from the "
            "dimensions cache was: \"" << loc << "\" but we expected it to be " << getLocation()
            << "\".  Unrecoverable!";
        THROW_NCML_INTERNAL_ERROR(ss.str());
    }

#if 0
    unsigned int n = 0;
    istr >> n >> ws;
    BESDEBUG("ncml", "AggMemberDatasetWithDimensionCacheBase::loadDimensionCacheInternal() - n: " << n << endl);
    for (unsigned int i = 0; i < n; ++i) {
        Dimension newDim;
        istr >> newDim.name >> ws;
        BESDEBUG("ncml", "AggMemberDatasetWithDimensionCacheBase::loadDimensionCacheInternal() - newDim.name: " << newDim.name << endl);
        istr >> newDim.size >> ws;
        BESDEBUG("ncml", "AggMemberDatasetWithDimensionCacheBase::loadDimensionCacheInternal() - newDim.size: " << newDim.size << endl);
        if (istr.bad()) {
            // Best we can do is throw an internal error for now.
            // Perhaps later throw something else that causes a
            // recreation of the cache
            THROW_NCML_INTERNAL_ERROR("Parsing dimension cache failed to deserialize from stream.");
        }
        _dimensionCache.push_back(newDim);
    }
#endif

    unsigned long numDims = 0;
    unsigned int dimCount = 0;
    string dimName;

    istr >> numDims >> ws;
    if(istr.fail()){
        THROW_NCML_INTERNAL_ERROR("Parsing dimension cache FAIL. Unable to read number of dimensions from cache file.");
    }
    if(numDims>maxDims){
        stringstream msg;
        msg << "Parsing dimension cache FAIL. Dimension count exceeds limits. Changing value of the ncml module configuration "
            "key " << MAX_DIMENSION_COUNT_KEY << " may help. numDims: "<< numDims << "  maxDims: "<< maxDims;
        THROW_NCML_INTERNAL_ERROR(msg.str());
    }
    BESDEBUG("ncml", "AggMemberDatasetWithDimensionCacheBase::loadDimensionCacheInternal() - numDims: " << numDims << endl);

    while(istr.peek()!=EOF){
        Dimension newDim;
        istr >> newDim.name >> ws;
        if(istr.fail()){
            THROW_NCML_INTERNAL_ERROR("Parsing dimension cache FAIL. Unable to read dimension name from cache.");
        }
        BESDEBUG("ncml", "AggMemberDatasetWithDimensionCacheBase::loadDimensionCacheInternal() - newDim.name: " << newDim.name << endl);

        if(istr.peek()==EOF){
            THROW_NCML_INTERNAL_ERROR("Parsing dimension cache FAIL. Unexpected EOF. Expected to find dimension size value.");
        }

        istr >> newDim.size >> ws;
        if(istr.fail()){
            THROW_NCML_INTERNAL_ERROR("Parsing dimension cache FAIL. Unable to read dimension size from cache.");
        }
        BESDEBUG("ncml", "AggMemberDatasetWithDimensionCacheBase::loadDimensionCacheInternal() - newDim.size: " << newDim.size << endl);

        dimCount++;

        if(dimCount > numDims){
            stringstream msg;
            msg << "Parsing the dimension cache failed because the number of dimensions found in the cache did "
                "not match the number indicated in the cache header. Expected " << numDims << " Found: " << dimCount;
            BESDEBUG("ncml", "AggMemberDatasetWithDimensionCacheBase::loadDimensionCacheInternal() - " << msg.str() << endl);
            THROW_NCML_INTERNAL_ERROR(msg.str());
        }
        _dimensionCache.push_back(newDim);
    }

    if(dimCount != numDims){
        stringstream msg;
        msg << "Parsing the dimension cache failed because the number of dimensions found in the cache did "
            "not match the number indicated in the cache header. Expected " << numDims << " Found: " << dimCount;
        BESDEBUG("ncml", "AggMemberDatasetWithDimensionCacheBase::loadDimensionCacheInternal() - " << msg.str() << endl);
        THROW_NCML_INTERNAL_ERROR(msg.str());
    }

    BESDEBUG("ncml", "Loaded dimension cache ("<< numDims << " dimensions) for dataset location = " << getLocation() << endl);

}

}
<|endoftext|>


<commit_before>// $Id$
// Category: geometry
//
// See the class description in the header file.

#include "AliSingleModuleConstruction.h"
#include "AliSingleModuleConstructionMessenger.h"
#include "AliSDManager.h"
#include "AliSensitiveDetector.h"
#include "AliGlobals.h"
#include "AliFiles.h"
#include "AliRun.h"

#include "TG4GeometryManager.h"

#include <G3SensVolVector.hh>
#include <G4SDManager.hh>
#include <G4UImanager.hh>
//#include <G4Element.hh>
#include <G4LogicalVolume.hh>
#include <G4LogicalVolumeStore.hh>

#include <TROOT.h>
#include <TCint.h>

G4VPhysicalVolume* AliSingleModuleConstruction::fgWorld = 0;

AliSingleModuleConstruction::AliSingleModuleConstruction(
                                G4String moduleName, G4int version,
				AliModuleType moduleType)
  : AliModuleConstruction(moduleName),
    fVersion(version),
    fType(moduleType),
    fProcessConfig(true),
    fAllLVSensitive(false)
{
//
  fSDManager = AliSDManager::Instance();

  moduleName.toLower();
  fMessenger = new AliSingleModuleConstructionMessenger(this, moduleName);
}

AliSingleModuleConstruction::AliSingleModuleConstruction(
                                const AliSingleModuleConstruction& right)
  : AliModuleConstruction(right)
{
//
  fVersion = right.fVersion;
  fType = right.fType;
  fProcessConfig = right.fProcessConfig;
  fAllLVSensitive = right.fAllLVSensitive;
  fSDManager = right.fSDManager;

  G4String moduleName = right.fModuleName;
  moduleName.toLower();
  fMessenger = new AliSingleModuleConstructionMessenger(this, moduleName);
}

AliSingleModuleConstruction::AliSingleModuleConstruction() {
//
}

AliSingleModuleConstruction::~AliSingleModuleConstruction() {
//
  delete fMessenger;
}

// operators

AliSingleModuleConstruction&
AliSingleModuleConstruction::operator=(const AliSingleModuleConstruction& right)
{
  // check assignement to self
  if (this == &right) return *this;

  // base class assignement
  AliModuleConstruction::operator=(right);

  fVersion = right.fVersion;
  fType = right.fType;
  fProcessConfig = right.fProcessConfig;
  fAllLVSensitive = right.fAllLVSensitive;
  fSDManager = right.fSDManager;

  return *this;
}

// private methods

void AliSingleModuleConstruction::CreateSensitiveDetectors()
{
// Creates sensitive detectors.
// ---

  if (fAllLVSensitive)
    CreateSensitiveDetectors1();
  else
    CreateSensitiveDetectors2();

  // set static number of logical volumes already processed
  G4LogicalVolumeStore* pLVStore = G4LogicalVolumeStore::GetInstance();
  fSDManager->SetNofLVWithSD(pLVStore->entries());
}

void AliSingleModuleConstruction::CreateSensitiveDetectors1()
{
// Creates sensitive detectors.
// Sensitive detectors are set to all logical volumes
// ---

  G4LogicalVolumeStore* pLVStore = G4LogicalVolumeStore::GetInstance();
  G4int nofLV = pLVStore->entries();

  G4int nofLVWithSD = fSDManager->GetNofLVWithSD();

  for (G4int i=nofLVWithSD; i<nofLV; i++) {
    G4LogicalVolume* lv = (*pLVStore)[i];
    fSDManager->CreateSD(lv, fAliModule);
  }
}

void AliSingleModuleConstruction::CreateSensitiveDetectors2()
{
// Creates sensitive detectors.
// Sensitive detectors are set only to logical volumes
// in G3SensVolVector.
// ---

  TG4GeometryManager* pGeometryManager = TG4GeometryManager::Instance();

  G3SensVolVector pSVVector
    = pGeometryManager->GetG3SensVolVector();

  G4int nofSV = pSVVector.entries();
  if (nofSV>0)
    for (G4int isv=0; isv<nofSV; isv++) {
      G4LogicalVolume* lv = pSVVector[isv];
      fSDManager->CreateSD(lv, fAliModule);
    }
}

// public methods

void AliSingleModuleConstruction::Configure()
{
// Executes the detector setup Root macro
// (extracted from AliRoot Config.C) and
// G4 macro.
// ---

  // filepaths and macro names
  G4String rootFilePath;
  G4String g4FilePath;

  if (fType == kDetector) {
    // macro file path
    //$AG4_INSTALL/macro/XXX/Config.C
    //$AG4_INSTALL/macro/XXX/Config.in
    rootFilePath = AliFiles::DetConfig1();
    rootFilePath.append(fModuleName);
    rootFilePath.append(AliFiles::DetConfig2());
    g4FilePath = rootFilePath;

    rootFilePath.append(AliFiles::DetConfig3());
    g4FilePath.append(AliFiles::DetConfig4());

    // path to g3calls.dat file
    //$AG4_INSTALL/macro/XXX/g3calls_vN.dat
    fDataFilePath = AliFiles::DetData1();
    fDataFilePath.append(fModuleName);
    fDataFilePath.append(AliFiles::DetData2());
    G4String version("v");
    AliGlobals::AppendNumberToString(version,fVersion);
    fDataFilePath.append(version);
    fDataFilePath.append(AliFiles::DetData3());
  }
  else if (fType == kStructure) {
    // macro file path is set to
    //$AG4_INSTALL/macro/STRUCT/XXXConfig.C
    //$AG4_INSTALL/macro/STRUCT/XXXConfig.in
    rootFilePath = AliFiles::DetConfig1();
    rootFilePath.append(AliFiles::STRUCT());
    rootFilePath.append(AliFiles::DetConfig2());
    rootFilePath.append(fModuleName);
    g4FilePath = rootFilePath;

    rootFilePath.append(AliFiles::DetConfig3());
    g4FilePath.append(AliFiles::DetConfig4());

    // path to g3calls.dat file
    //$AG4_INSTALL/macro/STRUCT/g3calls_XXXvN.dat
    fDataFilePath = AliFiles::DetData1();
    fDataFilePath.append(AliFiles::STRUCT());
    fDataFilePath.append(AliFiles::DetData2());
    fDataFilePath.append(fModuleName);
    G4String version("v");
    AliGlobals::AppendNumberToString(version,fVersion);
    fDataFilePath.append(version);
    fDataFilePath.append(AliFiles::DetData3());
  }

  if (fProcessConfig) {
    // load and execute aliroot macro
    gROOT->LoadMacro(rootFilePath);
    G4String macroName = AliFiles::DetConfigName1();
    AliGlobals::AppendNumberToString(macroName, fVersion);
    macroName.append(AliFiles::DetConfigName2());
    gInterpreter->ProcessLine(macroName);
  }

  // add process g4 config macro
  // execute Geant4 macro if file is specified as an argument
  G4String command = "/control/execute ";
  G4UImanager* pUI = G4UImanager::GetUIpointer();
  pUI->ApplyCommand(command + g4FilePath);

  // get AliModule created in Config.C macro
  fAliModule = gAlice->GetModule(fModuleName);
  if (!fAliModule) {
    G4String text = "AliSingleModuleConstruction::Configure:\n";
    text = text + "    AliModule " + fModuleName;
    text = text + " has not been found in gAlice.";
    AliGlobals::Exception(text);
  }
}

void AliSingleModuleConstruction::Construct()
{
// Constructs geometry.
// ---

  // print default element table
  // const G4ElementTable* table = G4Element::GetElementTable();
  // G4cout << "Default elemnt table: " << G4endl;
  // for (G4int i=0; i<table->entries(); i++) {
  //   G4cout << *(*table)[i] << G4endl;
  // }

  Configure();

  // get geometry manager
  TG4GeometryManager* pGeometryManager = TG4GeometryManager::Instance();

  // register module name in the name map
  pGeometryManager->SetMapSecond(fAliModule->GetName());

  if (fReadGeometry) {
    // create G3 geometry from g3calls.dat
    pGeometryManager->SetWriteGeometry(false);
    pGeometryManager->ReadG3Geometry(fDataFilePath);
  }
  else {
    // set geometry output stream for this module
    pGeometryManager->SetWriteGeometry(fWriteGeometry);
    if (fWriteGeometry)
      pGeometryManager->OpenOutFile(fDataFilePath);

    // create geometry from AliRoot

    // construct materials
    fAliModule->CreateMaterials();

    // construct G3 geometry
    fAliModule->CreateGeometry();

    if (fWriteGeometry)
      pGeometryManager->CloseOutFile();
  }

  // construct G4 geometry
  G4VPhysicalVolume* world = pGeometryManager->CreateG4Geometry();
  if (!fgWorld) fgWorld = world;

  // set the detector frame (envelope)
  // (without warning output if enevelope is not defined)
  SetDetFrame(false);

  // create sensitive detectors
  CreateSensitiveDetectors();

  // build sensitive detectors table
  fAliModule->Init();

  // reset TG4GeometryManager
  pGeometryManager->ClearG3Tables();

  // print current total number of logical volumes
  G4cout << "Current total number of sensitive volumes: "
         << pGeometryManager->NofVolumes() << G4endl;

#ifdef ALICE_VISUALIZE
  if (GetDetFrame()) {
    // set visualization attributes
    // if detector envelope is defined
    SetDetVisibility(true);
    SetDetColour("Yellow");
  }
#endif
}
<commit_msg>corrected Construct()<commit_after>// $Id$
// Category: geometry
//
// See the class description in the header file.

#include "AliSingleModuleConstruction.h"
#include "AliSingleModuleConstructionMessenger.h"
#include "AliSDManager.h"
#include "AliSensitiveDetector.h"
#include "AliGlobals.h"
#include "AliFiles.h"
#include "AliRun.h"

#include "TG4GeometryManager.h"

#include <G3SensVolVector.hh>
#include <G4SDManager.hh>
#include <G4UImanager.hh>
//#include <G4Element.hh>
#include <G4LogicalVolume.hh>
#include <G4LogicalVolumeStore.hh>

#include <TROOT.h>
#include <TCint.h>

G4VPhysicalVolume* AliSingleModuleConstruction::fgWorld = 0;

AliSingleModuleConstruction::AliSingleModuleConstruction(
                                G4String moduleName, G4int version,
				AliModuleType moduleType)
  : AliModuleConstruction(moduleName),
    fVersion(version),
    fType(moduleType),
    fProcessConfig(true),
    fAllLVSensitive(false)
{
//
  fSDManager = AliSDManager::Instance();

  moduleName.toLower();
  fMessenger = new AliSingleModuleConstructionMessenger(this, moduleName);
}

AliSingleModuleConstruction::AliSingleModuleConstruction(
                                const AliSingleModuleConstruction& right)
  : AliModuleConstruction(right)
{
//
  fVersion = right.fVersion;
  fType = right.fType;
  fProcessConfig = right.fProcessConfig;
  fAllLVSensitive = right.fAllLVSensitive;
  fSDManager = right.fSDManager;

  G4String moduleName = right.fModuleName;
  moduleName.toLower();
  fMessenger = new AliSingleModuleConstructionMessenger(this, moduleName);
}

AliSingleModuleConstruction::AliSingleModuleConstruction() {
//
}

AliSingleModuleConstruction::~AliSingleModuleConstruction() {
//
  delete fMessenger;
}

// operators

AliSingleModuleConstruction&
AliSingleModuleConstruction::operator=(const AliSingleModuleConstruction& right)
{
  // check assignement to self
  if (this == &right) return *this;

  // base class assignement
  AliModuleConstruction::operator=(right);

  fVersion = right.fVersion;
  fType = right.fType;
  fProcessConfig = right.fProcessConfig;
  fAllLVSensitive = right.fAllLVSensitive;
  fSDManager = right.fSDManager;

  return *this;
}

// private methods

void AliSingleModuleConstruction::CreateSensitiveDetectors()
{
// Creates sensitive detectors.
// ---

  if (fAllLVSensitive)
    CreateSensitiveDetectors1();
  else
    CreateSensitiveDetectors2();

  // set static number of logical volumes already processed
  G4LogicalVolumeStore* pLVStore = G4LogicalVolumeStore::GetInstance();
  fSDManager->SetNofLVWithSD(pLVStore->entries());
}

void AliSingleModuleConstruction::CreateSensitiveDetectors1()
{
// Creates sensitive detectors.
// Sensitive detectors are set to all logical volumes
// ---

  G4LogicalVolumeStore* pLVStore = G4LogicalVolumeStore::GetInstance();
  G4int nofLV = pLVStore->entries();

  G4int nofLVWithSD = fSDManager->GetNofLVWithSD();

  for (G4int i=nofLVWithSD; i<nofLV; i++) {
    G4LogicalVolume* lv = (*pLVStore)[i];
    fSDManager->CreateSD(lv, fAliModule);
  }
}

void AliSingleModuleConstruction::CreateSensitiveDetectors2()
{
// Creates sensitive detectors.
// Sensitive detectors are set only to logical volumes
// in G3SensVolVector.
// ---

  TG4GeometryManager* pGeometryManager = TG4GeometryManager::Instance();

  G3SensVolVector pSVVector
    = pGeometryManager->GetG3SensVolVector();

  G4int nofSV = pSVVector.entries();
  if (nofSV>0)
    for (G4int isv=0; isv<nofSV; isv++) {
      G4LogicalVolume* lv = pSVVector[isv];
      fSDManager->CreateSD(lv, fAliModule);
    }
}

// public methods

void AliSingleModuleConstruction::Configure()
{
// Executes the detector setup Root macro
// (extracted from AliRoot Config.C) and
// G4 macro.
// ---

  // filepaths and macro names
  G4String rootFilePath;
  G4String g4FilePath;

  if (fType == kDetector) {
    // macro file path
    //$AG4_INSTALL/macro/XXX/Config.C
    //$AG4_INSTALL/macro/XXX/Config.in
    rootFilePath = AliFiles::DetConfig1();
    rootFilePath.append(fModuleName);
    rootFilePath.append(AliFiles::DetConfig2());
    g4FilePath = rootFilePath;

    rootFilePath.append(AliFiles::DetConfig3());
    g4FilePath.append(AliFiles::DetConfig4());

    // path to g3calls.dat file
    //$AG4_INSTALL/macro/XXX/g3calls_vN.dat
    fDataFilePath = AliFiles::DetData1();
    fDataFilePath.append(fModuleName);
    fDataFilePath.append(AliFiles::DetData2());
    G4String version("v");
    AliGlobals::AppendNumberToString(version,fVersion);
    fDataFilePath.append(version);
    fDataFilePath.append(AliFiles::DetData3());
  }
  else if (fType == kStructure) {
    // macro file path is set to
    //$AG4_INSTALL/macro/STRUCT/XXXConfig.C
    //$AG4_INSTALL/macro/STRUCT/XXXConfig.in
    rootFilePath = AliFiles::DetConfig1();
    rootFilePath.append(AliFiles::STRUCT());
    rootFilePath.append(AliFiles::DetConfig2());
    rootFilePath.append(fModuleName);
    g4FilePath = rootFilePath;

    rootFilePath.append(AliFiles::DetConfig3());
    g4FilePath.append(AliFiles::DetConfig4());

    // path to g3calls.dat file
    //$AG4_INSTALL/macro/STRUCT/g3calls_XXXvN.dat
    fDataFilePath = AliFiles::DetData1();
    fDataFilePath.append(AliFiles::STRUCT());
    fDataFilePath.append(AliFiles::DetData2());
    fDataFilePath.append(fModuleName);
    G4String version("v");
    AliGlobals::AppendNumberToString(version,fVersion);
    fDataFilePath.append(version);
    fDataFilePath.append(AliFiles::DetData3());
  }

  if (fProcessConfig) {
    // load and execute aliroot macro
    gROOT->LoadMacro(rootFilePath);
    G4String macroName = AliFiles::DetConfigName1();
    AliGlobals::AppendNumberToString(macroName, fVersion);
    macroName.append(AliFiles::DetConfigName2());
    gInterpreter->ProcessLine(macroName);
  }

  // add process g4 config macro
  // execute Geant4 macro if file is specified as an argument
  G4String command = "/control/execute ";
  G4UImanager* pUI = G4UImanager::GetUIpointer();
  pUI->ApplyCommand(command + g4FilePath);

  // get AliModule created in Config.C macro
  fAliModule = gAlice->GetModule(fModuleName);
  if (!fAliModule) {
    G4String text = "AliSingleModuleConstruction::Configure:\n";
    text = text + "    AliModule " + fModuleName;
    text = text + " has not been found in gAlice.";
    AliGlobals::Exception(text);
  }
}

void AliSingleModuleConstruction::Construct()
{
// Constructs geometry.
// ---

  // print default element table
  // const G4ElementTable* table = G4Element::GetElementTable();
  // G4cout << "Default elemnt table: " << G4endl;
  // for (G4int i=0; i<table->entries(); i++) {
  //   G4cout << *(*table)[i] << G4endl;
  // }

  // Configure();

  // get geometry manager
  TG4GeometryManager* pGeometryManager = TG4GeometryManager::Instance();

  // register module name in the name map
  pGeometryManager->SetMapSecond(fAliModule->GetName());

  if (fReadGeometry) {
    // create G3 geometry from g3calls.dat
    pGeometryManager->SetWriteGeometry(false);
    pGeometryManager->ReadG3Geometry(fDataFilePath);
  }
  else {
    // set geometry output stream for this module
    pGeometryManager->SetWriteGeometry(fWriteGeometry);
    if (fWriteGeometry)
      pGeometryManager->OpenOutFile(fDataFilePath);

    // create geometry from AliRoot

    // construct materials
    fAliModule->CreateMaterials();

    // construct G3 geometry
    fAliModule->CreateGeometry();

    // construct geometry for display
    fAliModule->BuildGeometry();

    if (fWriteGeometry)
      pGeometryManager->CloseOutFile();
  }

  // construct G4 geometry
  G4VPhysicalVolume* world = pGeometryManager->CreateG4Geometry();
  if (!fgWorld) fgWorld = world;

  // set the detector frame (envelope)
  // (without warning output if enevelope is not defined)
  SetDetFrame(false);

  // create sensitive detectors
  CreateSensitiveDetectors();

  // build sensitive detectors table
  fAliModule->Init();

  // reset TG4GeometryManager
  pGeometryManager->ClearG3Tables();

  // print current total number of logical volumes
  G4cout << "Current total number of sensitive volumes: "
         << pGeometryManager->NofVolumes() << G4endl;

#ifdef ALICE_VISUALIZE
  if (GetDetFrame()) {
    // set visualization attributes
    // if detector envelope is defined
    SetDetVisibility(true);
    SetDetColour("Yellow");
  }
#endif
}
<|endoftext|>


<commit_before>// @(#)root/proofplayer:$Id$
// Author: G. Ganis May 2012

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TStatsFeedback                                                       //
//                                                                      //
// Utility class to display PROOF stats feedback histos during queries. //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TStatsFeedback.h"

#include "TError.h"
#include "TH1.h"
#include "TH2.h"
#include "THashList.h"
#include "TObjString.h"
#include "TProof.h"
#include "TProofDebug.h"
#include "TROOT.h"
#include "TSeqCollection.h"
#include "TStyle.h"
#include "TVirtualPad.h"

ClassImp(TStatsFeedback)

//______________________________________________________________________________
TStatsFeedback::TStatsFeedback(TProof *proof)
{
   // Constructor

   if (proof == 0) proof = gProof;

   TProof *p = dynamic_cast<TProof*>(proof);
   if (p == 0) {
      Error("TStatsFeedback", "no valid proof session found");
      SetBit(TObject::kInvalidObject);
      return;
   }
   fProof = p;
   fName = fProof->GetSessionTag();

   if (!(proof->Connect("Feedback(TList *objs)", "TStatsFeedback",
                  this, "Feedback(TList *objs)"))) {
      Error("TStatsFeedback", "Connect() failed");
      SetBit(TObject::kInvalidObject);
      return;
   }
}

//______________________________________________________________________________
TStatsFeedback::~TStatsFeedback()
{
   // Destructor

   fProof->Disconnect("Feedback(TList*)", this, "Feedback(TList*");
}

//______________________________________________________________________________
void TStatsFeedback::Feedback(TList *objs)
{
   // Display feedback

   TSeqCollection *canvases = gROOT->GetListOfCanvases();

   PDB(kFeedback,1) Info("Feedback", "%d Objects", objs->GetSize());

   // Attach to the histograms we want to plot
   TH1D *hevt = 0, *hpck = 0;
   TH1I *hass = 0;
   TIter next(objs);
   TObject *o = 0;
   while((o = next())) {
      if (!strcmp(o->GetName(), "PROOF_EventsHist")) {
         hevt = dynamic_cast<TH1D *>(o);
      } else if (!strcmp(o->GetName(), "PROOF_PacketsHist")) {
         hpck = dynamic_cast<TH1D *>(o);
      } else if (!strcmp(o->GetName(), "PROOF_ProcPcktHist")) {
         hass = dynamic_cast<TH1I *>(o);
      }
      if (hevt && hpck && hass) break;
   }
   if (!hevt && !hpck && !hass) {
      Warning("Feedback", "none of the requested histograms has been found!");
      return;
   }

   // Create or attach to canvas
   TString cvnm = TString::Format("Stats: %s", fProof->GetSessionTag());
   TVirtualPad *cv = 0;
   if (gROOT->GetListOfCanvases())
      cv = (TVirtualPad *) canvases->FindObject(cvnm.Data());
   if (!cv) {
      TString cvcmd = TString::Format("new TCanvas(\"%s\", \"Feedback Stats\",10,300,600,400)",
                                      cvnm.Data());
      if (!(cv = (TVirtualPad *) gROOT->ProcessLine(cvcmd))) {
         Warning("Feedback", "could not create canvas!");
         return;
      }
      PDB(kFeedback,2) Info("Feedback", "created canvas %s", cvnm.Data());
      // Create pads
      Int_t nh = 3;
      if (!hass) nh = 2;
      cv->Divide(1, nh);
   } else {
      cv->cd();
      PDB(kFeedback,2) Info("Feedback", "using canvas %s", cvnm.Data());
   }
   TVirtualPad *pd1 = (TVirtualPad *) cv->GetPad(1);
   TVirtualPad *pd2 = (TVirtualPad *) cv->GetPad(2);
   TVirtualPad *pd3 = (TVirtualPad *) cv->GetPad(3);

   UInt_t optstat = gStyle->GetOptStat();
   gStyle->SetOptStat(11);
   // Plot
   pd1->cd();
   if (hevt) {
      hevt->DrawCopy();
   }
   pd2->cd();
   if (hpck) {
      hpck->DrawCopy();
   }
   pd3->cd();
   if (hass) {
      hass->DrawCopy();
   }

   cv->cd();
   cv->Update();
   gStyle->SetOptStat(optstat);
}
<commit_msg>Add missing protection; cosmetics<commit_after>// @(#)root/proofplayer:$Id$
// Author: G. Ganis May 2012

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TStatsFeedback                                                       //
//                                                                      //
// Utility class to display PROOF stats feedback histos during queries. //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TStatsFeedback.h"

#include "TError.h"
#include "TH1.h"
#include "TH2.h"
#include "THashList.h"
#include "TObjString.h"
#include "TProof.h"
#include "TProofDebug.h"
#include "TROOT.h"
#include "TSeqCollection.h"
#include "TStyle.h"
#include "TVirtualPad.h"

ClassImp(TStatsFeedback)

//______________________________________________________________________________
TStatsFeedback::TStatsFeedback(TProof *proof)
{
   // Constructor

   if (proof == 0) proof = gProof;

   TProof *p = dynamic_cast<TProof*>(proof);
   if (p == 0) {
      Error("TStatsFeedback", "no valid proof session found");
      SetBit(TObject::kInvalidObject);
      return;
   }
   fProof = p;
   fName = fProof->GetSessionTag();

   if (!(proof->Connect("Feedback(TList *objs)", "TStatsFeedback",
                  this, "Feedback(TList *objs)"))) {
      Error("TStatsFeedback", "Connect() failed");
      SetBit(TObject::kInvalidObject);
      return;
   }
}

//______________________________________________________________________________
TStatsFeedback::~TStatsFeedback()
{
   // Destructor

   fProof->Disconnect("Feedback(TList*)", this, "Feedback(TList*");
}

//______________________________________________________________________________
void TStatsFeedback::Feedback(TList *objs)
{
   // Display feedback

   TSeqCollection *canvases = gROOT->GetListOfCanvases();

   PDB(kFeedback,1) Info("Feedback", "%d Objects", objs->GetSize());

   // Attach to the histograms we want to plot
   TH1D *hevt = 0, *hpck = 0;
   TH1I *hass = 0;
   TIter next(objs);
   TObject *o = 0;
   while((o = next())) {
      if (!strcmp(o->GetName(), "PROOF_EventsHist")) {
         hevt = dynamic_cast<TH1D *>(o);
      } else if (!strcmp(o->GetName(), "PROOF_PacketsHist")) {
         hpck = dynamic_cast<TH1D *>(o);
      } else if (!strcmp(o->GetName(), "PROOF_ProcPcktHist")) {
         hass = dynamic_cast<TH1I *>(o);
      }
      if (hevt && hpck && hass) break;
   }
   if (!hevt && !hpck && !hass) {
      Warning("Feedback", "none of the requested histograms has been found!");
      return;
   }

   // Number of histograms
   Int_t nh = 3;
   if (!hass) nh = 2;
   // Create or attach to canvas
   TString cvnm = TString::Format("Stats: %s", fProof->GetSessionTag());
   TVirtualPad *cv = 0;
   if (gROOT->GetListOfCanvases())
      cv = (TVirtualPad *) canvases->FindObject(cvnm.Data());
   if (cv && nh == 3 && !cv->GetPad(3)) SafeDelete(cv);
   if (!cv) {
      Int_t h = (nh == 3) ? 600 : 400;
      TString cvcmd = TString::Format("new TCanvas(\"%s\", \"Feedback Stats\",10,300,600,%d)",
                                      cvnm.Data(), h);
      if (!(cv = (TVirtualPad *) gROOT->ProcessLine(cvcmd))) {
         Warning("Feedback", "could not create canvas!");
         return;
      }
      PDB(kFeedback,2) Info("Feedback", "created canvas %s", cvnm.Data());
      // Create pads
      cv->Divide(1, nh);
   } else {
      cv->cd();
      PDB(kFeedback,2) Info("Feedback", "using canvas %s", cvnm.Data());
   }
   TVirtualPad *pd1 = (TVirtualPad *) cv->GetPad(1);
   TVirtualPad *pd2 = (TVirtualPad *) cv->GetPad(2);
   TVirtualPad *pd3 = (nh == 3) ? (TVirtualPad *) cv->GetPad(3) : 0;

   UInt_t optstat = gStyle->GetOptStat();
   gStyle->SetOptStat(11);
   // Plot
   if (hevt) {
      if (pd1) pd1->cd();
      hevt->SetFillColor(kGreen);
      hevt->DrawCopy();
   }
   if (hpck) {
      if (pd2) pd2->cd();
      hpck->SetFillColor(kAzure-5);
      hpck->DrawCopy();
   }
   if (hass) {
      if (pd3) pd3->cd();
      hass->SetFillColor(kGray);
      hass->SetMaximum(2);
      hass->DrawCopy();
   }

   cv->cd();
   cv->Update();
   gStyle->SetOptStat(optstat);
}
<|endoftext|>


<commit_before>#include <iostream>
#include <iomanip> // << fixed << setprecision(xxx)
#include <algorithm> // do { } while ( next_permutation(A, A+xxx) ) ;
#include <vector>
#include <string> // to_string(nnn) // substr(m, n) // stoi(nnn)
#include <complex>
#include <tuple> // get<n>(xxx)
#include <queue>
#include <stack>
#include <map> // if (M.find(key) != M.end()) { }
#include <set> // S.insert(M);
// if (S.find(key) != S.end()) { }
// for (auto it=S.begin(); it != S.end(); it++) { }
// auto it = S.lower_bound(M);
#include <random> // random_device rd; mt19937 mt(rd());
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib> // atoi(xxx)
using namespace std;

#define DEBUG 0 // change 0 -> 1 if we need debug.
// insert #if<tab> by my emacs. #if DEBUG == 1 ... #end

typedef long long ll;

// const int dx[4] = {1, 0, -1, 0};
// const int dy[4] = {0, 1, 0, -1};

// const int C = 1e6+10;
// const ll M = 1000000007;

string S;
int depth[100010];
set<int> dset[100010];

string henkan(int l, int r, int d) {
  char op = S[l];
  if (op != '+' && op != '-' && op != '*' && op != '/') {
    cerr << S.substr(l, r-l) << endl;
    return S.substr(l, r-l);
  }
  string opp = string{op};
  vector< tuple<int, int> > V;
  auto it = dset[d].lower_bound(l+2);
  int start = l+2;
  while (it != dset[d].end() && *it < r-1) {
    V.push_back(make_tuple(start, *it));
    start = (*it) + 1;
    it++;
  }
  V.push_back(make_tuple(start, r-1));
  cerr << start << " " << r-1 << endl;
  vector<string> W;
  for (auto it = V.begin(); it != V.end(); ++it) {
    int s = get<0>(*it);
    int g = get<1>(*it);
    W.push_back(henkan(s, g, d+1));
  }
  string ans = "";
  for (auto i = 0; i < (int)W.size(); ++i) {
    ans += W[i];
    if (i < (int)W.size()-1) {
      ans += opp;
    }
  }
  return "(" + ans + ")";
}

int main () {
  string S;
  cin >> S;
  cerr << S.substr(0, 4) << endl;
  fill(depth, depth+100010, 0);
  int cnt = 0;
  for (auto i = 0; i < (int)S.size(); ++i) {
    if (S[i] == '(') {
      cnt++;
    } else if (S[i] == ')') {
      cnt--;
    } else if (S[i] == ',') {
      depth[i] = cnt;
      dset[cnt].insert(i);
    }
  }
  cout << henkan(0, (int)S.size(), 0) << endl;
}
<commit_msg>tried B.cpp to 'B'<commit_after>#include <iostream>
#include <iomanip> // << fixed << setprecision(xxx)
#include <algorithm> // do { } while ( next_permutation(A, A+xxx) ) ;
#include <vector>
#include <string> // to_string(nnn) // substr(m, n) // stoi(nnn)
#include <complex>
#include <tuple> // get<n>(xxx)
#include <queue>
#include <stack>
#include <map> // if (M.find(key) != M.end()) { }
#include <set> // S.insert(M);
// if (S.find(key) != S.end()) { }
// for (auto it=S.begin(); it != S.end(); it++) { }
// auto it = S.lower_bound(M);
#include <random> // random_device rd; mt19937 mt(rd());
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib> // atoi(xxx)
using namespace std;

#define DEBUG 0 // change 0 -> 1 if we need debug.
// insert #if<tab> by my emacs. #if DEBUG == 1 ... #end

typedef long long ll;

// const int dx[4] = {1, 0, -1, 0};
// const int dy[4] = {0, 1, 0, -1};

// const int C = 1e6+10;
// const ll M = 1000000007;

string S;
int depth[100010];
set<int> dset[100010];

string henkan(int l, int r, int d) {
  char op = S[l];
  if (op != '+' && op != '-' && op != '*' && op != '/') {
    cerr << S.substr(l, r-l) << endl;
    return S.substr(l, r-l);
  }
  string opp = string{op};
  vector< tuple<int, int> > V;
  auto it = dset[d].lower_bound(l+2);
  int start = l+2;
  while (it != dset[d].end() && *it < r-1) {
    V.push_back(make_tuple(start, *it));
    start = (*it) + 1;
    it++;
  }
  V.push_back(make_tuple(start, r-1));
  cerr << start << " " << r-1 << endl;
  vector<string> W;
  for (auto it = V.begin(); it != V.end(); ++it) {
    int s = get<0>(*it);
    int g = get<1>(*it);
    W.push_back(henkan(s, g, d+1));
  }
  string ans = "";
  for (auto i = 0; i < (int)W.size(); ++i) {
    ans += W[i];
    if (i < (int)W.size()-1) {
      ans += opp;
    }
  }
  return "(" + ans + ")";
}

int main () {
  cin >> S;
  fill(depth, depth+100010, 0);
  int cnt = 0;
  for (auto i = 0; i < (int)S.size(); ++i) {
    if (S[i] == '(') {
      cnt++;
    } else if (S[i] == ')') {
      cnt--;
    } else if (S[i] == ',') {
      depth[i] = cnt;
      dset[cnt].insert(i);
    }
  }
  cout << henkan(0, (int)S.size(), 0) << endl;
}
<|endoftext|>


<commit_before>#define DEBUG 1

/**
 * File    : B.cpp
 * Author  : Kazune Takahashi
 * Created : 2/17/2019, 12:52:37 PM
 * Powered by Visual Studio Code
 */

#include <iostream>
#include <iomanip>   // << fixed << setprecision(xxx)
#include <algorithm> // do { } while ( next_permutation(A, A+xxx) ) ;
#include <vector>
#include <string> // to_string(nnn) // substr(m, n) // stoi(nnn)
#include <complex>
#include <tuple>
#include <queue>
#include <stack>
#include <map> // if (M.find(key) != M.end()) { }
#include <set>
#include <functional>
#include <random> // auto rd = bind(uniform_int_distribution<int>(0, 9), mt19937(10101010));
#include <chrono> // std::chrono::system_clock::time_point start_time, end_time;
// start = std::chrono::system_clock::now();
// double elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end_time-start_time).count();
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
using namespace std;

typedef long long ll;

// const int dx[4] = {1, 0, -1, 0};
// const int dy[4] = {0, 1, 0, -1};

// const int C = 1e6+10;
// const ll M = 1000000007;

int N, M, K;
vector<ll> A;
vector<ll> B;

int solve()
{
  if (K == 1)
  {
    ll sum_A = 0;
    ll sum_B = 0;
    for (auto i = 0; i < 100010; i++)
    {
      sum_A += A[i];
      sum_B += B[i];
    }
    if (sum_A < sum_B)
    {
      return 1;
    }
    else if (sum_A > sum_B)
    {
      return 0;
    }
    else
    {
      return 2;
    }
  }
  else
  {
    for (auto i = 0; i < 100010; i++)
    {
      if (A[i] < B[i])
      {
        return 1;
      }
      else if (A[i] > B[i])
      {
        return 0;
      }
    }
    return 2;
  }
}

int main()
{
  cin >> N >> M >> K;
  for (auto i = 0; i < N; i++)
  {
    ll t;
    cin >> t;
    A.push_back(t);
  }
  for (auto i = 0; i < M; i++)
  {
    ll t;
    cin >> t;
    B.push_back(t);
  }
  reverse(A.begin(), A.end());
  while ((int)A.size() < 100010)
  {
    A.push_back(0);
  }
  reverse(A.begin(), A.end());
  reverse(B.begin(), B.end());
  while ((int)B.size() < 100010)
  {
    B.push_back(0);
  }
  reverse(B.begin(), B.end());
  int s = solve();
  if (s == 0)
  {
    cout << "X" << endl;
  }
  else if (s == 1)
  {
    cout << "Y" << endl;
  }
  else
  {
    cout << "Same" << endl;
  }
}<commit_msg>submit B.cpp to 'B - Big Integers' (nikkei2019-final) [C++14 (GCC 5.4.1)]<commit_after>#define DEBUG 1

/**
 * File    : B.cpp
 * Author  : Kazune Takahashi
 * Created : 2/17/2019, 12:52:37 PM
 * Powered by Visual Studio Code
 */

#include <iostream>
#include <iomanip>   // << fixed << setprecision(xxx)
#include <algorithm> // do { } while ( next_permutation(A, A+xxx) ) ;
#include <vector>
#include <string> // to_string(nnn) // substr(m, n) // stoi(nnn)
#include <complex>
#include <tuple>
#include <queue>
#include <stack>
#include <map> // if (M.find(key) != M.end()) { }
#include <set>
#include <functional>
#include <random> // auto rd = bind(uniform_int_distribution<int>(0, 9), mt19937(10101010));
#include <chrono> // std::chrono::system_clock::time_point start_time, end_time;
// start = std::chrono::system_clock::now();
// double elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end_time-start_time).count();
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
using namespace std;

typedef long long ll;

// const int dx[4] = {1, 0, -1, 0};
// const int dy[4] = {0, 1, 0, -1};

// const int C = 1e6+10;
// const ll M = 1000000007;

int N, M, K;
vector<ll> A;
vector<ll> B;

int solve()
{
  if (K == 1)
  {
    ll sum_A = 0;
    ll sum_B = 0;
    for (auto i = 0; i < 100010; i++)
    {
      sum_A += A[i];
      sum_B += B[i];
    }
    if (sum_A < sum_B)
    {
      return 1;
    }
    else if (sum_A > sum_B)
    {
      return 0;
    }
    else
    {
      return 2;
    }
  }
  else
  {
    for (auto i = 0; i < 100010; i++)
    {
      if (A[i] < B[i])
      {
        return 1;
      }
      else if (A[i] > B[i])
      {
        return 0;
      }
    }
    return 2;
  }
}

int main()
{
  cin >> N >> M >> K;
  for (auto i = 0; i < N; i++)
  {
    ll t;
    cin >> t;
    A.push_back(t);
  }
  for (auto i = 0; i < M; i++)
  {
    ll t;
    cin >> t;
    B.push_back(t);
  }
  reverse(A.begin(), A.end());
  while ((int)A.size() < 100010)
  {
    A.push_back(0);
  }
  reverse(A.begin(), A.end());
  reverse(B.begin(), B.end());
  while ((int)B.size() < 100010)
  {
    B.push_back(0);
  }
  reverse(B.begin(), B.end());
  int s = solve();
  if (s == 1)
  {
    cout << "X" << endl;
  }
  else if (s == 0)
  {
    cout << "Y" << endl;
  }
  else
  {
    cout << "Same" << endl;
  }
}<|endoftext|>


<commit_before>/**************************************************************************
 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
 *                                                                        *
 * Author: The ALICE Off-line Project.                                    *
 * Contributors are mentioned in the code where appropriate.              *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/

/* $Id$ */
// Author: Andrei Gheata, 31/05/2006

//==============================================================================
//   AliAnalysysDataSlot - Class representing a data slot of an analysis task.
//      An analysis slot enforces a certain data type required by the Exec()
//      method of the analysis task. The slot must be connected to a data
//      container with data of the same type.
//
// The class should not be directly created by users - it is created by
// each AliAnalysisTask when defining its input/output slots using:
//
//    AliAnalysisTask::SetInput(Int_t index, TClass *type);
//    AliAnalysisTask::SetOutput(TClass *type);
//
// An existing data contaner (AliAnalysisDataContainer) can be connected to the
// input/output slots of an analysis task using:
//
//   AliAnalysisModule::ConnectInput(AliAnalysisTask *task, Int_t islot,
//                                   AliAnalysisDataContainer *cont)
//   AliAnalysisModule::ConnectOutput(AliAnalysisTask *task,
//                                    AliAnalysisDataContainer *cont)
// To connect a slot to a data container, the data types declared by both must
// match.
//==============================================================================

#include <Riostream.h>
#include <TROOT.h>
#include <TClass.h>
#include <TTree.h>
#include <TLeaf.h>

#include "AliAnalysisDataSlot.h"
#include "AliAnalysisTask.h"
#include "AliAnalysisDataContainer.h"

ClassImp(AliAnalysisDataSlot)

//______________________________________________________________________________
AliAnalysisDataSlot::AliAnalysisDataSlot(TClass *type, AliAnalysisTask *task)
                    :TNamed(),
                     fType(type),
                     fParent(task),
                     fContainer(NULL)
{
// Default constructor.
   SetTitle(fType->GetName());
}

//______________________________________________________________________________
AliAnalysisDataSlot::AliAnalysisDataSlot(const AliAnalysisDataSlot &slot)
                    :TNamed(slot),
                     fType(NULL),
                     fParent(slot.fParent),
                     fContainer(slot.fContainer)
{
// Copy ctor.
   GetType();
}

//______________________________________________________________________________
AliAnalysisDataSlot& AliAnalysisDataSlot::operator=(const AliAnalysisDataSlot &slot)
{
// Assignment
   if (&slot == this) return *this;
   TNamed::operator=(slot);
   GetType();
   fParent = slot.fParent;
   fContainer = slot.fContainer;
   return *this;
}

//______________________________________________________________________________
Bool_t AliAnalysisDataSlot::ConnectContainer(AliAnalysisDataContainer *cont)
{
// Connect the data slot with a given container. The operation will succeed only
// if the type defined by the slot inherits from the type enforced by the container.
// The error message in case of failure is posted by the caller.
   if (!cont || !GetType()) return kFALSE;
   if (!fType->InheritsFrom(cont->GetType())) {
     cout<<"Data slot of type "<<GetTitle()<<" of task "<<fParent->GetName()<<" cannot be connected to data container "<<cont->GetName()<<" of type "<<cont->GetTitle()<<endl;
     //AliError(Form("Data slot of type %s of task %s cannot be connected to data container %s of type %s", fType->GetName(), fParent->GetName(), cont->GetName(), cont->GetType()->GetName()));
      return kFALSE;
   }
   fContainer = cont;
   return kTRUE;
}

//______________________________________________________________________________
TClass *AliAnalysisDataSlot::GetType() const
{
// Get class type for this slot.
   AliAnalysisDataSlot *slot = (AliAnalysisDataSlot*)this;
   if (!fType) slot->SetType(gROOT->GetClass(fTitle.Data()));
   if (!fType) printf("AliAnalysisDataSlot: Unknown class: %s\n", GetTitle());
   return fType;
}

//______________________________________________________________________________
void *AliAnalysisDataSlot::GetBranchAddress(const char *branchname) const
{
// Retrieve the address for a given branch. One should always test this before
// using SetBranchAddress because the address gets set by the first caller.
// Call this in MyTask::Init()
   if (!GetType()) return NULL;
   if (!fType->InheritsFrom(TTree::Class())) {
     cout<<"Cannot call GetBranchAddress() for data slot of task "<<fParent->GetName()<<" not pointing to tree-type data"<<endl;
     //AliFatal(Form("Cannot call GetBranchAddress() for data slot of task %s not pointing to tree-type data", fParent->GetName()));
      return NULL;
   }
   if (!IsDataReady()) {
     cout<<"Cannot call GetBranchAddress() for data slot of task "<<fParent->GetName()<<" while data is not ready"<<endl;
     //AliFatal(Form("Cannot call GetBranchAddress() for data slot of task %s while data is not ready", fParent->GetName()));
      return NULL;
   }
   TTree *tree = (TTree*)GetData();
   TBranch *br = tree->GetBranch(branchname);
   if (!br) {
     cout<<"Branch "<<branchname<<" not found in tree "<<tree->GetName()<<" as input of task "<<fParent->GetName()<<"..."<<endl;
     //AliFatal(Form("Branch %s not found in tree %s as input of task %s...", branchname, tree->GetName(), fParent->GetName()));
      return NULL;
   }
   return br->GetAddress();
}

//______________________________________________________________________________
Int_t AliAnalysisDataSlot::EnableBranch(const char *bname, TTree *tree)
{
// Static method to enable recursively a branch in a tree (why this in not in ROOT?)
   TBranch *branch = tree->GetBranch(bname);
   Int_t count = 0;
//   static Int_t indent = 0;
   if (!branch) return count;
//   TString s;
//   for (Int_t i=0; i<indent; i++) s += " ";
   count++;
//   printf("%sbranch %s: kDoNotProcess=%d\n",s.Data(), branch->GetName(), branch->TestBit(kDoNotProcess));
   branch->SetBit(kDoNotProcess, kFALSE);
   TIter next(branch->GetListOfBranches());
   TBranch *branch_sub;
   // Activate all sub-branches
//   indent++;
   while ((branch_sub=(TBranch*)next())) {
      count += AliAnalysisDataSlot::EnableBranch(branch_sub->GetName(), tree);
   }
//   indent--;
   return count;
}

//______________________________________________________________________________
Bool_t AliAnalysisDataSlot::SetBranchAddress(const char *branchname, void *address)
{
// Set a branch address for input tree. To be called during MyTask::Init()
// only if GetBranchAddress() returns a NULL pointer for a tree-type slot.
   if (GetBranchAddress(branchname)) {
      Error("SetBranchAddress","Branch address for %s already set by other task. Call first GetBranchAddress() in %s::ConnectInputData()",branchname, fParent->GetName());
      return kFALSE;
   }
   TTree *tree = (TTree*)GetData();
   tree->SetBranchAddress(branchname, address);
   return kTRUE;
}

//______________________________________________________________________________
TObject *AliAnalysisDataSlot::GetData() const
{
// Retreives the data from the container if it is ready.
   if (!fContainer) {
     cout<<"Data slot of type "<<GetTitle()<<" of task "<<fParent->GetName()<<" has no connected data container"<<endl;
     //AliError(Form("Data slot of type %s of task %s has no connected data container",fType->GetName(), fParent->GetName()));
      return NULL;
   }
   if (!fContainer->IsDataReady()) return NULL;
   return fContainer->GetData();
}

//______________________________________________________________________________
Bool_t  AliAnalysisDataSlot::IsDataReady() const
{
// Check if data for this slot is ready in its container.
   if (!fContainer) {
     cout<<"Data slot of type "<<GetTitle()<<" of task "<<fParent->GetName()<<" has no connected data container"<<endl;
     //AliError(Form("Data slot of type %s of task %s has no connected data container",fType->GetName(), fParent->GetName()));
      return kFALSE;
   }
   return fContainer->IsDataReady();
}

<commit_msg>Supressed warning when a data slot is not connected to a container<commit_after>/**************************************************************************
 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
 *                                                                        *
 * Author: The ALICE Off-line Project.                                    *
 * Contributors are mentioned in the code where appropriate.              *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/

/* $Id$ */
// Author: Andrei Gheata, 31/05/2006

//==============================================================================
//   AliAnalysysDataSlot - Class representing a data slot of an analysis task.
//      An analysis slot enforces a certain data type required by the Exec()
//      method of the analysis task. The slot must be connected to a data
//      container with data of the same type.
//
// The class should not be directly created by users - it is created by
// each AliAnalysisTask when defining its input/output slots using:
//
//    AliAnalysisTask::SetInput(Int_t index, TClass *type);
//    AliAnalysisTask::SetOutput(TClass *type);
//
// An existing data contaner (AliAnalysisDataContainer) can be connected to the
// input/output slots of an analysis task using:
//
//   AliAnalysisModule::ConnectInput(AliAnalysisTask *task, Int_t islot,
//                                   AliAnalysisDataContainer *cont)
//   AliAnalysisModule::ConnectOutput(AliAnalysisTask *task,
//                                    AliAnalysisDataContainer *cont)
// To connect a slot to a data container, the data types declared by both must
// match.
//==============================================================================

#include <Riostream.h>
#include <TROOT.h>
#include <TClass.h>
#include <TTree.h>
#include <TLeaf.h>

#include "AliAnalysisDataSlot.h"
#include "AliAnalysisTask.h"
#include "AliAnalysisDataContainer.h"

ClassImp(AliAnalysisDataSlot)

//______________________________________________________________________________
AliAnalysisDataSlot::AliAnalysisDataSlot(TClass *type, AliAnalysisTask *task)
                    :TNamed(),
                     fType(type),
                     fParent(task),
                     fContainer(NULL)
{
// Default constructor.
   SetTitle(fType->GetName());
}

//______________________________________________________________________________
AliAnalysisDataSlot::AliAnalysisDataSlot(const AliAnalysisDataSlot &slot)
                    :TNamed(slot),
                     fType(NULL),
                     fParent(slot.fParent),
                     fContainer(slot.fContainer)
{
// Copy ctor.
   GetType();
}

//______________________________________________________________________________
AliAnalysisDataSlot& AliAnalysisDataSlot::operator=(const AliAnalysisDataSlot &slot)
{
// Assignment
   if (&slot == this) return *this;
   TNamed::operator=(slot);
   GetType();
   fParent = slot.fParent;
   fContainer = slot.fContainer;
   return *this;
}

//______________________________________________________________________________
Bool_t AliAnalysisDataSlot::ConnectContainer(AliAnalysisDataContainer *cont)
{
// Connect the data slot with a given container. The operation will succeed only
// if the type defined by the slot inherits from the type enforced by the container.
// The error message in case of failure is posted by the caller.
   if (!cont || !GetType()) return kFALSE;
   if (!fType->InheritsFrom(cont->GetType())) {
     cout<<"Data slot of type "<<GetTitle()<<" of task "<<fParent->GetName()<<" cannot be connected to data container "<<cont->GetName()<<" of type "<<cont->GetTitle()<<endl;
     //AliError(Form("Data slot of type %s of task %s cannot be connected to data container %s of type %s", fType->GetName(), fParent->GetName(), cont->GetName(), cont->GetType()->GetName()));
      return kFALSE;
   }
   fContainer = cont;
   return kTRUE;
}

//______________________________________________________________________________
TClass *AliAnalysisDataSlot::GetType() const
{
// Get class type for this slot.
   AliAnalysisDataSlot *slot = (AliAnalysisDataSlot*)this;
   if (!fType) slot->SetType(gROOT->GetClass(fTitle.Data()));
   if (!fType) printf("AliAnalysisDataSlot: Unknown class: %s\n", GetTitle());
   return fType;
}

//______________________________________________________________________________
void *AliAnalysisDataSlot::GetBranchAddress(const char *branchname) const
{
// Retrieve the address for a given branch. One should always test this before
// using SetBranchAddress because the address gets set by the first caller.
// Call this in MyTask::Init()
   if (!GetType()) return NULL;
   if (!fType->InheritsFrom(TTree::Class())) {
     cout<<"Cannot call GetBranchAddress() for data slot of task "<<fParent->GetName()<<" not pointing to tree-type data"<<endl;
     //AliFatal(Form("Cannot call GetBranchAddress() for data slot of task %s not pointing to tree-type data", fParent->GetName()));
      return NULL;
   }
   if (!IsDataReady()) {
     cout<<"Cannot call GetBranchAddress() for data slot of task "<<fParent->GetName()<<" while data is not ready"<<endl;
     //AliFatal(Form("Cannot call GetBranchAddress() for data slot of task %s while data is not ready", fParent->GetName()));
      return NULL;
   }
   TTree *tree = (TTree*)GetData();
   TBranch *br = tree->GetBranch(branchname);
   if (!br) {
     cout<<"Branch "<<branchname<<" not found in tree "<<tree->GetName()<<" as input of task "<<fParent->GetName()<<"..."<<endl;
     //AliFatal(Form("Branch %s not found in tree %s as input of task %s...", branchname, tree->GetName(), fParent->GetName()));
      return NULL;
   }
   return br->GetAddress();
}

//______________________________________________________________________________
Int_t AliAnalysisDataSlot::EnableBranch(const char *bname, TTree *tree)
{
// Static method to enable recursively a branch in a tree (why this in not in ROOT?)
   TBranch *branch = tree->GetBranch(bname);
   Int_t count = 0;
//   static Int_t indent = 0;
   if (!branch) return count;
//   TString s;
//   for (Int_t i=0; i<indent; i++) s += " ";
   count++;
//   printf("%sbranch %s: kDoNotProcess=%d\n",s.Data(), branch->GetName(), branch->TestBit(kDoNotProcess));
   branch->SetBit(kDoNotProcess, kFALSE);
   TIter next(branch->GetListOfBranches());
   TBranch *branch_sub;
   // Activate all sub-branches
//   indent++;
   while ((branch_sub=(TBranch*)next())) {
      count += AliAnalysisDataSlot::EnableBranch(branch_sub->GetName(), tree);
   }
//   indent--;
   return count;
}

//______________________________________________________________________________
Bool_t AliAnalysisDataSlot::SetBranchAddress(const char *branchname, void *address)
{
// Set a branch address for input tree. To be called during MyTask::Init()
// only if GetBranchAddress() returns a NULL pointer for a tree-type slot.
   if (GetBranchAddress(branchname)) {
      Error("SetBranchAddress","Branch address for %s already set by other task. Call first GetBranchAddress() in %s::ConnectInputData()",branchname, fParent->GetName());
      return kFALSE;
   }
   TTree *tree = (TTree*)GetData();
   tree->SetBranchAddress(branchname, address);
   return kTRUE;
}

//______________________________________________________________________________
TObject *AliAnalysisDataSlot::GetData() const
{
// Retreives the data from the container if it is ready.
   if (!fContainer) {
     //AliError(Form("Data slot of type %s of task %s has no connected data container",fType->GetName(), fParent->GetName()));
      return NULL;
   }
   if (!fContainer->IsDataReady()) return NULL;
   return fContainer->GetData();
}

//______________________________________________________________________________
Bool_t  AliAnalysisDataSlot::IsDataReady() const
{
// Check if data for this slot is ready in its container.
   if (!fContainer) {
     cout<<"Data slot of type "<<GetTitle()<<" of task "<<fParent->GetName()<<" has no connected data container"<<endl;
     //AliError(Form("Data slot of type %s of task %s has no connected data container",fType->GetName(), fParent->GetName()));
      return kFALSE;
   }
   return fContainer->IsDataReady();
}

<|endoftext|>