text:
/*    Copyright (c) 2010-2018, Delft University of Technology
 *    All rigths reserved
 *
 *    This file is part of the Tudat. Redistribution and use in source and
 *    binary forms, with or without modification, are permitted exclusively
 *    under the terms of the Modified BSD license. You should have received
 *    a copy of the license with this file. If not, please or visit:
 *    http://tudat.tudelft.nl/LICENSE.
 */

#define BOOST_TEST_MAIN

#include <string>

#include <boost/make_shared.hpp>
#include <boost/test/unit_test.hpp>

#include "Tudat/Mathematics/BasicMathematics/linearAlgebra.h"
#include "Tudat/Astrodynamics/BasicAstrodynamics/physicalConstants.h"
#include "Tudat/Astrodynamics/BasicAstrodynamics/orbitalElementConversions.h"

#include "Tudat/External/SpiceInterface/spiceInterface.h"
#include "Tudat/Mathematics/NumericalIntegrators/rungeKuttaCoefficients.h"
#include "Tudat/Mathematics/Interpolators/lagrangeInterpolator.h"
#include "Tudat/Astrodynamics/BasicAstrodynamics/accelerationModel.h"
#include "Tudat/Astrodynamics/BasicAstrodynamics/keplerPropagator.h"
#include "Tudat/InputOutput/basicInputOutput.h"

#include "Tudat/Astrodynamics/BasicAstrodynamics/orbitalElementConversions.h"
#include "Tudat/SimulationSetup/EnvironmentSetup/body.h"
#include "Tudat/Astrodynamics/Propagators/nBodyCowellStateDerivative.h"
#include "Tudat/SimulationSetup/PropagationSetup/dynamicsSimulator.h"
#include "Tudat/Mathematics/NumericalIntegrators/createNumericalIntegrator.h"
#include "Tudat/SimulationSetup/EnvironmentSetup/createBodies.h"
#include "Tudat/SimulationSetup/EstimationSetup/createNumericalSimulator.h"
#include "Tudat/SimulationSetup/EnvironmentSetup/defaultBodies.h"

namespace tudat
{

namespace unit_tests
{

//Using declarations.
using namespace tudat::ephemerides;
using namespace tudat::interpolators;
using namespace tudat::numerical_integrators;
using namespace tudat::spice_interface;
using namespace tudat::simulation_setup;
using namespace tudat::basic_astrodynamics;
using namespace tudat::orbital_element_conversions;
using namespace tudat::propagators;
using namespace tudat;

BOOST_AUTO_TEST_SUITE( test_forwards_backwards_rpopagation )

template< typename TimeType >
std::shared_ptr< IntegratorSettings< TimeType > > getIntegrationSettings(
        const int integratorCase, const int initialTime, const bool propagateForwards )
{
    double initialTimeMultiplier = ( propagateForwards ? 1.0 : -1.0 );
    std::shared_ptr< IntegratorSettings< TimeType > > integratorSettings;
    if( integratorCase == 0 )
    {
        integratorSettings = std::make_shared< IntegratorSettings< TimeType > >
                ( rungeKutta4, initialTime, initialTimeMultiplier * 300.0 );
    }
    else if( integratorCase < 5 )
    {
        RungeKuttaCoefficients::CoefficientSets coefficientSet = RungeKuttaCoefficients::undefinedCoefficientSet;
        if( integratorCase == 1 )
        {
            coefficientSet = RungeKuttaCoefficients::rungeKuttaFehlberg45;
        }
        else if( integratorCase == 2 )
        {
            coefficientSet = RungeKuttaCoefficients::rungeKuttaFehlberg56;

        }
        else if( integratorCase == 3 )
        {
            coefficientSet = RungeKuttaCoefficients::rungeKuttaFehlberg78;

        }
        else if( integratorCase == 4 )
        {
            coefficientSet = RungeKuttaCoefficients::rungeKutta87DormandPrince;

        }
        integratorSettings = std::make_shared< RungeKuttaVariableStepSizeSettings< TimeType > >
                ( initialTime, initialTimeMultiplier * 300.0, coefficientSet, 1.0E-3, 3600.0 );
    }
    return integratorSettings;
}

//! Test to ensure that forward and backward in time integration are properly and consistently performed using different
//! integrators. The state of the Moon is numerically propagated forward, and the back to the original time. The test checks
//! if the state at the midpoint is sufficiently close for the foprward and backward intgrations.
template< typename TimeType = double, typename StateScalarType = double >
Eigen::Matrix< StateScalarType, 6, 1 > propagateForwardBackwards( const int integratorCase )
{
    //Load spice kernels.
    spice_interface::loadStandardSpiceKernels( );

    // Define bodies in simulation.
    std::vector< std::string > bodyNames;
    bodyNames.push_back( "Earth" );
    bodyNames.push_back( "Moon" );
    bodyNames.push_back( "Sun" );
    bodyNames.push_back( "Mars" );
    bodyNames.push_back( "Venus" );

    // Specify initial time
    double initialEphemerisTime = 1.0E7;
    double finalEphemerisTime = initialEphemerisTime + 86400.0;
    double buffer = 3600.0;

    // Create bodies needed in simulation
    std::map< std::string, std::shared_ptr< BodySettings > > bodySettings =
            getDefaultBodySettings( bodyNames, initialEphemerisTime - 2.0 * buffer, finalEphemerisTime + 2.0 * buffer );
    std::dynamic_pointer_cast< InterpolatedSpiceEphemerisSettings >( bodySettings[ "Moon" ]->ephemerisSettings )->
            resetFrameOrigin( "Earth" );
    NamedBodyMap bodyMap = createBodies( bodySettings );
    setGlobalFrameBodyEphemerides( bodyMap, "Earth", "ECLIPJ2000" );

    // Set accelerations between bodies that are to be taken into account.
    SelectedAccelerationMap accelerationMap;
    std::map< std::string, std::vector< std::shared_ptr< AccelerationSettings > > > accelerationsOfMoon;
    accelerationsOfMoon[ "Earth" ].push_back( std::make_shared< AccelerationSettings >( central_gravity ) );
    accelerationsOfMoon[ "Sun" ].push_back( std::make_shared< AccelerationSettings >( central_gravity ) );
    accelerationMap[ "Moon" ] = accelerationsOfMoon;

    // Propagate the moon only
    std::vector< std::string > bodiesToIntegrate;
    bodiesToIntegrate.push_back( "Moon" );
    std::vector< std::string > centralBodies;
    centralBodies.push_back( "Earth" );

    // Define settings for numerical integrator.
    std::shared_ptr< IntegratorSettings< TimeType > > integratorSettings = getIntegrationSettings< TimeType >(
                integratorCase, initialEphemerisTime, true );

    // Propagate forwards
    {

        // Create acceleration models and propagation settings.
        Eigen::Matrix< StateScalarType, 6, 1  > systemInitialState =
                spice_interface::getBodyCartesianStateAtEpoch(
                    bodiesToIntegrate[ 0 ], centralBodies[ 0 ], "ECLIPJ2000", "NONE", initialEphemerisTime ).
                template cast< StateScalarType >( );
        AccelerationMap accelerationModelMap = createAccelerationModelsMap(
                    bodyMap, accelerationMap, bodiesToIntegrate, centralBodies );
        std::shared_ptr< TranslationalStatePropagatorSettings< StateScalarType > > propagatorSettings =
                std::make_shared< TranslationalStatePropagatorSettings< StateScalarType > >
                ( centralBodies, accelerationModelMap, bodiesToIntegrate, systemInitialState, finalEphemerisTime + buffer );

        // Create dynamics simulation object.
        SingleArcDynamicsSimulator< StateScalarType, TimeType > dynamicsSimulator(
                    bodyMap, integratorSettings, propagatorSettings, true, true, true );
    }

    double testTime = initialEphemerisTime + ( finalEphemerisTime - initialEphemerisTime ) / 2.0;
    Eigen::Vector6d forwardState = bodyMap.at( "Moon" )->getEphemeris( )->getCartesianState( testTime );

    // Re-define settings for numerical integrator.
    integratorSettings = getIntegrationSettings< TimeType >( integratorCase, finalEphemerisTime, false );

    // Propagate backwards
    {
        // Create acceleration models and propagation settings.
        Eigen::Matrix< StateScalarType, 6, 1  > systemInitialState =
                spice_interface::getBodyCartesianStateAtEpoch(
                    bodiesToIntegrate[ 0 ], centralBodies[ 0 ], "ECLIPJ2000", "NONE", finalEphemerisTime ).
                template cast< StateScalarType >( );
        AccelerationMap accelerationModelMap = createAccelerationModelsMap(
                    bodyMap, accelerationMap, bodiesToIntegrate, centralBodies );
        std::shared_ptr< TranslationalStatePropagatorSettings< StateScalarType > > propagatorSettings =
                std::make_shared< TranslationalStatePropagatorSettings< StateScalarType > >
                ( centralBodies, accelerationModelMap, bodiesToIntegrate, systemInitialState, initialEphemerisTime - buffer );

        // Create dynamics simulation object.
        SingleArcDynamicsSimulator< StateScalarType, TimeType > dynamicsSimulator(
                    bodyMap, integratorSettings, propagatorSettings, true, true, true );
    }

    Eigen::Vector6d backwardState = bodyMap.at( "Moon" )->getEphemeris( )->getCartesianState( testTime );

    return forwardState - backwardState;
}

BOOST_AUTO_TEST_CASE( testCowellPropagatorKeplerCompare )
{
    for( unsigned int j = 0; j < 5; j++ )
    {
        Eigen::Vector6d stateDifference = propagateForwardBackwards< double, double >( j );
        for( int i = 0; i < 3; i++ )
        {
            BOOST_CHECK_SMALL( std::fabs( stateDifference( i ) ), 0.1 );
            BOOST_CHECK_SMALL( std::fabs( stateDifference( i + 3 ) ), 1.0E-4 );
        }
    }
}

BOOST_AUTO_TEST_SUITE_END( )

}

}

max_stars_repo_name: J-Westin/tudat

max_issues_repo_name: J-Westin/tudat

max_forks_repo_name: J-Westin/tudat


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

#include <algorithm>
#include <cstdio>
#include <cstdarg>
#include <getopt.h>
#include <execinfo.h>
#include <csignal>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <numeric>

#include <std_msgs/Float64.h>
#include <diagnostic_updater/DiagnosticStatusWrapper.h>
//#include <combined_robot_hw/combined_robot_hw.h>
#include <controller_manager/controller_manager.h>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/max.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <realtime_tools/realtime_publisher.h>
#include <diagnostic_msgs/DiagnosticArray.h>
#include <string>
#include <vector>
#include <tinyxml.h>
//
#include <minas_control/minas_hardware_interface.h>

using boost::accumulators::accumulator_set;
using boost::accumulators::stats;
using boost::accumulators::extract_result;
using boost::accumulators::tag::max;
using boost::accumulators::tag::mean;
using std::string;
using std::vector;
using std::accumulate;
using realtime_tools::RealtimePublisher;

static struct
{
  char *program_;
  bool stats_;
  double period;
  std::string iface_;
  bool simulation_;
}
g_options;

void Usage(const string &msg = "")
{
  fprintf(stderr, "Usage: %s [options]\n", g_options.program_);
  fprintf(stderr, "  Available options\n");
  fprintf(stderr, "    -i, --interface             NIC interface name for EtherCAT\n");
  fprintf(stderr, "    -l, --loopback              Use loopback interface for Controller (i.e. simulation mode)\n");
  fprintf(stderr, "    -p, --period                RT loop period in msec\n");
  fprintf(stderr, "    -s, --stats                 Publish statistics on the RT loop "
      "jitter on \"node_name/realtime\" in seconds\n");
  fprintf(stderr, "    -h, --help                  Print this message and exit\n");
  if (msg != "")
  {
    fprintf(stderr, "Error: %s\n", msg.c_str());
    exit(-1);
  }
  else
    exit(0);
}

static int g_quit = 0;
static const int SEC_2_NSEC = 1e+9;
static const int SEC_2_USEC = 1e6;

static struct
{
  accumulator_set<double, stats<max, mean> > ec_acc;
  accumulator_set<double, stats<max, mean> > cm_acc;
  accumulator_set<double, stats<max, mean> > loop_acc;
  accumulator_set<double, stats<max, mean> > jitter_acc;
  int overruns;
  int recent_overruns;
  int last_overrun;
  int last_severe_overrun;
  double overrun_loop_sec;
  double overrun_ec;
  double overrun_cm;

  // These values are set when realtime loop does not meet performance expectations
  bool rt_loop_not_making_timing;
  double halt_rt_loop_frequency;
  double rt_loop_frequency;
}
g_stats;

static void publishDiagnostics(RealtimePublisher<diagnostic_msgs::DiagnosticArray> &publisher)
{
  if (publisher.trylock())
  {
    accumulator_set<double, stats<max, mean> > zero;
    vector<diagnostic_msgs::DiagnosticStatus> statuses;
    diagnostic_updater::DiagnosticStatusWrapper status;

    static double max_ec = 0, max_cm = 0, max_loop = 0, max_jitter = 0;
    double avg_ec, avg_cm, avg_loop, avg_jitter;

    avg_ec = extract_result<mean>(g_stats.ec_acc);
    avg_cm = extract_result<mean>(g_stats.cm_acc);
    avg_loop = extract_result<mean>(g_stats.loop_acc);
    max_ec = std::max(max_ec, extract_result<max>(g_stats.ec_acc));
    max_cm = std::max(max_cm, extract_result<max>(g_stats.cm_acc));
    max_loop = std::max(max_loop, extract_result<max>(g_stats.loop_acc));
    g_stats.ec_acc = zero;
    g_stats.cm_acc = zero;
    g_stats.loop_acc = zero;

    // Publish average loop jitter
    avg_jitter = extract_result<mean>(g_stats.jitter_acc);
    max_jitter = std::max(max_jitter, extract_result<max>(g_stats.jitter_acc));
    g_stats.jitter_acc = zero;

    status.addf("Max EtherCAT roundtrip (us)", "%.2f", max_ec * SEC_2_USEC);
    status.addf("Avg EtherCAT roundtrip (us)", "%.2f", avg_ec * SEC_2_USEC);
    status.addf("Max Controller Manager roundtrip (us)", "%.2f", max_cm * SEC_2_USEC);
    status.addf("Avg Controller Manager roundtrip (us)", "%.2f", avg_cm * SEC_2_USEC);
    status.addf("Max Total Loop roundtrip (us)", "%.2f", max_loop * SEC_2_USEC);
    status.addf("Avg Total Loop roundtrip (us)", "%.2f", avg_loop * SEC_2_USEC);
    status.addf("Max Loop Jitter (us)", "%.2f", max_jitter * SEC_2_USEC);
    status.addf("Avg Loop Jitter (us)", "%.2f", avg_jitter * SEC_2_USEC);
    status.addf("Control Loop Overruns", "%d", g_stats.overruns);
    status.addf("Recent Control Loop Overruns", "%d", g_stats.recent_overruns);
    status.addf("Last Control Loop Overrun Cause", "ec: %.2fus, cm: %.2fus",
                g_stats.overrun_ec*SEC_2_USEC, g_stats.overrun_cm * SEC_2_USEC);
    status.addf("Last Overrun Loop Time (us)", "%.2f", g_stats.overrun_loop_sec * SEC_2_USEC);
    status.addf("Realtime Loop Frequency", "%.4f", g_stats.rt_loop_frequency);

    status.name = "Realtime Control Loop";
    if (g_stats.overruns > 0 && g_stats.last_overrun < 30)
    {
      if (g_stats.last_severe_overrun < 30)
        status.level = 1;
      else
        status.level = 0;
      status.message = "Realtime loop used too much time in the last 30 seconds.";
    }
    else
    {
      status.level = 0;
      status.message = "OK";
    }
    g_stats.recent_overruns = 0;
    ++g_stats.last_overrun;
    ++g_stats.last_severe_overrun;

    if (g_stats.rt_loop_not_making_timing)
      status.mergeSummaryf(status.ERROR, "realtime loop only ran at %.4f Hz", g_stats.halt_rt_loop_frequency);

    statuses.push_back(status);
    publisher.msg_.status = statuses;
    publisher.msg_.header.stamp = ros::Time::now();
    publisher.unlockAndPublish();
  }
}

static inline double now()
{
  struct timespec n;
  clock_gettime(CLOCK_MONOTONIC, &n);
  return static_cast<double>(n.tv_nsec) / SEC_2_NSEC + n.tv_sec;
}

static void timespecInc(struct timespec &tick, int nsec)
{
  tick.tv_nsec += nsec;
  while (tick.tv_nsec >= SEC_2_NSEC)
  {
    tick.tv_nsec -= SEC_2_NSEC;
    ++tick.tv_sec;
  }
}

class RTLoopHistory
{
public:
  RTLoopHistory(unsigned length, double default_value) :
    index_(0),
    length_(length),
    history_(length, default_value)
  {
  }

  void sample(double value)
  {
    index_ = (index_ + 1) % length_;
    history_[index_] = value;
  }

  double average() const
  {
    return accumulate(history_.begin(), history_.end(), 0.0) / static_cast<double>(length_);
  }

protected:
  unsigned index_;
  unsigned length_;
  vector<double> history_;
};

static void* terminate_control(RealtimePublisher<diagnostic_msgs::DiagnosticArray> *publisher,
                               RealtimePublisher<std_msgs::Float64> *rtpublisher,
                               const char* message,
                               const char* data = NULL)
{
  ROS_FATAL(message, data);
  publisher->stop();
  delete rtpublisher;
  ros::shutdown();
  return reinterpret_cast<void*>(- 1);
}

void *controlLoop(void */*unused_param*/)  // NOLINT(readability/casting)
{
  double last_published, last_loop_start;
  int policy;
  TiXmlElement *root;
  TiXmlElement *root_element;

  ros::NodeHandle nh;

  RealtimePublisher<diagnostic_msgs::DiagnosticArray> publisher(nh, "/diagnostics", 2);
  RealtimePublisher<std_msgs::Float64> *rtpublisher = NULL;

  // Realtime loop should be running at least 3/4 of given frequency
  // or at specified min acceptable frequency
  double period_in_secs = 1e+9 * g_options.period;
  double given_frequency = 1 / period_in_secs;
  double min_acceptable_rt_loop_frequency = 0.75 * given_frequency;
  if (nh.getParam("min_acceptable_rt_loop_frequency", min_acceptable_rt_loop_frequency))
    ROS_WARN("min_acceptable_rt_loop_frequency changed to %f", min_acceptable_rt_loop_frequency);

  unsigned rt_cycle_count = 0;
  double last_rt_monitor_time;

  // Calculate realtime loop frequency every 200msec
  double rt_loop_monitor_period = 0.2;
  // Keep history of last 3 calculation intervals.
  RTLoopHistory rt_loop_history(3, 1000.0);

  if (g_options.stats_)
    rtpublisher = new RealtimePublisher<std_msgs::Float64>(nh, "realtime", 2);

  // Load robot description
  TiXmlDocument xml;
  struct stat st;

  // Initialize the hardware interface
  minas_control::MinasHardwareInterface robot(g_options.iface_, g_options.simulation_);

  // Create controller manager
  controller_manager::ControllerManager cm(&robot);

  // Publish one-time before entering real-time to pre-allocate message vectors
  publishDiagnostics(publisher);

  // Set to realtime scheduler for this thread
  struct sched_param thread_param;
  policy = SCHED_FIFO;
  thread_param.sched_priority = sched_get_priority_max(policy);
  pthread_setschedparam(pthread_self(), policy, &thread_param);

  struct timespec tick;
  clock_gettime(CLOCK_REALTIME, &tick);
  ros::Duration durp(g_options.period / 1e+9);

  // Snap to the nearest second
  tick.tv_nsec = (tick.tv_nsec / g_options.period + 1) * g_options.period;
  clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &tick, NULL);

  last_published = now();
  last_rt_monitor_time = now();
  last_loop_start = now();
  while (!g_quit)
  {
    // Track how long the actual loop takes
    double this_loop_start = now();
    g_stats.loop_acc(this_loop_start - last_loop_start);
    last_loop_start = this_loop_start;

    double start = now();

    ros::Time this_moment(tick.tv_sec, tick.tv_nsec);
    robot.read(this_moment, durp);
    double after_ec = now();
    cm.update(this_moment, durp);
    robot.write(this_moment, durp);
    double end = now();

    g_stats.ec_acc(after_ec - start);
    g_stats.cm_acc(end - after_ec);

    if ((end - last_published) > 1.0)
    {
      publishDiagnostics(publisher);
      last_published = end;
    }

    // Realtime loop should run about with the set frequency by default 1000Hz.
    // Missing timing on control cycles usually causes a controller glitch and actuators to jerk.
    // When realtime loop misses a lot of cycles controllers will perform poorly and may cause robot to shake.
    ++rt_cycle_count;
    if ((start - last_rt_monitor_time) > rt_loop_monitor_period)
    {
      // Calculate new average rt loop frequency
      double rt_loop_frequency = static_cast<double>(rt_cycle_count) / rt_loop_monitor_period;

      // Use last X samples of frequency when deciding whether or not to halt
      rt_loop_history.sample(rt_loop_frequency);
      double avg_rt_loop_frequency = rt_loop_history.average();
      if (avg_rt_loop_frequency < min_acceptable_rt_loop_frequency)
      {
        if (!g_stats.rt_loop_not_making_timing)
        {
          // Only update this value if motors when this first occurs (used for diagnostics error message)
          g_stats.halt_rt_loop_frequency = avg_rt_loop_frequency;
        }
        g_stats.rt_loop_not_making_timing = true;
      }
      g_stats.rt_loop_frequency = avg_rt_loop_frequency;
      rt_cycle_count = 0;
      last_rt_monitor_time = start;
    }

    // Compute end of next g_options.period
    timespecInc(tick, g_options.period);

    struct timespec before;
    clock_gettime(CLOCK_REALTIME, &before);
    if ((before.tv_sec + static_cast<double>(before.tv_nsec) / SEC_2_NSEC) >
        (tick.tv_sec + static_cast<double>(tick.tv_nsec) / SEC_2_NSEC))
    {
      // Total amount of time the loop took to run
      g_stats.overrun_loop_sec = (before.tv_sec + static_cast<double>(before.tv_nsec) / SEC_2_NSEC) -
        (tick.tv_sec + static_cast<double>(tick.tv_nsec) / SEC_2_NSEC);

      // We overran, snap to next "g_options.period"
      tick.tv_sec = before.tv_sec;
      tick.tv_nsec = (before.tv_nsec / g_options.period) * g_options.period;
      timespecInc(tick, g_options.period);

      // initialize overruns
      if (g_stats.overruns == 0)
      {
        g_stats.last_overrun = 1000;
        g_stats.last_severe_overrun = 1000;
      }
      // check for overruns
      if (g_stats.recent_overruns > 10)
        g_stats.last_severe_overrun = 0;
      g_stats.last_overrun = 0;

      ++g_stats.overruns;
      ++g_stats.recent_overruns;
      g_stats.overrun_ec = after_ec - start;
      g_stats.overrun_cm = end - after_ec;
    }

    // Sleep until end of g_options.period
    clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &tick, NULL);

    // Calculate RT loop jitter
    struct timespec after;
    clock_gettime(CLOCK_REALTIME, &after);
    double jitter = (after.tv_sec - tick.tv_sec + static_cast<double>(after.tv_nsec - tick.tv_nsec) / SEC_2_NSEC);

    g_stats.jitter_acc(jitter);

    // Publish realtime loops statistics, if requested
    if (rtpublisher && rtpublisher->trylock())
    {
      rtpublisher->msg_.data = jitter;
      rtpublisher->unlockAndPublish();
    }
  }

  publisher.stop();
  delete rtpublisher;
  robot.shutdown();
  ros::shutdown();
  return NULL;
}

void quitRequested(int sig)
{
  g_quit = 1;
}

#define CLOCK_PRIO 0
#define CONTROL_PRIO 0

static pthread_t controlThread;
static pthread_attr_t controlThreadAttr;

int main(int argc, char *argv[])
{
  bool mlock_all = true;
  // Keep the kernel from swapping us out
  if (mlockall(MCL_CURRENT | MCL_FUTURE) < 0)
  {
    perror("Failed to lock memory. It is recommended to set permission to executables, for example: sudo setcap cap_net_raw,cap_ipc_lock=+ep main");
    mlock_all = false;
  }

  // Initialize ROS and parse command-line arguments
  ros::init(argc, argv, "realtime_loop");

  // Parse options
  g_options.program_ = argv[0];
  g_options.period = 1e+6;  // 1 ms in nanoseconds
  g_options.simulation_ = false; // default use NIC interface

  while (true)
  {
    static struct option long_options[] = {
      {"help", no_argument, 0, 'h'},
      {"interface", required_argument, 0, 'i'},
      {"loopback", no_argument, 0, 'l'},
      {"stats", no_argument, 0, 's'},
      {"period", required_argument, 0, 'p'},
    };
    int option_index = 0;
    int c = getopt_long(argc, argv, "hi:slp:", long_options, &option_index);
    if (c == -1)
      break;

    switch (c)
    {
      case 'h':
        Usage();
        break;
      case 'i':
        g_options.iface_ = std::string(optarg);
        break;
      case 's':
        g_options.stats_ = true;
        break;
      case 'l':
        g_options.simulation_ = true;
        break;
      case 'p':
        // convert period given in msec to nsec
        g_options.period = fabs(atof(optarg))*1e+6;
        break;
    }
  }
  if ( mlock_all == false ) {
    // when mlock is false, on simulation, it'ok
    if ( g_options.simulation_ == true) {
      ROS_WARN("Continue running without mlockall");
    }
    // on real robot, something wrong, forget to setcap cap_net_raw,cap_ipc_lock=+ep
    if ( g_options.simulation_ == false ) {
      perror("Exitting .. real robots needs mlockall to run in realtime");
      exit(EXIT_FAILURE);
    }
  }
  if (optind < argc)
    Usage("Extra arguments");

  ros::NodeHandle node;

  // Catch attempts to quit
  signal(SIGTERM, quitRequested);
  signal(SIGINT, quitRequested);
  signal(SIGHUP, quitRequested);

  // Start thread
  int rv = pthread_create(&controlThread, &controlThreadAttr, controlLoop, 0);
  if (rv != 0)
  {
    ROS_FATAL("Unable to create control thread: rv = %d", rv);
    exit(EXIT_FAILURE);
  }

  ros::spin();
  pthread_join(controlThread, reinterpret_cast<void **>(&rv));

  return rv;
}

ext: cpp


max_stars_repo_name: kezenator/adventofcode

max_issues_repo_name: kezenator/adventofcode

max_forks_repo_name: kezenator/adventofcode


text:
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 <map>
#include <string>
#include <util/Log.hpp>

#include <boost/filesystem.hpp>
#include <boost/regex.hpp>

#include <gtest/gtest.h>

#include <geode/AuthenticatedView.hpp>
#include <geode/Cache.hpp>
#include <geode/PoolManager.hpp>
#include <geode/RegionFactory.hpp>
#include <geode/RegionShortcut.hpp>

using apache::geode::client::CacheClosedException;
using apache::geode::client::CacheFactory;
using apache::geode::client::LogLevel;
using apache::geode::client::RegionShortcut;

namespace {

const auto __1K__ = 1024;
const auto __4K__ = 4 * __1K__;
const auto __1M__ = (__1K__ * __1K__);
const auto __1G__ = (__1K__ * __1K__ * __1K__);

const auto LENGTH_OF_BANNER = 16;

const char* __1KStringLiteral =
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";

auto testFileNames = {"LoggingTest.log", "geode-native.log"};

class LoggingTest : public testing::Test {
  void scrubTestLogFiles() {
    for (auto name : testFileNames) {
      // Close logger, just in case
      apache::geode::client::Log::close();

      if (boost::filesystem::exists(name)) {
        boost::filesystem::remove(name);
      }

      std::map<int32_t, boost::filesystem::path> rolledFiles;
      LoggingTest::findRolledFiles(boost::filesystem::current_path().string(),
                                   name, rolledFiles);
      for (auto& item : rolledFiles) {
        boost::filesystem::remove(item.second);
      }
    }
  }

  void SetUp() override { scrubTestLogFiles(); }

  void TearDown() override { scrubTestLogFiles(); }

 public:
  static void writeRolledLogFile(const boost::filesystem::path& logdir,
                                 const char* filename, int32_t rollIndex) {
    auto baseName = boost::filesystem::path(filename).stem().string();
    auto rolledPath =
        logdir / boost::filesystem::path(baseName + "-" +
                                         std::to_string(rollIndex) + ".log");
    auto rolledFile = fopen(rolledPath.string().c_str(), "w");
    fwrite("Test", 1, 4, rolledFile);
    fclose(rolledFile);
  }

  static int numOfLinesInFile(const char* fname) {
    char line[2048];
    int ln_cnt = 0;
    FILE* fp = fopen(fname, "r");
    if (fp == nullptr) {
      return 0;
    }
    while (!!(fgets(line, sizeof line, fp))) {
      ++ln_cnt;
    }

    if (!feof(fp)) {
      fclose(fp);
      return -2;
    }
    fclose(fp);
    return ln_cnt;
  }

  static int expected(LogLevel level) {
    int expected = static_cast<int>(level);
    if (level >= LogLevel::Default) {
      expected--;
    }
    return expected;
  }

  static int expectedWithBanner(LogLevel level) {
    int expected = LoggingTest::expected(level);
    if (level != LogLevel::None) {
      expected += LENGTH_OF_BANNER;
    }
    return expected;
  }

  static void verifyLineCountAtLevel(LogLevel level) {
    for (auto logFilename : testFileNames) {
      apache::geode::client::Log::init(level, logFilename);

      LOGERROR("Error Message");
      LOGWARN("Warning Message");
      LOGINFO("Info Message");
      LOGCONFIG("Config Message");
      LOGFINE("Fine Message");
      LOGFINER("Finer Message");
      LOGFINEST("Finest Message");
      LOGDEBUG("Debug Message");

      int lines = LoggingTest::numOfLinesInFile(logFilename);

      ASSERT_TRUE(lines == LoggingTest::expectedWithBanner(level));

      apache::geode::client::Log::close();
      boost::filesystem::remove(logFilename);
    }
  }

  static void findRolledFiles(
      const std::string& logFilePath, const boost::filesystem::path& filename,
      std::map<int32_t, boost::filesystem::path>& rolledFiles) {
    const auto basePath =
        boost::filesystem::absolute(boost::filesystem::path(logFilePath)) /
        filename;
    const auto filterstring = basePath.stem().string() + "-(\\d+)\\.log$";
    const boost::regex my_filter(filterstring);

    rolledFiles.clear();

    boost::filesystem::directory_iterator end_itr;
    for (boost::filesystem::directory_iterator i(
             basePath.parent_path().string());
         i != end_itr; ++i) {
      if (boost::filesystem::is_regular_file(i->status())) {
        std::string rootFilename = i->path().filename().string();
        boost::regex testPattern(filterstring);
        boost::match_results<std::string::const_iterator> testMatches;
        if (boost::regex_search(
                std::string::const_iterator(rootFilename.begin()),
                rootFilename.cend(), testMatches, testPattern)) {
          auto index = std::atoi(
              std::string(testMatches[1].first, testMatches[1].second).c_str());
          rolledFiles[index] = i->path();
        }
      }
    }
  }

  static size_t calculateUsedDiskSpace(const std::string& logFilePath) {
    std::map<int32_t, boost::filesystem::path> rolledLogFiles{};
    findRolledFiles(boost::filesystem::current_path().string(),
                    boost::filesystem::path(logFilePath), rolledLogFiles);

    auto usedSpace = boost::filesystem::file_size(logFilePath);
    for (auto const& item : rolledLogFiles) {
      usedSpace += boost::filesystem::file_size(item.second);
    }

    return usedSpace;
  }

  void verifyDiskSpaceNotLeakedForFile(const char* filename) {
    const int NUMBER_OF_ITERATIONS = 4 * __1K__;
    const int DISK_SPACE_LIMIT = 2 * __1M__;

    std::string logfileName = filename ? filename : "geode-native.log";

    // Start/stop logger several times, make sure it's picking up any/all
    // existing logs in its disk space calculations.
    for (auto j = 0; j < 5; j++) {
      ASSERT_NO_THROW(apache::geode::client::Log::init(
          apache::geode::client::LogLevel::Debug, logfileName, 1, 2));
      for (auto i = 0; i < NUMBER_OF_ITERATIONS; i++) {
        LOGDEBUG(__1KStringLiteral);
      }
      apache::geode::client::Log::close();

      // Original file should still be around
      ASSERT_TRUE(boost::filesystem::exists(logfileName));

      // We wrote 4x the log file limit, and 2x the disk space limit, so
      // there should be one 'rolled' file.  Its name should be of the form
      // <base>-n.log, where n is some reasonable number.
      auto usedSpace = calculateUsedDiskSpace(logfileName);
      ASSERT_TRUE(usedSpace < DISK_SPACE_LIMIT);
    }
  }
};

/**
 * Verify we can initialize the logger with any combination of level,
 * filename, file size limit, and disk space limit
 */
TEST_F(LoggingTest, logInit) {
  for (auto logFilename : testFileNames) {
    // Check all valid levels
    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::None, logFilename, 1, 4));
    apache::geode::client::Log::close();
    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Error, logFilename, 1, 4));
    apache::geode::client::Log::close();
    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Warning, logFilename, 1, 4));
    apache::geode::client::Log::close();
    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Info, logFilename, 1, 4));
    apache::geode::client::Log::close();
    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Default, logFilename, 1, 4));
    apache::geode::client::Log::close();
    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Config, logFilename, 1, 4));
    apache::geode::client::Log::close();
    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Fine, logFilename, 1, 4));
    apache::geode::client::Log::close();
    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Finer, logFilename, 1, 4));
    apache::geode::client::Log::close();
    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Finest, logFilename, 1, 4));
    apache::geode::client::Log::close();
    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Debug, logFilename, 1, 4));
    apache::geode::client::Log::close();
    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::All, logFilename, 1, 4));
    apache::geode::client::Log::close();
    // Specify a disk space limit smaller than the file size limit
    ASSERT_THROW(
        apache::geode::client::Log::init(
            apache::geode::client::LogLevel::Config, logFilename, __1K__, 4),
        apache::geode::client::IllegalArgumentException);

    // Specify a file size limit above max allowed
    ASSERT_THROW(
        apache::geode::client::Log::init(
            apache::geode::client::LogLevel::Config, logFilename, __1G__),
        apache::geode::client::IllegalArgumentException);

    // Specify a disk space limit above max allowed
    ASSERT_THROW(
        apache::geode::client::Log::init(
            apache::geode::client::LogLevel::Config, logFilename, 1, __1G__),
        apache::geode::client::IllegalArgumentException);

    // Init twice without closing
    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::All, logFilename, 1, 4));
    ASSERT_THROW(apache::geode::client::Log::init(
                     apache::geode::client::LogLevel::All, logFilename, 1, 4),
                 apache::geode::client::IllegalStateException);
    apache::geode::client::Log::close();
  }

  // Init with valid filename
  ASSERT_NO_THROW(apache::geode::client::Log::init(
      apache::geode::client::LogLevel::Config, "LoggingTest.log"));
  apache::geode::client::Log::close();

  // Init with legal filename with (), #, and space
  ASSERT_NO_THROW(apache::geode::client::Log::init(
      apache::geode::client::LogLevel::Config, "LoggingTest (#).log"));
  apache::geode::client::Log::close();
  boost::filesystem::remove("LoggingTest (#).log");

  // Init with invalid filename
  ASSERT_THROW(apache::geode::client::Log::init(
                   apache::geode::client::LogLevel::Config, "#?$?%.log"),
               apache::geode::client::IllegalArgumentException);

  // Specify disk or file limit without a filename
  ASSERT_NO_THROW(apache::geode::client::Log::init(
      apache::geode::client::LogLevel::Config, nullptr, 4));
  apache::geode::client::Log::close();
  ASSERT_NO_THROW(apache::geode::client::Log::init(
      apache::geode::client::LogLevel::Config, nullptr, 0, 4));
  apache::geode::client::Log::close();
}

TEST_F(LoggingTest, logToFileAtEachLevel) {
  for (auto logFilename : testFileNames) {
    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Debug, logFilename));
    LOGDEBUG("This is a debug string");
    LOGDEBUG("This is a formatted debug string (%d)", __1K__);
    apache::geode::client::Log::close();
    ASSERT_TRUE(boost::filesystem::exists(logFilename));
    ASSERT_TRUE(boost::filesystem::file_size(logFilename) > 0);
    boost::filesystem::remove(logFilename);
    ASSERT_FALSE(boost::filesystem::exists(logFilename));

    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Finest, logFilename));
    LOGFINEST("This is a 'finest' string");
    LOGFINEST("This is a formatted 'finest' string (%d)", __1K__);
    apache::geode::client::Log::close();
    ASSERT_TRUE(boost::filesystem::exists(logFilename));
    ASSERT_TRUE(boost::filesystem::file_size(logFilename) > 0);
    boost::filesystem::remove(logFilename);

    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Finer, logFilename));
    LOGFINER("This is a 'finer' string");
    LOGFINER("This is a formatted 'finer' string (%d)", __1K__);
    apache::geode::client::Log::close();
    ASSERT_TRUE(boost::filesystem::exists(logFilename));
    ASSERT_TRUE(boost::filesystem::file_size(logFilename) > 0);
    boost::filesystem::remove(logFilename);

    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Fine, logFilename));
    LOGFINE("This is a 'fine' string");
    LOGFINE("This is a formatted 'fine' string (%d)", __1K__);
    apache::geode::client::Log::close();
    ASSERT_TRUE(boost::filesystem::exists(logFilename));
    ASSERT_TRUE(boost::filesystem::file_size(logFilename) > 0);
    boost::filesystem::remove(logFilename);

    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Config, logFilename));
    LOGCONFIG("This is a 'config' string");
    LOGCONFIG("This is a formatted 'config' string (%d)", __1K__);
    apache::geode::client::Log::close();
    ASSERT_TRUE(boost::filesystem::exists(logFilename));
    ASSERT_TRUE(boost::filesystem::file_size(logFilename) > 0);
    boost::filesystem::remove(logFilename);

    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Info, logFilename));
    LOGINFO("This is a 'finer' string");
    LOGINFO("This is a formatted 'finer' string (%d)", __1K__);
    apache::geode::client::Log::close();
    ASSERT_TRUE(boost::filesystem::exists(logFilename));
    ASSERT_TRUE(boost::filesystem::file_size(logFilename) > 0);
    boost::filesystem::remove(logFilename);

    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Warning, logFilename));
    LOGWARN("This is a 'warning' string");
    LOGWARN("This is a formatted 'warning' string (%d)", __1K__);
    apache::geode::client::Log::close();
    ASSERT_TRUE(boost::filesystem::exists(logFilename));
    ASSERT_TRUE(boost::filesystem::file_size(logFilename) > 0);
    boost::filesystem::remove(logFilename);

    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Error, logFilename));
    LOGERROR("This is a 'error' string");
    LOGERROR("This is a formatted 'error' string (%d)", __1K__);
    apache::geode::client::Log::close();
    ASSERT_TRUE(boost::filesystem::exists(logFilename));
    ASSERT_TRUE(boost::filesystem::file_size(logFilename) > 0);
    boost::filesystem::remove(logFilename);
  }
}

TEST_F(LoggingTest, verifyFileSizeLimit) {
  for (auto logFilename : testFileNames) {
    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Debug, logFilename, 1, 5));
    for (auto i = 0; i < 4 * __1K__; i++) {
      LOGDEBUG(__1KStringLiteral);
    }
    apache::geode::client::Log::close();

    // Original file should still be around
    ASSERT_TRUE(boost::filesystem::exists(logFilename));

    // Check for 'rolled' log files.  With a 1MB file size limit and each logged
    // string having a length of 1K chars, we should have at least one less
    // rolled log file than the number of strings logged, i.e. 3 rolled files
    // for 4K strings in this case.  spdlog rolled files look like
    // <<basename>>.<<#>>.<<extension>>, so for LoggingTest.log we should find
    // LoggingTest.1.log, LoggingTest.2.log, etc.
    auto base = boost::filesystem::path(logFilename).stem();
    auto ext = boost::filesystem::path(logFilename).extension();

    // File size limit is treated as a "soft" limit.  If the last message in the
    // log puts the file size over the limit, the file is rolled and the message
    // is preserved intact, rather than truncated or split across files.  We'll
    // assume the file size never exceeds 110% of the specified limit.
    auto adjustedFileSizeLimit =
        static_cast<uint32_t>(static_cast<uint64_t>(__1M__) * 11 / 10);

    for (auto i = 0; i < 4; i++) {
      auto rolledLogFileName =
          base.string() + "-" + std::to_string(i) + ext.string();

      ASSERT_TRUE(boost::filesystem::exists(rolledLogFileName));
      ASSERT_TRUE(adjustedFileSizeLimit >
                  boost::filesystem::file_size(rolledLogFileName));
    }
  }
}

TEST_F(LoggingTest, verifyDiskSpaceLimit) {
  for (auto logFilename : testFileNames) {
    const int NUMBER_OF_ITERATIONS = 4 * __1K__;
    const int DISK_SPACE_LIMIT = 2 * __1M__;

    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Debug, logFilename, 1, 2));
    for (auto i = 0; i < NUMBER_OF_ITERATIONS; i++) {
      LOGDEBUG(__1KStringLiteral);
    }
    apache::geode::client::Log::close();

    // Original file should still be around
    ASSERT_TRUE(boost::filesystem::exists(logFilename));

    auto size = boost::filesystem::file_size(logFilename);
    auto numRolledFilesFound = 0;
    auto base = boost::filesystem::path(logFilename).stem();
    auto ext = boost::filesystem::path(logFilename).extension();

    // We wrote 4x the log file limit, and 2x the disk space limit, so
    // there should be one 'rolled' file.  Its name should be of the form
    // <base>-n.log, where n is some reasonable number.
    std::map<int32_t, boost::filesystem::path> rolledFiles;
    LoggingTest::findRolledFiles(boost::filesystem::current_path().string(),
                                 logFilename, rolledFiles);
    ASSERT_TRUE(rolledFiles.size() == 1);

    auto rolledFile = rolledFiles.begin()->second;
    size += boost::filesystem::file_size(rolledFile);

    ASSERT_TRUE(size <= DISK_SPACE_LIMIT);
  }
}

TEST_F(LoggingTest, verifyWithExistingRolledFile) {
  for (auto logFilename : testFileNames) {
    LoggingTest::writeRolledLogFile(boost::filesystem::current_path(),
                                    logFilename, 11);
    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Debug, logFilename, 1, 5));
    for (auto i = 0; i < 2 * __1K__; i++) {
      LOGDEBUG(__1KStringLiteral);
    }
    apache::geode::client::Log::close();

    // Original file should still be around
    ASSERT_TRUE(boost::filesystem::exists(logFilename));

    // Check for 'rolled' log files.  With a 1MB file size limit and each logged
    // string having a length of 1K chars, we should have at least one less
    // rolled log file than the number of strings logged, i.e. 3 rolled files
    // for 4K strings in this case.  spdlog rolled files look like
    // <<basename>>.<<#>>.<<extension>>, so for LoggingTest.log we should find
    // LoggingTest.1.log, LoggingTest.2.log, etc.
    auto base = boost::filesystem::path(logFilename).stem();
    auto ext = boost::filesystem::path(logFilename).extension();

    // File size limit is treated as a "soft" limit.  If the last message in the
    // log puts the file size over the limit, the file is rolled and the message
    // is preserved intact, rather than truncated or split across files.  We'll
    // assume the file size never exceeds 110% of the specified limit.
    auto adjustedFileSizeLimit =
        static_cast<uint32_t>(static_cast<uint64_t>(__1M__) * 11 / 10);

    auto rolledLogFileName =
        base.string() + "-" + std::to_string(12) + ext.string();

    ASSERT_TRUE(boost::filesystem::exists(rolledLogFileName));
    ASSERT_TRUE(adjustedFileSizeLimit >
                boost::filesystem::file_size(rolledLogFileName));
  }
}

void verifyWithPath(const boost::filesystem::path& path, int32_t fileSizeLimit,
                    int64_t diskSpaceLimit) {
  for (auto logFilename : testFileNames) {
    auto relativePath = path / boost::filesystem::path(logFilename);

    ASSERT_NO_THROW(apache::geode::client::Log::init(
        apache::geode::client::LogLevel::Debug, relativePath.string(),
        fileSizeLimit, diskSpaceLimit));
    for (auto i = 0; i < ((3 * fileSizeLimit) / 2) * __1K__; i++) {
      LOGDEBUG(__1KStringLiteral);
    }
    apache::geode::client::Log::close();

    // Original file should still be around
    ASSERT_TRUE(boost::filesystem::exists(relativePath));

    // Check for 'rolled' log files.  With a 1MB file size limit and each logged
    // string having a length of 1K chars, we should have at least one less
    // rolled log file than the number of strings logged, i.e. 3 rolled files
    // for 4K strings in this case.  spdlog rolled files look like
    // <<basename>>.<<#>>.<<extension>>, so for LoggingTest.log we should find
    // LoggingTest.1.log, LoggingTest.2.log, etc.
    auto base = boost::filesystem::path(relativePath).stem();
    auto ext = boost::filesystem::path(relativePath).extension();

    // File size limit is treated as a "soft" limit.  If the last message in the
    // log puts the file size over the limit, the file is rolled and the message
    // is preserved intact, rather than truncated or split across files.  We'll
    // assume the file size never exceeds 110% of the specified limit.
    auto adjustedFileSizeLimit = static_cast<uint32_t>(
        static_cast<uint64_t>(__1M__ * fileSizeLimit) * 11 / 10);

    auto rolledLogFileName =
        relativePath.parent_path() /
        boost::filesystem::path(base.string() + "-" + std::to_string(0) +
                                ext.string());

    if (fileSizeLimit == diskSpaceLimit) {
      // If the limits are equal, we should *never* roll logs, just delete the
      // current file and start over
      ASSERT_FALSE(boost::filesystem::exists(rolledLogFileName));
    } else {
      ASSERT_TRUE(boost::filesystem::exists(rolledLogFileName));
      ASSERT_TRUE(adjustedFileSizeLimit >
                  boost::filesystem::file_size(rolledLogFileName));
    }
    ASSERT_TRUE(adjustedFileSizeLimit >
                boost::filesystem::file_size(relativePath));
  }
}

TEST_F(LoggingTest, verifyWithRelativePathFromCWD) {
  auto relativePath = boost::filesystem::path("foo/bar");

  verifyWithPath(relativePath, 1, 5);

  boost::filesystem::remove_all(boost::filesystem::path("foo"));
}

TEST_F(LoggingTest, verifyWithAbsolutePath) {
  auto absolutePath =
      boost::filesystem::absolute(boost::filesystem::path("foo/bar"));

  verifyWithPath(absolutePath, 1, 5);

  boost::filesystem::remove_all(boost::filesystem::path("foo"));
}

TEST_F(LoggingTest, setLimitsEqualAndRoll) {
  verifyWithPath(boost::filesystem::path(), 1, 1);
}

// Logger is supposed to tack the '.log' extension on any file that doesn't
// already have it.
TEST_F(LoggingTest, verifyExtension) {
  apache::geode::client::Log::init(LogLevel::All, "foo");
  LOGINFO("...");
  apache::geode::client::Log::close();
  ASSERT_TRUE(LoggingTest::numOfLinesInFile("foo.log") > 0);
  boost::filesystem::remove("foo.log");

  apache::geode::client::Log::init(LogLevel::All, "foo.txt");
  LOGINFO("...");
  apache::geode::client::Log::close();
  ASSERT_TRUE(LoggingTest::numOfLinesInFile("foo.txt.log") > 0);
  boost::filesystem::remove("foo.txt.log");
}

// Old version of logger didn't distinguish between rolled log file and
// filename containing '-', so would crash in an atoi() call if you used
// '-' in your log file name.
TEST_F(LoggingTest, verifyFilenameWithDash) {
  apache::geode::client::Log::init(LogLevel::All, "foo-bar.log");
  LOGINFO("...");
  apache::geode::client::Log::close();
  ASSERT_TRUE(LoggingTest::numOfLinesInFile("foo-bar.log") > 0);
  boost::filesystem::remove("foo-bar.log");
}

TEST_F(LoggingTest, countLinesAllLevels) {
  for (LogLevel level : {
           LogLevel::Error,
           LogLevel::Warning,
           LogLevel::Info,
           LogLevel::Default,
           LogLevel::Config,
           LogLevel::Fine,
           LogLevel::Finer,
           LogLevel::Finest,
           LogLevel::Debug,
       }) {
    for (auto logFilename : testFileNames) {
      apache::geode::client::Log::init(level, logFilename);

      LOGERROR("Error Message");
      LOGWARN("Warning Message");
      LOGINFO("Info Message");
      LOGCONFIG("Config Message");
      LOGFINE("Fine Message");
      LOGFINER("Finer Message");
      LOGFINEST("Finest Message");
      LOGDEBUG("Debug Message");

      int lines = LoggingTest::numOfLinesInFile(logFilename);

      ASSERT_TRUE(lines == LoggingTest::expectedWithBanner(level));

      apache::geode::client::Log::close();
      boost::filesystem::remove(logFilename);
    }
  }
}

TEST_F(LoggingTest, countLinesConfigOnwards) {
  verifyLineCountAtLevel(LogLevel::Config);
}

TEST_F(LoggingTest, countLinesInfoOnwards) {
  verifyLineCountAtLevel(LogLevel::Info);
}

TEST_F(LoggingTest, countLinesWarningOnwards) {
  verifyLineCountAtLevel(LogLevel::Warning);
}

TEST_F(LoggingTest, countLinesErrorOnly) {
  verifyLineCountAtLevel(LogLevel::Error);
}

TEST_F(LoggingTest, countLinesNone) { verifyLineCountAtLevel(LogLevel::None); }

TEST_F(LoggingTest, verifyDiskSpaceNotLeaked) {
  for (auto logFilename : testFileNames) {
    verifyDiskSpaceNotLeakedForFile(logFilename);
  }
}

TEST_F(LoggingTest, verifyDiskSpaceNotLeakedWithDefaultLogName) {
  verifyDiskSpaceNotLeakedForFile(nullptr);
}

}  // namespace

max_stars_repo_name: onichols-pivotal/geode-native

max_issues_repo_name: onichols-pivotal/geode-native

max_forks_repo_name: onichols-pivotal/geode-native


text:
#include <catch.hpp>

#include "game.hpp"

#include <numeric_range.hpp>

#include <boost/optional/optional_io.hpp>

using Aronda::Game;

TEST_CASE("Testing the game state placing a stone on en empty board", "[game]")
{
    Game g;

    SECTION("Playing 1 stone on an empty 3 capacity square")
    {
        Aronda::Square s{18};
        CHECK(g.currentState().squareState(s) == Aronda::SquareState{{0, 0}, {}});
        REQUIRE(g.placeStone({s, Aronda::black, 1}).has_value());
        CHECK(g.currentState().squareState(s) == Aronda::SquareState{{1, 0}, {}});
    }

    SECTION("Playing 2 stone on an empty 3 capacity square")
    {
        Aronda::Square s{18};
        CHECK(g.currentState().squareState(s) == Aronda::SquareState{{0, 0}, {}});
        CHECK_NOTHROW(g.placeStone({s, Aronda::black, 2}));
        CHECK(g.currentState().squareState(s) == Aronda::SquareState{{2, 0}, {}});
    }

    SECTION("Playing a third stone on an empty 3 capacity square locks it")
    {
        Aronda::Square s{18};
        CHECK(g.currentState().squareState(s) == Aronda::SquareState{{0, 0}, {}});
        REQUIRE(g.placeStone({s, Aronda::black, 2}).has_value());
        CHECK(g.currentState().squareState(s) == Aronda::SquareState{{2, 0}, {}});
        CHECK_NOTHROW(g.placeStone({s, Aronda::black, 1}));
        CHECK(g.currentState().squareState(s) == Aronda::SquareState{{3, 0}, {Aronda::black}});
    }

    SECTION("Trying to place a fourth stone on an 3 capacity square is forbidden")
    {
        Aronda::Square s{18};
        CHECK(g.currentState().squareState(s) == Aronda::SquareState{{0, 0}, {}});
        REQUIRE(g.placeStone({s, Aronda::black, 2}).has_value());
        CHECK(g.currentState().squareState(s) == Aronda::SquareState{{2, 0}, {}});
        const auto res = g.placeStone({s, Aronda::black, 2});
        REQUIRE(!res.has_value());
        CHECK(res.error() == Aronda::InvalidMove::TooManyStones);
        CHECK(g.currentState().squareState(s) == Aronda::SquareState{{2, 0}, {}});
    }

    SECTION("Playing 1 stone on an empty 2 capacity square")
    {
        Aronda::Square s{19};
        CHECK(g.currentState().squareState(s) == Aronda::SquareState{{0, 0}, {}});
        REQUIRE(g.placeStone({s, Aronda::black, 1}).has_value());
        CHECK(g.currentState().squareState(s) == Aronda::SquareState{{1, 0}, {}});
    }

    SECTION("Playing 2 stone on an empty 2 capacity square locks it")
    {
        Aronda::Square s{19};
        CHECK(g.currentState().squareState(s) == Aronda::SquareState{{0, 0}, {}});
        REQUIRE(g.placeStone({s, Aronda::black, 2}).has_value());
        CHECK(g.currentState().squareState(s) == Aronda::SquareState{{2, 0}, {Aronda::black}});
    }

    SECTION("Trying to place a stone on a locked square is forbidden")
    {
        Aronda::Square s{19};
        CHECK(g.currentState().squareState(s) == Aronda::SquareState{{0, 0}, {}});
        REQUIRE(g.placeStone({s, Aronda::black, 2}).has_value());
        CHECK(g.currentState().squareState(s) == Aronda::SquareState{{2, 0}, {Aronda::black}});
        const auto res = g.placeStone({s, Aronda::black, 1});
        REQUIRE(!res.has_value());
        CHECK(res.error() == Aronda::InvalidMove::SquareLocked);
        CHECK(g.currentState().squareState(s) == Aronda::SquareState{{2, 0}, {Aronda::black}});
    }
}

TEST_CASE("Testing the current player in the game state", "[game]")
{
    Game g;

    SECTION("Black to play only one stone when starting a game")
    {
        CHECK(g.currentPlayer() == Aronda::black);
        CHECK(g.stonesLeftToPlay() == 1);
    }

    SECTION("First couple of moves to check whose turn is it and how many moves he should play")
    {
        REQUIRE(g.placeStone({Aronda::Square{19}, Aronda::black, 1}).has_value());
        CHECK(g.currentPlayer() == Aronda::white);
        CHECK(g.stonesLeftToPlay() == 2);

        REQUIRE(g.placeStone({Aronda::Square{21}, Aronda::white, 1}).has_value());
        CHECK(g.currentPlayer() == Aronda::white);
        CHECK(g.stonesLeftToPlay() == 1);

        REQUIRE(g.placeStone({Aronda::Square{21}, Aronda::white, 1}).has_value());
        CHECK(g.currentPlayer() == Aronda::black);
        CHECK(g.stonesLeftToPlay() == 2);
    }
}

max_stars_repo_name: julienlopez/Aronda

max_issues_repo_name: julienlopez/Aronda

max_forks_repo_name: julienlopez/Aronda


text:
/* C++ headers */
#include <random>

/* boost test framework. */
#define BOOST_TEST_MAIN
#define BOOST_TEST_ALTERNATIVE_INIT_API
#define BOOST_TEST_MODULE math library test
#include <boost/test/unit_test.hpp>

// enable loading of SIMD types.
#define ML_INCLUDE_SIMD

/* user headers. */
#include "ml/all.h"

/*
 * Helpers.
 */

void random_initialize_real_std_vector(std::size_t n, std::vector<ml::vec4>& v)
{
    std::random_device rnd_device;
    std::mt19937 mersenne_engine{rnd_device()};
    std::uniform_real_distribution<float> dist{-1, 1};

    auto gen = [&dist, &mersenne_engine]()
    {
        return ml::vec4{dist(mersenne_engine), dist(mersenne_engine), dist(mersenne_engine), dist(mersenne_engine)};
    };

    v.resize(n);
    generate(std::begin(v), std::end(v), gen);
}

/*
 * memory alignment.
 */
inline bool
  is_aligned(const void* ptr, std::uintptr_t alignment) noexcept
{
    auto iptr = reinterpret_cast<std::uintptr_t>(ptr);
    return !(iptr % alignment);
}

BOOST_AUTO_TEST_SUITE(math)

/*
 * vec2 tests.
 */

BOOST_AUTO_TEST_CASE(vec2)
{
    ml::vec2 v1{1, 0}, v2{0, 1};
    BOOST_TEST(v1.area(v2) == 1.0f);

    ml::vec2 v3{std::cos(M_PI / 4), std::sin(M_PI / 4)};
    ml::vec2 v4{std::cos(M_PI / 4 + M_PI / 2), std::sin(M_PI / 4 + M_PI / 2)};
    BOOST_TEST(v3.area(v4) == 1.0f, boost::test_tools::tolerance(1e-6f));
}

/*
 * tvec2 tests.
 */

BOOST_AUTO_TEST_CASE(tvec2)
{
    ml::tvec2<ml::fixed_24_8_t> v = {1.23, 2.24};
    BOOST_TEST(ml::to_float(v.x) == 1.23f, boost::test_tools::tolerance(4e-3f));
    BOOST_TEST(ml::to_float(v.y) == 2.24f, boost::test_tools::tolerance(4e-3f));
}

/*
 * vec2_fixed tests.
 */

BOOST_AUTO_TEST_CASE(vec2_fixed)
{
    const ml::vec2_fixed<8> v1{1, 0}, v2{0, 1};
    BOOST_TEST(v1.area(v2) == ml::vec2_fixed<8>::type{1});
    BOOST_TEST(v2.area(v1) == ml::vec2_fixed<8>::type{-1});
    BOOST_TEST(v1.area(v1) == ml::vec2_fixed<8>::type{0});
    BOOST_TEST(v2.area(v2) == ml::vec2_fixed<8>::type{0});
}

/*
 * vec4 tests.
 */

// check that we actually using both non-simd and simd types.
static_assert(!std::is_same<ml::vec4, ml::simd::vec4>::value);
static_assert(!std::is_same<ml::mat4x4, ml::simd::mat4x4>::value);

// paranoid checks.
static_assert(sizeof(ml::vec4) == 4 * sizeof(float));
static_assert(sizeof(ml::vec4) == sizeof(__m128));

BOOST_AUTO_TEST_CASE(vec4_alignment)
{
    std::vector<ml::vec4> v;
    random_initialize_real_std_vector(10, v);

    // check vector alignment while being paranoid.
    BOOST_TEST_MESSAGE("Checking 16-byte vector alignment");
    for(size_t i = 0; i < v.size(); ++i)
    {
        BOOST_CHECK(is_aligned(&v[i], 16));
    }
}

BOOST_AUTO_TEST_CASE(vec4_simd_initialization)
{
    ml::simd::vec4 v1{1, 2, 3, 4};
    ml::simd::vec4 v2{1, 2, 3};
    ml::simd::vec4 v3{ml::vec3{1, 2, 3}, 4};

    BOOST_TEST((v1.x == 1 && v1.y == 2 && v1.z == 3 && v1.w == 4));
    BOOST_TEST((v2.x == 1 && v2.y == 2 && v2.z == 3 && v2.w == 1)); /* w-component defaults to one if not specified. */
    BOOST_TEST((v3.x == 1 && v3.y == 2 && v3.z == 3 && v3.w == 4));

    float v[4] = {1, 2, 3, 4};
    ml::simd::vec4 v4{v};
    ml::vec4 v5{v};

    BOOST_TEST((v4.x == 1 && v4.y == 2 && v4.z == 3 && v4.w == 4));
    BOOST_TEST((v5.x == 1 && v5.y == 2 && v5.z == 3 && v5.w == 4));
}

BOOST_AUTO_TEST_CASE(vec4_simd_comparisons)
{
    BOOST_TEST((ml::simd::vec4(1, 2, 3, 4) == ml::simd::vec4(1, 2, 3, 4)));
    BOOST_TEST(!(ml::simd::vec4(1, 2, 3, 4) == ml::simd::vec4(1, 2, 3, 0)));
    BOOST_TEST(!(ml::simd::vec4(1, 2, 3, 4) != ml::simd::vec4(1, 2, 3, 4)));
    BOOST_TEST((ml::simd::vec4(1, 2, 3, 4) != ml::simd::vec4(1, 2, 3, 0)));
}

BOOST_AUTO_TEST_CASE(vec4_component_product)
{
    BOOST_TEST((ml::vec4(1, 2, 3, 4) * ml::vec4(4, 3, 2, 1) == ml::vec4(4, 6, 6, 4)));
    BOOST_TEST((ml::vec4(0, 1, 0, 1) * ml::vec4(-1, 0, -1, 0) == ml::vec4(0, 0, 0, 0)));
    BOOST_TEST((ml::vec4(-2, -3, 3, 2) * ml::vec4(-1, 0, 4, -4) == ml::vec4(2, 0, 12, -8)));
}

BOOST_AUTO_TEST_CASE(vec4_simd_component_product)
{
    BOOST_TEST((ml::simd::vec4(1, 2, 3, 4) * ml::simd::vec4(4, 3, 2, 1) == ml::simd::vec4(4, 6, 6, 4)));
    BOOST_TEST((ml::simd::vec4(0, 1, 0, 1) * ml::simd::vec4(-1, 0, -1, 0) == ml::simd::vec4(0, 0, 0, 0)));
    BOOST_TEST((ml::simd::vec4(-2, -3, 3, 2) * ml::simd::vec4(-1, 0, 4, -4) == ml::simd::vec4(2, 0, 12, -8)));
}

BOOST_AUTO_TEST_CASE(vec4_dot)
{
    BOOST_TEST(ml::dot(ml::vec4(1, -1, 1, -1), ml::vec4(1, -1, 1, -1)) == 4);
    BOOST_TEST(ml::dot(ml::vec4(1, 2, 3, 4), ml::vec4(4, 3, 2, 1)) == 20);
    BOOST_TEST(ml::dot(ml::vec4(1, 2, 3, 4), ml::vec4(-2, 1, -4, 3)) == 0);
}

BOOST_AUTO_TEST_CASE(vec4_simd_dot)
{
    BOOST_TEST(ml::dot(ml::simd::vec4(1, -1, 1, -1), ml::simd::vec4(1, -1, 1, -1)) == 4);
    BOOST_TEST(ml::dot(ml::simd::vec4(1, 2, 3, 4), ml::simd::vec4(4, 3, 2, 1)) == 20);
    BOOST_TEST(ml::dot(ml::simd::vec4(1, 2, 3, 4), ml::simd::vec4(-2, 1, -4, 3)) == 0);
}

BOOST_AUTO_TEST_CASE(vec4_is_zero)
{
    BOOST_TEST(!ml::vec4(0, 0, 0, 1).is_zero());
    BOOST_TEST(ml::vec4(0, 0, 0, 0).is_zero());
}

BOOST_AUTO_TEST_CASE(vec4_simd_is_zero)
{
    BOOST_TEST(!ml::simd::vec4(0, 0, 0, 1).is_zero());
    BOOST_TEST(ml::simd::vec4(0, 0, 0, 0).is_zero());
}

/*
 * mat4x4 tests.
 */

// compare non-simd and simd vec4
bool operator==(const ml::vec4& v1, const ml::simd::vec4& v2)
{
    return v1.x == v2.x && v1.y == v2.y && v1.z == v2.z && v1.w == v2.w;
}

// compare non-simd and simd mat4x4
bool operator==(const ml::mat4x4& m1, const ml::simd::mat4x4& m2)
{
    return m1.rows[0] == m2.rows[0] && m1.rows[1] == m2.rows[1] && m1.rows[2] == m2.rows[2] && m1.rows[3] == m2.rows[3];
}

// initialize simd vec4 from non-simd vec4
ml::simd::vec4 vec_simd_init(const ml::vec4& v)
{
    return {v.x, v.y, v.z, v.w};
}

// initialize simd mat4x4 from non-simd mat4x4
ml::simd::mat4x4 mat_simd_init(const ml::mat4x4& m)
{
    return {vec_simd_init(m.rows[0]), vec_simd_init(m.rows[1]), vec_simd_init(m.rows[2]), vec_simd_init(m.rows[3])};
}

BOOST_AUTO_TEST_CASE(mat4x4_multiplication)
{
    ml::mat4x4 m1{
      {1, 2, 3, 4},
      {2, 4, 3, 1},
      {3, 1, 4, 2},
      {4, 2, 1, 3}};
    m1 = m1 * m1;

    ml::simd::mat4x4 m2{
      {1, 2, 3, 4},
      {2, 4, 3, 1},
      {3, 1, 4, 2},
      {4, 2, 1, 3}};
    m2 = m2 * m2;

    BOOST_TEST((m1.rows[0] == m2.rows[0]));
    BOOST_TEST((m1.rows[1] == m2.rows[1]));
    BOOST_TEST((m1.rows[2] == m2.rows[2]));
    BOOST_TEST((m1.rows[3] == m2.rows[3]));

    m1.transpose();
    m2.transpose();

    // for better tracking in case of test failure.
    auto m1_t = m1;
    auto m2_t = m2;
    BOOST_TEST((m1_t.rows[0] == m2_t.rows[0]));
    BOOST_TEST((m1_t.rows[1] == m2_t.rows[1]));
    BOOST_TEST((m1_t.rows[2] == m2_t.rows[2]));
    BOOST_TEST((m1_t.rows[3] == m2_t.rows[3]));
}

template<typename T>
T get_random_vec4()
{
    return {static_cast<float>(rand() % 9 - 5), static_cast<float>(rand() % 9 - 5), static_cast<float>(rand() % 9 - 5), static_cast<float>(rand() % 9 + 1)};
}

template<typename M, typename T>
M get_random_mat()
{
    return {
      get_random_vec4<T>(),
      get_random_vec4<T>(),
      get_random_vec4<T>(),
      get_random_vec4<T>()};
}

BOOST_AUTO_TEST_CASE(mat4x4_randomized_multiplication)
{
    // random multiplication tests.
    for(int i = 0; i < 1000; ++i)
    {
        ml::mat4x4 rm1 = get_random_mat<ml::mat4x4, ml::vec4>();
        ml::mat4x4 rm2 = get_random_mat<ml::mat4x4, ml::vec4>();

        ml::simd::mat4x4 rm1_simd = mat_simd_init(rm1), rm2_simd = mat_simd_init(rm2);

        ml::mat4x4 res = rm1 * rm2;
        ml::simd::mat4x4 res_simd = rm1_simd * rm2_simd;

        BOOST_REQUIRE(res.rows[0] == res_simd.rows[0]);
        BOOST_REQUIRE(res.rows[1] == res_simd.rows[1]);
        BOOST_REQUIRE(res.rows[2] == res_simd.rows[2]);
        BOOST_REQUIRE(res.rows[3] == res_simd.rows[3]);
    }
}

/*
 * math functions.
 */

BOOST_AUTO_TEST_CASE(lerp)
{
    BOOST_TEST(ml::lerp<float>(0.3f, 1.f, 2.f) == static_cast<float>(1.f * (1.f - 0.3f) + 2.f * 0.3f), boost::test_tools::tolerance(1e-8f));
    BOOST_TEST(ml::lerp<double>(0.3, 1, 2) == static_cast<double>(1.0 * (1.0 - 0.3) + 2.0 * 0.3), boost::test_tools::tolerance(1e-8));

    ml::vec4 a{1.1, 2.2, 3.3, 4.4};
    ml::vec4 b{-9.3, -10.4, -11.5, -12.6};
    ml::vec4 l{ml::lerp<ml::vec4>(0.4, a, b)};
    BOOST_TEST(l.x == -3.06f, boost::test_tools::tolerance(1e-6f));
    BOOST_TEST(l.y == -2.84f, boost::test_tools::tolerance(1e-6f));
    BOOST_TEST(l.z == -2.62f, boost::test_tools::tolerance(1e-6f));
    BOOST_TEST(l.w == -2.4f, boost::test_tools::tolerance(1e-6f));

    for(int i = 0; i < 100; ++i)
    {
        float t = static_cast<float>(i) / 100.f;

        l = ml::lerp(t, a, b);
        BOOST_TEST(l.x == ml::lerp(t, a.x, b.x), boost::test_tools::tolerance(1e-5f));
        BOOST_TEST(l.y == ml::lerp(t, a.y, b.y), boost::test_tools::tolerance(1e-5f));
        BOOST_TEST(l.z == ml::lerp(t, a.z, b.z), boost::test_tools::tolerance(1e-5f));
        BOOST_TEST(l.w == ml::lerp(t, a.w, b.w), boost::test_tools::tolerance(1e-5f));
    }
}

BOOST_AUTO_TEST_CASE(radians_degrees)
{
    /* these are just linear transformations.... */

    BOOST_TEST(ml::to_radians(0) == 0);
    BOOST_TEST(ml::to_radians(90) == static_cast<float>(M_PI_2), boost::test_tools::tolerance(1e-9f));
    BOOST_TEST(ml::to_radians(180) == static_cast<float>(M_PI), boost::test_tools::tolerance(1e-9f));
    BOOST_TEST(ml::to_radians(270) == static_cast<float>(3 * M_PI_2), boost::test_tools::tolerance(1e-9f));
    BOOST_TEST(ml::to_radians(360) == static_cast<float>(2 * M_PI), boost::test_tools::tolerance(1e-9f));
    BOOST_TEST(ml::to_radians(-90) == static_cast<float>(-M_PI_2), boost::test_tools::tolerance(1e-9f));
    BOOST_TEST(ml::to_radians(-180) == static_cast<float>(-M_PI), boost::test_tools::tolerance(1e-9f));
    BOOST_TEST(ml::to_radians(-270) == static_cast<float>(-3 * M_PI_2), boost::test_tools::tolerance(1e-9f));
    BOOST_TEST(ml::to_radians(-360) == static_cast<float>(-2 * M_PI), boost::test_tools::tolerance(1e-9f));

    BOOST_TEST(ml::to_degrees(0) == 0);
    BOOST_TEST(ml::to_degrees(static_cast<float>(M_PI_2)) == static_cast<float>(90), boost::test_tools::tolerance(1e-9f));
    BOOST_TEST(ml::to_degrees(static_cast<float>(M_PI)) == static_cast<float>(180), boost::test_tools::tolerance(1e-9f));
    BOOST_TEST(ml::to_degrees(static_cast<float>(3 * M_PI_2)) == static_cast<float>(270), boost::test_tools::tolerance(1e-9f));
    BOOST_TEST(ml::to_degrees(static_cast<float>(2 * M_PI)) == static_cast<float>(360), boost::test_tools::tolerance(1e-9f));
    BOOST_TEST(ml::to_degrees(static_cast<float>(-M_PI_2)) == static_cast<float>(-90), boost::test_tools::tolerance(1e-9f));
    BOOST_TEST(ml::to_degrees(static_cast<float>(-M_PI)) == static_cast<float>(-180), boost::test_tools::tolerance(1e-9f));
    BOOST_TEST(ml::to_degrees(static_cast<float>(-3 * M_PI_2)) == static_cast<float>(-270), boost::test_tools::tolerance(1e-9f));
    BOOST_TEST(ml::to_degrees(static_cast<float>(-2 * M_PI)) == static_cast<float>(-360), boost::test_tools::tolerance(1e-9f));

    for(int i = 0; i < 100; ++i)
    {
        BOOST_TEST(ml::to_radians(ml::to_degrees(static_cast<float>(i))) == static_cast<float>(i), boost::test_tools::tolerance(1e-6f));
        BOOST_TEST(ml::to_degrees(ml::to_radians(static_cast<float>(i))) == static_cast<float>(i), boost::test_tools::tolerance(1e-6f));
    }
}

BOOST_AUTO_TEST_SUITE_END()

max_stars_repo_name: flubbe/ml

max_issues_repo_name: flubbe/ml

max_forks_repo_name: flubbe/ml


text:
// Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/

/// @file dialog_selection.cpp
/// @brief Select Lines dialogue box and logic
/// @ingroup secondary_ui
///

#include "config.h"

#include "dialog_selection.h"

#include "ass_dialogue.h"
#include "ass_file.h"
#include "compat.h"
#include "help_button.h"
#include "include/aegisub/context.h"
#include "libresrc/libresrc.h"
#include "options.h"
#include "search_replace_engine.h"
#include "selection_controller.h"
#include "utils.h"

#include <libaegisub/of_type_adaptor.h>

#include <algorithm>
#include <boost/algorithm/string/find.hpp>
#include <boost/algorithm/string/predicate.hpp>

#include <wx/checkbox.h>
#include <wx/combobox.h>
#include <wx/msgdlg.h>
#include <wx/radiobox.h>
#include <wx/radiobut.h>
#include <wx/sizer.h>
#include <wx/textctrl.h>

enum {
	ACTION_SET = 0,
	ACTION_ADD,
	ACTION_SUB,
	ACTION_INTERSECT
};

enum {
	MODE_EXACT = 0,
	MODE_CONTAINS,
	MODE_REGEXP
};

static std::set<AssDialogue*> process(std::string const& match_text, bool match_case, int mode, bool invert, bool comments, bool dialogue, int field_n, AssFile *ass) {
	SearchReplaceSettings settings = {
		match_text,
		std::string(),
		static_cast<SearchReplaceSettings::Field>(field_n),
		SearchReplaceSettings::Limit::ALL,
		match_case,
		mode == MODE_REGEXP,
		false,
		false,
		mode == MODE_EXACT
	};

	auto predicate = SearchReplaceEngine::GetMatcher(settings);

	std::set<AssDialogue*> matches;
	for (auto diag : ass->Line | agi::of_type<AssDialogue>()) {
		if (diag->Comment && !comments) continue;
		if (!diag->Comment && !dialogue) continue;

		if (invert != predicate(diag, 0))
			matches.insert(diag);
	}

	return matches;
}

DialogSelection::DialogSelection(agi::Context *c) :
wxDialog (c->parent, -1, _("Select"), wxDefaultPosition, wxDefaultSize, wxCAPTION)
, con(c)
{
	SetIcon(GETICON(select_lines_button_16));

	wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);

	wxSizerFlags main_flags = wxSizerFlags().Expand().Border();

	wxRadioButton *select_matching_lines = nullptr;
	{
		wxSizer *match_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Match"));
		{
			wxSizerFlags radio_flags = wxSizerFlags().Border(wxLEFT | wxRIGHT);
			wxSizer *match_radio_line = new wxBoxSizer(wxHORIZONTAL);
			match_radio_line->Add(select_matching_lines = new wxRadioButton(this, -1, _("&Matches"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP), radio_flags);
			match_radio_line->Add(select_unmatching_lines = new wxRadioButton(this, -1, _("&Doesn't Match")), radio_flags);
			match_radio_line->Add(case_sensitive = new wxCheckBox(this, -1, _("Match c&ase")), radio_flags);
			match_sizer->Add(match_radio_line);
		}
		match_sizer->Add(match_text = new wxTextCtrl(this, -1, to_wx(OPT_GET("Tool/Select Lines/Text")->GetString())), main_flags);

		main_sizer->Add(match_sizer, main_flags);
	}

	{
		wxString modes[] = { _("&Exact match"), _("&Contains"), _("&Regular Expression match") };
		main_sizer->Add(match_mode = new wxRadioBox(this, -1, _("Mode"), wxDefaultPosition, wxDefaultSize, 3, modes, 1), main_flags);
	}

	{
		wxString fields[] = { _("&Text"), _("&Style"), _("Act&or"), _("E&ffect") };
		main_sizer->Add(dialogue_field = new wxRadioBox(this, -1, _("In Field"), wxDefaultPosition, wxDefaultSize, 4, fields), main_flags);
	}

	{
		wxSizer *comment_sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Match dialogues/comments"));
		comment_sizer->Add(apply_to_dialogue = new wxCheckBox(this, -1, _("D&ialogues")), wxSizerFlags().Border());
		comment_sizer->Add(apply_to_comments = new wxCheckBox(this, -1, _("Comme&nts")), wxSizerFlags().Border());
		main_sizer->Add(comment_sizer, main_flags);
	}

	{
		wxString actions[] = { _("Set se&lection"), _("&Add to selection"), _("S&ubtract from selection"), _("Intersect &with selection") };
		main_sizer->Add(selection_change_type = new wxRadioBox(this, -1, _("Action"), wxDefaultPosition, wxDefaultSize, 4, actions, 1), main_flags);
	}

	main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL | wxHELP), main_flags);

	SetSizerAndFit(main_sizer);
	CenterOnParent();

	dialogue_field->SetSelection(OPT_GET("Tool/Select Lines/Field")->GetInt());
	selection_change_type->SetSelection(OPT_GET("Tool/Select Lines/Action")->GetInt());
	case_sensitive->SetValue(OPT_GET("Tool/Select Lines/Match/Case")->GetBool());
	apply_to_dialogue->SetValue(OPT_GET("Tool/Select Lines/Match/Dialogue")->GetBool());
	apply_to_comments->SetValue(OPT_GET("Tool/Select Lines/Match/Comment")->GetBool());
	select_unmatching_lines->SetValue(!!OPT_GET("Tool/Select Lines/Condition")->GetInt());
	select_matching_lines->SetValue(!select_unmatching_lines->GetValue());
	match_mode->SetSelection(OPT_GET("Tool/Select Lines/Mode")->GetInt());

	Bind(wxEVT_BUTTON, &DialogSelection::Process, this, wxID_OK);
	Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Select Lines"), wxID_HELP);
	apply_to_comments->Bind(wxEVT_CHECKBOX, std::bind(&DialogSelection::OnDialogueCheckbox, this, apply_to_dialogue));
	apply_to_dialogue->Bind(wxEVT_CHECKBOX, std::bind(&DialogSelection::OnDialogueCheckbox, this, apply_to_comments));
}

DialogSelection::~DialogSelection() {
	OPT_SET("Tool/Select Lines/Text")->SetString(from_wx(match_text->GetValue()));
	OPT_SET("Tool/Select Lines/Condition")->SetInt(select_unmatching_lines->GetValue());
	OPT_SET("Tool/Select Lines/Field")->SetInt(dialogue_field->GetSelection());
	OPT_SET("Tool/Select Lines/Action")->SetInt(selection_change_type->GetSelection());
	OPT_SET("Tool/Select Lines/Mode")->SetInt(match_mode->GetSelection());
	OPT_SET("Tool/Select Lines/Match/Case")->SetBool(case_sensitive->IsChecked());
	OPT_SET("Tool/Select Lines/Match/Dialogue")->SetBool(apply_to_dialogue->IsChecked());
	OPT_SET("Tool/Select Lines/Match/Comment")->SetBool(apply_to_comments->IsChecked());
}

void DialogSelection::Process(wxCommandEvent&) {
	std::set<AssDialogue*> matches;

	try {
		matches = process(
			from_wx(match_text->GetValue()), case_sensitive->IsChecked(),
			match_mode->GetSelection(), select_unmatching_lines->GetValue(),
			apply_to_comments->IsChecked(), apply_to_dialogue->IsChecked(),
			dialogue_field->GetSelection(), con->ass);
	}
	catch (agi::Exception const&) {
		Close();
		return;
	}

	int action = selection_change_type->GetSelection();

	SubtitleSelection old_sel, new_sel;
	if (action != ACTION_SET)
		con->selectionController->GetSelectedSet(old_sel);

	wxString message;
	size_t count = 0;
	switch (action) {
		case ACTION_SET:
			new_sel = matches;
			switch (count = new_sel.size()) {
				case 0:  message = _("Selection was set to no lines"); break;
				case 1:  message = _("Selection was set to one line"); break;
				default: message = wxString::Format(_("Selection was set to %u lines"), (unsigned)count);
			}
			break;

		case ACTION_ADD:
			set_union(old_sel.begin(), old_sel.end(), matches.begin(), matches.end(), inserter(new_sel, new_sel.begin()));
			switch (count = new_sel.size() - old_sel.size()) {
				case 0:  message = _("No lines were added to selection"); break;
				case 1:  message = _("One line was added to selection"); break;
				default: message = wxString::Format(_("%u lines were added to selection"), (unsigned)count);
			}
			break;

		case ACTION_SUB:
			set_difference(old_sel.begin(), old_sel.end(), matches.begin(), matches.end(), inserter(new_sel, new_sel.begin()));
			switch (count = old_sel.size() - new_sel.size()) {
				case 0:  message = _("No lines were removed from selection"); break;
				case 1:  message = _("One line was removed from selection"); break;
				default: message = wxString::Format(_("%u lines were removed from selection"), (unsigned)count);
			}
			break;

		case ACTION_INTERSECT:
			set_intersection(old_sel.begin(), old_sel.end(), matches.begin(), matches.end(), inserter(new_sel, new_sel.begin()));
			switch (count = old_sel.size() - new_sel.size()) {
				case 0:  message = _("No lines were removed from selection"); break;
				case 1:  message = _("One line was removed from selection"); break;
				default: message = wxString::Format(_("%u lines were removed from selection"), (unsigned)count);
			}
			break;
	}

	if (count == 0)
		wxMessageBox(message, _("Selection"), wxOK | wxCENTER, this);
	else
		StatusTimeout(message);

	if (new_sel.size() && !new_sel.count(con->selectionController->GetActiveLine()))
		con->selectionController->SetActiveLine(*new_sel.begin());
	con->selectionController->SetSelectedSet(new_sel);

	AssDialogue *new_active = con->selectionController->GetActiveLine();
	if (new_sel.size() && !new_sel.count(new_active))
		new_active = *new_sel.begin();
	con->selectionController->SetSelectionAndActive(new_sel, new_active);

	Close();
}

void DialogSelection::OnDialogueCheckbox(wxCheckBox *chk) {
	if(!apply_to_dialogue->IsChecked() && !apply_to_comments->GetValue())
		chk->SetValue(true);
}

max_stars_repo_name: rcombs/Aegisub

max_issues_repo_name: rcombs/Aegisub

max_forks_repo_name: rcombs/Aegisub


text:
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE LibmeshEntityTester
#include <boost/test/included/unit_test.hpp>

#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <vector>

#include <LibmeshAdjacencies.hpp>
#include <LibmeshEntity.hpp>
#include <LibmeshEntityExtraData.hpp>

#include <Teuchos_Array.hpp>
#include <Teuchos_DefaultComm.hpp>
#include <Teuchos_DefaultMpiComm.hpp>
#include <Teuchos_FancyOStream.hpp>
#include <Teuchos_OpaqueWrapper.hpp>
#include <Teuchos_RCP.hpp>
#include <Teuchos_Tuple.hpp>
#include <Teuchos_VerboseObject.hpp>

#include <libmesh/cell_hex8.h>
#include <libmesh/libmesh.h>
#include <libmesh/mesh.h>
#include <libmesh/node.h>
#include <libmesh/parallel.h>
#include <libmesh/point.h>

//---------------------------------------------------------------------------//
// MPI Setup
//---------------------------------------------------------------------------//

template <class Ordinal>
Teuchos::RCP<const Teuchos::Comm<Ordinal>> getDefaultComm()
{
#ifdef HAVE_MPI
    return Teuchos::DefaultComm<Ordinal>::getComm();
#else
    return Teuchos::rcp( new Teuchos::SerialComm<Ordinal>() );
#endif
}

//---------------------------------------------------------------------------//
// Hex-8 test.
BOOST_AUTO_TEST_CASE( checkLibmeshEntity )
{
	const std::string argv_string = "unit_test --keep-cout";
	const char *argv_char = argv_string.c_str();
	Teuchos::GlobalMPISession mpiSession(
			&boost::unit_test::framework::master_test_suite().argc,
			&boost::unit_test::framework::master_test_suite().argv);
	auto comm = Teuchos::DefaultComm<int>::getComm();
	auto mpi_comm = Teuchos::rcp_dynamic_cast<const Teuchos::MpiComm<int>>(
			comm);
	auto opaque_comm = mpi_comm->getRawMpiComm();
	auto raw_comm = (*opaque_comm)();

    // Create the mesh.
    int space_dim = 3;
    libMesh::LibMeshInit libmesh_init( 1, &argv_char, raw_comm );
    BOOST_VERIFY( libMesh::initialized() );
    BOOST_VERIFY( (int)libmesh_init.comm().rank() == comm->getRank() );
    Teuchos::RCP<libMesh::Mesh> mesh =
        Teuchos::rcp( new libMesh::Mesh( libmesh_init.comm(), space_dim ) );

    // Create the nodes.
    int rank = comm->getRank();
    Teuchos::Array<libMesh::Node *> nodes( 8 );
    double node_coords[3];
    node_coords[0] = 0.0;
    node_coords[1] = 0.0;
    node_coords[2] = 0.0;
    nodes[0] = mesh->add_point(
        libMesh::Point( node_coords[0], node_coords[1], node_coords[2] ), 0,
        rank );

    node_coords[0] = 1.0;
    node_coords[1] = 0.0;
    node_coords[2] = 0.0;
    nodes[1] = mesh->add_point(
        libMesh::Point( node_coords[0], node_coords[1], node_coords[2] ), 1,
        rank );

    node_coords[0] = 1.0;
    node_coords[1] = 1.0;
    node_coords[2] = 0.0;
    nodes[2] = mesh->add_point(
        libMesh::Point( node_coords[0], node_coords[1], node_coords[2] ), 2,
        rank );

    node_coords[0] = 0.0;
    node_coords[1] = 1.0;
    node_coords[2] = 0.0;
    nodes[3] = mesh->add_point(
        libMesh::Point( node_coords[0], node_coords[1], node_coords[2] ), 3,
        rank );

    node_coords[0] = 0.0;
    node_coords[1] = 0.0;
    node_coords[2] = 1.0;
    nodes[4] = mesh->add_point(
        libMesh::Point( node_coords[0], node_coords[1], node_coords[2] ), 4,
        rank );

    node_coords[0] = 1.0;
    node_coords[1] = 0.0;
    node_coords[2] = 1.0;
    nodes[5] = mesh->add_point(
        libMesh::Point( node_coords[0], node_coords[1], node_coords[2] ), 5,
        rank );

    node_coords[0] = 1.0;
    node_coords[1] = 1.0;
    node_coords[2] = 1.0;
    nodes[6] = mesh->add_point(
        libMesh::Point( node_coords[0], node_coords[1], node_coords[2] ), 6,
        rank );

    node_coords[0] = 0.0;
    node_coords[1] = 1.0;
    node_coords[2] = 1.0;
    nodes[7] = mesh->add_point(
        libMesh::Point( node_coords[0], node_coords[1], node_coords[2] ), 7,
        rank );

    // Make a hex-8.
    libMesh::Elem *hex_elem = mesh->add_elem( new libMesh::Hex8 );
    hex_elem->processor_id() = rank;
    hex_elem->set_id() = 2 * rank;
    for ( int i = 0; i < 8; ++i )
        hex_elem->set_node( i ) = nodes[i];

    // Make 2 subdomains and put the hex-8 in the first subdomain.
    int subdomain_1_id = 1;
    int subdomain_2_id = 2;
    std::set<libMesh::subdomain_id_type> subdomain_ids;
    subdomain_ids.insert( subdomain_1_id );
    subdomain_ids.insert( subdomain_2_id );
    hex_elem->subdomain_id() = subdomain_1_id;

    // Make 2 boundaries and add the first elem side to one and first node to
    // the second.
    int boundary_1_id = 1;
    int boundary_2_id = 2;
    mesh->get_boundary_info().add_side( hex_elem, 0, boundary_1_id );
    mesh->get_boundary_info().add_node( nodes[0], boundary_2_id );

    // Check libmesh validity.
    mesh->libmesh_assert_valid_parallel_ids();

    // Make an adjacency data structure.
    LibmeshAdapter::LibmeshAdjacencies adjacencies( mesh );

    // Create a  entity for the hex.
   auto dtk_entity =
        LibmeshAdapter::LibmeshEntity<libMesh::Elem>(
            Teuchos::ptr( hex_elem ), mesh.ptr(),
            Teuchos::ptrFromRef( adjacencies ) );

    // Print out the entity.
    Teuchos::RCP<Teuchos::FancyOStream> fancy_out =
        Teuchos::VerboseObjectBase::getDefaultOStream();
//    dtk_entity.describe( *fancy_out );

    // Test the entity.
    BOOST_VERIFY( hex_elem->id() == dtk_entity.id() );
    BOOST_VERIFY( rank == dtk_entity.ownerRank() );
    BOOST_VERIFY( space_dim == dtk_entity.topologicalDimension() );
    BOOST_VERIFY( space_dim == dtk_entity.physicalDimension() );

    BOOST_VERIFY( dtk_entity.inBlock( subdomain_1_id ) );
    BOOST_VERIFY( !dtk_entity.inBlock( subdomain_2_id ) );

    BOOST_VERIFY( dtk_entity.onBoundary( boundary_1_id ) );
    BOOST_VERIFY( !dtk_entity.onBoundary( boundary_2_id ) );

   auto elem_extra_data =
        dtk_entity.extraData();
   BOOST_VERIFY( hex_elem ==
                   Teuchos::rcp_dynamic_cast<
                       LibmeshAdapter::LibmeshEntityExtraData<libMesh::Elem>>(
                       elem_extra_data )
                       ->d_libmesh_geom.getRawPtr() );

    Teuchos::Tuple<double, 6> hex_bounds;
    dtk_entity.boundingBox( hex_bounds );
    BOOST_VERIFY( 0.0 == hex_bounds[0] );
    BOOST_VERIFY( 0.0 == hex_bounds[1] );
    BOOST_VERIFY( 0.0 == hex_bounds[2] );
    BOOST_VERIFY( 1.0 == hex_bounds[3] );
    BOOST_VERIFY( 1.0 == hex_bounds[4] );
    BOOST_VERIFY( 1.0 == hex_bounds[5] );

    // Test a node.
  auto dtk_node =
        LibmeshAdapter::LibmeshEntity<libMesh::Node>(
            Teuchos::ptr( nodes[0] ), mesh.ptr(),
            Teuchos::ptrFromRef( adjacencies ) );
  BOOST_VERIFY( nodes[0]->id() == dtk_node.id() );
  BOOST_VERIFY( rank == dtk_node.ownerRank() );
  BOOST_VERIFY( 0 == dtk_node.topologicalDimension() );
  BOOST_VERIFY( space_dim == dtk_node.physicalDimension() );

  BOOST_VERIFY( dtk_node.inBlock( subdomain_1_id ) );
  BOOST_VERIFY( !dtk_node.inBlock( subdomain_2_id ) );

  BOOST_VERIFY( !dtk_node.onBoundary( boundary_1_id ) );
  BOOST_VERIFY( dtk_node.onBoundary( boundary_2_id ) );

    auto node_extra_data =
        dtk_node.extraData();
    BOOST_VERIFY( nodes[0] ==
                   Teuchos::rcp_dynamic_cast<
				   LibmeshAdapter::LibmeshEntityExtraData<libMesh::Node>>(
                       node_extra_data )
                       ->d_libmesh_geom.getRawPtr() );

    Teuchos::Tuple<double, 6> node_bounds;
    dtk_node.boundingBox( node_bounds );
    BOOST_VERIFY( 0.0 == node_bounds[0] );
    BOOST_VERIFY( 0.0 == node_bounds[1] );
    BOOST_VERIFY( 0.0 == node_bounds[2] );
    BOOST_VERIFY( 0.0 == node_bounds[3] );
    BOOST_VERIFY( 0.0 == node_bounds[4] );
    BOOST_VERIFY( 0.0 == node_bounds[5] );

    // Print out the node.
//    dtk_node.describe( *fancy_out );
}

//---------------------------------------------------------------------------//
// end tstLibmeshEntity.cpp
//---------------------------------------------------------------------------//


text:
#include "dknn/dknn.hpp"
#include "dknn/__detail__/dataset.hpp"
#include "dknn/__detail__/local_jobs.hpp"
#include "dknn/__detail__/gather.hpp"

#include <mpi.h>

#include <boost/format.hpp>
#include <string>

namespace dknn {
  using std::string;
  using std::vector;

  static int _rank = 0;
  static int _world_size = 1;

  bool init(int* pargc, char*** pargv) {
    MPI_Init(pargc, pargv);

    MPI_Comm_size(MPI_COMM_WORLD, &_world_size);
    MPI_Comm_rank(MPI_COMM_WORLD, &_rank);

    if (_world_size <= 0) {
      auto fmt = boost::format("invalid world size: %1%");
      std::cerr << fmt % _world_size << std::endl;
      return false;
    }

    if (_rank < 0 || _rank >= _world_size) {
      auto fmt = boost::format("invalid node rank: %1%; world size = %2%");
      std::cerr << fmt % _rank % _world_size << std::endl;
      return false;
    }

    if (!__dknn_gather__init()) {
      auto fmt = boost::format("MPI type commit failed");
      std::cerr << fmt << std::endl;
      return false;
    }

    auto argv = *pargv;
    load_feature_dataset(argv[1]);

    return true;
  }

  void term() {
    MPI_Barrier(MPI_COMM_WORLD);
    MPI_Finalize();
  }

  int node_rank() {
    return _rank;
  }

  int world_size() {
    return _world_size;
  }

  static knn_set_query_result_t crop_nearest_k(
    size_t k, knn_set_query_result_t gathered_set_query_result) {
    knn_set_query_result_t result;

    for (auto& query_result : gathered_set_query_result) {
      auto match_comparison = [](auto const& a, auto const& b) {
        auto const& [id_a, distance_a, class_a] = a;
        auto const& [id_b, distance_b, class_b] = b;
        return distance_a <= distance_b;
      };
      std::partial_sort(
        query_result.begin(), query_result.begin() + k, query_result.end(),
        match_comparison);

      knn_query_result_t nearest_k;
      for (size_t i = 0; i < k; i++)
        nearest_k.emplace_back(query_result.at(i));
      result.emplace_back(nearest_k);
    }
    return result;
  }

  static feature_class_t classify(knn_query_result_t const& result) {
    std::map<feature_class_t, size_t> counts;
    // zero-initialize
    for (auto const& [_, __, _class] : result)
      counts[_class] = 0;

    for (auto const& [_, __, _class] : result)
      counts[_class]++;

    auto count_compare =
      [](auto const& class_count_a, auto const& class_count_b) {
        auto const& [class_a, count_a] = class_count_a;
        auto const& [class_b, count_b] = class_count_b;
        return count_a < count_b;
      };

    auto const& [max_class, max_count] =
      *std::max_element(counts.begin(), counts.end(), count_compare);
    return max_class;
  }

  static vector<feature_class_t> classify(
    knn_set_query_result_t const& set_query_results) {
    vector<feature_class_t> result;
    for (auto const& query_result : set_query_results)
      result.emplace_back(classify(query_result));
    return result;
  }

  vector<feature_class_t> mpi_brute_force_nearest_k(
    size_t k, feature_set_t const& query_set) {
    // this part is executed in *scattered context*
    // (i.e. executed in each node concurrently).
    auto scattered_knn_results = nearest_k(k, query_set);

    // then we gather the result to the master node.
    auto gathered_knn_results = gather(k, scattered_knn_results);
    auto nearest_k_set = crop_nearest_k(k, gathered_knn_results);
    return classify(nearest_k_set);
  }
}  // namespace dknn

max_stars_repo_name: didrod/dknn

max_issues_repo_name: didrod/dknn

max_forks_repo_name: didrod/dknn


text:
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2016 by EMC Corporation, All Rights Reserved
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is EMC Corporation
///
/// @author Andrey Abramov
/// @author Vasiliy Nabatchikov
////////////////////////////////////////////////////////////////////////////////

#if defined (__GNUC__)
  #pragma GCC diagnostic push
  #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  #pragma GCC diagnostic ignored "-Wunused-variable"
#endif

  #include <boost/filesystem/operations.hpp>

#if defined (__GNUC__)
  #pragma GCC diagnostic pop
#endif

#include "log.hpp"
#include "utils/utf8_path.hpp"

#include "so_utils.hpp"

#if defined(_MSC_VER) // Microsoft compiler
  #define NOMINMAX
  #include <windows.h>

  NS_LOCAL

  std::string dlerror() {
    auto error = GetLastError();

    if (!error) {
      return std::string();
    }

    LPVOID lpMsgBuf;
    auto bufLen = FormatMessage(
      FORMAT_MESSAGE_ALLOCATE_BUFFER |
      FORMAT_MESSAGE_FROM_SYSTEM |
      FORMAT_MESSAGE_IGNORE_INSERTS,
      NULL,
      error,
      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
      (LPTSTR)&lpMsgBuf,
      0,
      NULL
    );

    if (!bufLen) {
      return std::string();
    }

    auto lpMsgStr = (LPTSTR)lpMsgBuf;
    std::string result(lpMsgStr, lpMsgStr+bufLen);

    LocalFree(lpMsgBuf);

    return result;
  }

  NS_END

#elif defined(__GNUC__) // GNU compiler
  #include <dlfcn.h>
#else
  #error define your copiler
#endif

NS_LOCAL

#if defined(_MSC_VER) // Microsoft compiler
  const std::string FILENAME_EXTENSION(".dll");
#elif defined(__APPLE__) // MacOS
  const std::string FILENAME_EXTENSION(".dylib");
#elif defined(__GNUC__) // GNU compiler
  const std::string FILENAME_EXTENSION(".so");
#else
  const std::string FILENAME_EXTENSION("");
#endif

NS_END

NS_ROOT

void* load_library(const char* soname, int mode /* = 2 */) {
  if (!soname) {
    return nullptr;
  }

  std::string name(soname);

  name += FILENAME_EXTENSION;

#if defined(_MSC_VER) // Microsoft compiler
  UNUSED(mode);
  auto handle = static_cast<void*>(::LoadLibraryA(name.c_str()));
#elif defined(__GNUC__) // GNU compiler
  auto handle = dlopen(name.c_str(), mode);
#endif

  if (!handle) {
    #ifdef _WIN32
      IR_FRMT_ERROR("load failed of shared object: %s error: %s", name.c_str(), dlerror().c_str());
    #else
      IR_FRMT_ERROR("load failed of shared object: %s error: %s", name.c_str(), dlerror());
    #endif
  }

  return handle;
}

void* get_function(void* handle, const char* fname) {
#if defined(_MSC_VER) // Microsoft compiler
  return static_cast<void*>(::GetProcAddress(static_cast<HINSTANCE>(handle), fname));
#elif defined(__GNUC__) // GNU compiler
  return dlsym(handle, fname);
#endif
}

bool free_library(void* handle) {
#if defined(_MSC_VER) // Microsoft compiler
  return TRUE == ::FreeLibrary(static_cast<HINSTANCE>(handle));
#elif defined(__GNUC__) // GNU compiler
  return dlclose(handle);
#endif
}

void load_libraries(
  const std::string& path,
  const std::string& prefix,
  const std::string& suffix
) {
  ::boost::filesystem::path pluginPath(path);

  if (!::boost::filesystem::is_directory(pluginPath)) {
    auto u8path = (utf8_path(pluginPath)).utf8();

    IR_FRMT_INFO("library load failed, not a plugin path: %s", u8path.c_str());

    return; // no plugins directory
  }

  for (::boost::filesystem::directory_iterator itr(pluginPath), end; itr != end; ++itr) {
    if (::boost::filesystem::is_directory(itr->status())) {
      continue;
    }

    auto& file = itr->path();
    auto extension = utf8_path(file.extension()).utf8();

    if (FILENAME_EXTENSION != extension.c_str()) {
      continue; // skip non-library extensions
    }

    auto stem = utf8_path(file.stem()).utf8();

    if (stem.size() < prefix.size() + suffix.size() ||
        strncmp(stem.c_str(), prefix.c_str(), prefix.size()) != 0 ||
        strncmp(stem.c_str() + stem.size() - suffix.size(), suffix.c_str(), suffix.size()) != 0) {
      continue; // filename does not match
    }

    auto u8path = (utf8_path(file.parent_path())/=stem).utf8(); // strip extension

    // FIMXE check double-load of same dll
    void* handle = load_library(u8path.c_str(), 1);

    if (!handle) {
      IR_FRMT_ERROR("library load failed for path: %s", u8path.c_str());
    }
  }
}

NS_END // ROOT

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------