<commit_before>// Copyright 2014-2015 Open Source Robotics Foundation, Inc.
//
// 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 <ccpp_dds_dcps.h>
#include <dds_dcps.h>

#include "rmw/allocators.h"
#include "rmw/error_handling.h"
#include "rmw/impl/cpp/macros.hpp"
#include "rmw/rmw.h"
#include "rmw/types.h"

#include "identifier.hpp"
#include "types.hpp"

// The extern "C" here enforces that overloading is not used.
extern "C"
{
rmw_ret_t check_attach_condition_error(DDS::ReturnCode_t retcode)
{
  if (retcode == DDS::RETCODE_OK) {
    return RMW_RET_OK;
  }
  if (retcode == DDS::RETCODE_OUT_OF_RESOURCES) {
    RMW_SET_ERROR_MSG("failed to attach condition to waitset: out of resources");
  } else if (retcode == DDS::RETCODE_BAD_PARAMETER) {
    RMW_SET_ERROR_MSG("failed to attach condition to waitset: condition pointer was invalid");
  } else {
    RMW_SET_ERROR_MSG("failed to attach condition to waitset");
  }
  return RMW_RET_ERROR;
}

rmw_ret_t
rmw_wait(
  rmw_subscriptions_t * subscriptions,
  rmw_guard_conditions_t * guard_conditions,
  rmw_services_t * services,
  rmw_clients_t * clients,
  rmw_waitset_t * waitset,
  const rmw_time_t * wait_timeout)
{
  // To ensure that we properly clean up the wait set, we declare an
  // object whose destructor will detach what we attached (this was previously
  // being done inside the destructor of the wait set.
  struct atexit_t
  {
    ~atexit_t()
    {
      // Manually detach conditions and clear sequences, to ensure a clean wait set for next time.
      if (!waitset) {
        RMW_SET_ERROR_MSG("waitset handle is null");
        return;
      }
      RMW_CHECK_TYPE_IDENTIFIERS_MATCH(
        waitset handle,
        waitset->implementation_identifier, opensplice_cpp_identifier,
        return )
      OpenSpliceWaitSetInfo * waitset_info = static_cast<OpenSpliceWaitSetInfo *>(waitset->data);
      if (!waitset_info) {
        RMW_SET_ERROR_MSG("WaitSet implementation struct is null");
        return;
      }

      DDS::WaitSet * dds_waitset = static_cast<DDS::WaitSet *>(waitset_info->waitset);
      if (!dds_waitset) {
        RMW_SET_ERROR_MSG("DDS waitset handle is null");
        return;
      }

      DDS::ConditionSeq * attached_conditions =
        static_cast<DDS::ConditionSeq *>(waitset_info->attached_conditions);
      if (!attached_conditions) {
        RMW_SET_ERROR_MSG("DDS condition sequence handle is null");
        return;
      }

      DDS::ReturnCode_t retcode;
      retcode = dds_waitset->get_conditions(*attached_conditions);
      if (retcode != DDS::RETCODE_OK) {
        RMW_SET_ERROR_MSG("Failed to get attached conditions for waitset");
        return;
      }

      for (uint32_t i = 0; i < attached_conditions->length(); ++i) {
        retcode = dds_waitset->detach_condition((*attached_conditions)[i]);
        if (retcode != DDS::RETCODE_OK) {
          RMW_SET_ERROR_MSG("Failed to get detach condition from waitset");
        }
      }

      DDS::ConditionSeq * active_conditions =
        static_cast<DDS::ConditionSeq *>(waitset_info->active_conditions);
      if (!active_conditions) {
        RMW_SET_ERROR_MSG("DDS condition sequence handle is null");
        return;
      }

      // Disassociate conditions left in active_conditions so that when the
      // waitset (and therefore the active_conditions sequence) are destroyed
      // they do not try to free the entities to which they point.
      // These entities are already being deleted (are owned) by other entities
      // like rmw_guard_conditions_t and rmw_node_t.
      // Without this step, sporadic bad memory accesses could occur when the
      // items added to the waitset were destroyed before the waitset.
      // The items in active_conditions are not being leaked though because
      // other entities own them and are responsible for removing them.
      for (uint32_t i = 0; i < active_conditions->length(); ++i) {
        active_conditions->get_buffer()[i] = nullptr;
      }
    }
    rmw_waitset_t * waitset = NULL;
  } atexit;

  atexit.waitset = waitset;

  if (!waitset) {
    RMW_SET_ERROR_MSG("waitset handle is null");
    return RMW_RET_ERROR;
  }
  RMW_CHECK_TYPE_IDENTIFIERS_MATCH(
    waitset,
    waitset->implementation_identifier, opensplice_cpp_identifier,
    return RMW_RET_ERROR);

  OpenSpliceWaitSetInfo * waitset_info = static_cast<OpenSpliceWaitSetInfo *>(waitset->data);
  if (!waitset_info) {
    RMW_SET_ERROR_MSG("WaitSet implementation struct is null");
    return RMW_RET_ERROR;
  }

  DDS::WaitSet * dds_waitset = static_cast<DDS::WaitSet *>(waitset_info->waitset);
  if (!dds_waitset) {
    RMW_SET_ERROR_MSG("DDS waitset handle is null");
    return RMW_RET_ERROR;
  }

  DDS::ConditionSeq * active_conditions =
    static_cast<DDS::ConditionSeq *>(waitset_info->active_conditions);
  if (!active_conditions) {
    RMW_SET_ERROR_MSG("DDS condition sequence handle is null");
    return RMW_RET_ERROR;
  }

  DDS::ConditionSeq * attached_conditions =
    static_cast<DDS::ConditionSeq *>(waitset_info->attached_conditions);
  if (!attached_conditions) {
    RMW_SET_ERROR_MSG("DDS condition sequence handle is null");
    return RMW_RET_ERROR;
  }

  // add a condition for each subscriber
  for (size_t i = 0; i < subscriptions->subscriber_count; ++i) {
    OpenSpliceStaticSubscriberInfo * subscriber_info =
      static_cast<OpenSpliceStaticSubscriberInfo *>(subscriptions->subscribers[i]);
    if (!subscriber_info) {
      RMW_SET_ERROR_MSG("subscriber info handle is null");
      return RMW_RET_ERROR;
    }
    DDS::ReadCondition * read_condition = subscriber_info->read_condition;
    if (!read_condition) {
      RMW_SET_ERROR_MSG("read condition handle is null");
      return RMW_RET_ERROR;
    }
    rmw_ret_t status = check_attach_condition_error(
      dds_waitset->attach_condition(read_condition));
    if (status != RMW_RET_OK) {
      return status;
    }
  }

  // add a condition for each guard condition
  if (guard_conditions) {
    for (size_t i = 0; i < guard_conditions->guard_condition_count; ++i) {
      DDS::GuardCondition * guard_condition =
        static_cast<DDS::GuardCondition *>(guard_conditions->guard_conditions[i]);
      if (!guard_condition) {
        RMW_SET_ERROR_MSG("guard condition handle is null");
        return RMW_RET_ERROR;
      }
      rmw_ret_t status = check_attach_condition_error(
        dds_waitset->attach_condition(guard_condition));
      if (status != RMW_RET_OK) {
        return status;
      }
    }
  }

  // add a condition for each service
  for (size_t i = 0; i < services->service_count; ++i) {
    OpenSpliceStaticServiceInfo * service_info =
      static_cast<OpenSpliceStaticServiceInfo *>(services->services[i]);
    if (!service_info) {
      RMW_SET_ERROR_MSG("service info handle is null");
      return RMW_RET_ERROR;
    }
    DDS::ReadCondition * read_condition = service_info->read_condition_;
    if (!read_condition) {
      RMW_SET_ERROR_MSG("read condition handle is null");
      return RMW_RET_ERROR;
    }
    rmw_ret_t status = check_attach_condition_error(
      dds_waitset->attach_condition(read_condition));
    if (status != RMW_RET_OK) {
      return status;
    }
  }

  // add a condition for each client
  for (size_t i = 0; i < clients->client_count; ++i) {
    OpenSpliceStaticClientInfo * client_info =
      static_cast<OpenSpliceStaticClientInfo *>(clients->clients[i]);
    if (!client_info) {
      RMW_SET_ERROR_MSG("client info handle is null");
      return RMW_RET_ERROR;
    }
    DDS::ReadCondition * read_condition = client_info->read_condition_;
    if (!read_condition) {
      RMW_SET_ERROR_MSG("read condition handle is null");
      return RMW_RET_ERROR;
    }
    rmw_ret_t status = check_attach_condition_error(
      dds_waitset->attach_condition(read_condition));
    if (status != RMW_RET_OK) {
      return status;
    }
  }

  // invoke wait until one of the conditions triggers

  DDS::Duration_t timeout;
  if (!wait_timeout) {
    timeout = DDS::DURATION_INFINITE;
  } else {
    timeout.sec = static_cast<DDS::Long>(wait_timeout->sec);
    timeout.nanosec = static_cast<DDS::Long>(wait_timeout->nsec);
  }
  DDS::ReturnCode_t status = dds_waitset->wait(*active_conditions, timeout);

  if (status != DDS::RETCODE_OK && status != DDS::RETCODE_TIMEOUT) {
    RMW_SET_ERROR_MSG("failed to wait on waitset");
    return RMW_RET_ERROR;
  }

  // set subscriber handles to zero for all not triggered status conditions
  for (size_t i = 0; i < subscriptions->subscriber_count; ++i) {
    OpenSpliceStaticSubscriberInfo * subscriber_info =
      static_cast<OpenSpliceStaticSubscriberInfo *>(subscriptions->subscribers[i]);
    if (!subscriber_info) {
      RMW_SET_ERROR_MSG("subscriber info handle is null");
      return RMW_RET_ERROR;
    }
    DDS::ReadCondition * read_condition = subscriber_info->read_condition;
    if (!read_condition) {
      RMW_SET_ERROR_MSG("read condition handle is null");
      return RMW_RET_ERROR;
    }
    if (!read_condition->get_trigger_value()) {
      // if the status condition was not triggered
      // reset the subscriber handle
      subscriptions->subscribers[i] = 0;
    }
    if (dds_waitset->detach_condition(read_condition) != DDS::RETCODE_OK) {
      RMW_SET_ERROR_MSG("failed to detach guard condition");
      return RMW_RET_ERROR;
    }
  }

  if (guard_conditions) {
    for (size_t i = 0; i < guard_conditions->guard_condition_count; ++i) {
      DDS::GuardCondition * guard_condition =
        static_cast<DDS::GuardCondition *>(guard_conditions->guard_conditions[i]);
      if (!guard_condition) {
        RMW_SET_ERROR_MSG("guard condition handle is null");
        return RMW_RET_ERROR;
      }

      if (!guard_condition->get_trigger_value()) {
        guard_conditions->guard_conditions[i] = 0;
      } else {
        // reset the trigger value for triggered guard conditions
        if (guard_condition->set_trigger_value(false) != DDS::RETCODE_OK) {
          RMW_SET_ERROR_MSG("failed to set trigger value to false");
          return RMW_RET_ERROR;
        }
      }
      if (dds_waitset->detach_condition(guard_condition) != DDS::RETCODE_OK) {
        RMW_SET_ERROR_MSG("failed to detach guard condition");
        return RMW_RET_ERROR;
      }
    }
  }

  // set service handles to zero for all not triggered conditions
  for (size_t i = 0; i < services->service_count; ++i) {
    OpenSpliceStaticServiceInfo * service_info =
      static_cast<OpenSpliceStaticServiceInfo *>(services->services[i]);
    if (!service_info) {
      RMW_SET_ERROR_MSG("service info handle is null");
      return RMW_RET_ERROR;
    }
    DDS::ReadCondition * read_condition = service_info->read_condition_;
    if (!read_condition) {
      RMW_SET_ERROR_MSG("read condition handle is null");
      return RMW_RET_ERROR;
    }

    // search for service condition in active set
    uint32_t j = 0;
    for (; j < active_conditions->length(); ++j) {
      if ((*active_conditions)[j] == read_condition) {
        break;
      }
    }
    // if service condition is not found in the active set
    // reset the service handle
    if (!(j < active_conditions->length())) {
      services->services[i] = 0;
    }
    if (dds_waitset->detach_condition(read_condition) != DDS::RETCODE_OK) {
      RMW_SET_ERROR_MSG("failed to detach guard condition");
      return RMW_RET_ERROR;
    }
  }

  // set client handles to zero for all not triggered conditions
  for (size_t i = 0; i < clients->client_count; ++i) {
    OpenSpliceStaticClientInfo * client_info =
      static_cast<OpenSpliceStaticClientInfo *>(clients->clients[i]);
    if (!client_info) {
      RMW_SET_ERROR_MSG("client info handle is null");
      return RMW_RET_ERROR;
    }
    DDS::ReadCondition * read_condition = client_info->read_condition_;
    if (!read_condition) {
      RMW_SET_ERROR_MSG("read condition handle is null");
      return RMW_RET_ERROR;
    }

    // search for service condition in active set
    uint32_t j = 0;
    for (; j < active_conditions->length(); ++j) {
      if ((*active_conditions)[j] == read_condition) {
        break;
      }
    }
    // if client condition is not found in the active set
    // reset the client handle
    if (!(j < active_conditions->length())) {
      clients->clients[i] = 0;
    }
    if (dds_waitset->detach_condition(read_condition) != DDS::RETCODE_OK) {
      RMW_SET_ERROR_MSG("failed to detach guard condition");
      return RMW_RET_ERROR;
    }
  }

  if (status == DDS::RETCODE_TIMEOUT) {
    return RMW_RET_TIMEOUT;
  }
  return RMW_RET_OK;
}
}  // extern "C"
<commit_msg>pointers are checked for null<commit_after>// Copyright 2014-2015 Open Source Robotics Foundation, Inc.
//
// 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 <ccpp_dds_dcps.h>
#include <dds_dcps.h>

#include "rmw/allocators.h"
#include "rmw/error_handling.h"
#include "rmw/impl/cpp/macros.hpp"
#include "rmw/rmw.h"
#include "rmw/types.h"

#include "identifier.hpp"
#include "types.hpp"

// The extern "C" here enforces that overloading is not used.
extern "C"
{
rmw_ret_t check_attach_condition_error(DDS::ReturnCode_t retcode)
{
  if (retcode == DDS::RETCODE_OK) {
    return RMW_RET_OK;
  }
  if (retcode == DDS::RETCODE_OUT_OF_RESOURCES) {
    RMW_SET_ERROR_MSG("failed to attach condition to waitset: out of resources");
  } else if (retcode == DDS::RETCODE_BAD_PARAMETER) {
    RMW_SET_ERROR_MSG("failed to attach condition to waitset: condition pointer was invalid");
  } else {
    RMW_SET_ERROR_MSG("failed to attach condition to waitset");
  }
  return RMW_RET_ERROR;
}

rmw_ret_t
rmw_wait(
  rmw_subscriptions_t * subscriptions,
  rmw_guard_conditions_t * guard_conditions,
  rmw_services_t * services,
  rmw_clients_t * clients,
  rmw_waitset_t * waitset,
  const rmw_time_t * wait_timeout)
{
  // To ensure that we properly clean up the wait set, we declare an
  // object whose destructor will detach what we attached (this was previously
  // being done inside the destructor of the wait set.
  struct atexit_t
  {
    ~atexit_t()
    {
      // Manually detach conditions and clear sequences, to ensure a clean wait set for next time.
      if (!waitset) {
        RMW_SET_ERROR_MSG("waitset handle is null");
        return;
      }
      RMW_CHECK_TYPE_IDENTIFIERS_MATCH(
        waitset handle,
        waitset->implementation_identifier, opensplice_cpp_identifier,
        return )
      OpenSpliceWaitSetInfo * waitset_info = static_cast<OpenSpliceWaitSetInfo *>(waitset->data);
      if (!waitset_info) {
        RMW_SET_ERROR_MSG("WaitSet implementation struct is null");
        return;
      }

      DDS::WaitSet * dds_waitset = static_cast<DDS::WaitSet *>(waitset_info->waitset);
      if (!dds_waitset) {
        RMW_SET_ERROR_MSG("DDS waitset handle is null");
        return;
      }

      DDS::ConditionSeq * attached_conditions =
        static_cast<DDS::ConditionSeq *>(waitset_info->attached_conditions);
      if (!attached_conditions) {
        RMW_SET_ERROR_MSG("DDS condition sequence handle is null");
        return;
      }

      DDS::ReturnCode_t retcode;
      retcode = dds_waitset->get_conditions(*attached_conditions);
      if (retcode != DDS::RETCODE_OK) {
        RMW_SET_ERROR_MSG("Failed to get attached conditions for waitset");
        return;
      }

      for (uint32_t i = 0; i < attached_conditions->length(); ++i) {
        retcode = dds_waitset->detach_condition((*attached_conditions)[i]);
        if (retcode != DDS::RETCODE_OK) {
          RMW_SET_ERROR_MSG("Failed to get detach condition from waitset");
        }
      }

      DDS::ConditionSeq * active_conditions =
        static_cast<DDS::ConditionSeq *>(waitset_info->active_conditions);
      if (!active_conditions) {
        RMW_SET_ERROR_MSG("DDS condition sequence handle is null");
        return;
      }

      // Disassociate conditions left in active_conditions so that when the
      // waitset (and therefore the active_conditions sequence) are destroyed
      // they do not try to free the entities to which they point.
      // These entities are already being deleted (are owned) by other entities
      // like rmw_guard_conditions_t and rmw_node_t.
      // Without this step, sporadic bad memory accesses could occur when the
      // items added to the waitset were destroyed before the waitset.
      // The items in active_conditions are not being leaked though because
      // other entities own them and are responsible for removing them.
      for (uint32_t i = 0; i < active_conditions->length(); ++i) {
        active_conditions->get_buffer()[i] = nullptr;
      }
    }
    rmw_waitset_t * waitset = NULL;
  } atexit;

  atexit.waitset = waitset;

  if (!waitset) {
    RMW_SET_ERROR_MSG("waitset handle is null");
    return RMW_RET_ERROR;
  }
  RMW_CHECK_TYPE_IDENTIFIERS_MATCH(
    waitset,
    waitset->implementation_identifier, opensplice_cpp_identifier,
    return RMW_RET_ERROR);

  OpenSpliceWaitSetInfo * waitset_info = static_cast<OpenSpliceWaitSetInfo *>(waitset->data);
  if (!waitset_info) {
    RMW_SET_ERROR_MSG("WaitSet implementation struct is null");
    return RMW_RET_ERROR;
  }

  DDS::WaitSet * dds_waitset = static_cast<DDS::WaitSet *>(waitset_info->waitset);
  if (!dds_waitset) {
    RMW_SET_ERROR_MSG("DDS waitset handle is null");
    return RMW_RET_ERROR;
  }

  DDS::ConditionSeq * active_conditions =
    static_cast<DDS::ConditionSeq *>(waitset_info->active_conditions);
  if (!active_conditions) {
    RMW_SET_ERROR_MSG("DDS condition sequence handle is null");
    return RMW_RET_ERROR;
  }

  DDS::ConditionSeq * attached_conditions =
    static_cast<DDS::ConditionSeq *>(waitset_info->attached_conditions);
  if (!attached_conditions) {
    RMW_SET_ERROR_MSG("DDS condition sequence handle is null");
    return RMW_RET_ERROR;
  }

  // add a condition for each subscriber
  if (subscriptions) {
    for (size_t i = 0; i < subscriptions->subscriber_count; ++i) {
      OpenSpliceStaticSubscriberInfo * subscriber_info =
        static_cast<OpenSpliceStaticSubscriberInfo *>(subscriptions->subscribers[i]);
      if (!subscriber_info) {
        RMW_SET_ERROR_MSG("subscriber info handle is null");
        return RMW_RET_ERROR;
      }
      DDS::ReadCondition * read_condition = subscriber_info->read_condition;
      if (!read_condition) {
        RMW_SET_ERROR_MSG("read condition handle is null");
        return RMW_RET_ERROR;
      }
      rmw_ret_t status = check_attach_condition_error(
        dds_waitset->attach_condition(read_condition));
      if (status != RMW_RET_OK) {
        return status;
      }
    }
  }

  // add a condition for each guard condition
  if (guard_conditions) {
    for (size_t i = 0; i < guard_conditions->guard_condition_count; ++i) {
      DDS::GuardCondition * guard_condition =
        static_cast<DDS::GuardCondition *>(guard_conditions->guard_conditions[i]);
      if (!guard_condition) {
        RMW_SET_ERROR_MSG("guard condition handle is null");
        return RMW_RET_ERROR;
      }
      rmw_ret_t status = check_attach_condition_error(
        dds_waitset->attach_condition(guard_condition));
      if (status != RMW_RET_OK) {
        return status;
      }
    }
  }

  // add a condition for each service
  if (services) {
    for (size_t i = 0; i < services->service_count; ++i) {
      OpenSpliceStaticServiceInfo * service_info =
        static_cast<OpenSpliceStaticServiceInfo *>(services->services[i]);
      if (!service_info) {
        RMW_SET_ERROR_MSG("service info handle is null");
        return RMW_RET_ERROR;
      }
      DDS::ReadCondition * read_condition = service_info->read_condition_;
      if (!read_condition) {
        RMW_SET_ERROR_MSG("read condition handle is null");
        return RMW_RET_ERROR;
      }
      rmw_ret_t status = check_attach_condition_error(
        dds_waitset->attach_condition(read_condition));
      if (status != RMW_RET_OK) {
        return status;
      }
    }
  }

  // add a condition for each client
  if (clients) {
    for (size_t i = 0; i < clients->client_count; ++i) {
      OpenSpliceStaticClientInfo * client_info =
        static_cast<OpenSpliceStaticClientInfo *>(clients->clients[i]);
      if (!client_info) {
        RMW_SET_ERROR_MSG("client info handle is null");
        return RMW_RET_ERROR;
      }
      DDS::ReadCondition * read_condition = client_info->read_condition_;
      if (!read_condition) {
        RMW_SET_ERROR_MSG("read condition handle is null");
        return RMW_RET_ERROR;
      }
      rmw_ret_t status = check_attach_condition_error(
        dds_waitset->attach_condition(read_condition));
      if (status != RMW_RET_OK) {
        return status;
      }
    }
  }

  // invoke wait until one of the conditions triggers

  DDS::Duration_t timeout;
  if (!wait_timeout) {
    timeout = DDS::DURATION_INFINITE;
  } else {
    timeout.sec = static_cast<DDS::Long>(wait_timeout->sec);
    timeout.nanosec = static_cast<DDS::Long>(wait_timeout->nsec);
  }
  DDS::ReturnCode_t status = dds_waitset->wait(*active_conditions, timeout);

  if (status != DDS::RETCODE_OK && status != DDS::RETCODE_TIMEOUT) {
    RMW_SET_ERROR_MSG("failed to wait on waitset");
    return RMW_RET_ERROR;
  }

  // set subscriber handles to zero for all not triggered status conditions
  if (subscriptions) {
    for (size_t i = 0; i < subscriptions->subscriber_count; ++i) {
      OpenSpliceStaticSubscriberInfo * subscriber_info =
        static_cast<OpenSpliceStaticSubscriberInfo *>(subscriptions->subscribers[i]);
      if (!subscriber_info) {
        RMW_SET_ERROR_MSG("subscriber info handle is null");
        return RMW_RET_ERROR;
      }
      DDS::ReadCondition * read_condition = subscriber_info->read_condition;
      if (!read_condition) {
        RMW_SET_ERROR_MSG("read condition handle is null");
        return RMW_RET_ERROR;
      }
      if (!read_condition->get_trigger_value()) {
        // if the status condition was not triggered
        // reset the subscriber handle
        subscriptions->subscribers[i] = 0;
      }
      if (dds_waitset->detach_condition(read_condition) != DDS::RETCODE_OK) {
        RMW_SET_ERROR_MSG("failed to detach guard condition");
        return RMW_RET_ERROR;
      }
    }
  }

  if (guard_conditions) {
    for (size_t i = 0; i < guard_conditions->guard_condition_count; ++i) {
      DDS::GuardCondition * guard_condition =
        static_cast<DDS::GuardCondition *>(guard_conditions->guard_conditions[i]);
      if (!guard_condition) {
        RMW_SET_ERROR_MSG("guard condition handle is null");
        return RMW_RET_ERROR;
      }

      if (!guard_condition->get_trigger_value()) {
        guard_conditions->guard_conditions[i] = 0;
      } else {
        // reset the trigger value for triggered guard conditions
        if (guard_condition->set_trigger_value(false) != DDS::RETCODE_OK) {
          RMW_SET_ERROR_MSG("failed to set trigger value to false");
          return RMW_RET_ERROR;
        }
      }
      if (dds_waitset->detach_condition(guard_condition) != DDS::RETCODE_OK) {
        RMW_SET_ERROR_MSG("failed to detach guard condition");
        return RMW_RET_ERROR;
      }
    }
  }

  // set service handles to zero for all not triggered conditions
  if (services) {
    for (size_t i = 0; i < services->service_count; ++i) {
      OpenSpliceStaticServiceInfo * service_info =
        static_cast<OpenSpliceStaticServiceInfo *>(services->services[i]);
      if (!service_info) {
        RMW_SET_ERROR_MSG("service info handle is null");
        return RMW_RET_ERROR;
      }
      DDS::ReadCondition * read_condition = service_info->read_condition_;
      if (!read_condition) {
        RMW_SET_ERROR_MSG("read condition handle is null");
        return RMW_RET_ERROR;
      }

      // search for service condition in active set
      uint32_t j = 0;
      for (; j < active_conditions->length(); ++j) {
        if ((*active_conditions)[j] == read_condition) {
          break;
        }
      }
      // if service condition is not found in the active set
      // reset the service handle
      if (!(j < active_conditions->length())) {
        services->services[i] = 0;
      }
      if (dds_waitset->detach_condition(read_condition) != DDS::RETCODE_OK) {
        RMW_SET_ERROR_MSG("failed to detach guard condition");
        return RMW_RET_ERROR;
      }
    }
  }

  // set client handles to zero for all not triggered conditions
  if (clients) {
    for (size_t i = 0; i < clients->client_count; ++i) {
      OpenSpliceStaticClientInfo * client_info =
        static_cast<OpenSpliceStaticClientInfo *>(clients->clients[i]);
      if (!client_info) {
        RMW_SET_ERROR_MSG("client info handle is null");
        return RMW_RET_ERROR;
      }
      DDS::ReadCondition * read_condition = client_info->read_condition_;
      if (!read_condition) {
        RMW_SET_ERROR_MSG("read condition handle is null");
        return RMW_RET_ERROR;
      }

      // search for service condition in active set
      uint32_t j = 0;
      for (; j < active_conditions->length(); ++j) {
        if ((*active_conditions)[j] == read_condition) {
          break;
        }
      }
      // if client condition is not found in the active set
      // reset the client handle
      if (!(j < active_conditions->length())) {
        clients->clients[i] = 0;
      }
      if (dds_waitset->detach_condition(read_condition) != DDS::RETCODE_OK) {
        RMW_SET_ERROR_MSG("failed to detach guard condition");
        return RMW_RET_ERROR;
      }
    }
  }

  if (status == DDS::RETCODE_TIMEOUT) {
    return RMW_RET_TIMEOUT;
  }
  return RMW_RET_OK;
}
}  // extern "C"
<|endoftext|>


<commit_before>#include "readDMAT.h"

#include "verbose.h"
#include <cstdio>

#include <Eigen/Dense>

// Static helper method reads the first to elements in the given file
// Inputs:
//   fp  file pointer of .dmat file that was just opened
// Outputs:
//   num_rows  number of rows
//   num_cols number of columns
// Returns true on success, false on failure
static inline bool readDMAT_read_header(FILE * fp, int & num_rows, int & num_cols)
{
  // first line contains number of rows and number of columns
  int res = fscanf(fp,"%d %d\n",&num_cols,&num_rows);
  if(res != 2)
  {
    fprintf(stderr,"IOError: readDMAT() first row should be [num cols] [num rows]...\n");
    return false;
  }
  // check that number of columns and rows are sane
  if(num_cols < 0)
  {
    fprintf(stderr,"IOError: readDMAT() number of columns %d < 0\n",num_cols);
    return false;
  }
  if(num_rows < 0)
  {
    fprintf(stderr,"IOError: readDMAT() number of rows %d < 0\n",num_rows);
    return false;
  }
  return true;
}

#ifndef IGL_NO_EIGEN
template <typename DerivedW>
IGL_INLINE bool igl::readDMAT(const std::string file_name,
  Eigen::PlainObjectBase<DerivedW> & W)
{
  FILE * fp = fopen(file_name.c_str(),"r");
  if(fp == NULL)
  {
    fprintf(stderr,"IOError: readDMAT() could not open %s...\n",file_name.c_str());
    return false;
  }
  int num_rows,num_cols;
  bool head_success = readDMAT_read_header(fp,num_rows,num_cols);
  if(!head_success)
  {
    fclose(fp);
    return false;
  }

  // Resize output to fit matrix
  W.resize(num_rows,num_cols);

  // Loop over columns slowly
  for(int j = 0;j < num_cols;j++)
  {
    // loop over rows (down columns) quickly
    for(int i = 0;i < num_rows;i++)
    {
      double d;
      if(fscanf(fp," %lg",&d) != 1)
      {
        fclose(fp);
        fprintf(
          stderr,
          "IOError: readDMAT() bad format after reading %d entries\n",
          j*num_rows + i);
        return false;
      }
      W(i,j) = d;
    }
  }

  fclose(fp);
  return true;
}
#endif

template <typename Scalar>
IGL_INLINE bool igl::readDMAT(
  const std::string file_name,
  std::vector<std::vector<Scalar> > & W)
{
  FILE * fp = fopen(file_name.c_str(),"r");
  if(fp == NULL)
  {
    fprintf(stderr,"IOError: readDMAT() could not open %s...\n",file_name.c_str());
    return false;
  }
  int num_rows,num_cols;
  bool head_success = readDMAT_read_header(fp,num_rows,num_cols);
  if(!head_success)
  {
    fclose(fp);
    return false;
  }

  // Resize for output
  W.resize(num_rows,typename std::vector<Scalar>(num_cols));

  // Loop over columns slowly
  for(int j = 0;j < num_cols;j++)
  {
    // loop over rows (down columns) quickly
    for(int i = 0;i < num_rows;i++)
    {
      double d;
      if(fscanf(fp," %lg",&d) != 1)
      {
        fclose(fp);
        fprintf(
          stderr,
          "IOError: readDMAT() bad format after reading %d entries\n",
          j*num_rows + i);
        return false;
      }
      W[i][j] = (Scalar)d;
    }
  }

  fclose(fp);
  return true;
}

#ifndef IGL_HEADER_ONLY
// Explicit template specialization
template bool igl::readDMAT<Eigen::Matrix<double, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
#endif
<commit_msg>no eigen in readDmat<commit_after>#include "readDMAT.h"

#include "verbose.h"
#include <cstdio>
#include <iostream>

// Static helper method reads the first to elements in the given file
// Inputs:
//   fp  file pointer of .dmat file that was just opened
// Outputs:
//   num_rows  number of rows
//   num_cols number of columns
// Returns true on success, false on failure
static inline bool readDMAT_read_header(FILE * fp, int & num_rows, int & num_cols)
{
  // first line contains number of rows and number of columns
  int res = fscanf(fp,"%d %d\n",&num_cols,&num_rows);
  if(res != 2)
  {
    fprintf(stderr,"IOError: readDMAT() first row should be [num cols] [num rows]...\n");
    return false;
  }
  // check that number of columns and rows are sane
  if(num_cols < 0)
  {
    fprintf(stderr,"IOError: readDMAT() number of columns %d < 0\n",num_cols);
    return false;
  }
  if(num_rows < 0)
  {
    fprintf(stderr,"IOError: readDMAT() number of rows %d < 0\n",num_rows);
    return false;
  }
  return true;
}

#ifndef IGL_NO_EIGEN
template <typename DerivedW>
IGL_INLINE bool igl::readDMAT(const std::string file_name,
  Eigen::PlainObjectBase<DerivedW> & W)
{
  FILE * fp = fopen(file_name.c_str(),"r");
  if(fp == NULL)
  {
    fprintf(stderr,"IOError: readDMAT() could not open %s...\n",file_name.c_str());
    return false;
  }
  int num_rows,num_cols;
  bool head_success = readDMAT_read_header(fp,num_rows,num_cols);
  if(!head_success)
  {
    fclose(fp);
    return false;
  }

  // Resize output to fit matrix
  W.resize(num_rows,num_cols);

  // Loop over columns slowly
  for(int j = 0;j < num_cols;j++)
  {
    // loop over rows (down columns) quickly
    for(int i = 0;i < num_rows;i++)
    {
      double d;
      if(fscanf(fp," %lg",&d) != 1)
      {
        fclose(fp);
        fprintf(
          stderr,
          "IOError: readDMAT() bad format after reading %d entries\n",
          j*num_rows + i);
        return false;
      }
      W(i,j) = d;
    }
  }

  fclose(fp);
  return true;
}
#endif

template <typename Scalar>
IGL_INLINE bool igl::readDMAT(
  const std::string file_name,
  std::vector<std::vector<Scalar> > & W)
{
  FILE * fp = fopen(file_name.c_str(),"r");
  if(fp == NULL)
  {
    fprintf(stderr,"IOError: readDMAT() could not open %s...\n",file_name.c_str());
    return false;
  }
  int num_rows,num_cols;
  bool head_success = readDMAT_read_header(fp,num_rows,num_cols);
  if(!head_success)
  {
    fclose(fp);
    return false;
  }

  // Resize for output
  W.resize(num_rows,typename std::vector<Scalar>(num_cols));

  // Loop over columns slowly
  for(int j = 0;j < num_cols;j++)
  {
    // loop over rows (down columns) quickly
    for(int i = 0;i < num_rows;i++)
    {
      double d;
      if(fscanf(fp," %lg",&d) != 1)
      {
        fclose(fp);
        fprintf(
          stderr,
          "IOError: readDMAT() bad format after reading %d entries\n",
          j*num_rows + i);
        return false;
      }
      W[i][j] = (Scalar)d;
    }
  }

  fclose(fp);
  return true;
}

#ifndef IGL_HEADER_ONLY
// Explicit template specialization
template bool igl::readDMAT<Eigen::Matrix<double, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
#endif
<|endoftext|>


<commit_before>#include "command.h"
#include "image.h"
#include "algo/loop.h"
#include "math/math.h"

#include "image/average_space.h"
#include "interp/linear.h"
#include "interp/nearest.h"
#include "interp/cubic.h"
#include "interp/sinc.h"
#include "filter/reslice.h"

using namespace MR;
using namespace App;

const char* interp_choices[] = { "nearest", "linear", "cubic", "sinc", NULL }; // TODO
const char* space_choices[] = { "voxel", "image1", "image2", "average", NULL };

template <class ValueType>
inline void meansquared (const ValueType& value1,
  const ValueType& value2,
  Eigen::Matrix<ValueType, Eigen::Dynamic, 1>& cost){
  cost.array() += Math::pow2 (value1 - value2);
}

template <class ValueType>
inline void meansquared (const Eigen::Matrix<ValueType,Eigen::Dynamic, 1>& value1,
  const Eigen::Matrix<ValueType,Eigen::Dynamic, 1>& value2,
  Eigen::Matrix<ValueType, Eigen::Dynamic, 1>& cost) {
  cost.array() += (value1 - value2).array().square();
}

// TODO: there must be a better way of doing this?
template <class ImageType1, class ImageType2, class TransformType, class OversampleType, class ValueType>
void reslice(size_t interp, ImageType1& input, ImageType2& output, const TransformType& trafo = Adapter::NoTransform, const OversampleType& oversample = Adapter::AutoOverSample, const ValueType out_of_bounds_value = 0.f){
  switch(interp){
    case 0:
      Filter::reslice<Interp::Nearest> (input, output, trafo, Adapter::AutoOverSample, out_of_bounds_value);
      DEBUG("Nearest");
      break;
    case 1:
      Filter::reslice<Interp::Linear> (input, output, trafo, Adapter::AutoOverSample, out_of_bounds_value);
      DEBUG("Linear");
      break;
    case 2:
      Filter::reslice<Interp::Cubic> (input, output, trafo, Adapter::AutoOverSample, out_of_bounds_value);
      DEBUG("Cubic");
      break;
    case 3:
      Filter::reslice<Interp::Sinc> (input, output, trafo, Adapter::AutoOverSample, out_of_bounds_value);
      DEBUG("Sinc");
      break;
    default:
      throw Exception ("Fixme: interpolation value invalid");
  }
}

void usage ()
{
  AUTHOR = "David Raffelt (david.raffelt@florey.edu.au)";

  DESCRIPTION
  + "computes a dissimilarity metric between two images. Currently only the mean squared difference is implemented";

  ARGUMENTS
  + Argument ("image1", "the first input image.").type_image_in ()
  + Argument ("image2", "the second input image.").type_image_in ();

  OPTIONS
  + Option ("space",
        "voxel (default): per voxel "
        "image1: scanner space of image 1 "
        "image2: scanner space of image 2 "
        "average: scanner space of the average affine transformation of image 1 and 2 ")
    +   Argument ("iteration method").type_choice (space_choices)

    + Option ("interp",
        "set the interpolation method to use when reslicing (choices: nearest, linear, cubic, sinc. Default: cubic).")
    + Argument ("method").type_choice (interp_choices)

    + Option ("mask1", "mask for image 1")
    + Argument ("image").type_image_in ()

    + Option ("mask2", "mask for image 2")
    + Argument ("image").type_image_in ()

    + Option ("nonormalisation",
        "do not normalise the dissimilarity metric to the number of voxels.")

    + Option ("overlap",
        "output number of voxels that were used.");

}

typedef double value_type;
typedef Image<bool> MaskType;

void run ()
{
  auto input1 = Image<value_type>::open (argument[0]).with_direct_io (Stride::contiguous_along_axis (3));;
  auto input2 = Image<value_type>::open (argument[1]).with_direct_io (Stride::contiguous_along_axis (3));;

  const size_t dimensions = input1.ndim();
  if (input1.ndim() != input2.ndim())
    throw Exception ("both images have to have the same number of dimensions");
  DEBUG("dimensions: " + str(dimensions));
  if (dimensions > 4) throw Exception ("images have to be 3D or 4D");

  size_t volumes(1);
  if (dimensions == 4) {
    volumes = input1.size(3);
    if (input1.size(3) != input2.size(3)){
      throw Exception ("both images have to have the same number of volumes");
    }
  }
  DEBUG("volumes: " + str(volumes));

  int space = 0;  // voxel
  auto opt = get_options ("space");
  if (opt.size())
    space = opt[0][0];

  MaskType mask1;
  bool use_mask1 = get_options ("mask1").size()==1;
  if (use_mask1) {
    mask1 = Image<bool>::open(get_options ("mask1")[0][0]);
    if (mask1.ndim() != 3) throw Exception ("mask has to be 3D");
  }

  MaskType mask2;
  bool use_mask2 = get_options ("mask2").size()==1;
  if (use_mask2){
    mask2 = Image<bool>::open(get_options ("mask2")[0][0]);
    if (mask2.ndim() != 3) throw Exception ("mask has to be 3D");
  }

  bool nonormalisation = false;
  if (get_options ("nonormalisation").size())
    nonormalisation = true;
  ssize_t n_voxels = input1.size(0) * input1.size(1) * input1.size(2);

  value_type out_of_bounds_value = 0.0;

  Eigen::Matrix<value_type, Eigen::Dynamic, 1> sos = Eigen::Matrix<value_type, Eigen::Dynamic, 1>::Zero (volumes);
  if (space==0){
    DEBUG("per-voxel");
    check_dimensions (input1, input2);
    if (use_mask1 or use_mask2)
      n_voxels = 0;
    if (use_mask1 and use_mask2) {
      if (dimensions == 3) {
        for (auto i = Loop() (input1, input2, mask1, mask2); i ;++i)
          if (mask1.value() and mask2.value()){
            ++n_voxels;
            meansquared<value_type>(input1.value(), input2.value(), sos);
          }
      } else { // 4D
        for (auto i = Loop(0, 3) (input1, input2, mask1, mask2); i ;++i) {
          if (mask1.value() and mask2.value()){
            ++n_voxels;
            meansquared<value_type>(input1.row(3), input2.row(3), sos);
          }
        }
      }
    } else if (use_mask1) {
        if (dimensions == 3) {
          for (auto i = Loop() (input1, input2, mask1); i ;++i)
            if (mask1.value()){
              ++n_voxels;
              meansquared<value_type>(input1.value(), input2.value(), sos);
            }
        } else { // 4D
          for (auto i = Loop(0, 3) (input1, input2, mask1); i ;++i) {
            if (mask1.value()){
              ++n_voxels;
              meansquared<value_type>(input1.row(3), input2.row(3), sos);
            }
          }
        }
    } else if (use_mask2){
      if (dimensions == 3) {
          for (auto i = Loop() (input1, input2, mask2); i ;++i)
            if (mask2.value()){
              ++n_voxels;
              meansquared<value_type>(input1.value(), input2.value(), sos);
            }
        } else { // 4D
          for (auto i = Loop(0, 3) (input1, input2, mask2); i ;++i) {
            if (mask2.value()){
              ++n_voxels;
              meansquared<value_type>(input1.row(3), input2.row(3), sos);
            }
          }
        }
    } else {
      for (auto i = Loop() (input1, input2); i ;++i)
        meansquared<value_type>(input1.value(), input2.value(), sos);
    }
  } else {
    DEBUG("scanner space");
    int interp = 2;  // cubic
    opt = get_options ("interp");
    if (opt.size())
      interp = opt[0][0];

    auto output1 = Header::scratch(input1.original_header(),"-").get_image<value_type>();
    auto output2 = Header::scratch(input2.original_header(),"-").get_image<value_type>();

    MaskType output1mask;
    MaskType output2mask;

    if (space == 1){
      DEBUG("image 1");
      output1 = input1;
      output1mask = mask1;
      output2 = Header::scratch(input1.original_header(),"-").get_image<value_type>();
      output2mask = Header::scratch(input1.original_header(),"-").get_image<bool>();
      {
        LogLevelLatch log_level (0);
        reslice(interp,input2, output2, Adapter::NoTransform, Adapter::AutoOverSample, out_of_bounds_value);
        if (use_mask2)
          Filter::reslice<Interp::Nearest> (mask2, output2mask, Adapter::NoTransform, Adapter::AutoOverSample, 0);
      }
    }

    if (space == 2) {
      DEBUG("image 2");
      output1 = Header::scratch(input2.original_header(),"-").get_image<value_type>();
      output1mask = Header::scratch(input2.original_header(),"-").get_image<bool>();
      output2 = input2;
      output2mask = mask2;
      {
        LogLevelLatch log_level (0);
        reslice(interp, input1, output1, Adapter::NoTransform, Adapter::AutoOverSample, out_of_bounds_value);
        if (use_mask1)
          Filter::reslice<Interp::Nearest> (mask1, output1mask, Adapter::NoTransform, Adapter::AutoOverSample, 0);
      }
      n_voxels = input2.size(0) * input2.size(1) * input2.size(2);
    }

    if (space == 3) {
      DEBUG("average space");
      std::vector<Header> headers;
      headers.push_back (input1.original_header());
      headers.push_back (input2.original_header());
      default_type template_res = 1.0;
      auto padding = Eigen::Matrix<default_type, 4, 1>(0, 0, 0, 1.0);
      std::vector<Eigen::Transform<double, 3, Eigen::Projective>> transform_header_with;

      auto template_header = compute_minimum_average_header<double,Eigen::Transform<double, 3, Eigen::Projective>>(headers, template_res, padding, transform_header_with);
      template_header.set_ndim(input1.ndim());

      output1 = Image<value_type>::scratch(input1.original_header(),"-");
      output2 = Image<value_type>::scratch(input1.original_header(),"-");
      output1mask = Header::scratch(template_header,"-").get_image<bool>();
      output2mask = Header::scratch(template_header,"-").get_image<bool>();
      {
        LogLevelLatch log_level (0);
        reslice(interp, input1, output1, Adapter::NoTransform, Adapter::AutoOverSample, out_of_bounds_value);
        reslice(interp, input2, output2, Adapter::NoTransform, Adapter::AutoOverSample, out_of_bounds_value);
        if (use_mask1)
          Filter::reslice<Interp::Nearest> (mask1, output1mask, Adapter::NoTransform, Adapter::AutoOverSample, 0);
        if (use_mask2)
          Filter::reslice<Interp::Nearest> (mask2, output2mask, Adapter::NoTransform, Adapter::AutoOverSample, 0);
      }
      n_voxels = output1.size(0) * output1.size(1) * output1.size(2);
    }

    if (use_mask1 or use_mask2)
      n_voxels = 0;
    if (use_mask1 and use_mask2){
      if (dimensions == 3) {
        for (auto i = Loop() (output1, output2, output1mask, output2mask); i ;++i)
          if (output1mask.value() and output2mask.value()){
            ++n_voxels;
            meansquared<value_type>(output1.value(), output2.value(), sos);
          }
      } else { // 4D
        for (auto i = Loop(0, 3) (output1, output2, output1mask, output2mask); i ;++i) {
          if (output1mask.value() and output2mask.value()){
            ++n_voxels;
            meansquared<value_type>(output1.row(3), output2.row(3), sos);
          }
        }
      }
    } else if (use_mask1){
      if (dimensions == 3) {
        for (auto i = Loop() (output1, output2, output1mask); i ;++i)
          if (output1mask.value()){
            ++n_voxels;
            meansquared<value_type>(output1.value(), output2.value(), sos);
          }
      } else { // 4D
        for (auto i = Loop(0, 3) (output1, output2, output1mask); i ;++i) {
          if (output1mask.value()){
            ++n_voxels;
            meansquared<value_type>(output1.row(3), output2.row(3), sos);
          }
        }
      }
    } else if (use_mask2){
      if (dimensions == 3) {
        for (auto i = Loop() (output1, output2, output2mask); i ;++i)
          if (output2mask.value()){
            ++n_voxels;
            meansquared<value_type>(output1.value(), output2.value(), sos);
          }
      } else { // 4D
        for (auto i = Loop(0, 3) (output1, output2, output2mask); i ;++i) {
          if (output2mask.value()){
            ++n_voxels;
            meansquared<value_type>(output1.row(3), output2.row(3), sos);
          }
        }
      }
    } else {
      if (dimensions == 3) {
        for (auto i = Loop() (output1, output2); i ;++i)
          meansquared<value_type>(output1.value(), output2.value(), sos);
      } else { // 4D
        for (auto i = Loop(0, 3) (output1, output2); i ;++i)
          meansquared<value_type>(output1.row(3), output2.row(3), sos);
      }
    }

  }
  DEBUG ("n_voxels:" + str(n_voxels));
  if (n_voxels==0)
    WARN("number of overlapping voxels is zero");

  if (!nonormalisation)
    sos.array() /= static_cast<value_type>(n_voxels);
  std::cout << str(sos.transpose()) << std::endl;

  if (get_options ("overlap").size())
    std::cout << str(n_voxels) << std::endl;
}

<commit_msg>mrmetric: fixed goof up introduced in previous commit<commit_after>#include "command.h"
#include "image.h"
#include "algo/loop.h"
#include "math/math.h"

#include "image/average_space.h"
#include "interp/linear.h"
#include "interp/nearest.h"
#include "interp/cubic.h"
#include "interp/sinc.h"
#include "filter/reslice.h"

using namespace MR;
using namespace App;

const char* interp_choices[] = { "nearest", "linear", "cubic", "sinc", NULL }; // TODO
const char* space_choices[] = { "voxel", "image1", "image2", "average", NULL };

template <class ValueType>
inline void meansquared (const ValueType& value1,
  const ValueType& value2,
  Eigen::Matrix<ValueType, Eigen::Dynamic, 1>& cost){
  cost.array() += Math::pow2 (value1 - value2);
}

template <class ValueType>
inline void meansquared (const Eigen::Matrix<ValueType,Eigen::Dynamic, 1>& value1,
  const Eigen::Matrix<ValueType,Eigen::Dynamic, 1>& value2,
  Eigen::Matrix<ValueType, Eigen::Dynamic, 1>& cost) {
  cost.array() += (value1 - value2).array().square();
}

// TODO: there must be a better way of doing this?
template <class ImageType1, class ImageType2, class TransformType, class OversampleType, class ValueType>
void reslice(size_t interp, ImageType1& input, ImageType2& output, const TransformType& trafo = Adapter::NoTransform, const OversampleType& oversample = Adapter::AutoOverSample, const ValueType out_of_bounds_value = 0.f){
  switch(interp){
    case 0:
      Filter::reslice<Interp::Nearest> (input, output, trafo, Adapter::AutoOverSample, out_of_bounds_value);
      DEBUG("Nearest");
      break;
    case 1:
      Filter::reslice<Interp::Linear> (input, output, trafo, Adapter::AutoOverSample, out_of_bounds_value);
      DEBUG("Linear");
      break;
    case 2:
      Filter::reslice<Interp::Cubic> (input, output, trafo, Adapter::AutoOverSample, out_of_bounds_value);
      DEBUG("Cubic");
      break;
    case 3:
      Filter::reslice<Interp::Sinc> (input, output, trafo, Adapter::AutoOverSample, out_of_bounds_value);
      DEBUG("Sinc");
      break;
    default:
      throw Exception ("Fixme: interpolation value invalid");
  }
}

void usage ()
{
  AUTHOR = "David Raffelt (david.raffelt@florey.edu.au)";

  DESCRIPTION
  + "computes a dissimilarity metric between two images. Currently only the mean squared difference is implemented";

  ARGUMENTS
  + Argument ("image1", "the first input image.").type_image_in ()
  + Argument ("image2", "the second input image.").type_image_in ();

  OPTIONS
  + Option ("space",
        "voxel (default): per voxel "
        "image1: scanner space of image 1 "
        "image2: scanner space of image 2 "
        "average: scanner space of the average affine transformation of image 1 and 2 ")
    +   Argument ("iteration method").type_choice (space_choices)

    + Option ("interp",
        "set the interpolation method to use when reslicing (choices: nearest, linear, cubic, sinc. Default: cubic).")
    + Argument ("method").type_choice (interp_choices)

    + Option ("mask1", "mask for image 1")
    + Argument ("image").type_image_in ()

    + Option ("mask2", "mask for image 2")
    + Argument ("image").type_image_in ()

    + Option ("nonormalisation",
        "do not normalise the dissimilarity metric to the number of voxels.")

    + Option ("overlap",
        "output number of voxels that were used.");

}

typedef double value_type;
typedef Image<bool> MaskType;

void run ()
{
  auto input1 = Image<value_type>::open (argument[0]).with_direct_io (Stride::contiguous_along_axis (3));;
  auto input2 = Image<value_type>::open (argument[1]).with_direct_io (Stride::contiguous_along_axis (3));;

  const size_t dimensions = input1.ndim();
  if (input1.ndim() != input2.ndim())
    throw Exception ("both images have to have the same number of dimensions");
  DEBUG("dimensions: " + str(dimensions));
  if (dimensions > 4) throw Exception ("images have to be 3D or 4D");

  size_t volumes(1);
  if (dimensions == 4) {
    volumes = input1.size(3);
    if (input1.size(3) != input2.size(3)){
      throw Exception ("both images have to have the same number of volumes");
    }
  }
  DEBUG("volumes: " + str(volumes));

  int space = 0;  // voxel
  auto opt = get_options ("space");
  if (opt.size())
    space = opt[0][0];

  MaskType mask1;
  bool use_mask1 = get_options ("mask1").size()==1;
  if (use_mask1) {
    mask1 = Image<bool>::open(get_options ("mask1")[0][0]);
    if (mask1.ndim() != 3) throw Exception ("mask has to be 3D");
  }

  MaskType mask2;
  bool use_mask2 = get_options ("mask2").size()==1;
  if (use_mask2){
    mask2 = Image<bool>::open(get_options ("mask2")[0][0]);
    if (mask2.ndim() != 3) throw Exception ("mask has to be 3D");
  }

  bool nonormalisation = false;
  if (get_options ("nonormalisation").size())
    nonormalisation = true;
  ssize_t n_voxels = input1.size(0) * input1.size(1) * input1.size(2);

  value_type out_of_bounds_value = 0.0;

  Eigen::Matrix<value_type, Eigen::Dynamic, 1> sos = Eigen::Matrix<value_type, Eigen::Dynamic, 1>::Zero (volumes);
  if (space==0){
    DEBUG("per-voxel");
    check_dimensions (input1, input2);
    if (use_mask1 or use_mask2)
      n_voxels = 0;
    if (use_mask1 and use_mask2) {
      if (dimensions == 3) {
        for (auto i = Loop() (input1, input2, mask1, mask2); i ;++i)
          if (mask1.value() and mask2.value()){
            ++n_voxels;
            meansquared<value_type>(input1.value(), input2.value(), sos);
          }
      } else { // 4D
        for (auto i = Loop(0, 3) (input1, input2, mask1, mask2); i ;++i) {
          if (mask1.value() and mask2.value()){
            ++n_voxels;
            meansquared<value_type>(input1.row(3), input2.row(3), sos);
          }
        }
      }
    } else if (use_mask1) {
        if (dimensions == 3) {
          for (auto i = Loop() (input1, input2, mask1); i ;++i)
            if (mask1.value()){
              ++n_voxels;
              meansquared<value_type>(input1.value(), input2.value(), sos);
            }
        } else { // 4D
          for (auto i = Loop(0, 3) (input1, input2, mask1); i ;++i) {
            if (mask1.value()){
              ++n_voxels;
              meansquared<value_type>(input1.row(3), input2.row(3), sos);
            }
          }
        }
    } else if (use_mask2){
      if (dimensions == 3) {
          for (auto i = Loop() (input1, input2, mask2); i ;++i)
            if (mask2.value()){
              ++n_voxels;
              meansquared<value_type>(input1.value(), input2.value(), sos);
            }
        } else { // 4D
          for (auto i = Loop(0, 3) (input1, input2, mask2); i ;++i) {
            if (mask2.value()){
              ++n_voxels;
              meansquared<value_type>(input1.row(3), input2.row(3), sos);
            }
          }
        }
    } else {
      for (auto i = Loop() (input1, input2); i ;++i)
        meansquared<value_type>(input1.value(), input2.value(), sos);
    }
  } else {
    DEBUG("scanner space");
    int interp = 2;  // cubic
    opt = get_options ("interp");
    if (opt.size())
      interp = opt[0][0];

    auto output1 = Header::scratch(input1.original_header(),"-").get_image<value_type>();
    auto output2 = Header::scratch(input2.original_header(),"-").get_image<value_type>();

    MaskType output1mask;
    MaskType output2mask;

    if (space == 1){
      DEBUG("image 1");
      output1 = input1;
      output1mask = mask1;
      output2 = Header::scratch(input1.original_header(),"-").get_image<value_type>();
      output2mask = Header::scratch(input1.original_header(),"-").get_image<bool>();
      {
        LogLevelLatch log_level (0);
        reslice(interp,input2, output2, Adapter::NoTransform, Adapter::AutoOverSample, out_of_bounds_value);
        if (use_mask2)
          Filter::reslice<Interp::Nearest> (mask2, output2mask, Adapter::NoTransform, Adapter::AutoOverSample, 0);
      }
    }

    if (space == 2) {
      DEBUG("image 2");
      output1 = Header::scratch(input2.original_header(),"-").get_image<value_type>();
      output1mask = Header::scratch(input2.original_header(),"-").get_image<bool>();
      output2 = input2;
      output2mask = mask2;
      {
        LogLevelLatch log_level (0);
        reslice(interp, input1, output1, Adapter::NoTransform, Adapter::AutoOverSample, out_of_bounds_value);
        if (use_mask1)
          Filter::reslice<Interp::Nearest> (mask1, output1mask, Adapter::NoTransform, Adapter::AutoOverSample, 0);
      }
      n_voxels = input2.size(0) * input2.size(1) * input2.size(2);
    }

    if (space == 3) {
      DEBUG("average space");
      std::vector<Header> headers;
      headers.push_back (input1.original_header());
      headers.push_back (input2.original_header());
      default_type template_res = 1.0;
      auto padding = Eigen::Matrix<default_type, 4, 1>(0, 0, 0, 1.0);
      std::vector<Eigen::Transform<double, 3, Eigen::Projective>> transform_header_with;

      auto template_header = compute_minimum_average_header<double,Eigen::Transform<double, 3, Eigen::Projective>>(headers, template_res, padding, transform_header_with);
      template_header.set_ndim(input1.ndim());

      output1 = Image<value_type>::scratch(template_header,"-");
      output2 = Image<value_type>::scratch(template_header,"-");
      output1mask = Header::scratch(template_header,"-").get_image<bool>();
      output2mask = Header::scratch(template_header,"-").get_image<bool>();
      {
        LogLevelLatch log_level (0);
        reslice(interp, input1, output1, Adapter::NoTransform, Adapter::AutoOverSample, out_of_bounds_value);
        reslice(interp, input2, output2, Adapter::NoTransform, Adapter::AutoOverSample, out_of_bounds_value);
        if (use_mask1)
          Filter::reslice<Interp::Nearest> (mask1, output1mask, Adapter::NoTransform, Adapter::AutoOverSample, 0);
        if (use_mask2)
          Filter::reslice<Interp::Nearest> (mask2, output2mask, Adapter::NoTransform, Adapter::AutoOverSample, 0);
      }
      n_voxels = output1.size(0) * output1.size(1) * output1.size(2);
    }

    if (use_mask1 or use_mask2)
      n_voxels = 0;
    if (use_mask1 and use_mask2){
      if (dimensions == 3) {
        for (auto i = Loop() (output1, output2, output1mask, output2mask); i ;++i)
          if (output1mask.value() and output2mask.value()){
            ++n_voxels;
            meansquared<value_type>(output1.value(), output2.value(), sos);
          }
      } else { // 4D
        for (auto i = Loop(0, 3) (output1, output2, output1mask, output2mask); i ;++i) {
          if (output1mask.value() and output2mask.value()){
            ++n_voxels;
            meansquared<value_type>(output1.row(3), output2.row(3), sos);
          }
        }
      }
    } else if (use_mask1){
      if (dimensions == 3) {
        for (auto i = Loop() (output1, output2, output1mask); i ;++i)
          if (output1mask.value()){
            ++n_voxels;
            meansquared<value_type>(output1.value(), output2.value(), sos);
          }
      } else { // 4D
        for (auto i = Loop(0, 3) (output1, output2, output1mask); i ;++i) {
          if (output1mask.value()){
            ++n_voxels;
            meansquared<value_type>(output1.row(3), output2.row(3), sos);
          }
        }
      }
    } else if (use_mask2){
      if (dimensions == 3) {
        for (auto i = Loop() (output1, output2, output2mask); i ;++i)
          if (output2mask.value()){
            ++n_voxels;
            meansquared<value_type>(output1.value(), output2.value(), sos);
          }
      } else { // 4D
        for (auto i = Loop(0, 3) (output1, output2, output2mask); i ;++i) {
          if (output2mask.value()){
            ++n_voxels;
            meansquared<value_type>(output1.row(3), output2.row(3), sos);
          }
        }
      }
    } else {
      if (dimensions == 3) {
        for (auto i = Loop() (output1, output2); i ;++i)
          meansquared<value_type>(output1.value(), output2.value(), sos);
      } else { // 4D
        for (auto i = Loop(0, 3) (output1, output2); i ;++i)
          meansquared<value_type>(output1.row(3), output2.row(3), sos);
      }
    }

  }
  DEBUG ("n_voxels:" + str(n_voxels));
  if (n_voxels==0)
    WARN("number of overlapping voxels is zero");

  if (!nonormalisation)
    sos.array() /= static_cast<value_type>(n_voxels);
  std::cout << str(sos.transpose()) << std::endl;

  if (get_options ("overlap").size())
    std::cout << str(n_voxels) << std::endl;
}

<|endoftext|>


<commit_before>#pragma once
/**
	@file
	@brief operator class
	@author MITSUNARI Shigeo(@herumi)
	@license modified new BSD license
	http://opensource.org/licenses/BSD-3-Clause
*/
#include <mcl/op.hpp>
#include <mcl/util.hpp>
#include <mcl/array_iterator.hpp>
#ifdef _MSC_VER
	#ifndef MCL_FORCE_INLINE
		#define MCL_FORCE_INLINE __forceinline
	#endif
	#pragma warning(push)
	#pragma warning(disable : 4714)
#else
	#ifndef MCL_FORCE_INLINE
		#define MCL_FORCE_INLINE __attribute__((always_inline))
	#endif
#endif

namespace mcl { namespace fp {

template<class T>
struct Empty {};

/*
	T must have add, sub, mul, inv, neg
*/
template<class T, class E = Empty<T> >
struct Operator : public E {
	template<class S> MCL_FORCE_INLINE T& operator+=(const S& rhs) { T::add(static_cast<T&>(*this), static_cast<const T&>(*this), rhs); return static_cast<T&>(*this); }
	template<class S> MCL_FORCE_INLINE T& operator-=(const S& rhs) { T::sub(static_cast<T&>(*this), static_cast<const T&>(*this), rhs); return static_cast<T&>(*this); }
	template<class S> friend MCL_FORCE_INLINE T operator+(const T& a, const S& b) { T c; T::add(c, a, b); return c; }
	template<class S> friend MCL_FORCE_INLINE T operator-(const T& a, const S& b) { T c; T::sub(c, a, b); return c; }
	template<class S> MCL_FORCE_INLINE T& operator*=(const S& rhs) { T::mul(static_cast<T&>(*this), static_cast<const T&>(*this), rhs); return static_cast<T&>(*this); }
	template<class S> friend MCL_FORCE_INLINE T operator*(const T& a, const S& b) { T c; T::mul(c, a, b); return c; }
	MCL_FORCE_INLINE T& operator/=(const T& rhs) { T c; T::inv(c, rhs); T::mul(static_cast<T&>(*this), static_cast<const T&>(*this), c); return static_cast<T&>(*this); }
	static MCL_FORCE_INLINE void div(T& c, const T& a, const T& b) { T t; T::inv(t, b); T::mul(c, a, t); }
	friend MCL_FORCE_INLINE T operator/(const T& a, const T& b) { T c; T::inv(c, b); c *= a; return c; }
	MCL_FORCE_INLINE T operator-() const { T c; T::neg(c, static_cast<const T&>(*this)); return c; }
	/*
		powGeneric = pow if T = Fp, Fp2, Fp6
		pow is for GT (use GLV method and unitaryInv)
		powGeneric is for Fp12
	*/
	static void pow(T& z, const T& x, const Unit *y, size_t yn, bool isNegative = false)
	{
		if (powArrayGLV && yn > 1) {
			powArrayGLV(z, x, y, yn, isNegative, false);
			return;
		}
		powArrayBase(z, x, y, yn, isNegative, false);
	}
	template<class tag2, size_t maxBitSize2, template<class _tag, size_t _maxBitSize> class FpT>
	static void pow(T& z, const T& x, const FpT<tag2, maxBitSize2>& y)
	{
		fp::Block b;
		y.getBlock(b);
		pow(z, x, b.p, b.n);
	}
	static void pow(T& z, const T& x, int64_t y)
	{
		const uint64_t u = fp::abs_(y);
#if MCL_SIZEOF_UNIT == 8
		pow(z, x, &u, 1, y < 0);
#else
		uint32_t ua[2] = { uint32_t(u), uint32_t(u >> 32) };
		size_t un = ua[1] ? 2 : 1;
		pow(z, x, ua, un, y < 0);
#endif
	}
	static void pow(T& z, const T& x, const mpz_class& y)
	{
		pow(z, x, gmp::getUnit(y), gmp::getUnitSize(y), y < 0);
	}
	template<class tag2, size_t maxBitSize2, template<class _tag, size_t _maxBitSize> class FpT>
	static void powGeneric(T& z, const T& x, const FpT<tag2, maxBitSize2>& y)
	{
		fp::Block b;
		y.getBlock(b);
		powArrayBase(z, x, b.p, b.n, false, false);
	}
	static void powGeneric(T& z, const T& x, const mpz_class& y)
	{
		powArrayBase(z, x, gmp::getUnit(y), gmp::getUnitSize(y), y < 0, false);
	}
	static void setPowArrayGLV(void f(T& z, const T& x, const Unit *y, size_t yn, bool isNegative, bool constTime), size_t g(T& z, const T *xVec, const mpz_class *yVec, size_t n) = 0)
	{
		powArrayGLV = f;
		powVecNGLV = g;
	}
	static const size_t powVecMaxN = 16;
	template<class tag, size_t maxBitSize, template<class _tag, size_t _maxBitSize>class FpT>
	static void powVec(T& z, const T* xVec, const FpT<tag, maxBitSize> *yVec, size_t n)
	{
		assert(powVecNGLV);
		T r;
		r.setOne();
		const size_t N = mcl::fp::maxMulVecNGLV;
		mpz_class myVec[N];
		while (n > 0) {
			T t;
			size_t tn = fp::min_(n, N);
			for (size_t i = 0; i < tn; i++) {
				bool b;
				yVec[i].getMpz(&b, myVec[i]);
				assert(b); (void)b;
			}
			size_t done = powVecNGLV(t, xVec, myVec, tn);
			r *= t;
			xVec += done;
			yVec += done;
			n -= done;
		}
		z = r;
	}
private:
	static void (*powArrayGLV)(T& z, const T& x, const Unit *y, size_t yn, bool isNegative, bool constTime);
	static size_t (*powVecNGLV)(T& z, const T* xVec, const mpz_class *yVec, size_t n);
	static void powArrayBase(T& z, const T& x, const Unit *y, size_t yn, bool isNegative, bool)
	{
		while (yn > 0 && y[yn - 1] == 0) {
			yn--;
		}
		if (yn == 0 || (yn == 1 && y[0] == 1)) {
			z = 1;
			return;
		}
		const size_t w = 4;
		const size_t N = 1 << w;
		uint8_t idxTbl[sizeof(T) * 8 / w];
		mcl::fp::ArrayIterator<Unit> iter(y, sizeof(Unit) * 8 * yn, w);
		size_t idxN = 0;
		while (iter.hasNext()) {
			assert(idxN < sizeof(idxTbl));
			idxTbl[idxN++] = iter.getNext();
		}
		assert(idxN > 0);
		T tbl[N];
		tbl[1] = x;
		for (size_t i = 2; i < N; i++) {
			tbl[i] = tbl[i-1] * x;
		}
		uint32_t idx = idxTbl[idxN - 1];
		z = idx == 0 ? 1 : tbl[idx];
		for (size_t i = 1; i < idxN; i++) {
			for (size_t j = 0; j < w; j++) {
				T::sqr(z, z);
			}
			idx = idxTbl[idxN - 1 - i];
			if (idx) {
				z *= tbl[idx];
			}
		}
		if (isNegative) {
			T::inv(z, z);
		}
	}
};

template<class T, class E>
void (*Operator<T, E>::powArrayGLV)(T& z, const T& x, const Unit *y, size_t yn, bool isNegative, bool constTime);

template<class T, class E>
size_t (*Operator<T, E>::powVecNGLV)(T& z, const T* xVec, const mpz_class *yVec, size_t n);

/*
	T must have save and load
*/
template<class T, class E = Empty<T> >
struct Serializable : public E {
	// may set *pb = false if the size of str is too large.
	void setStr(bool *pb, const char *str, int ioMode = 0)
	{
		size_t len = strlen(str);
		size_t n = deserialize(str, len, ioMode);
		*pb = n > 0 && n == len;
	}
	// return strlen(buf) if success else 0
	size_t getStr(char *buf, size_t maxBufSize, int ioMode = 0) const
	{
		size_t n = serialize(buf, maxBufSize, ioMode);
		if (n == 0 || n == maxBufSize - 1) return 0;
		buf[n] = '\0';
		return n;
	}
#ifndef CYBOZU_DONT_USE_STRING
	// may throw exception if the size of str is too large.
	void setStr(const std::string& str, int ioMode = 0)
	{
		cybozu::StringInputStream is(str);
		static_cast<T&>(*this).load(is, ioMode);
	}
	void getStr(std::string& str, int ioMode = 0) const
	{
		str.clear();
		cybozu::StringOutputStream os(str);
		static_cast<const T&>(*this).save(os, ioMode);
	}
	std::string getStr(int ioMode = 0) const
	{
		std::string str;
		getStr(str, ioMode);
		return str;
	}
	std::string serializeToHexStr() const
	{
		std::string str(sizeof(T) * 2, 0);
		size_t n = serialize(&str[0], str.size(), IoSerializeHexStr);
		str.resize(n);
		return str;
	}
#ifndef CYBOZU_DONT_USE_EXCEPTION
	void deserializeHexStr(const std::string& str)
	{
		size_t n = deserialize(str.c_str(), str.size(), IoSerializeHexStr);
		if (n == 0) throw cybozu::Exception("bad str") << str;
	}
#endif
#endif
	// return written bytes
	size_t serialize(void *buf, size_t maxBufSize, int ioMode = IoSerialize) const
	{
		cybozu::MemoryOutputStream os(buf, maxBufSize);
		bool b;
		static_cast<const T&>(*this).save(&b, os, ioMode);
		return b ? os.getPos() : 0;
	}
	// return read bytes
	size_t deserialize(const void *buf, size_t bufSize, int ioMode = IoSerialize)
	{
		cybozu::MemoryInputStream is(buf, bufSize);
		bool b;
		static_cast<T&>(*this).load(&b, is, ioMode);
		return b ? is.getPos() : 0;
	}
};

} } // mcl::fp

<commit_msg>fix condition of zero<commit_after>#pragma once
/**
	@file
	@brief operator class
	@author MITSUNARI Shigeo(@herumi)
	@license modified new BSD license
	http://opensource.org/licenses/BSD-3-Clause
*/
#include <mcl/op.hpp>
#include <mcl/util.hpp>
#include <mcl/array_iterator.hpp>
#ifdef _MSC_VER
	#ifndef MCL_FORCE_INLINE
		#define MCL_FORCE_INLINE __forceinline
	#endif
	#pragma warning(push)
	#pragma warning(disable : 4714)
#else
	#ifndef MCL_FORCE_INLINE
		#define MCL_FORCE_INLINE __attribute__((always_inline))
	#endif
#endif

namespace mcl { namespace fp {

template<class T>
struct Empty {};

/*
	T must have add, sub, mul, inv, neg
*/
template<class T, class E = Empty<T> >
struct Operator : public E {
	template<class S> MCL_FORCE_INLINE T& operator+=(const S& rhs) { T::add(static_cast<T&>(*this), static_cast<const T&>(*this), rhs); return static_cast<T&>(*this); }
	template<class S> MCL_FORCE_INLINE T& operator-=(const S& rhs) { T::sub(static_cast<T&>(*this), static_cast<const T&>(*this), rhs); return static_cast<T&>(*this); }
	template<class S> friend MCL_FORCE_INLINE T operator+(const T& a, const S& b) { T c; T::add(c, a, b); return c; }
	template<class S> friend MCL_FORCE_INLINE T operator-(const T& a, const S& b) { T c; T::sub(c, a, b); return c; }
	template<class S> MCL_FORCE_INLINE T& operator*=(const S& rhs) { T::mul(static_cast<T&>(*this), static_cast<const T&>(*this), rhs); return static_cast<T&>(*this); }
	template<class S> friend MCL_FORCE_INLINE T operator*(const T& a, const S& b) { T c; T::mul(c, a, b); return c; }
	MCL_FORCE_INLINE T& operator/=(const T& rhs) { T c; T::inv(c, rhs); T::mul(static_cast<T&>(*this), static_cast<const T&>(*this), c); return static_cast<T&>(*this); }
	static MCL_FORCE_INLINE void div(T& c, const T& a, const T& b) { T t; T::inv(t, b); T::mul(c, a, t); }
	friend MCL_FORCE_INLINE T operator/(const T& a, const T& b) { T c; T::inv(c, b); c *= a; return c; }
	MCL_FORCE_INLINE T operator-() const { T c; T::neg(c, static_cast<const T&>(*this)); return c; }
	/*
		powGeneric = pow if T = Fp, Fp2, Fp6
		pow is for GT (use GLV method and unitaryInv)
		powGeneric is for Fp12
	*/
	static void pow(T& z, const T& x, const Unit *y, size_t yn, bool isNegative = false)
	{
		if (powArrayGLV && yn > 1) {
			powArrayGLV(z, x, y, yn, isNegative, false);
			return;
		}
		powArrayBase(z, x, y, yn, isNegative, false);
	}
	template<class tag2, size_t maxBitSize2, template<class _tag, size_t _maxBitSize> class FpT>
	static void pow(T& z, const T& x, const FpT<tag2, maxBitSize2>& y)
	{
		fp::Block b;
		y.getBlock(b);
		pow(z, x, b.p, b.n);
	}
	static void pow(T& z, const T& x, int64_t y)
	{
		const uint64_t u = fp::abs_(y);
#if MCL_SIZEOF_UNIT == 8
		pow(z, x, &u, 1, y < 0);
#else
		uint32_t ua[2] = { uint32_t(u), uint32_t(u >> 32) };
		size_t un = ua[1] ? 2 : 1;
		pow(z, x, ua, un, y < 0);
#endif
	}
	static void pow(T& z, const T& x, const mpz_class& y)
	{
		pow(z, x, gmp::getUnit(y), gmp::getUnitSize(y), y < 0);
	}
	template<class tag2, size_t maxBitSize2, template<class _tag, size_t _maxBitSize> class FpT>
	static void powGeneric(T& z, const T& x, const FpT<tag2, maxBitSize2>& y)
	{
		fp::Block b;
		y.getBlock(b);
		powArrayBase(z, x, b.p, b.n, false, false);
	}
	static void powGeneric(T& z, const T& x, const mpz_class& y)
	{
		powArrayBase(z, x, gmp::getUnit(y), gmp::getUnitSize(y), y < 0, false);
	}
	static void setPowArrayGLV(void f(T& z, const T& x, const Unit *y, size_t yn, bool isNegative, bool constTime), size_t g(T& z, const T *xVec, const mpz_class *yVec, size_t n) = 0)
	{
		powArrayGLV = f;
		powVecNGLV = g;
	}
	static const size_t powVecMaxN = 16;
	template<class tag, size_t maxBitSize, template<class _tag, size_t _maxBitSize>class FpT>
	static void powVec(T& z, const T* xVec, const FpT<tag, maxBitSize> *yVec, size_t n)
	{
		assert(powVecNGLV);
		T r;
		r.setOne();
		const size_t N = mcl::fp::maxMulVecNGLV;
		mpz_class myVec[N];
		while (n > 0) {
			T t;
			size_t tn = fp::min_(n, N);
			for (size_t i = 0; i < tn; i++) {
				bool b;
				yVec[i].getMpz(&b, myVec[i]);
				assert(b); (void)b;
			}
			size_t done = powVecNGLV(t, xVec, myVec, tn);
			r *= t;
			xVec += done;
			yVec += done;
			n -= done;
		}
		z = r;
	}
private:
	static void (*powArrayGLV)(T& z, const T& x, const Unit *y, size_t yn, bool isNegative, bool constTime);
	static size_t (*powVecNGLV)(T& z, const T* xVec, const mpz_class *yVec, size_t n);
	static void powArrayBase(T& z, const T& x, const Unit *y, size_t yn, bool isNegative, bool)
	{
		while (yn > 0 && y[yn - 1] == 0) {
			yn--;
		}
		if (yn == 0) {
			z = 1;
			return;
		}
		const size_t w = 4;
		const size_t N = 1 << w;
		uint8_t idxTbl[sizeof(T) * 8 / w];
		mcl::fp::ArrayIterator<Unit> iter(y, sizeof(Unit) * 8 * yn, w);
		size_t idxN = 0;
		while (iter.hasNext()) {
			assert(idxN < sizeof(idxTbl));
			idxTbl[idxN++] = iter.getNext();
		}
		assert(idxN > 0);
		T tbl[N];
		tbl[1] = x;
		for (size_t i = 2; i < N; i++) {
			tbl[i] = tbl[i-1] * x;
		}
		uint32_t idx = idxTbl[idxN - 1];
		z = idx == 0 ? 1 : tbl[idx];
		for (size_t i = 1; i < idxN; i++) {
			for (size_t j = 0; j < w; j++) {
				T::sqr(z, z);
			}
			idx = idxTbl[idxN - 1 - i];
			if (idx) {
				z *= tbl[idx];
			}
		}
		if (isNegative) {
			T::inv(z, z);
		}
	}
};

template<class T, class E>
void (*Operator<T, E>::powArrayGLV)(T& z, const T& x, const Unit *y, size_t yn, bool isNegative, bool constTime);

template<class T, class E>
size_t (*Operator<T, E>::powVecNGLV)(T& z, const T* xVec, const mpz_class *yVec, size_t n);

/*
	T must have save and load
*/
template<class T, class E = Empty<T> >
struct Serializable : public E {
	// may set *pb = false if the size of str is too large.
	void setStr(bool *pb, const char *str, int ioMode = 0)
	{
		size_t len = strlen(str);
		size_t n = deserialize(str, len, ioMode);
		*pb = n > 0 && n == len;
	}
	// return strlen(buf) if success else 0
	size_t getStr(char *buf, size_t maxBufSize, int ioMode = 0) const
	{
		size_t n = serialize(buf, maxBufSize, ioMode);
		if (n == 0 || n == maxBufSize - 1) return 0;
		buf[n] = '\0';
		return n;
	}
#ifndef CYBOZU_DONT_USE_STRING
	// may throw exception if the size of str is too large.
	void setStr(const std::string& str, int ioMode = 0)
	{
		cybozu::StringInputStream is(str);
		static_cast<T&>(*this).load(is, ioMode);
	}
	void getStr(std::string& str, int ioMode = 0) const
	{
		str.clear();
		cybozu::StringOutputStream os(str);
		static_cast<const T&>(*this).save(os, ioMode);
	}
	std::string getStr(int ioMode = 0) const
	{
		std::string str;
		getStr(str, ioMode);
		return str;
	}
	std::string serializeToHexStr() const
	{
		std::string str(sizeof(T) * 2, 0);
		size_t n = serialize(&str[0], str.size(), IoSerializeHexStr);
		str.resize(n);
		return str;
	}
#ifndef CYBOZU_DONT_USE_EXCEPTION
	void deserializeHexStr(const std::string& str)
	{
		size_t n = deserialize(str.c_str(), str.size(), IoSerializeHexStr);
		if (n == 0) throw cybozu::Exception("bad str") << str;
	}
#endif
#endif
	// return written bytes
	size_t serialize(void *buf, size_t maxBufSize, int ioMode = IoSerialize) const
	{
		cybozu::MemoryOutputStream os(buf, maxBufSize);
		bool b;
		static_cast<const T&>(*this).save(&b, os, ioMode);
		return b ? os.getPos() : 0;
	}
	// return read bytes
	size_t deserialize(const void *buf, size_t bufSize, int ioMode = IoSerialize)
	{
		cybozu::MemoryInputStream is(buf, bufSize);
		bool b;
		static_cast<T&>(*this).load(&b, is, ioMode);
		return b ? is.getPos() : 0;
	}
};

} } // mcl::fp

<|endoftext|>


<commit_before>/*
 * Copyright 2004-2015 Cray Inc.
 * Other additional copyright holders may be indicated within.
 *
 * The entirety of this work is 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.
 */

// checkParsed.cpp

#include "passes.h"

#include "stmt.h"
#include "expr.h"
#include "astutil.h"
#include "stlUtil.h"

static void checkNamedArguments(CallExpr* call);
static void checkPrivateDecls(DefExpr* def);
static void checkParsedVar(VarSymbol* var);
static void checkFunction(FnSymbol* fn);
static void checkExportedNames();
static void checkModule(ModuleSymbol* mod);

void
checkParsed() {
  forv_Vec(CallExpr, call, gCallExprs) {
    checkNamedArguments(call);
  }

  forv_Vec(DefExpr, def, gDefExprs) {
    if (toVarSymbol(def->sym))
      // The test for FLAG_TEMP allows compiler-generated (temporary) variables
      // to be declared without an explicit type or initializer expression.
      if ((!def->init || def->init->isNoInitExpr())
          && !def->exprType && !def->sym->hasFlag(FLAG_TEMP))
        if (isBlockStmt(def->parentExpr) && !isArgSymbol(def->parentSymbol))
          if (def->parentExpr != rootModule->block)
            if (!def->sym->hasFlag(FLAG_INDEX_VAR))
              USR_FATAL_CONT(def->sym,
                             "Variable '%s' is not initialized or has no type",
                             def->sym->name);

    checkPrivateDecls(def);
  }

  forv_Vec(VarSymbol, var, gVarSymbols) {
    checkParsedVar(var);
  }

  forv_Vec(FnSymbol, fn, gFnSymbols) {
    checkFunction(fn);
  }

  forv_Vec(ModuleSymbol, mod, gModuleSymbols) {
    checkModule(mod);
  }

  checkExportedNames();
}

static void
checkNamedArguments(CallExpr* call) {
  Vec<const char*> names;

  for_actuals(expr, call) {
    if (NamedExpr* named = toNamedExpr(expr)) {
      forv_Vec(const char, name, names) {
        if (!strcmp(name, named->name))
          USR_FATAL_CONT(named,
                         "The named argument '%s' is used more "
                         "than once in the same function call.",
                         name);
      }

      names.add(named->name);
    }
  }
}

static void checkPrivateDecls(DefExpr* def) {
  if (def->sym->hasFlag(FLAG_PRIVATE)) {
    // The symbol has been declared private.
    if (def->parentSymbol) {
      if (toFnSymbol(def->parentSymbol)) {
        // The parent symbol of this definition is a FnSymbol.  Private
        // symbols at the function scope are meaningless because there is no
        // way for anything outside the function to access its locals, so warn
        // the user.
        USR_WARN(def,
                 "Private declarations within function bodies are meaningless");
        // Don't want to waste time treating this symbol as if it could be
        // accessed from an outer scope, so remove the flag.
        def->sym->removeFlag(FLAG_PRIVATE);
      } else if (ModuleSymbol *mod = toModuleSymbol(def->parentSymbol)) {
        // The parent symbol is a module symbol.  Could still be invalid.
        if (mod->block != def->parentExpr) {
          // The block in which we are defined is not the top level module
          // block.  Private symbols at this scope are meaningless, so warn
          // the user.
          USR_WARN(def,
                   "Private declarations within nested blocks are meaningless");
          def->sym->removeFlag(FLAG_PRIVATE);
        }
      } else if (TypeSymbol *t = toTypeSymbol(def->parentSymbol)) {
        if (toAggregateType(t->type)) {
          USR_FATAL_CONT(def, "Can't apply private to the fields or methods of a class or record yet");
          // Private fields and methods require further discussion before they
          // are implemented.
        }
      }
    }
  }
}

static void
checkParsedVar(VarSymbol* var) {
  if (var->isParameter() && !var->immediate)
    if (!var->defPoint->init &&
        (toFnSymbol(var->defPoint->parentSymbol) ||
         toModuleSymbol(var->defPoint->parentSymbol)))
      USR_FATAL_CONT(var, "Top-level params must be initialized.");

  if (var->defPoint->init && var->defPoint->init->isNoInitExpr()) {
    if (var->hasFlag(FLAG_CONST))
      USR_FATAL_CONT(var, "const variables specified with noinit must be explicitly initialized.");
  }

  //
  // Verify that config variables are only at Module scope i.e. it is
  // an error if any config variable is not an immediate child of a
  // module

  if (var->hasFlag(FLAG_CONFIG) &&
      isModuleSymbol(var->defPoint->parentSymbol) == false) {
    const char* varType = NULL;

    if (var->hasFlag(FLAG_PARAM))
      varType = "parameters";
    else if (var->hasFlag(FLAG_CONST))
      varType = "constants";
    else
      varType = "variables";

    USR_FATAL_CONT(var->defPoint,
                   "Configuration %s are allowed only at module scope.", varType);
  }
}

static void
checkFunction(FnSymbol* fn) {
  // Ensure that the lhs of "=" and "<op>=" is passed by ref.
  if (fn->hasFlag(FLAG_ASSIGNOP))
    if (fn->getFormal(1)->intent != INTENT_REF)
      USR_WARN(fn, "The left operand of '=' and '<op>=' should have 'ref' intent.");

  if (!strcmp(fn->name, "this") && fn->hasFlag(FLAG_NO_PARENS))
    USR_FATAL_CONT(fn, "method 'this' must have parentheses");

  if (!strcmp(fn->name, "these") && fn->hasFlag(FLAG_NO_PARENS))
    USR_FATAL_CONT(fn, "method 'these' must have parentheses");

  if (fn->retTag == RET_PARAM && fn->retExprType != NULL)
    USR_WARN(fn, "providing an explicit return type on a 'param' function currently leads to incorrect results; as a workaround, remove the return type specification in function '%s'", fn->name);

  std::vector<CallExpr*> calls;
  collectMyCallExprs(fn, calls, fn);
  bool isIterator = fn->isIterator();
  bool notInAFunction = !isIterator && (fn->getModule()->initFn == fn);
  int numVoidReturns = 0, numNonVoidReturns = 0, numYields = 0;

  for_vector(CallExpr, call, calls) {
    if (call->isPrimitive(PRIM_RETURN)) {
      if (notInAFunction)
        USR_FATAL_CONT(call, "return statement is not in a function or iterator");
      else {
        SymExpr* sym = toSymExpr(call->get(1));
        if (sym && sym->var == gVoid)
          numVoidReturns++;
        else {
          if (isIterator)
            USR_FATAL_CONT(call, "returning a value in an iterator");
          else
            numNonVoidReturns++;
        }
      }
    }
    else if (call->isPrimitive(PRIM_YIELD)) {
      if (notInAFunction)
        USR_FATAL_CONT(call, "yield statement is outside an iterator");
      else if (!isIterator)
        USR_FATAL_CONT(call, "yield statement is in a non-iterator function");
      else
        numYields++;
    }
  }

  if (numVoidReturns != 0 && numNonVoidReturns != 0)
    USR_FATAL_CONT(fn, "Not all returns in this function return a value");
  if (isIterator && numYields == 0)
    USR_FATAL_CONT(fn, "iterator does not yield a value");
  if (!isIterator &&
      fn->retTag == RET_REF &&
      numNonVoidReturns == 0) {
    USR_FATAL_CONT(fn, "function declared 'var' but does not return anything");
  }
}

//
// This is a special test to ensure that there are no instances of a return
// or yield statement at the top level of a module.  This "special" semantic
// check is needed to resolve 4 test applications that failed once the
// insertion of a Module init function was moved to a later pass.
//
// Those tests have historically relied on the matching call in checkFunction
// which was executed when scanning the module initFunction.
//
// This is probably a good anchor for a family of tests of this form.
//

static void
checkModule(ModuleSymbol* mod) {
  for_alist(stmt, mod->block->body) {
    if (CallExpr* call = toCallExpr(stmt)) {
      if (call->isPrimitive(PRIM_RETURN)) {
        USR_FATAL_CONT(call, "return statement is not in a function or iterator");

      } else if (call->isPrimitive(PRIM_YIELD)) {
        USR_FATAL_CONT(call, "yield statement is outside an iterator");
      }
    }
  }
}

static void
checkExportedNames()
{
  // The right side of the map is a dummy Boolean.
  // We are just using the map to implement a set.
  HashMap<const char*, StringHashFns, bool> names;
  forv_Vec(FnSymbol, fn, gFnSymbols) {
    if (!fn->hasFlag(FLAG_EXPORT))
      continue;

    const char* name = fn->cname;
    if (names.get(name))
      USR_FATAL_CONT(fn, "The name %s cannot be exported twice from the same compilation unit.", name);
    names.put(name, true);
  }
}

<commit_msg>Fix errors with warning message insertion for invalid private declarations<commit_after>/*
 * Copyright 2004-2015 Cray Inc.
 * Other additional copyright holders may be indicated within.
 *
 * The entirety of this work is 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.
 */

// checkParsed.cpp

#include "passes.h"

#include "stmt.h"
#include "expr.h"
#include "astutil.h"
#include "stlUtil.h"

static void checkNamedArguments(CallExpr* call);
static void checkPrivateDecls(DefExpr* def);
static void checkParsedVar(VarSymbol* var);
static void checkFunction(FnSymbol* fn);
static void checkExportedNames();
static void checkModule(ModuleSymbol* mod);

void
checkParsed() {
  forv_Vec(CallExpr, call, gCallExprs) {
    checkNamedArguments(call);
  }

  forv_Vec(DefExpr, def, gDefExprs) {
    if (toVarSymbol(def->sym))
      // The test for FLAG_TEMP allows compiler-generated (temporary) variables
      // to be declared without an explicit type or initializer expression.
      if ((!def->init || def->init->isNoInitExpr())
          && !def->exprType && !def->sym->hasFlag(FLAG_TEMP))
        if (isBlockStmt(def->parentExpr) && !isArgSymbol(def->parentSymbol))
          if (def->parentExpr != rootModule->block)
            if (!def->sym->hasFlag(FLAG_INDEX_VAR))
              USR_FATAL_CONT(def->sym,
                             "Variable '%s' is not initialized or has no type",
                             def->sym->name);

    checkPrivateDecls(def);
  }

  forv_Vec(VarSymbol, var, gVarSymbols) {
    checkParsedVar(var);
  }

  forv_Vec(FnSymbol, fn, gFnSymbols) {
    checkFunction(fn);
  }

  forv_Vec(ModuleSymbol, mod, gModuleSymbols) {
    checkModule(mod);
  }

  checkExportedNames();
}

static void
checkNamedArguments(CallExpr* call) {
  Vec<const char*> names;

  for_actuals(expr, call) {
    if (NamedExpr* named = toNamedExpr(expr)) {
      forv_Vec(const char, name, names) {
        if (!strcmp(name, named->name))
          USR_FATAL_CONT(named,
                         "The named argument '%s' is used more "
                         "than once in the same function call.",
                         name);
      }

      names.add(named->name);
    }
  }
}

static void checkPrivateDecls(DefExpr* def) {
  if (def->sym->hasFlag(FLAG_PRIVATE)) {
    // The symbol has been declared private.
    if (def->parentSymbol) {
      if (toFnSymbol(def->parentSymbol)) {
        // The parent symbol of this definition is a FnSymbol.  Private
        // symbols at the function scope are meaningless because there is no
        // way for anything outside the function to access its locals, so warn
        // the user.
        USR_WARN(def,
                 "Private declarations within function bodies are meaningless");
        // Don't want to waste time treating this symbol as if it could be
        // accessed from an outer scope, so remove the flag.
        def->sym->removeFlag(FLAG_PRIVATE);
      } else if (ModuleSymbol *mod = toModuleSymbol(def->parentSymbol)) {
        // The parent symbol is a module symbol.  Could still be invalid.
        if (mod->block != def->parentExpr) {
          if (BlockStmt* block = toBlockStmt(def->parentExpr)) {
            // Scopeless blocks are used to define multiple symbols, for
            // instance.  Those are valid "nested" blocks for private symbols.
            if (block->blockTag != BLOCK_SCOPELESS) {
              // The block in which we are defined is not the top level module
              // block.  Private symbols at this scope are meaningless, so warn
              // the user.
              USR_WARN(def,
                       "Private declarations within nested blocks are meaningless");
              def->sym->removeFlag(FLAG_PRIVATE);
            }
          } else {
            // There are many situations which could lead to this else branch.
            // Most of them will not reach here due to being banned at parse
            // time.  However, those that aren't excluded by syntax errors will
            // be caught here.
            USR_WARN(def, "Private declarations are meaningless outside of module level declarations");
            def->sym->removeFlag(FLAG_PRIVATE);
          }
        }
      } else if (TypeSymbol *t = toTypeSymbol(def->parentSymbol)) {
        if (toAggregateType(t->type)) {
          USR_FATAL_CONT(def, "Can't apply private to the fields or methods of a class or record yet");
          // Private fields and methods require further discussion before they
          // are implemented.
        }
      }
    }
  }
}

static void
checkParsedVar(VarSymbol* var) {
  if (var->isParameter() && !var->immediate)
    if (!var->defPoint->init &&
        (toFnSymbol(var->defPoint->parentSymbol) ||
         toModuleSymbol(var->defPoint->parentSymbol)))
      USR_FATAL_CONT(var, "Top-level params must be initialized.");

  if (var->defPoint->init && var->defPoint->init->isNoInitExpr()) {
    if (var->hasFlag(FLAG_CONST))
      USR_FATAL_CONT(var, "const variables specified with noinit must be explicitly initialized.");
  }

  //
  // Verify that config variables are only at Module scope i.e. it is
  // an error if any config variable is not an immediate child of a
  // module

  if (var->hasFlag(FLAG_CONFIG) &&
      isModuleSymbol(var->defPoint->parentSymbol) == false) {
    const char* varType = NULL;

    if (var->hasFlag(FLAG_PARAM))
      varType = "parameters";
    else if (var->hasFlag(FLAG_CONST))
      varType = "constants";
    else
      varType = "variables";

    USR_FATAL_CONT(var->defPoint,
                   "Configuration %s are allowed only at module scope.", varType);
  }
}

static void
checkFunction(FnSymbol* fn) {
  // Ensure that the lhs of "=" and "<op>=" is passed by ref.
  if (fn->hasFlag(FLAG_ASSIGNOP))
    if (fn->getFormal(1)->intent != INTENT_REF)
      USR_WARN(fn, "The left operand of '=' and '<op>=' should have 'ref' intent.");

  if (!strcmp(fn->name, "this") && fn->hasFlag(FLAG_NO_PARENS))
    USR_FATAL_CONT(fn, "method 'this' must have parentheses");

  if (!strcmp(fn->name, "these") && fn->hasFlag(FLAG_NO_PARENS))
    USR_FATAL_CONT(fn, "method 'these' must have parentheses");

  if (fn->retTag == RET_PARAM && fn->retExprType != NULL)
    USR_WARN(fn, "providing an explicit return type on a 'param' function currently leads to incorrect results; as a workaround, remove the return type specification in function '%s'", fn->name);

  std::vector<CallExpr*> calls;
  collectMyCallExprs(fn, calls, fn);
  bool isIterator = fn->isIterator();
  bool notInAFunction = !isIterator && (fn->getModule()->initFn == fn);
  int numVoidReturns = 0, numNonVoidReturns = 0, numYields = 0;

  for_vector(CallExpr, call, calls) {
    if (call->isPrimitive(PRIM_RETURN)) {
      if (notInAFunction)
        USR_FATAL_CONT(call, "return statement is not in a function or iterator");
      else {
        SymExpr* sym = toSymExpr(call->get(1));
        if (sym && sym->var == gVoid)
          numVoidReturns++;
        else {
          if (isIterator)
            USR_FATAL_CONT(call, "returning a value in an iterator");
          else
            numNonVoidReturns++;
        }
      }
    }
    else if (call->isPrimitive(PRIM_YIELD)) {
      if (notInAFunction)
        USR_FATAL_CONT(call, "yield statement is outside an iterator");
      else if (!isIterator)
        USR_FATAL_CONT(call, "yield statement is in a non-iterator function");
      else
        numYields++;
    }
  }

  if (numVoidReturns != 0 && numNonVoidReturns != 0)
    USR_FATAL_CONT(fn, "Not all returns in this function return a value");
  if (isIterator && numYields == 0)
    USR_FATAL_CONT(fn, "iterator does not yield a value");
  if (!isIterator &&
      fn->retTag == RET_REF &&
      numNonVoidReturns == 0) {
    USR_FATAL_CONT(fn, "function declared 'var' but does not return anything");
  }
}

//
// This is a special test to ensure that there are no instances of a return
// or yield statement at the top level of a module.  This "special" semantic
// check is needed to resolve 4 test applications that failed once the
// insertion of a Module init function was moved to a later pass.
//
// Those tests have historically relied on the matching call in checkFunction
// which was executed when scanning the module initFunction.
//
// This is probably a good anchor for a family of tests of this form.
//

static void
checkModule(ModuleSymbol* mod) {
  for_alist(stmt, mod->block->body) {
    if (CallExpr* call = toCallExpr(stmt)) {
      if (call->isPrimitive(PRIM_RETURN)) {
        USR_FATAL_CONT(call, "return statement is not in a function or iterator");

      } else if (call->isPrimitive(PRIM_YIELD)) {
        USR_FATAL_CONT(call, "yield statement is outside an iterator");
      }
    }
  }
}

static void
checkExportedNames()
{
  // The right side of the map is a dummy Boolean.
  // We are just using the map to implement a set.
  HashMap<const char*, StringHashFns, bool> names;
  forv_Vec(FnSymbol, fn, gFnSymbols) {
    if (!fn->hasFlag(FLAG_EXPORT))
      continue;

    const char* name = fn->cname;
    if (names.get(name))
      USR_FATAL_CONT(fn, "The name %s cannot be exported twice from the same compilation unit.", name);
    names.put(name, true);
  }
}

<|endoftext|>


<commit_before>// Catch
#include <catch.hpp>

// C++ Standard Library
#include <cstdlib>
#include <cmath>

// Armadillo
#include <armadillo>

// HOP
#include <hop>

TEST_CASE("Random rotation matrix", "") {
  SECTION("2-dimensional rotation") {
    arma::Col<double>::fixed<10000> angles;
    for (std::size_t n = 0; n < angles.n_elem; ++n) {
      arma::Col<double>::fixed<2> rotatedUnitVector = hop::getRandomRotationMatrix(2) * arma::Col<double>::fixed<2>({-2, 1});
      angles.at(n) = std::atan2(rotatedUnitVector.at(1), rotatedUnitVector.at(0));
    }

    arma::Col<arma::uword> histogram = arma::hist(angles);
    CHECK(0.05 > static_cast<double>(histogram.max() - histogram.min()) / angles.n_elem);
  }

  SECTION("3-dimensional rotation") {
    arma::Col<double>::fixed<10000> rollAngles;
    arma::Col<double>::fixed<10000> pitchAngles;
    arma::Col<double>::fixed<10000> yawAngles;
    for (std::size_t n = 0; n < rollAngles.n_elem; ++n) {
      arma::Col<double>::fixed<3> rotatedUnitVector = hop::getRandomRotationMatrix(3) * arma::Col<double>::fixed<3>({-2, 1, 3});
      rollAngles.at(n) = std::atan2(rotatedUnitVector.at(1), rotatedUnitVector.at(0));
      pitchAngles.at(n) = std::atan2(rotatedUnitVector.at(2), rotatedUnitVector.at(1));
      yawAngles.at(n) = std::atan2(rotatedUnitVector.at(0), rotatedUnitVector.at(2));
    }

    arma::Col<arma::uword> histogram;

    histogram = arma::hist(rollAngles);
    CHECK(0.02 > static_cast<double>(histogram.max() - histogram.min()) / rollAngles.n_elem);

    histogram = arma::hist(pitchAngles);
    CHECK(0.02 > static_cast<double>(histogram.max() - histogram.min()) / pitchAngles.n_elem);

    histogram = arma::hist(yawAngles);
    CHECK(0.02 > static_cast<double>(histogram.max() - histogram.min()) / yawAngles.n_elem);
  }
}

TEST_CASE("Random permutation", "") {
  SECTION("Full permutation") {
    arma::Mat<arma::uword>::fixed<10, 10000> permutations;
    for (std::size_t n = 0; n < permutations.n_cols; ++n) {
      permutations.col(n) = hop::getRandomPermutation(permutations.n_rows);
    }

    arma::Col<arma::uword> centers(permutations.n_rows);
    for (std::size_t n = 0; n < permutations.n_rows; ++n) {
      centers.at(n) = n;
    }

    for (std::size_t n = 0; n < permutations.n_rows; ++n) {
      arma::Row<arma::uword> histogram = arma::hist(permutations.row(n), centers);
      CHECK(0.05 > static_cast<double>(histogram.max() - histogram.min()) / permutations.n_cols);
    }
  }

  SECTION("Partial permutation") {
    arma::Mat<arma::uword>::fixed<10, 20000> permutations;
    for (std::size_t n = 0; n < permutations.n_cols; ++n) {
      permutations.col(n) = hop::getRandomPermutation(permutations.n_rows + 1, permutations.n_rows);
    }

    arma::Col<arma::uword> centers(permutations.n_rows);
    for (std::size_t n = 0; n < permutations.n_rows; ++n) {
      centers.at(n) = n;
    }

    for (std::size_t n = 0; n < permutations.n_rows; ++n) {
      arma::Row<arma::uword> histogram = arma::hist(permutations.row(n), centers);
      CHECK(0.1 > static_cast<double>(histogram.max() - histogram.min()) / permutations.n_cols);
    }
  }
}

<commit_msg>test: Increased sample size for permutation tests<commit_after>// Catch
#include <catch.hpp>

// C++ Standard Library
#include <cstdlib>
#include <cmath>

// Armadillo
#include <armadillo>

// HOP
#include <hop>

TEST_CASE("Random rotation matrix", "") {
  SECTION("2-dimensional rotation") {
    arma::Col<double>::fixed<10000> angles;
    for (std::size_t n = 0; n < angles.n_elem; ++n) {
      arma::Col<double>::fixed<2> rotatedUnitVector = hop::getRandomRotationMatrix(2) * arma::Col<double>::fixed<2>({-2, 1});
      angles.at(n) = std::atan2(rotatedUnitVector.at(1), rotatedUnitVector.at(0));
    }

    arma::Col<arma::uword> histogram = arma::hist(angles);
    CHECK(0.05 > static_cast<double>(histogram.max() - histogram.min()) / angles.n_elem);
  }

  SECTION("3-dimensional rotation") {
    arma::Col<double>::fixed<10000> rollAngles;
    arma::Col<double>::fixed<10000> pitchAngles;
    arma::Col<double>::fixed<10000> yawAngles;
    for (std::size_t n = 0; n < rollAngles.n_elem; ++n) {
      arma::Col<double>::fixed<3> rotatedUnitVector = hop::getRandomRotationMatrix(3) * arma::Col<double>::fixed<3>({-2, 1, 3});
      rollAngles.at(n) = std::atan2(rotatedUnitVector.at(1), rotatedUnitVector.at(0));
      pitchAngles.at(n) = std::atan2(rotatedUnitVector.at(2), rotatedUnitVector.at(1));
      yawAngles.at(n) = std::atan2(rotatedUnitVector.at(0), rotatedUnitVector.at(2));
    }

    arma::Col<arma::uword> histogram;

    histogram = arma::hist(rollAngles);
    CHECK(0.02 > static_cast<double>(histogram.max() - histogram.min()) / rollAngles.n_elem);

    histogram = arma::hist(pitchAngles);
    CHECK(0.02 > static_cast<double>(histogram.max() - histogram.min()) / pitchAngles.n_elem);

    histogram = arma::hist(yawAngles);
    CHECK(0.02 > static_cast<double>(histogram.max() - histogram.min()) / yawAngles.n_elem);
  }
}

TEST_CASE("Random permutation", "") {
  SECTION("Full permutation") {
    arma::Mat<arma::uword>::fixed<10, 10000> permutations;
    for (std::size_t n = 0; n < permutations.n_cols; ++n) {
      permutations.col(n) = hop::getRandomPermutation(permutations.n_rows);
    }

    arma::Col<arma::uword> centers(permutations.n_rows);
    for (std::size_t n = 0; n < permutations.n_rows; ++n) {
      centers.at(n) = n;
    }

    for (std::size_t n = 0; n < permutations.n_rows; ++n) {
      arma::Row<arma::uword> histogram = arma::hist(permutations.row(n), centers);
      CHECK(0.05 > static_cast<double>(histogram.max() - histogram.min()) / permutations.n_cols);
    }
  }

  SECTION("Partial permutation") {
    arma::Mat<arma::uword>::fixed<10, 40000> permutations;
    for (std::size_t n = 0; n < permutations.n_cols; ++n) {
      permutations.col(n) = hop::getRandomPermutation(permutations.n_rows + 1, permutations.n_rows);
    }

    arma::Col<arma::uword> centers(permutations.n_rows);
    for (std::size_t n = 0; n < permutations.n_rows; ++n) {
      centers.at(n) = n;
    }

    for (std::size_t n = 0; n < permutations.n_rows; ++n) {
      arma::Row<arma::uword> histogram = arma::hist(permutations.row(n), centers);
      CHECK(0.1 > static_cast<double>(histogram.max() - histogram.min()) / permutations.n_cols);
    }
  }
}

<|endoftext|>