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

#define __STDC_LIMIT_MACROS

// STL
#include <algorithm>

// HyperDex
#include "common/datatypes.h"
#include "common/range_searches.h"

using hyperdex::attribute_check;
using hyperdex::datatype_info;
using hyperdex::range;

static bool
range_search(const attribute_check& check, range* r)
{
    switch (check.predicate)
    {
        case HYPERPREDICATE_EQUALS:
            r->attr = check.attr;
            r->type = check.datatype;
            r->start = check.value;
            r->end = check.value;
            r->has_start = true;
            r->has_end = true;
            r->invalid = false;
            return true;
        case HYPERPREDICATE_LESS_EQUAL:
            r->attr = check.attr;
            r->type = check.datatype;
            r->end = check.value;
            r->has_start = false;
            r->has_end = true;
            r->invalid = false;
            return true;
        case HYPERPREDICATE_GREATER_EQUAL:
            r->attr = check.attr;
            r->type = check.datatype;
            r->start = check.value;
            r->has_start = true;
            r->has_end = false;
            r->invalid = false;
            return true;
        case HYPERPREDICATE_FAIL:
        case HYPERPREDICATE_CONTAINS_LESS_THAN:
        case HYPERPREDICATE_REGEX:
        case HYPERPREDICATE_LENGTH_EQUALS:
        case HYPERPREDICATE_LENGTH_LESS_EQUAL:
        case HYPERPREDICATE_LENGTH_GREATER_EQUAL:
        case HYPERPREDICATE_CONTAINS:
        default:
            return false;
    }
}

static bool
compress_ranges(const range* range_ptr, const range* range_end, range* r)
{
    r->attr = range_ptr->attr;
    r->type = range_ptr->type;
    r->start = e::slice();
    r->end = e::slice();
    r->has_start = false;
    r->has_end = false;
    r->invalid = false;
    datatype_info* di = datatype_info::lookup(range_ptr->type);

    if (!di)
    {
        return false;
    }

    for (; range_ptr < range_end; ++range_ptr)
    {
        // bump/set the start if necessary
        if (range_ptr->has_start && r->has_start)
        {
            if (di->compare(range_ptr->start, r->start) > 0)
            {
                r->start = range_ptr->start;
            }
        }
        else if (range_ptr->has_start)
        {
            r->start = range_ptr->start;
            r->has_start = true;
        }

        // cut/set the end if necessary
        if (range_ptr->has_end && r->has_end)
        {
            if (di->compare(range_ptr->end, r->end) < 0)
            {
                r->end = range_ptr->end;
            }
        }
        else if (range_ptr->has_end)
        {
            r->end = range_ptr->end;
            r->has_end = true;
        }
    }

    if (r->has_start && r->has_end && di->compare(r->start, r->end) > 0)
    {
        r->invalid = true;
    }

    return true;
}

bool
hyperdex :: range_searches(const std::vector<attribute_check>& checks,
                           std::vector<range>* ranges)
{
    std::vector<range> raw_ranges;
    raw_ranges.reserve(checks.size());

    for (size_t i = 0; i < checks.size(); ++i)
    {
        range r;

        if (range_search(checks[i], &r))
        {
            raw_ranges.push_back(r);
        }
    }

    std::sort(raw_ranges.begin(), raw_ranges.end());
    const range* range_ptr = &raw_ranges.front();
    const range* range_end = range_ptr + raw_ranges.size();
    ranges->clear();

    while (range_ptr < range_end)
    {
        const range* tmp = range_ptr;

        while (tmp < range_end && range_ptr->attr == tmp->attr)
        {
            assert(range_ptr->attr == tmp->attr);
            assert(range_ptr->type == tmp->type);
            ++tmp;
        }

        assert(tmp == range_end || range_ptr->attr < tmp->attr);
        range r;

        if (compress_ranges(range_ptr, tmp, &r))
        {
            ranges->push_back(r);
        }
    }

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

#define __STDC_LIMIT_MACROS

// STL
#include <algorithm>

// HyperDex
#include "common/datatypes.h"
#include "common/range_searches.h"

using hyperdex::attribute_check;
using hyperdex::datatype_info;
using hyperdex::range;

static bool
range_search(const attribute_check& check, range* r)
{
    switch (check.predicate)
    {
        case HYPERPREDICATE_EQUALS:
            r->attr = check.attr;
            r->type = check.datatype;
            r->start = check.value;
            r->end = check.value;
            r->has_start = true;
            r->has_end = true;
            r->invalid = false;
            return true;
        case HYPERPREDICATE_LESS_EQUAL:
            r->attr = check.attr;
            r->type = check.datatype;
            r->end = check.value;
            r->has_start = false;
            r->has_end = true;
            r->invalid = false;
            return true;
        case HYPERPREDICATE_GREATER_EQUAL:
            r->attr = check.attr;
            r->type = check.datatype;
            r->start = check.value;
            r->has_start = true;
            r->has_end = false;
            r->invalid = false;
            return true;
        case HYPERPREDICATE_FAIL:
        case HYPERPREDICATE_CONTAINS_LESS_THAN:
        case HYPERPREDICATE_REGEX:
        case HYPERPREDICATE_LENGTH_EQUALS:
        case HYPERPREDICATE_LENGTH_LESS_EQUAL:
        case HYPERPREDICATE_LENGTH_GREATER_EQUAL:
        case HYPERPREDICATE_CONTAINS:
        default:
            return false;
    }
}

static bool
compress_ranges(const range* range_ptr, const range* range_end, range* r)
{
    r->attr = range_ptr->attr;
    r->type = range_ptr->type;
    r->start = e::slice();
    r->end = e::slice();
    r->has_start = false;
    r->has_end = false;
    r->invalid = false;
    datatype_info* di = datatype_info::lookup(range_ptr->type);

    if (!di)
    {
        return false;
    }

    for (; range_ptr < range_end; ++range_ptr)
    {
        // bump/set the start if necessary
        if (range_ptr->has_start && r->has_start)
        {
            if (di->compare(range_ptr->start, r->start) > 0)
            {
                r->start = range_ptr->start;
            }
        }
        else if (range_ptr->has_start)
        {
            r->start = range_ptr->start;
            r->has_start = true;
        }

        // cut/set the end if necessary
        if (range_ptr->has_end && r->has_end)
        {
            if (di->compare(range_ptr->end, r->end) < 0)
            {
                r->end = range_ptr->end;
            }
        }
        else if (range_ptr->has_end)
        {
            r->end = range_ptr->end;
            r->has_end = true;
        }
    }

    if (r->has_start && r->has_end && di->compare(r->start, r->end) > 0)
    {
        r->invalid = true;
    }

    return true;
}

bool
hyperdex :: range_searches(const std::vector<attribute_check>& checks,
                           std::vector<range>* ranges)
{
    std::vector<range> raw_ranges;
    raw_ranges.reserve(checks.size());

    for (size_t i = 0; i < checks.size(); ++i)
    {
        range r;

        if (range_search(checks[i], &r))
        {
            raw_ranges.push_back(r);
        }
    }

    std::sort(raw_ranges.begin(), raw_ranges.end());
    const range* range_ptr = &raw_ranges.front();
    const range* range_end = range_ptr + raw_ranges.size();
    ranges->clear();

    while (range_ptr < range_end)
    {
        const range* tmp = range_ptr;

        while (tmp < range_end && range_ptr->attr == tmp->attr)
        {
            assert(range_ptr->attr == tmp->attr);
            assert(range_ptr->type == tmp->type);
            ++tmp;
        }

        assert(tmp == range_end || range_ptr->attr < tmp->attr);
        range r;

        if (compress_ranges(range_ptr, tmp, &r))
        {
            ranges->push_back(r);
        }

        range_ptr = tmp;
    }

    return true;
}
<|endoftext|>


<commit_before>//---------------------------------------------------------------------------//
/*
  Copyright (c) 2012, Stuart R. Slattery
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are
  met:

  *: Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.

  *: Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.

  *: Neither the name of the University of Wisconsin - Madison nor the
  names of its contributors may be used to endorse or promote products
  derived from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//---------------------------------------------------------------------------//
/*!
 * \brief DTK_LibmeshEntityImpl.cpp
 * \author Stuart R. Slattery
 * \brief Libmesh entity implementation.
 */
//---------------------------------------------------------------------------//

#include <limits>
#include <vector>

#include "DTK_LibmeshEntityImpl.hpp"
#include <DTK_DBC.hpp>

#include <libmesh/node.h>
#include <libmesh/elem.h>

namespace DataTransferKit
{
//---------------------------------------------------------------------------//
// libMesh::Elem specializations.
//---------------------------------------------------------------------------//
// Constructor.
template<>
LibmeshEntityImpl<libMesh::Elem>::LibmeshEntityImpl(
    const Teuchos::Ptr<libMesh::Elem>& libmesh_geom,
    const Teuchos::Ptr<libMesh::MeshBase>& libmesh_mesh,
    const Teuchos::Ptr<LibmeshAdjacencies>& adjacencies )
    : d_extra_data( new LibmeshEntityExtraData<libMesh::Elem>(libmesh_geom) )
    , d_mesh( libmesh_mesh )
    , d_adjacencies( adjacencies )
{ /* ... */ }

//---------------------------------------------------------------------------//
// Get the unique global identifier for the entity.
template<>
DataTransferKit::EntityId LibmeshEntityImpl<libMesh::Elem>::id() const
{
    DTK_REQUIRE( d_extra_data->d_libmesh_geom->valid_id() );
    return d_extra_data->d_libmesh_geom->id();
}

//---------------------------------------------------------------------------//
// Get the parallel rank that owns the entity.
template<>
int LibmeshEntityImpl<libMesh::Elem>::ownerRank() const
{
    DTK_REQUIRE( d_extra_data->d_libmesh_geom->valid_processor_id() );
    return d_extra_data->d_libmesh_geom->processor_id();
}

//---------------------------------------------------------------------------//
// Return the topological dimension of the entity.
template<>
int LibmeshEntityImpl<libMesh::Elem>::topologicalDimension() const
{
    return d_extra_data->d_libmesh_geom->dim();
}

//---------------------------------------------------------------------------//
// Return the physical dimension of the entity.
template<>
int LibmeshEntityImpl<libMesh::Elem>::physicalDimension() const
{
    return d_mesh->mesh_dimension();
}

//---------------------------------------------------------------------------//
// Get the extra data on the entity.
template<>
Teuchos::RCP<DataTransferKit::EntityExtraData>
LibmeshEntityImpl<libMesh::Elem>::extraData() const
{
    return d_extra_data;
}

//---------------------------------------------------------------------------//
// Return the Cartesian bounding box around an entity.
template<>
void LibmeshEntityImpl<libMesh::Elem>::boundingBox(
    Teuchos::Tuple<double,6>& bounds ) const
{
    unsigned int num_nodes = d_extra_data->d_libmesh_geom->n_nodes();
    int space_dim = this->physicalDimension();
    double max = std::numeric_limits<double>::max();
    bounds = Teuchos::tuple( max, max, max, -max, -max, -max );
    for ( unsigned int n = 0; n < num_nodes; ++n )
    {
	const libMesh::Point& node = d_extra_data->d_libmesh_geom->point(n);
	for ( int d = 0; d < space_dim; ++d )
	{
	    bounds[d] = std::min( bounds[d], node(d) );
	    bounds[d+3] = std::max( bounds[d+3], node(d) );
	}
    }
    for ( int d = space_dim; d < 3; ++d )
    {
	bounds[d] = -max;
	bounds[d+3] = max;
    }
}

//---------------------------------------------------------------------------//
// Determine if an entity is in the block with the given id.
template<>
bool LibmeshEntityImpl<libMesh::Elem>::inBlock( const int block_id ) const
{
    return ( block_id == d_extra_data->d_libmesh_geom->subdomain_id() );
}

//---------------------------------------------------------------------------//
// Determine if an entity is on the boundary with the given id.
template<>
bool LibmeshEntityImpl<libMesh::Elem>::onBoundary( const int boundary_id ) const
{
    bool on_boundary = false;
    int n_sides = d_extra_data->d_libmesh_geom->n_sides();
    for ( int s = 0; s < n_sides; ++s )
    {
	on_boundary = d_mesh->get_boundary_info().has_boundary_id(
	    d_extra_data->d_libmesh_geom.getRawPtr(),
	    s, boundary_id );
	if ( on_boundary )
	{
	    break;
	}
    }
    return on_boundary;
}

//---------------------------------------------------------------------------//
// Provide a verbose description of the object.
template<>
void LibmeshEntityImpl<libMesh::Elem>::describe(
    Teuchos::FancyOStream& out,
    const Teuchos::EVerbosityLevel /*verb_level*/ ) const
{
    unsigned int num_nodes = d_extra_data->d_libmesh_geom->n_nodes();

    out << std::endl;
    out << "---" << std::endl;
    out << "LibMesh Element" << std::endl;
    out << "Owner rank: " << ownerRank() << std::endl;
    out << d_extra_data->d_libmesh_geom->get_info() << std::endl;
    out << "---" << std::endl;
}

//---------------------------------------------------------------------------//
// libMesh::Node specializations.
//---------------------------------------------------------------------------//
// Constructor.
template<>
LibmeshEntityImpl<libMesh::Node>::LibmeshEntityImpl(
    const Teuchos::Ptr<libMesh::Node>& libmesh_geom,
    const Teuchos::Ptr<libMesh::MeshBase>& libmesh_mesh,
    const Teuchos::Ptr<LibmeshAdjacencies>& adjacencies )
    : d_extra_data( new LibmeshEntityExtraData<libMesh::Node>(libmesh_geom) )
    , d_mesh( libmesh_mesh )
    , d_adjacencies( adjacencies )
{ /* ... */ }

//---------------------------------------------------------------------------//
// Get the unique global identifier for the entity.
template<>
DataTransferKit::EntityId LibmeshEntityImpl<libMesh::Node>::id() const
{
    DTK_REQUIRE( d_extra_data->d_libmesh_geom->valid_id() );
    return d_extra_data->d_libmesh_geom->id();
}

//---------------------------------------------------------------------------//
// Get the parallel rank that owns the entity.
template<>
int LibmeshEntityImpl<libMesh::Node>::ownerRank() const
{
    DTK_REQUIRE( d_extra_data->d_libmesh_geom->valid_processor_id() );
    return d_extra_data->d_libmesh_geom->processor_id();
}

//---------------------------------------------------------------------------//
// Return the topological dimension of the entity.
template<>
int LibmeshEntityImpl<libMesh::Node>::topologicalDimension() const
{
    return 0;
}

//---------------------------------------------------------------------------//
// Return the physical dimension of the entity.
template<>
int LibmeshEntityImpl<libMesh::Node>::physicalDimension() const
{
    return d_mesh->mesh_dimension();
}

//---------------------------------------------------------------------------//
// Get the extra data on the entity.
template<>
Teuchos::RCP<DataTransferKit::EntityExtraData>
LibmeshEntityImpl<libMesh::Node>::extraData() const
{
    return d_extra_data;
}

//---------------------------------------------------------------------------//
// Return the Cartesian bounding box around an entity.
template<>
void LibmeshEntityImpl<libMesh::Node>::boundingBox(
    Teuchos::Tuple<double,6>& bounds ) const
{
    int space_dim = this->physicalDimension();
    for ( int d = 0; d < space_dim; ++d )
    {
	bounds[d] = (*(d_extra_data->d_libmesh_geom))(d);
	bounds[d+3] = (*(d_extra_data->d_libmesh_geom))(d);
    }
}

//---------------------------------------------------------------------------//
// Determine if an entity is in the block with the given id.
template<>
bool LibmeshEntityImpl<libMesh::Node>::inBlock( const int block_id ) const
{
    Teuchos::Array<Teuchos::Ptr<libMesh::Elem> > node_elems;
    d_adjacencies->getLibmeshAdjacencies( d_extra_data->d_libmesh_geom,
				   node_elems );
    for ( auto& elem : node_elems )
    {
	if ( block_id == elem->subdomain_id() )
	{
	    return true;
	}
    }

    return false;
}

//---------------------------------------------------------------------------//
// Determine if an entity is on the boundary with the given id.
template<>
bool LibmeshEntityImpl<libMesh::Node>::onBoundary( const int boundary_id ) const
{
    return d_mesh->get_boundary_info().has_boundary_id(
	d_extra_data->d_libmesh_geom.getRawPtr(), boundary_id );
}

//---------------------------------------------------------------------------//
// Provide a verbose description of the object.
template<>
void LibmeshEntityImpl<libMesh::Node>::describe(
    Teuchos::FancyOStream& out,
    const Teuchos::EVerbosityLevel /*verb_level*/ ) const
{
    int space_dim = this->physicalDimension();

    out << std::endl;
    out << "---" << std::endl;
    out << "LibMesh Node" << std::endl;
    out << "Id: " << id() << std::endl;
    out << "Owner rank: " << ownerRank() << std::endl;
    out << d_extra_data->d_libmesh_geom->get_info() << std::endl;
    out << "---" << std::endl;
}

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

} // end namespace DataTransferKit

//---------------------------------------------------------------------------//
// end DTK_LibmeshEntityImpl.cpp
//---------------------------------------------------------------------------//
<commit_msg>removing unused variables in libmesh entity impl<commit_after>//---------------------------------------------------------------------------//
/*
  Copyright (c) 2012, Stuart R. Slattery
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are
  met:

  *: Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.

  *: Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.

  *: Neither the name of the University of Wisconsin - Madison nor the
  names of its contributors may be used to endorse or promote products
  derived from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//---------------------------------------------------------------------------//
/*!
 * \brief DTK_LibmeshEntityImpl.cpp
 * \author Stuart R. Slattery
 * \brief Libmesh entity implementation.
 */
//---------------------------------------------------------------------------//

#include <limits>
#include <vector>

#include "DTK_LibmeshEntityImpl.hpp"
#include <DTK_DBC.hpp>

#include <libmesh/node.h>
#include <libmesh/elem.h>

namespace DataTransferKit
{
//---------------------------------------------------------------------------//
// libMesh::Elem specializations.
//---------------------------------------------------------------------------//
// Constructor.
template<>
LibmeshEntityImpl<libMesh::Elem>::LibmeshEntityImpl(
    const Teuchos::Ptr<libMesh::Elem>& libmesh_geom,
    const Teuchos::Ptr<libMesh::MeshBase>& libmesh_mesh,
    const Teuchos::Ptr<LibmeshAdjacencies>& adjacencies )
    : d_extra_data( new LibmeshEntityExtraData<libMesh::Elem>(libmesh_geom) )
    , d_mesh( libmesh_mesh )
    , d_adjacencies( adjacencies )
{ /* ... */ }

//---------------------------------------------------------------------------//
// Get the unique global identifier for the entity.
template<>
DataTransferKit::EntityId LibmeshEntityImpl<libMesh::Elem>::id() const
{
    DTK_REQUIRE( d_extra_data->d_libmesh_geom->valid_id() );
    return d_extra_data->d_libmesh_geom->id();
}

//---------------------------------------------------------------------------//
// Get the parallel rank that owns the entity.
template<>
int LibmeshEntityImpl<libMesh::Elem>::ownerRank() const
{
    DTK_REQUIRE( d_extra_data->d_libmesh_geom->valid_processor_id() );
    return d_extra_data->d_libmesh_geom->processor_id();
}

//---------------------------------------------------------------------------//
// Return the topological dimension of the entity.
template<>
int LibmeshEntityImpl<libMesh::Elem>::topologicalDimension() const
{
    return d_extra_data->d_libmesh_geom->dim();
}

//---------------------------------------------------------------------------//
// Return the physical dimension of the entity.
template<>
int LibmeshEntityImpl<libMesh::Elem>::physicalDimension() const
{
    return d_mesh->mesh_dimension();
}

//---------------------------------------------------------------------------//
// Get the extra data on the entity.
template<>
Teuchos::RCP<DataTransferKit::EntityExtraData>
LibmeshEntityImpl<libMesh::Elem>::extraData() const
{
    return d_extra_data;
}

//---------------------------------------------------------------------------//
// Return the Cartesian bounding box around an entity.
template<>
void LibmeshEntityImpl<libMesh::Elem>::boundingBox(
    Teuchos::Tuple<double,6>& bounds ) const
{
    unsigned int num_nodes = d_extra_data->d_libmesh_geom->n_nodes();
    int space_dim = this->physicalDimension();
    double max = std::numeric_limits<double>::max();
    bounds = Teuchos::tuple( max, max, max, -max, -max, -max );
    for ( unsigned int n = 0; n < num_nodes; ++n )
    {
	const libMesh::Point& node = d_extra_data->d_libmesh_geom->point(n);
	for ( int d = 0; d < space_dim; ++d )
	{
	    bounds[d] = std::min( bounds[d], node(d) );
	    bounds[d+3] = std::max( bounds[d+3], node(d) );
	}
    }
    for ( int d = space_dim; d < 3; ++d )
    {
	bounds[d] = -max;
	bounds[d+3] = max;
    }
}

//---------------------------------------------------------------------------//
// Determine if an entity is in the block with the given id.
template<>
bool LibmeshEntityImpl<libMesh::Elem>::inBlock( const int block_id ) const
{
    return ( block_id == d_extra_data->d_libmesh_geom->subdomain_id() );
}

//---------------------------------------------------------------------------//
// Determine if an entity is on the boundary with the given id.
template<>
bool LibmeshEntityImpl<libMesh::Elem>::onBoundary( const int boundary_id ) const
{
    bool on_boundary = false;
    int n_sides = d_extra_data->d_libmesh_geom->n_sides();
    for ( int s = 0; s < n_sides; ++s )
    {
	on_boundary = d_mesh->get_boundary_info().has_boundary_id(
	    d_extra_data->d_libmesh_geom.getRawPtr(),
	    s, boundary_id );
	if ( on_boundary )
	{
	    break;
	}
    }
    return on_boundary;
}

//---------------------------------------------------------------------------//
// Provide a verbose description of the object.
template<>
void LibmeshEntityImpl<libMesh::Elem>::describe(
    Teuchos::FancyOStream& out,
    const Teuchos::EVerbosityLevel /*verb_level*/ ) const
{
    out << std::endl;
    out << "---" << std::endl;
    out << "LibMesh Element" << std::endl;
    out << "Owner rank: " << ownerRank() << std::endl;
    out << d_extra_data->d_libmesh_geom->get_info() << std::endl;
    out << "---" << std::endl;
}

//---------------------------------------------------------------------------//
// libMesh::Node specializations.
//---------------------------------------------------------------------------//
// Constructor.
template<>
LibmeshEntityImpl<libMesh::Node>::LibmeshEntityImpl(
    const Teuchos::Ptr<libMesh::Node>& libmesh_geom,
    const Teuchos::Ptr<libMesh::MeshBase>& libmesh_mesh,
    const Teuchos::Ptr<LibmeshAdjacencies>& adjacencies )
    : d_extra_data( new LibmeshEntityExtraData<libMesh::Node>(libmesh_geom) )
    , d_mesh( libmesh_mesh )
    , d_adjacencies( adjacencies )
{ /* ... */ }

//---------------------------------------------------------------------------//
// Get the unique global identifier for the entity.
template<>
DataTransferKit::EntityId LibmeshEntityImpl<libMesh::Node>::id() const
{
    DTK_REQUIRE( d_extra_data->d_libmesh_geom->valid_id() );
    return d_extra_data->d_libmesh_geom->id();
}

//---------------------------------------------------------------------------//
// Get the parallel rank that owns the entity.
template<>
int LibmeshEntityImpl<libMesh::Node>::ownerRank() const
{
    DTK_REQUIRE( d_extra_data->d_libmesh_geom->valid_processor_id() );
    return d_extra_data->d_libmesh_geom->processor_id();
}

//---------------------------------------------------------------------------//
// Return the topological dimension of the entity.
template<>
int LibmeshEntityImpl<libMesh::Node>::topologicalDimension() const
{
    return 0;
}

//---------------------------------------------------------------------------//
// Return the physical dimension of the entity.
template<>
int LibmeshEntityImpl<libMesh::Node>::physicalDimension() const
{
    return d_mesh->mesh_dimension();
}

//---------------------------------------------------------------------------//
// Get the extra data on the entity.
template<>
Teuchos::RCP<DataTransferKit::EntityExtraData>
LibmeshEntityImpl<libMesh::Node>::extraData() const
{
    return d_extra_data;
}

//---------------------------------------------------------------------------//
// Return the Cartesian bounding box around an entity.
template<>
void LibmeshEntityImpl<libMesh::Node>::boundingBox(
    Teuchos::Tuple<double,6>& bounds ) const
{
    int space_dim = this->physicalDimension();
    for ( int d = 0; d < space_dim; ++d )
    {
	bounds[d] = (*(d_extra_data->d_libmesh_geom))(d);
	bounds[d+3] = (*(d_extra_data->d_libmesh_geom))(d);
    }
}

//---------------------------------------------------------------------------//
// Determine if an entity is in the block with the given id.
template<>
bool LibmeshEntityImpl<libMesh::Node>::inBlock( const int block_id ) const
{
    Teuchos::Array<Teuchos::Ptr<libMesh::Elem> > node_elems;
    d_adjacencies->getLibmeshAdjacencies( d_extra_data->d_libmesh_geom,
				   node_elems );
    for ( auto& elem : node_elems )
    {
	if ( block_id == elem->subdomain_id() )
	{
	    return true;
	}
    }

    return false;
}

//---------------------------------------------------------------------------//
// Determine if an entity is on the boundary with the given id.
template<>
bool LibmeshEntityImpl<libMesh::Node>::onBoundary( const int boundary_id ) const
{
    return d_mesh->get_boundary_info().has_boundary_id(
	d_extra_data->d_libmesh_geom.getRawPtr(), boundary_id );
}

//---------------------------------------------------------------------------//
// Provide a verbose description of the object.
template<>
void LibmeshEntityImpl<libMesh::Node>::describe(
    Teuchos::FancyOStream& out,
    const Teuchos::EVerbosityLevel /*verb_level*/ ) const
{
    out << std::endl;
    out << "---" << std::endl;
    out << "LibMesh Node" << std::endl;
    out << "Id: " << id() << std::endl;
    out << "Owner rank: " << ownerRank() << std::endl;
    out << d_extra_data->d_libmesh_geom->get_info() << std::endl;
    out << "---" << std::endl;
}

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

} // end namespace DataTransferKit

//---------------------------------------------------------------------------//
// end DTK_LibmeshEntityImpl.cpp
//---------------------------------------------------------------------------//
<|endoftext|>


<commit_before>/*
Resembla
https://github.com/tuem/resembla

Copyright 2017 Takashi Uemura

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "real_feature_aggregator.hpp"

#include <algorithm>
#include <iostream>

namespace resembla {

Feature::real_type RealFeatureAggregatorImpl::operator()(Feature::real_type a, Feature::real_type b) const
{
    auto c = (std::abs(a) + std::abs(b) - 2.0 * std::abs(a - b)) / 2.0;
#ifdef DEBUG
    std::cerr << "real-type feature: a=" << a << ", b=" << b << ", c=" << c << std::endl;
#endif
    return std::min(std::max(c, -1.0), 1.0);
}

}
<commit_msg>use ifdef<commit_after>/*
Resembla
https://github.com/tuem/resembla

Copyright 2017 Takashi Uemura

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "real_feature_aggregator.hpp"

#include <cmath>
#include <algorithm>

#ifdef DEBUG
#include <iostream>
#endif

namespace resembla {

Feature::real_type RealFeatureAggregatorImpl::operator()(Feature::real_type a, Feature::real_type b) const
{
    auto c = (std::abs(a) + std::abs(b) - 2.0 * std::abs(a - b)) / 2.0;
#ifdef DEBUG
    std::cerr << "real-type feature: a=" << a << ", b=" << b << ", c=" << c << std::endl;
#endif
    return std::min(std::max(c, -1.0), 1.0);
}

}
<|endoftext|>


<commit_before>#include "builtin/channel.hpp"
#include "builtin/class.hpp"
#include "builtin/exception.hpp"
#include "builtin/fiber.hpp"
#include "builtin/fixnum.hpp"
#include "builtin/location.hpp"
#include "builtin/lookuptable.hpp"
#include "builtin/nativemethod.hpp"
#include "builtin/thread.hpp"
#include "builtin/tuple.hpp"
#include "builtin/symbol.hpp"
#include "call_frame.hpp"
#include "dtrace/dtrace.h"
#include "environment.hpp"
#include "instruments/tooling.hpp"
#include "object_utils.hpp"
#include "ontology.hpp"
#include "on_stack.hpp"

/* HACK: returns a value that should identify a native thread
 * for debugging threading issues. The winpthreads library
 * defines pthread_t to be a structure not a pointer.
 */
intptr_t thread_debug_self() {
#ifdef RBX_WINDOWS
  return (intptr_t)(pthread_self()).p;
#else
  return (intptr_t)pthread_self();
#endif
}

static intptr_t thread_debug_id(pthread_t thr) {
#ifdef RBX_WINDOWS
    return (intptr_t)thr.p;
#else
    return (intptr_t)thr;
#endif
}

namespace rubinius {

  void Thread::init(STATE) {
    GO(thread).set(ontology::new_class(state, "Thread", G(object)));
    G(thread)->set_object_type(state, Thread::type);
  }

  Thread* Thread::create(STATE, VM* target, Object* self, Run runner,
                         bool main_thread, bool system_thread)
  {
    Thread* thr = state->new_object<Thread>(G(thread));

    thr->thread_id(state, Fixnum::from(target->thread_id()));
    thr->sleep(state, cFalse);
    thr->control_channel(state, nil<Channel>());
    thr->locals(state, LookupTable::create(state));
    thr->recursive_objects(state, LookupTable::create(state));
    thr->group(state, cNil);
    thr->result(state, cFalse);
    thr->exception(state, nil<Exception>());
    thr->critical(state, cFalse);
    thr->dying(state, cFalse);
    thr->joins(state, Array::create(state, 1));
    thr->killed(state, cFalse);

    thr->vm_ = target;
    thr->klass(state, as<Class>(self));
    thr->runner_ = runner;
    thr->init_lock_.init();
    thr->system_thread_ = system_thread;

    target->thread.set(thr);

    return thr;
  }

  Object* send_run(STATE) {
    return state->vm()->thread.get()->send(state, NULL, state->symbol("__run__"));
  }

  Thread* Thread::allocate(STATE, Object* self) {
    VM* vm = state->shared().new_vm();
    Thread* thread = Thread::create(state, vm, self, send_run);

    return thread;
  }

  Thread* Thread::current(STATE) {
    return state->vm()->thread.get();
  }

  Object* Thread::unlock_locks(STATE, GCToken gct, CallFrame* calling_environment) {
    LockedObjects& los = vm_->locked_objects();
    for(LockedObjects::iterator i = los.begin();
        i != los.end();
        ++i) {
      (*i)->unlock_for_terminate(state, gct, calling_environment);
    }
    los.clear();
    return cNil;
  }

  Object* Thread::locals_aref(STATE, Symbol* key) {
    /*
     * If we're not trying to set values on the current thread,
     * we will set thread locals anyway and not use fiber locals.
     */
    if(state->vm() != vm()) {
      return locals()->aref(state, key);
    }
    Fiber* fib = state->vm()->current_fiber.get();
    if(fib->nil_p() || fib->root_p()) {
      return locals()->aref(state, key);
    }
    if(try_as<LookupTable>(fib->locals())) {
      return fib->locals()->aref(state, key);
    }
    return cNil;
  }

  Object* Thread::locals_store(STATE, Symbol* key, Object* value) {
    /*
     * If we're not trying to set values on the current thread,
     * we will set thread locals anyway and not use fiber locals.
     */
    check_frozen(state);
    if(state->vm() != vm()) {
      return locals()->store(state, key, value);
    }
    Fiber* fib = state->vm()->current_fiber.get();
    if(fib->nil_p() || fib->root_p()) {
      return locals()->store(state, key, value);
    }
    if(fib->locals()->nil_p()) {
      fib->locals(state, LookupTable::create(state));
    }
    return fib->locals()->store(state, key, value);
  }

  Object* Thread::locals_remove(STATE, Symbol* key) {
    check_frozen(state);
    if(state->vm() != vm()) {
      return locals()->remove(state, key);
    }
    Fiber* fib = state->vm()->current_fiber.get();
    if(fib->nil_p() || fib->root_p()) {
      return locals()->remove(state, key);
    }
    if(fib->locals()->nil_p()) {
      return cNil;
    }
    return fib->locals()->remove(state, key);
  }

  Array* Thread::locals_keys(STATE) {
    /*
     * If we're not trying to set values on the current thread,
     * we will set thread locals anyway and not use fiber locals.
     */
    if(state->vm() != vm()) {
      return locals()->all_keys(state);
    }
    Fiber* fib = state->vm()->current_fiber.get();
    if(fib->nil_p() || fib->root_p()) {
      return locals()->all_keys(state);
    }
    if(try_as<LookupTable>(fib->locals())) {
      return fib->locals()->all_keys(state);
    }
    return Array::create(state, 0);
  }

  Object* Thread::locals_has_key(STATE, Symbol* key) {
    /*
     * If we're not trying to set values on the current thread,
     * we will set thread locals anyway and not use fiber locals.
     */
    if(state->vm() != vm()) {
      return locals()->has_key(state, key);
    }
    Fiber* fib = state->vm()->current_fiber.get();
    if(fib->nil_p() || fib->root_p()) {
      return locals()->has_key(state, key);
    }
    if(try_as<LookupTable>(fib->locals())) {
      return fib->locals()->has_key(state, key);
    }
    return cFalse;
  }

  int Thread::start_new_thread(STATE, const pthread_attr_t &attrs) {
    Thread* self = this;
    OnStack<1> os(state, self);

    self->init_lock_.lock();
    int error = pthread_create(&vm_->os_thread(), &attrs, in_new_thread, (void*)vm_);
    if(error) {
      return error;
    }

    // We can't return from here until the new thread completes a minimal
    // initialization. After the initialization, it unlocks init_lock_.
    // So, wait here until we can lock init_lock_ after that.
    self->init_lock_.lock();

    // We locked init_lock_. And we are sure that the new thread completed
    // the initialization.
    // Locking init_lock_ isn't needed anymore, so unlock it.
    self->init_lock_.unlock();

    return 0;
  }

  void* Thread::in_new_thread(void* ptr) {
    VM* vm = reinterpret_cast<VM*>(ptr);

    State state_obj(vm), *state = &state_obj;

    int calculate_stack = 0;
    NativeMethod::init_thread(state);

    {
      std::ostringstream tn;
      tn << "rbx.ruby." << vm->thread_id();
      VM::set_current(vm, tn.str());
    }

    RUBINIUS_THREAD_START(tn.str().c_str(), vm->thread_id(), 0);

    if(cDebugThreading) {
      std::cerr << "[THREAD " << vm->thread_id()
                << " (" << (unsigned int)thread_debug_self() << ") started thread]\n";
    }

    vm->set_root_stack(reinterpret_cast<uintptr_t>(&calculate_stack), THREAD_STACK_SIZE);

    GCTokenImpl gct;

    // Lock the thread object and unlock it at __run__ in the ruby land.
    vm->thread->hard_lock(state, gct, 0);
    vm->thread->alive(state, cTrue);
    vm->thread->init_lock_.unlock();

    // Become GC-dependent after unlocking init_lock_ to avoid deadlocks.
    // gc_dependent may lock when it detects GC is happening. Also the parent
    // thread is locked until init_lock_ is unlocked by this child thread.
    state->gc_dependent(gct, 0);

    vm->shared.tool_broker()->thread_start(state);
    Object* ret = vm->thread->runner_(state);
    vm->shared.tool_broker()->thread_stop(state);

    if(!ret) {
      if(vm->thread_state()->raise_reason() == cExit) {
        vm->shared.env()->halt_and_exit(state);
      }
    }

    // Clear the call_frame, so that if we wait for GC going independent,
    // the GC doesn't see pointers into now-unallocated CallFrames
    vm->set_call_frame(0);

    LockedObjects& los = vm->locked_objects();
    for(LockedObjects::iterator i = los.begin();
        i != los.end();
        ++i) {
      (*i)->unlock_for_terminate(state, gct, 0);
    }

    vm->thread->init_lock_.lock();
    NativeMethod::cleanup_thread(state);

    vm->thread->alive(state, cFalse);
    vm->thread->cleanup();
    vm->thread->init_lock_.unlock();

    vm->shared.gc_independent(state, 0);
    vm->shared.clear_critical(state);

    VM::discard(state, vm);

    if(cDebugThreading) {
      std::cerr << "[LOCK thread " << vm->thread_id() << " exited]\n";
    }

    RUBINIUS_THREAD_STOP(tn.str().c_str(), vm->thread_id(), 0);
    return 0;
  }

  Object* Thread::fork(STATE) {
    // If the thread is already alive or already ran,
    // we can't use it anymore.
    if(CBOOL(alive()) || !vm_) {
      return Primitives::failure();
    }

    pthread_attr_t attrs;
    pthread_attr_init(&attrs);
    pthread_attr_setstacksize(&attrs, THREAD_STACK_SIZE);
    pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);

    int error = start_new_thread(state, attrs);

    if(error) {
      char buf[RBX_STRERROR_BUFSIZE];
      char* err = RBX_STRERROR(error, buf, RBX_STRERROR_BUFSIZE);
      Exception::thread_error(state, err);
    }
    return cNil;
  }

  int Thread::fork_attached(STATE) {
    pthread_attr_t attrs;
    pthread_attr_init(&attrs);
    pthread_attr_setstacksize(&attrs, THREAD_STACK_SIZE);

    return start_new_thread(state, attrs);
  }

  Object* Thread::pass(STATE, CallFrame* calling_environment) {
    atomic::pause();
    return cNil;
  }

  Array* Thread::list(STATE) {
    return state->shared().vm_threads(state);
  }

  Object* Thread::priority(STATE) {
    pthread_t id = vm_->os_thread();

    int _policy;
    struct sched_param params;

    if(pthread_getschedparam(id, &_policy, &params) == 0) {
      return Fixnum::from(params.sched_priority);
    }

    return cNil;
  }

  Object* Thread::raise(STATE, GCToken gct, Exception* exc, CallFrame* calling_environment) {
    utilities::thread::SpinLock::LockGuard lg(init_lock_);
    Thread* self = this;
    OnStack<2> os(state, self, exc);

    VM* vm = self->vm_;
    if(!vm) {
      return cNil;
    }

    vm->register_raise(state, exc);

    vm->wakeup(state, gct, calling_environment);
    return exc;
  }

  Object* Thread::current_exception(STATE) {
    utilities::thread::SpinLock::LockGuard lg(init_lock_);
    return vm_->thread_state()->current_exception();
  }

  Object* Thread::kill(STATE, GCToken gct, CallFrame* calling_environment) {
    utilities::thread::SpinLock::LockGuard lg(init_lock_);
    Thread* self = this;
    OnStack<1> os(state, self);

    VM* vm = self->vm_;
    if(!vm) {
      return cNil;
    }

    if(state->vm()->thread.get() == self) {
      vm_->thread_state_.raise_thread_kill();
      return NULL;
    } else {
      vm->register_kill(state);
      vm->wakeup(state, gct, calling_environment);
      return self;
    }
  }

  Object* Thread::set_priority(STATE, Fixnum* new_priority) {
    return new_priority;
  }

  Thread* Thread::wakeup(STATE, GCToken gct, CallFrame* calling_environment) {
    utilities::thread::SpinLock::LockGuard lg(init_lock_);
    Thread* self = this;
    OnStack<1> os(state, self);

    VM* vm = self->vm_;
    if(!CBOOL(alive()) || !vm) {
      return force_as<Thread>(Primitives::failure());
    }

    vm->wakeup(state, gct, calling_environment);

    return self;
  }

  Tuple* Thread::context(STATE) {
    utilities::thread::SpinLock::LockGuard lg(init_lock_);

    VM* vm = vm_;
    if(!vm) return nil<Tuple>();

    CallFrame* cf = vm->saved_call_frame()->top_ruby_frame();

    VariableScope* scope = cf->promote_scope(state);

    return Tuple::from(state, 3, Fixnum::from(cf->ip()), cf->compiled_code, scope);
  }

  Array* Thread::mri_backtrace(STATE, GCToken gct, CallFrame* calling_environment) {
    utilities::thread::SpinLock::LockGuard lg(init_lock_);

    VM* vm = vm_;
    if(!vm) return nil<Array>();
    StopTheWorld stop(state, gct, calling_environment);

    CallFrame* cf = vm->saved_call_frame();

    return Location::mri_backtrace(state, cf);
  }

  void Thread::cleanup() {
    vm_ = NULL;
  }

  void Thread::init_lock() {
    init_lock_.init();
  }

  Object* Thread::join(STATE, GCToken gct, CallFrame* calling_environment) {
    state->set_call_frame(calling_environment);

    init_lock_.lock();

    VM* vm = vm_;

    if(!vm) {
      init_lock_.unlock();
      return cTrue;
    }

    pthread_t id = vm->os_thread();

    if(cDebugThreading) {
      std::cerr << "[THREAD joining " << thread_debug_id(id) << "]\n";
    }

    init_lock_.unlock();

    state->gc_independent(gct, calling_environment);
    void* val;
    int err = pthread_join(id, &val);
    state->gc_dependent(gct, calling_environment);

    switch(err) {
    case 0:
      break;
    case EDEADLK:
      std::cerr << "Join deadlock: " << thread_debug_id(id) << "/" << thread_debug_self() << "\n";
      break;
    case EINVAL:
      std::cerr << "Invalid thread id: " << thread_debug_id(id) << "\n";
      break;
    case ESRCH:
      // This means that the thread finished execution and detached
      // itself. We treat this as having joined it.
      break;
    }

    return cTrue;
  }

  Object* Thread::set_critical(STATE, Object* obj, CallFrame* calling_environment) {
    state->set_call_frame(calling_environment);
    if(CBOOL(obj)) {
      state->shared().set_critical(state, calling_environment);
      return cTrue;
    } else {
      state->shared().clear_critical(state);
      return cFalse;
    }
  }
}
<commit_msg>Fix undeclared identifier in vm/builtin/thread.cpp<commit_after>#include "builtin/channel.hpp"
#include "builtin/class.hpp"
#include "builtin/exception.hpp"
#include "builtin/fiber.hpp"
#include "builtin/fixnum.hpp"
#include "builtin/location.hpp"
#include "builtin/lookuptable.hpp"
#include "builtin/nativemethod.hpp"
#include "builtin/thread.hpp"
#include "builtin/tuple.hpp"
#include "builtin/symbol.hpp"
#include "call_frame.hpp"
#include "dtrace/dtrace.h"
#include "environment.hpp"
#include "instruments/tooling.hpp"
#include "object_utils.hpp"
#include "ontology.hpp"
#include "on_stack.hpp"

/* HACK: returns a value that should identify a native thread
 * for debugging threading issues. The winpthreads library
 * defines pthread_t to be a structure not a pointer.
 */
intptr_t thread_debug_self() {
#ifdef RBX_WINDOWS
  return (intptr_t)(pthread_self()).p;
#else
  return (intptr_t)pthread_self();
#endif
}

static intptr_t thread_debug_id(pthread_t thr) {
#ifdef RBX_WINDOWS
    return (intptr_t)thr.p;
#else
    return (intptr_t)thr;
#endif
}

namespace rubinius {

  void Thread::init(STATE) {
    GO(thread).set(ontology::new_class(state, "Thread", G(object)));
    G(thread)->set_object_type(state, Thread::type);
  }

  Thread* Thread::create(STATE, VM* target, Object* self, Run runner,
                         bool main_thread, bool system_thread)
  {
    Thread* thr = state->new_object<Thread>(G(thread));

    thr->thread_id(state, Fixnum::from(target->thread_id()));
    thr->sleep(state, cFalse);
    thr->control_channel(state, nil<Channel>());
    thr->locals(state, LookupTable::create(state));
    thr->recursive_objects(state, LookupTable::create(state));
    thr->group(state, cNil);
    thr->result(state, cFalse);
    thr->exception(state, nil<Exception>());
    thr->critical(state, cFalse);
    thr->dying(state, cFalse);
    thr->joins(state, Array::create(state, 1));
    thr->killed(state, cFalse);

    thr->vm_ = target;
    thr->klass(state, as<Class>(self));
    thr->runner_ = runner;
    thr->init_lock_.init();
    thr->system_thread_ = system_thread;

    target->thread.set(thr);

    return thr;
  }

  Object* send_run(STATE) {
    return state->vm()->thread.get()->send(state, NULL, state->symbol("__run__"));
  }

  Thread* Thread::allocate(STATE, Object* self) {
    VM* vm = state->shared().new_vm();
    Thread* thread = Thread::create(state, vm, self, send_run);

    return thread;
  }

  Thread* Thread::current(STATE) {
    return state->vm()->thread.get();
  }

  Object* Thread::unlock_locks(STATE, GCToken gct, CallFrame* calling_environment) {
    LockedObjects& los = vm_->locked_objects();
    for(LockedObjects::iterator i = los.begin();
        i != los.end();
        ++i) {
      (*i)->unlock_for_terminate(state, gct, calling_environment);
    }
    los.clear();
    return cNil;
  }

  Object* Thread::locals_aref(STATE, Symbol* key) {
    /*
     * If we're not trying to set values on the current thread,
     * we will set thread locals anyway and not use fiber locals.
     */
    if(state->vm() != vm()) {
      return locals()->aref(state, key);
    }
    Fiber* fib = state->vm()->current_fiber.get();
    if(fib->nil_p() || fib->root_p()) {
      return locals()->aref(state, key);
    }
    if(try_as<LookupTable>(fib->locals())) {
      return fib->locals()->aref(state, key);
    }
    return cNil;
  }

  Object* Thread::locals_store(STATE, Symbol* key, Object* value) {
    /*
     * If we're not trying to set values on the current thread,
     * we will set thread locals anyway and not use fiber locals.
     */
    check_frozen(state);
    if(state->vm() != vm()) {
      return locals()->store(state, key, value);
    }
    Fiber* fib = state->vm()->current_fiber.get();
    if(fib->nil_p() || fib->root_p()) {
      return locals()->store(state, key, value);
    }
    if(fib->locals()->nil_p()) {
      fib->locals(state, LookupTable::create(state));
    }
    return fib->locals()->store(state, key, value);
  }

  Object* Thread::locals_remove(STATE, Symbol* key) {
    check_frozen(state);
    if(state->vm() != vm()) {
      return locals()->remove(state, key);
    }
    Fiber* fib = state->vm()->current_fiber.get();
    if(fib->nil_p() || fib->root_p()) {
      return locals()->remove(state, key);
    }
    if(fib->locals()->nil_p()) {
      return cNil;
    }
    return fib->locals()->remove(state, key);
  }

  Array* Thread::locals_keys(STATE) {
    /*
     * If we're not trying to set values on the current thread,
     * we will set thread locals anyway and not use fiber locals.
     */
    if(state->vm() != vm()) {
      return locals()->all_keys(state);
    }
    Fiber* fib = state->vm()->current_fiber.get();
    if(fib->nil_p() || fib->root_p()) {
      return locals()->all_keys(state);
    }
    if(try_as<LookupTable>(fib->locals())) {
      return fib->locals()->all_keys(state);
    }
    return Array::create(state, 0);
  }

  Object* Thread::locals_has_key(STATE, Symbol* key) {
    /*
     * If we're not trying to set values on the current thread,
     * we will set thread locals anyway and not use fiber locals.
     */
    if(state->vm() != vm()) {
      return locals()->has_key(state, key);
    }
    Fiber* fib = state->vm()->current_fiber.get();
    if(fib->nil_p() || fib->root_p()) {
      return locals()->has_key(state, key);
    }
    if(try_as<LookupTable>(fib->locals())) {
      return fib->locals()->has_key(state, key);
    }
    return cFalse;
  }

  int Thread::start_new_thread(STATE, const pthread_attr_t &attrs) {
    Thread* self = this;
    OnStack<1> os(state, self);

    self->init_lock_.lock();
    int error = pthread_create(&vm_->os_thread(), &attrs, in_new_thread, (void*)vm_);
    if(error) {
      return error;
    }

    // We can't return from here until the new thread completes a minimal
    // initialization. After the initialization, it unlocks init_lock_.
    // So, wait here until we can lock init_lock_ after that.
    self->init_lock_.lock();

    // We locked init_lock_. And we are sure that the new thread completed
    // the initialization.
    // Locking init_lock_ isn't needed anymore, so unlock it.
    self->init_lock_.unlock();

    return 0;
  }

  void* Thread::in_new_thread(void* ptr) {
    VM* vm = reinterpret_cast<VM*>(ptr);

    State state_obj(vm), *state = &state_obj;

    int calculate_stack = 0;
    NativeMethod::init_thread(state);

    std::string thread_name;

    {
      std::ostringstream tn;
      tn << "rbx.ruby." << vm->thread_id();
      VM::set_current(vm, tn.str());
      thread_name = tn.str();
    }

    RUBINIUS_THREAD_START(thread_name.c_str(), vm->thread_id(), 0);

    if(cDebugThreading) {
      std::cerr << "[THREAD " << vm->thread_id()
                << " (" << (unsigned int)thread_debug_self() << ") started thread]\n";
    }

    vm->set_root_stack(reinterpret_cast<uintptr_t>(&calculate_stack), THREAD_STACK_SIZE);

    GCTokenImpl gct;

    // Lock the thread object and unlock it at __run__ in the ruby land.
    vm->thread->hard_lock(state, gct, 0);
    vm->thread->alive(state, cTrue);
    vm->thread->init_lock_.unlock();

    // Become GC-dependent after unlocking init_lock_ to avoid deadlocks.
    // gc_dependent may lock when it detects GC is happening. Also the parent
    // thread is locked until init_lock_ is unlocked by this child thread.
    state->gc_dependent(gct, 0);

    vm->shared.tool_broker()->thread_start(state);
    Object* ret = vm->thread->runner_(state);
    vm->shared.tool_broker()->thread_stop(state);

    if(!ret) {
      if(vm->thread_state()->raise_reason() == cExit) {
        vm->shared.env()->halt_and_exit(state);
      }
    }

    // Clear the call_frame, so that if we wait for GC going independent,
    // the GC doesn't see pointers into now-unallocated CallFrames
    vm->set_call_frame(0);

    LockedObjects& los = vm->locked_objects();
    for(LockedObjects::iterator i = los.begin();
        i != los.end();
        ++i) {
      (*i)->unlock_for_terminate(state, gct, 0);
    }

    vm->thread->init_lock_.lock();
    NativeMethod::cleanup_thread(state);

    vm->thread->alive(state, cFalse);
    vm->thread->cleanup();
    vm->thread->init_lock_.unlock();

    vm->shared.gc_independent(state, 0);
    vm->shared.clear_critical(state);

    VM::discard(state, vm);

    if(cDebugThreading) {
      std::cerr << "[LOCK thread " << vm->thread_id() << " exited]\n";
    }

    RUBINIUS_THREAD_STOP(thread_name.c_str(), vm->thread_id(), 0);
    return 0;
  }

  Object* Thread::fork(STATE) {
    // If the thread is already alive or already ran,
    // we can't use it anymore.
    if(CBOOL(alive()) || !vm_) {
      return Primitives::failure();
    }

    pthread_attr_t attrs;
    pthread_attr_init(&attrs);
    pthread_attr_setstacksize(&attrs, THREAD_STACK_SIZE);
    pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);

    int error = start_new_thread(state, attrs);

    if(error) {
      char buf[RBX_STRERROR_BUFSIZE];
      char* err = RBX_STRERROR(error, buf, RBX_STRERROR_BUFSIZE);
      Exception::thread_error(state, err);
    }
    return cNil;
  }

  int Thread::fork_attached(STATE) {
    pthread_attr_t attrs;
    pthread_attr_init(&attrs);
    pthread_attr_setstacksize(&attrs, THREAD_STACK_SIZE);

    return start_new_thread(state, attrs);
  }

  Object* Thread::pass(STATE, CallFrame* calling_environment) {
    atomic::pause();
    return cNil;
  }

  Array* Thread::list(STATE) {
    return state->shared().vm_threads(state);
  }

  Object* Thread::priority(STATE) {
    pthread_t id = vm_->os_thread();

    int _policy;
    struct sched_param params;

    if(pthread_getschedparam(id, &_policy, &params) == 0) {
      return Fixnum::from(params.sched_priority);
    }

    return cNil;
  }

  Object* Thread::raise(STATE, GCToken gct, Exception* exc, CallFrame* calling_environment) {
    utilities::thread::SpinLock::LockGuard lg(init_lock_);
    Thread* self = this;
    OnStack<2> os(state, self, exc);

    VM* vm = self->vm_;
    if(!vm) {
      return cNil;
    }

    vm->register_raise(state, exc);

    vm->wakeup(state, gct, calling_environment);
    return exc;
  }

  Object* Thread::current_exception(STATE) {
    utilities::thread::SpinLock::LockGuard lg(init_lock_);
    return vm_->thread_state()->current_exception();
  }

  Object* Thread::kill(STATE, GCToken gct, CallFrame* calling_environment) {
    utilities::thread::SpinLock::LockGuard lg(init_lock_);
    Thread* self = this;
    OnStack<1> os(state, self);

    VM* vm = self->vm_;
    if(!vm) {
      return cNil;
    }

    if(state->vm()->thread.get() == self) {
      vm_->thread_state_.raise_thread_kill();
      return NULL;
    } else {
      vm->register_kill(state);
      vm->wakeup(state, gct, calling_environment);
      return self;
    }
  }

  Object* Thread::set_priority(STATE, Fixnum* new_priority) {
    return new_priority;
  }

  Thread* Thread::wakeup(STATE, GCToken gct, CallFrame* calling_environment) {
    utilities::thread::SpinLock::LockGuard lg(init_lock_);
    Thread* self = this;
    OnStack<1> os(state, self);

    VM* vm = self->vm_;
    if(!CBOOL(alive()) || !vm) {
      return force_as<Thread>(Primitives::failure());
    }

    vm->wakeup(state, gct, calling_environment);

    return self;
  }

  Tuple* Thread::context(STATE) {
    utilities::thread::SpinLock::LockGuard lg(init_lock_);

    VM* vm = vm_;
    if(!vm) return nil<Tuple>();

    CallFrame* cf = vm->saved_call_frame()->top_ruby_frame();

    VariableScope* scope = cf->promote_scope(state);

    return Tuple::from(state, 3, Fixnum::from(cf->ip()), cf->compiled_code, scope);
  }

  Array* Thread::mri_backtrace(STATE, GCToken gct, CallFrame* calling_environment) {
    utilities::thread::SpinLock::LockGuard lg(init_lock_);

    VM* vm = vm_;
    if(!vm) return nil<Array>();
    StopTheWorld stop(state, gct, calling_environment);

    CallFrame* cf = vm->saved_call_frame();

    return Location::mri_backtrace(state, cf);
  }

  void Thread::cleanup() {
    vm_ = NULL;
  }

  void Thread::init_lock() {
    init_lock_.init();
  }

  Object* Thread::join(STATE, GCToken gct, CallFrame* calling_environment) {
    state->set_call_frame(calling_environment);

    init_lock_.lock();

    VM* vm = vm_;

    if(!vm) {
      init_lock_.unlock();
      return cTrue;
    }

    pthread_t id = vm->os_thread();

    if(cDebugThreading) {
      std::cerr << "[THREAD joining " << thread_debug_id(id) << "]\n";
    }

    init_lock_.unlock();

    state->gc_independent(gct, calling_environment);
    void* val;
    int err = pthread_join(id, &val);
    state->gc_dependent(gct, calling_environment);

    switch(err) {
    case 0:
      break;
    case EDEADLK:
      std::cerr << "Join deadlock: " << thread_debug_id(id) << "/" << thread_debug_self() << "\n";
      break;
    case EINVAL:
      std::cerr << "Invalid thread id: " << thread_debug_id(id) << "\n";
      break;
    case ESRCH:
      // This means that the thread finished execution and detached
      // itself. We treat this as having joined it.
      break;
    }

    return cTrue;
  }

  Object* Thread::set_critical(STATE, Object* obj, CallFrame* calling_environment) {
    state->set_call_frame(calling_environment);
    if(CBOOL(obj)) {
      state->shared().set_critical(state, calling_environment);
      return cTrue;
    } else {
      state->shared().clear_critical(state);
      return cFalse;
    }
  }
}
<|endoftext|>


<commit_before><commit_msg>Move UsePolyPolygonForComplexGradient() into gradient group in outdev.hxx<commit_after><|endoftext|>


<commit_before>// Copyright © 2012, Université catholique de Louvain
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// *  Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// *  Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#ifndef __OZCALLS_H
#define __OZCALLS_H

#include "mozartcore.hh"
#include "emulate.hh"

#include <type_traits>

#ifndef MOZART_GENERATOR

namespace mozart {

namespace ozcalls {

//////////////////////////////
// Calling Oz code from C++ //
//////////////////////////////

namespace internal {

/**
 * Specializable routine to initialize the input arguments of an ozcall.
 */
template <typename T, bool synchronous>
struct InitOneArg {
  static void call(VM vm, UnstableNode& dest, T&& value) {
    dest.init(vm, std::forward<T>(value));
  }
};

// Specializations for asynchronous calls

template <>
struct InitOneArg<OutputParam<UnstableNode>, false> {
  static void call(VM vm, UnstableNode& dest, OutputParam<UnstableNode> param) {
    StableNode* variable = new (vm) StableNode(vm, OptVar::build(vm));
    param.value = Reference::build(vm, variable);
    dest = Reference::build(vm, variable);
  }
};

template <>
struct InitOneArg<OutputParam<RichNode>, false> {
  static void call(VM vm, UnstableNode& dest, OutputParam<RichNode> param) {
    StableNode* variable = new (vm) StableNode(vm, OptVar::build(vm));
    param.value = *variable;
    dest = Reference::build(vm, variable);
  }
};

template <typename T>
struct InitOneArg<OutputParam<T>, false> {
  static_assert(!std::is_same<T, T>::value,
    "out(V) with a C++ value is only permitted in synchronous calls");
};

// Specializations for synchronous calls

template <typename T>
struct InitOneArg<OutputParam<T>, true> {
  static void call(VM vm, UnstableNode& dest, OutputParam<T> param) {
    dest = OptVar::build(vm);
  }
};

template <typename T>
struct InitOneArg<nullable<T&>, true> {
  static void call(VM vm, UnstableNode& dest, nullable<T&> param) {
    if (param.isNull())
      dest = buildTuple(vm, MOZART_STR("none"));
    else
      dest = buildTuple(vm, MOZART_STR("some"), OptVar::build(vm));
  }
};

template <typename T>
struct InitOneArg<nullable<T&>&, true>: public InitOneArg<nullable<T&>, true> {
};

template <size_t i, bool synchronous>
inline
void initNodesFromVariadicArgs(VM vm, UnstableNode nodes[]) {
}

template <size_t i, bool synchronous, typename Head, typename... Tail>
inline
void initNodesFromVariadicArgs(VM vm, UnstableNode nodes[],
                               Head&& head, Tail&&... tail) {
  InitOneArg<Head, synchronous>::call(vm, nodes[i],
                                      std::forward<Head>(head));
  initNodesFromVariadicArgs<i+1, synchronous>(vm, nodes,
                                              std::forward<Tail>(tail)...);
}

template <bool synchronous, typename... Args>
inline
void initInputArguments(VM vm, UnstableNode unstables[], RichNode riches[],
                        Args&&... args) {
  constexpr size_t argc = sizeof...(args);

  internal::initNodesFromVariadicArgs<0, synchronous>(
    vm, unstables, std::forward<Args>(args)...);

  for (size_t i = 0; i < argc; i++)
    riches[i] = unstables[i];
}

/** Metastruct that defines how to process the output arguments */
template <size_t inputIdx, size_t outputIdx, typename... Args>
struct OutputProcessing {
};

/** Base case specialization */
template <size_t inputIdx, size_t outputIdx>
struct OutputProcessing<inputIdx, outputIdx> {
  static constexpr size_t outArgc() {
    return outputIdx;
  }

  static void initOutputArguments(VM vm, StaticArray<StableNode> outputs,
                                  UnstableNode inputs[]) {
  }

  static void processOutputArguments(VM vm, StaticArray<StableNode> outputs) {
  }
};

/** Input parameter specialization */
template <size_t inputIdx, size_t outputIdx, typename Head, typename... Tail>
struct OutputProcessing<inputIdx, outputIdx, Head, Tail...> {
  typedef OutputProcessing<inputIdx+1, outputIdx, Tail...> Next;

  static constexpr size_t outArgc() {
    return Next::outArgc();
  }

  static void initOutputArguments(VM vm, StaticArray<StableNode> outputs,
                                  UnstableNode inputs[]) {
    Next::initOutputArguments(vm, outputs, inputs);
  }

  static void processOutputArguments(VM vm, StaticArray<StableNode> outputs,
                                     Head&& head, Tail&&... tail) {
    Next::processOutputArguments(vm, outputs, std::forward<Tail>(tail)...);
  }
};

/** Output parameter specialization */
template <size_t inputIdx, size_t outputIdx, typename T, typename... Tail>
struct OutputProcessing<inputIdx, outputIdx, OutputParam<T>, Tail...> {
  typedef OutputProcessing<inputIdx+1, outputIdx+1, Tail...> Next;

  static constexpr size_t outArgc() {
    return Next::outArgc();
  }

  static void initOutputArguments(VM vm, StaticArray<StableNode> outputs,
                                  UnstableNode inputs[]) {
    outputs[outputIdx].init(vm, inputs[inputIdx]);
    Next::initOutputArguments(vm, outputs, inputs);
  }

  static void processOutputArguments(VM vm, StaticArray<StableNode> outputs,
                                     OutputParam<T> head, Tail&&... tail) {
    head.value = getArgument<T>(vm, outputs[outputIdx]);
    Next::processOutputArguments(vm, outputs, std::forward<Tail>(tail)...);
  }
};

/** Optional output parameter specialization */
template <size_t inputIdx, size_t outputIdx, typename T, typename... Tail>
struct OutputProcessing<inputIdx, outputIdx, nullable<T&>, Tail...> {
  typedef OutputProcessing<inputIdx+1, outputIdx+1, Tail...> Next;

  static constexpr size_t outArgc() {
    return Next::outArgc();
  }

  static void initOutputArguments(VM vm, StaticArray<StableNode> outputs,
                                  UnstableNode inputs[]) {
    outputs[outputIdx].init(vm, inputs[inputIdx]);
    Next::initOutputArguments(vm, outputs, inputs);
  }

  static void processOutputArguments(VM vm, StaticArray<StableNode> outputs,
                                     nullable<T&> head, Tail&&... tail) {
    using namespace patternmatching;

    if (!head.isNull()) {
      RichNode value;
      matchesTuple(vm, outputs[outputIdx], MOZART_STR("some"), capture(value));
      head.get() = getArgument<T>(vm, value);
    }

    Next::processOutputArguments(vm, outputs, std::forward<Tail>(tail)...);
  }
};

template <size_t inputIdx, size_t outputIdx, typename T, typename... Tail>
struct OutputProcessing<inputIdx, outputIdx, nullable<T&>&, Tail...>
  : public OutputProcessing<inputIdx, outputIdx, nullable<T&>, Tail...> {
};

} // namespace internal

void asyncOzCall(VM vm, Space* space, RichNode callable) {
  new (vm) Thread(vm, space, callable, 0, nullptr);
}

template <typename... Args>
void asyncOzCall(VM vm, Space* space, RichNode callable, Args&&... args) {
  constexpr size_t argc = sizeof...(args);

  UnstableNode unstableArgs[argc];
  RichNode arguments[argc];
  internal::initInputArguments<false>(
    vm, unstableArgs, arguments, std::forward<Args>(args)...);

  new (vm) Thread(vm, space, callable, argc, arguments);
}

template <typename... Args>
void asyncOzCall(VM vm, RichNode callable, Args&&... args) {
  asyncOzCall(vm, vm->getCurrentSpace(), callable,
              std::forward<Args>(args)...);
}

namespace internal {

template <bool reflective, typename Effect, typename... Args>
bool syncCallGeneric(VM vm, const nchar* identity, const Effect& effect,
                     Args&&... args) {
  constexpr size_t argc = sizeof...(args);

  typedef internal::OutputProcessing<0, 0, Args...> OutputProcessing;

  constexpr size_t outArgc = OutputProcessing::outArgc();

  /* Most unfortunately, gcc does not yet support capturing a parameter pack
   * in a lambda. See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41934
   * It makes it awkward to reuse performNonIdempotentStep. This is why we have
   * some code duplication with this routine.
   */

  assert(vm->isIntermediateStateAvailable());

  IntermediateState& intermediateState = vm->getIntermediateState();
  auto checkPoint = intermediateState.makeCheckPoint(vm);
  RichNode state;

  if (!intermediateState.fetch(vm, identity,
                               patternmatching::capture(state))) {
    // Initialize the input arguments

    UnstableNode unstableArgs[argc];
    internal::initNodesFromVariadicArgs<0, /* synchronous = */ true>(
      vm, unstableArgs, std::forward<Args>(args)...);

    // Perform the effect

    UnstableNode terminationVar;
    effect(vm, unstableArgs, terminationVar);

    // Build the output of the first step

    UnstableNode outputTuple = Tuple::build(vm, outArgc+1,
                                            vm->coreatoms.sharp);
    StaticArray<StableNode> outputs =
      RichNode(outputTuple).as<Tuple>().getElementsArray();

    OutputProcessing::initOutputArguments(vm, outputs, unstableArgs);
    outputs[outArgc].init(vm, std::move(terminationVar));

    // Store the result of the first step in the intermediate state

    intermediateState.resetAndStore(vm, checkPoint, identity, outputTuple);
    state = outputTuple;
    state.ensureStable(vm);
  }

  {
    StaticArray<StableNode> outputs =
      state.as<Tuple>().getElementsArray();

    RichNode terminationVar = outputs[outArgc];
    if (terminationVar.isTransient())
      waitFor(vm, terminationVar);

    if (reflective && !terminationVar.is<Unit>())
      return false;

    // Now the thread is terminated, we can fetch the arguments

    OutputProcessing::processOutputArguments(vm, outputs,
                                             std::forward<Args>(args)...);

    return true;
  }
}

}

template <typename... Args>
void ozCall(VM vm, const nchar* identity, RichNode callable, Args&&... args) {
  internal::syncCallGeneric</* reflective = */ false>(
    vm, identity,
    [callable] (VM vm, UnstableNode unstableArgs[],
                UnstableNode& terminationVar) {
      constexpr size_t argc = sizeof...(Args);

      RichNode arguments[argc];
      for (size_t i = 0; i < argc; i++)
        arguments[i] = unstableArgs[i];

      Thread* thr = new (vm) Thread(vm, vm->getCurrentSpace(),
                                    callable, argc, arguments);

      terminationVar.copy(vm, thr->getTerminationVar());
    },
    std::forward<Args>(args)...
  );
}

template <typename... Args>
void ozCall(VM vm, RichNode callable, Args&&... args) {
  ozCall(vm, MOZART_STR("::mozart::ozcalls::ozCall"), callable,
         std::forward<Args>(args)...);
}

namespace internal {

template <typename Label, typename... Args>
inline
bool doReflectiveCall(VM vm, const nchar* identity, UnstableNode& stream,
                      Label&& label, Args&&... args) {
  return internal::syncCallGeneric</* reflective = */ true>(
    vm, identity,
    [&stream, &label] (VM vm, UnstableNode unstableArgs[],
                       UnstableNode& terminationVar) {
      constexpr size_t argc = sizeof...(Args);

      // Create the message

      auto message = Tuple::build(vm, argc, std::forward<Label>(label));
      auto messageElements = RichNode(message).as<Tuple>().getElementsArray();
      for (size_t i = 0; i < argc; i++)
        messageElements[i].init(vm, unstableArgs[i]);

      // Create the termination variable

      terminationVar = Variable::build(vm);

      // Send the message#terminationVar to the stream

      sendToReadOnlyStream(vm, stream, buildSharp(vm, message, terminationVar));
    },
    std::forward<Args>(args)...
  );
}

} // namespace internal

} // namespace ozcalls

} // namespace mozart

#endif

#endif // __OZCALLS_H
<commit_msg>Fixed a warning emitted by the clang compiler.<commit_after>// Copyright © 2012, Université catholique de Louvain
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// *  Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// *  Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#ifndef __OZCALLS_H
#define __OZCALLS_H

#include "mozartcore.hh"
#include "emulate.hh"

#include <type_traits>

#ifndef MOZART_GENERATOR

namespace mozart {

namespace ozcalls {

//////////////////////////////
// Calling Oz code from C++ //
//////////////////////////////

namespace internal {

/**
 * Specializable routine to initialize the input arguments of an ozcall.
 */
template <typename T, bool synchronous>
struct InitOneArg {
  static void call(VM vm, UnstableNode& dest, T&& value) {
    dest.init(vm, std::forward<T>(value));
  }
};

// Specializations for asynchronous calls

template <>
struct InitOneArg<OutputParam<UnstableNode>, false> {
  static void call(VM vm, UnstableNode& dest, OutputParam<UnstableNode> param) {
    StableNode* variable = new (vm) StableNode(vm, OptVar::build(vm));
    param.value = Reference::build(vm, variable);
    dest = Reference::build(vm, variable);
  }
};

template <>
struct InitOneArg<OutputParam<RichNode>, false> {
  static void call(VM vm, UnstableNode& dest, OutputParam<RichNode> param) {
    StableNode* variable = new (vm) StableNode(vm, OptVar::build(vm));
    param.value = *variable;
    dest = Reference::build(vm, variable);
  }
};

template <typename T>
struct InitOneArg<OutputParam<T>, false> {
  static_assert(!std::is_same<T, T>::value,
    "out(V) with a C++ value is only permitted in synchronous calls");
};

// Specializations for synchronous calls

template <typename T>
struct InitOneArg<OutputParam<T>, true> {
  static void call(VM vm, UnstableNode& dest, OutputParam<T> param) {
    dest = OptVar::build(vm);
  }
};

template <typename T>
struct InitOneArg<nullable<T&>, true> {
  static void call(VM vm, UnstableNode& dest, nullable<T&> param) {
    if (param.isNull())
      dest = buildTuple(vm, MOZART_STR("none"));
    else
      dest = buildTuple(vm, MOZART_STR("some"), OptVar::build(vm));
  }
};

template <typename T>
struct InitOneArg<nullable<T&>&, true>: public InitOneArg<nullable<T&>, true> {
};

template <size_t i, bool synchronous>
inline
void initNodesFromVariadicArgs(VM vm, UnstableNode nodes[]) {
}

template <size_t i, bool synchronous, typename Head, typename... Tail>
inline
void initNodesFromVariadicArgs(VM vm, UnstableNode nodes[],
                               Head&& head, Tail&&... tail) {
  InitOneArg<Head, synchronous>::call(vm, nodes[i],
                                      std::forward<Head>(head));
  initNodesFromVariadicArgs<i+1, synchronous>(vm, nodes,
                                              std::forward<Tail>(tail)...);
}

template <bool synchronous, typename... Args>
inline
void initInputArguments(VM vm, UnstableNode unstables[], RichNode riches[],
                        Args&&... args) {
  constexpr size_t argc = sizeof...(args);

  internal::initNodesFromVariadicArgs<0, synchronous>(
    vm, unstables, std::forward<Args>(args)...);

  for (size_t i = 0; i < argc; i++)
    riches[i] = unstables[i];
}

/** Metastruct that defines how to process the output arguments */
template <size_t inputIdx, size_t outputIdx, typename... Args>
struct OutputProcessing {
};

/** Base case specialization */
template <size_t inputIdx, size_t outputIdx>
struct OutputProcessing<inputIdx, outputIdx> {
  static constexpr size_t outArgc() {
    return outputIdx;
  }

  static void initOutputArguments(VM vm, StaticArray<StableNode> outputs,
                                  UnstableNode inputs[]) {
  }

  static void processOutputArguments(VM vm, StaticArray<StableNode> outputs) {
  }
};

/** Input parameter specialization */
template <size_t inputIdx, size_t outputIdx, typename Head, typename... Tail>
struct OutputProcessing<inputIdx, outputIdx, Head, Tail...> {
  typedef OutputProcessing<inputIdx+1, outputIdx, Tail...> Next;

  static constexpr size_t outArgc() {
    return Next::outArgc();
  }

  static void initOutputArguments(VM vm, StaticArray<StableNode> outputs,
                                  UnstableNode inputs[]) {
    Next::initOutputArguments(vm, outputs, inputs);
  }

  static void processOutputArguments(VM vm, StaticArray<StableNode> outputs,
                                     Head&& head, Tail&&... tail) {
    Next::processOutputArguments(vm, outputs, std::forward<Tail>(tail)...);
  }
};

/** Output parameter specialization */
template <size_t inputIdx, size_t outputIdx, typename T, typename... Tail>
struct OutputProcessing<inputIdx, outputIdx, OutputParam<T>, Tail...> {
  typedef OutputProcessing<inputIdx+1, outputIdx+1, Tail...> Next;

  static constexpr size_t outArgc() {
    return Next::outArgc();
  }

  static void initOutputArguments(VM vm, StaticArray<StableNode> outputs,
                                  UnstableNode inputs[]) {
    outputs[outputIdx].init(vm, inputs[inputIdx]);
    Next::initOutputArguments(vm, outputs, inputs);
  }

  static void processOutputArguments(VM vm, StaticArray<StableNode> outputs,
                                     OutputParam<T> head, Tail&&... tail) {
    head.value = getArgument<T>(vm, outputs[outputIdx]);
    Next::processOutputArguments(vm, outputs, std::forward<Tail>(tail)...);
  }
};

/** Optional output parameter specialization */
template <size_t inputIdx, size_t outputIdx, typename T, typename... Tail>
struct OutputProcessing<inputIdx, outputIdx, nullable<T&>, Tail...> {
  typedef OutputProcessing<inputIdx+1, outputIdx+1, Tail...> Next;

  static constexpr size_t outArgc() {
    return Next::outArgc();
  }

  static void initOutputArguments(VM vm, StaticArray<StableNode> outputs,
                                  UnstableNode inputs[]) {
    outputs[outputIdx].init(vm, inputs[inputIdx]);
    Next::initOutputArguments(vm, outputs, inputs);
  }

  static void processOutputArguments(VM vm, StaticArray<StableNode> outputs,
                                     nullable<T&> head, Tail&&... tail) {
    using namespace patternmatching;

    if (!head.isNull()) {
      RichNode value;
      matchesTuple(vm, outputs[outputIdx], MOZART_STR("some"), capture(value));
      head.get() = getArgument<T>(vm, value);
    }

    Next::processOutputArguments(vm, outputs, std::forward<Tail>(tail)...);
  }
};

template <size_t inputIdx, size_t outputIdx, typename T, typename... Tail>
struct OutputProcessing<inputIdx, outputIdx, nullable<T&>&, Tail...>
  : public OutputProcessing<inputIdx, outputIdx, nullable<T&>, Tail...> {
};

} // namespace internal

void asyncOzCall(VM vm, Space* space, RichNode callable) {
  new (vm) Thread(vm, space, callable, 0, nullptr);
}

template <typename... Args>
void asyncOzCall(VM vm, Space* space, RichNode callable, Args&&... args) {
  constexpr size_t argc = sizeof...(args);

  UnstableNode unstableArgs[argc];
  RichNode arguments[argc];
  internal::initInputArguments<false>(
    vm, unstableArgs, arguments, std::forward<Args>(args)...);

  new (vm) Thread(vm, space, callable, argc, arguments);
}

template <typename... Args>
void asyncOzCall(VM vm, RichNode callable, Args&&... args) {
  asyncOzCall(vm, vm->getCurrentSpace(), callable,
              std::forward<Args>(args)...);
}

namespace internal {

template <bool reflective, typename Effect, typename... Args>
bool syncCallGeneric(VM vm, const nchar* identity, const Effect& effect,
                     Args&&... args) {
  constexpr size_t argc = sizeof...(args);

  typedef internal::OutputProcessing<0, 0, Args...> OutputProcessing;

  constexpr size_t outArgc = OutputProcessing::outArgc();

  /* Most unfortunately, gcc does not yet support capturing a parameter pack
   * in a lambda. See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41934
   * It makes it awkward to reuse performNonIdempotentStep. This is why we have
   * some code duplication with this routine.
   */

  assert(vm->isIntermediateStateAvailable());

  IntermediateState& intermediateState = vm->getIntermediateState();
  auto checkPoint = intermediateState.makeCheckPoint(vm);
  RichNode state;

  if (!intermediateState.fetch(vm, identity,
                               patternmatching::capture(state))) {
    // Initialize the input arguments

    UnstableNode unstableArgs[argc];
    internal::initNodesFromVariadicArgs<0, /* synchronous = */ true>(
      vm, unstableArgs, std::forward<Args>(args)...);

    // Perform the effect

    UnstableNode terminationVar;
    effect(vm, unstableArgs, terminationVar);

    // Build the output of the first step

    UnstableNode outputTuple = Tuple::build(vm, outArgc+1,
                                            vm->coreatoms.sharp);
    StaticArray<StableNode> outputs =
      RichNode(outputTuple).as<Tuple>().getElementsArray();

    OutputProcessing::initOutputArguments(vm, outputs, unstableArgs);
    outputs[outArgc].init(vm, std::move(terminationVar));

    // Store the result of the first step in the intermediate state

    intermediateState.resetAndStore(vm, checkPoint, identity, outputTuple);
    state = outputTuple;
    state.ensureStable(vm);
  }

  {
    StaticArray<StableNode> outputs =
      state.as<Tuple>().getElementsArray();

    RichNode terminationVar = outputs[outArgc];
    if (terminationVar.isTransient())
      waitFor(vm, terminationVar);

    if (reflective && !terminationVar.is<Unit>())
      return false;

    // Now the thread is terminated, we can fetch the arguments

    OutputProcessing::processOutputArguments(vm, outputs,
                                             std::forward<Args>(args)...);

    return true;
  }
}

template <size_t count, size_t i = 0, typename F>
inline
auto static_for(const F& f) -> typename std::enable_if<i != count, void>::type {
  f(i);
  static_for<count, i+1, F>(f);
}

template <size_t count, size_t i = 0, typename F>
inline
auto static_for(const F& f) -> typename std::enable_if<i == count, void>::type {
}

}

template <typename... Args>
void ozCall(VM vm, const nchar* identity, RichNode callable, Args&&... args) {
  internal::syncCallGeneric</* reflective = */ false>(
    vm, identity,
    [callable] (VM vm, UnstableNode unstableArgs[],
                UnstableNode& terminationVar) {
      constexpr size_t argc = sizeof...(Args);

      RichNode arguments[argc];
      internal::static_for<argc>(
        [&] (size_t i) {
          arguments[i] = unstableArgs[i];
        }
      );

      Thread* thr = new (vm) Thread(vm, vm->getCurrentSpace(),
                                    callable, argc, arguments);

      terminationVar.copy(vm, thr->getTerminationVar());
    },
    std::forward<Args>(args)...
  );
}

template <typename... Args>
void ozCall(VM vm, RichNode callable, Args&&... args) {
  ozCall(vm, MOZART_STR("::mozart::ozcalls::ozCall"), callable,
         std::forward<Args>(args)...);
}

namespace internal {

template <typename Label, typename... Args>
inline
bool doReflectiveCall(VM vm, const nchar* identity, UnstableNode& stream,
                      Label&& label, Args&&... args) {
  return internal::syncCallGeneric</* reflective = */ true>(
    vm, identity,
    [&stream, &label] (VM vm, UnstableNode unstableArgs[],
                       UnstableNode& terminationVar) {
      constexpr size_t argc = sizeof...(Args);

      // Create the message

      auto message = Tuple::build(vm, argc, std::forward<Label>(label));
      auto messageElements = RichNode(message).as<Tuple>().getElementsArray();
      internal::static_for<argc>(
        [&] (size_t i) {
          messageElements[i].init(vm, unstableArgs[i]);
        }
      );

      // Create the termination variable

      terminationVar = Variable::build(vm);

      // Send the message#terminationVar to the stream

      sendToReadOnlyStream(vm, stream, buildSharp(vm, message, terminationVar));
    },
    std::forward<Args>(args)...
  );
}

} // namespace internal

} // namespace ozcalls

} // namespace mozart

#endif

#endif // __OZCALLS_H
<|endoftext|>


<commit_before>#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <omp.h>
#include <mpi.h>

#include "sort.h"
#include "common.h"

const int ser_n = 1<<9;

void merge(dataType *data, int n1, int n2, dataType *res) {
    // printf("Thread %d doing merge.\n", omp_get_thread_num());
    int i = 0, j = n1, k = 0;

    while(i < n1 && j < n2)
        if((long long)data[i].key < (long long)data[j].key)
            res[k++] = data[i++];
        else
            res[k++] = data[j++];

    while(i < n1)
        res[k++] = data[i++];
    while(j < n2)
        res[k++] = data[j++];
}

void mSort_helper(dataType *data, int n, dataType *res)   {
    // printf("Thread %d\n", omp_get_thread_num());
    if(n == 1)  {
        res[0] = data[0];
        return;
    }
    if(n == 0) {
        return;
    }
    if(n < 0)   {
        #ifdef DEBUG
            printf("n < 0 in mSort_helper.\n");
        #endif
        exit(1);
    }

    #pragma omp task if(n > ser_n) untied
    mSort_helper(res, n/2, data);

    #pragma omp task if(n > ser_n) untied
    mSort_helper(res+n/2, n-n/2, data+n/2);

    #pragma omp taskwait
    merge(data, n/2, n, res);

}

void mSort(dataType *data, int n_total)    {
    MPI_Status status;
    int mpirank, mpisize;
    MPI_Comm_rank(MPI_COMM_WORLD, &mpirank);
    MPI_Comm_size(MPI_COMM_WORLD, &mpisize);
    int n = n_total;

    // TODO: Send data to everyone
    // allocate the extra buffer merge sort needs
    dataType *res = new dataType[n];
    omp_set_num_threads(omp_get_num_procs()*4);

    // sort your segment using omp quick sort
    int a = mpi_seg_start(0, n, mpirank, mpisize),
        b = mpi_seg_end  (0, n, mpirank, mpisize);
    n = b-a;
    data = data+a;
    res = res+a;
    #pragma omp parallel
    {
        int i;
        #pragma omp for
        for (i = 0; i < n; ++i)
            res[i] = data[i];

        #pragma omp master// implicit nowait
        {
            //printf("%d threads\n", omp_get_num_threads());
            mSort_helper(res, n, data);
        }
    }
    data = data-a;
    res = res-a;
    n = n_total;

    MPI_Barrier(MPI_COMM_WORLD);
    // now we have a sorted segment with each node.
    int factor = 2;
    bool active = true;
    do {
        if(mpirank == 0)
            printf("factor: %d\n", factor);
        if(active && mpirank % factor != 0) {
            // hand over data to parent, and deactivate
            int parent = mpirank - (mpirank % factor);
            MPI_Send(&b, 1, MPI_INT, parent, factor+4096, MPI_COMM_WORLD);
            if(a < b)   {
                printf("%d sending %d to %d.\n", mpirank, a, b);
                MPI_Send(data+a, b-a, dataType_MPI(), parent,
                            factor, MPI_COMM_WORLD);
            }
            active = false;
        }
        else if(active && mpirank+factor/2 < mpisize) {
            // get data from all child, and merge
            int temp;
            MPI_Recv(&temp, 1, MPI_INT, MPI_ANY_SOURCE,
                        factor+4096, MPI_COMM_WORLD, &status);
            if(b < temp)    {
                MPI_Recv(data+b, temp-b, dataType_MPI(), MPI_ANY_SOURCE,
                            factor, MPI_COMM_WORLD, &status);
                printf("%d got %d to %d", mpirank, b, temp);
                merge(data+a, b-a, temp-b, res+a);
                b = temp;

                // copy back from res to data
                #pragma omp parallel for
                for(int i = a; i < b; ++i)
                    data[i] = res[i];
            }

        }
        factor *= 2;
        MPI_Barrier(MPI_COMM_WORLD);
    } while(divceil(mpisize, factor) > 1);

    delete [] res;
}
<commit_msg>Completed msort for MPI<commit_after>#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <omp.h>
#include <mpi.h>

#include "sort.h"
#include "common.h"

const int ser_n = 1<<9;

void merge(dataType *data, int n1, int n2, dataType *res) {
    // printf("Thread %d doing merge.\n", omp_get_thread_num());
    int i = 0, j = n1, k = 0;

    while(i < n1 && j < n2)
        if((long long)data[i].key < (long long)data[j].key)
            res[k++] = data[i++];
        else
            res[k++] = data[j++];

    while(i < n1)
        res[k++] = data[i++];
    while(j < n2)
        res[k++] = data[j++];
}

void mSort_helper(dataType *data, int n, dataType *res)   {
    // printf("Thread %d\n", omp_get_thread_num());
    if(n == 1)  {
        res[0] = data[0];
        return;
    }
    if(n == 0) {
        return;
    }
    if(n < 0)   {
        #ifdef DEBUG
            printf("n < 0 in mSort_helper.\n");
        #endif
        exit(1);
    }

    #pragma omp task if(n > ser_n) untied
    mSort_helper(res, n/2, data);

    #pragma omp task if(n > ser_n) untied
    mSort_helper(res+n/2, n-n/2, data+n/2);

    #pragma omp taskwait
    merge(data, n/2, n, res);

}

void mSort(dataType *data, int n_total)    {
    MPI_Status status;
    int mpirank, mpisize;
    MPI_Comm_rank(MPI_COMM_WORLD, &mpirank);
    MPI_Comm_size(MPI_COMM_WORLD, &mpisize);
    int n = n_total;

    // allocate the extra buffer merge sort needs
    dataType *res = new dataType[n];
    omp_set_num_threads(omp_get_num_procs()*4);

    const int a = mpi_seg_start(0, n, mpirank, mpisize);
    int       b = mpi_seg_end  (0, n, mpirank, mpisize);

    // send data to all nodes
    if(mpirank == 0)    {
        for(int i = 1; i < mpisize; ++i)    {
            int start = mpi_seg_start(0, n, i, mpisize),
                end   = mpi_seg_end  (0, n, i, mpisize);
            if(start < end)
                MPI_Send(data+start, end-start, dataType_MPI(), i,
                        8192+i, MPI_COMM_WORLD);
        }
    }
    else    {
        if(a < b)
            MPI_Recv(data+a, b-a, dataType_MPI(), 0,
                    8192+mpirank, MPI_COMM_WORLD, &status);
    }

    // sort only your segment using omp quick sort
    n = b-a;
    data = data+a;
    res = res+a;
    #pragma omp parallel
    {
        int i;

        #pragma omp for
        for (i = 0; i < n; ++i)
            res[i] = data[i];

        #pragma omp master// implicit nowait
        {
            //printf("%d threads\n", omp_get_num_threads());
            mSort_helper(res, n, data);
        }
    }
    data = data-a;
    res = res-a;
    n = n_total;

    // now we have a sorted segment with each node.
    int factor = 1;
    bool active = true;
    do {
        factor *= 2;
        if(mpirank % factor != 0) {
            // hand over data to parent, and deactivate
            int parent = mpirank - (mpirank % factor);
            MPI_Send(&b, 1, MPI_INT, parent, factor+4096, MPI_COMM_WORLD);
            if(a < b)   {
                MPI_Send(data+a, b-a, dataType_MPI(), parent,
                            factor, MPI_COMM_WORLD);
            }
            active = false;
        }
        else if(mpirank+factor/2 < mpisize) {
            // get data from all child, and merge
            int temp;
            MPI_Recv(&temp, 1, MPI_INT, MPI_ANY_SOURCE,
                        factor+4096, MPI_COMM_WORLD, &status);
            if(b < temp)    {
                MPI_Recv(data+b, temp-b, dataType_MPI(), MPI_ANY_SOURCE,
                            factor, MPI_COMM_WORLD, &status);
                merge(data+a, b-a, temp-a, res+a);
                b = temp;

                // copy back from res to data
                #pragma omp parallel for
                for(int i = a; i < b; ++i)
                    data[i] = res[i];
            }
        }
    } while(active && divceil(mpisize, factor) > 1);

    delete [] res;
}
<|endoftext|>