<|start_filename|>src/mc_control/samples/ExternalForces/src/states/UpdateForces.cpp<|end_filename|>
/*
 * Copyright 2021 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include "UpdateForces.h"
#include <mc_control/fsm/Controller.h>
#include <mc_rtc/ConfigurationHelpers.h>
#include "../mc_external_forces_controller.h"

void UpdateForces::configure(const mc_rtc::Configuration & config)
{
  config_.load(config);
}

void UpdateForces::start(mc_control::fsm::Controller & _ctl)
{
  auto & ctl = static_cast<ExternalForcesController &>(_ctl);

  for(auto task : ctl.solver().tasks())
  {
    if(auto stabilizerTask = dynamic_cast<mc_tasks::lipm_stabilizer::StabilizerTask *>(task))
    {
      stabilizerTask_ = stabilizerTask;
    }
    else if(auto impedanceTask = dynamic_cast<mc_tasks::force::ImpedanceTask *>(task))
    {
      impedanceTasks_.push_back(impedanceTask);
    }
  }

  startTime_ = ctl.t();

  output("OK");
}

bool UpdateForces::run(mc_control::fsm::Controller & _ctl)
{
  auto & ctl = static_cast<ExternalForcesController &>(_ctl);

  // Set target external forces of StabilizerTask and ImpedanceTask
  sva::ForceVecd targetWrench(Eigen::Vector3d::Zero(),
                              Eigen::Vector3d(0, 0, -20 * (1 - std::cos(0.5 * (ctl.t() - startTime_)))));
  std::vector<std::string> surfaceNames;
  std::vector<sva::ForceVecd> targetWrenches;
  std::vector<sva::MotionVecd> gains;
  for(const auto & impedanceTask : impedanceTasks_)
  {
    impedanceTask->targetWrench(targetWrench);
    surfaceNames.push_back(impedanceTask->surface());
    targetWrenches.push_back(targetWrench);
    gains.emplace_back(Eigen::Vector3d::Zero(), Eigen::Vector3d::UnitZ());
  }
  stabilizerTask_->setExternalWrenches(surfaceNames, targetWrenches, gains);

  return false;
}

void UpdateForces::teardown(mc_control::fsm::Controller &) {}

EXPORT_SINGLE_STATE("ExternalForces::UpdateForces", UpdateForces)


<|start_filename|>tests/testSolverTaskStorage.cpp<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include <mc_rbdyn/RobotLoader.h>
#include <mc_rbdyn/Robots.h>
#include <mc_solver/QPSolver.h>
#include <mc_tasks/CoMTask.h>

#include <boost/test/unit_test.hpp>

#include "utils.h"

/** This test verifies that task's life is properly extended by the solver */

/** Normal CoMTask with static boolean to check the object is deleted */
struct CheckCoMTask : public mc_tasks::CoMTask
{
  CheckCoMTask(const mc_rbdyn::Robots & robots, unsigned int robotIndex) : mc_tasks::CoMTask(robots, robotIndex) {}
  virtual ~CheckCoMTask() override
  {
    deleted = true;
  }
  static bool deleted;
};

bool CheckCoMTask::deleted = false;

BOOST_AUTO_TEST_CASE(TestSolverTaskStorage)
{
  configureRobotLoader();
  auto rm = mc_rbdyn::RobotLoader::get_robot_module("JVRC1");
  auto robots = mc_rbdyn::loadRobot(*rm);
  mc_solver::QPSolver solver(robots, 0.005);
  CheckCoMTask * t_ptr = nullptr;
  {
    auto t = std::make_shared<CheckCoMTask>(solver.robots(), 0);
    t_ptr = t.get();
    solver.addTask(t);
  }
  BOOST_REQUIRE(CheckCoMTask::deleted == false);
  solver.removeTask(t_ptr);
  BOOST_REQUIRE(CheckCoMTask::deleted == true);
}


<|start_filename|>src/mc_robots/jvrc1.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_rbdyn/RobotModule.h>

#include "api.h"

namespace mc_robots
{

struct MC_ROBOTS_DLLAPI JVRC1RobotModule : public mc_rbdyn::RobotModule
{
public:
  JVRC1RobotModule(bool fixed = false);
};

} // namespace mc_robots


<|start_filename|>include/mc_tasks/CoMTask.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_tasks/TrajectoryTaskGeneric.h>

namespace mc_tasks
{

/*! \brief Control a robot's CoM
 *
 * This task is a thin wrapper above the appropriate Tasks types
 *
 */
struct MC_TASKS_DLLAPI CoMTask : public TrajectoryTaskGeneric<tasks::qp::CoMTask>
{
  using TrajectoryBase = TrajectoryTaskGeneric<tasks::qp::CoMTask>;

public:
  /*! \brief Constructor
   *
   * \param robots Robots involved in the task
   *
   * \param robotIndex Select the robot which CoM should be controlled
   *
   * \param stiffness Task stiffness
   *
   * \param weight Task weight
   *
   */
  CoMTask(const mc_rbdyn::Robots & robots, unsigned int robotIndex, double stiffness = 5.0, double weight = 100.);

  void reset() override;

  /*! \brief Change the CoM target by a given amount
   *
   * \param com Modification applied to the CoM
   *
   */
  void move_com(const Eigen::Vector3d & com);

  /*! \brief Set the CoM target to a given position
   *
   * \param com New CoM target
   *
   */
  void com(const Eigen::Vector3d & com);

  /*! \brief Return the current CoM target
   *
   * \returns The current CoM target
   */
  const Eigen::Vector3d & com() const;

  /** \brief Actual CoM position (computed at the previous iteration)
   *
   * \return Return the current CoM position
   */
  const Eigen::Vector3d & actual() const;

  /*! \brief Load from configuration */
  void load(mc_solver::QPSolver &, const mc_rtc::Configuration & config) override;

protected:
  void addToGUI(mc_rtc::gui::StateBuilder &) override;
  void addToLogger(mc_rtc::Logger & logger) override;

private:
  unsigned int robot_index_;
  Eigen::Vector3d cur_com_;
};

} // namespace mc_tasks


<|start_filename|>src/mc_control/fsm/Executor.cpp<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include <mc_control/fsm/Executor.h>

#include <mc_control/fsm/Controller.h>

#include <mc_rtc/gui/Button.h>
#include <mc_rtc/gui/Form.h>
#include <mc_rtc/gui/Label.h>

namespace mc_control
{

namespace fsm
{

namespace
{
/** Always pick a steady clock */
using clock = typename std::conditional<std::chrono::high_resolution_clock::is_steady,
                                        std::chrono::high_resolution_clock,
                                        std::chrono::steady_clock>::type;
} // namespace

void Executor::init(Controller & ctl,
                    const mc_rtc::Configuration & config,
                    const std::string & name,
                    const std::vector<std::string> & category)
{
  config_ = config;
  name_ = name;
  config("Managed", managed_);
  config("StepByStep", step_by_step_);
  if(!managed_)
  {
    transition_map_.init(ctl.factory(), config);
    transition_triggered_ = true;
    next_state_ = transition_map_.initState();
  }
  auto gui = ctl.gui();
  if(gui)
  {
    if(category.size())
    {
      category_ = category;
    }
    else
    {
      category_ = {"FSM"};
      if(name.size())
      {
        category_.push_back(name);
      }
    }
    gui->addElement(category_, mc_rtc::gui::Button("Interrupt", [this]() { interrupt(); }),
                    mc_rtc::gui::Label("Current state", [this]() { return state(); }),
                    mc_rtc::gui::Label("Next state ready", [this]() { return ready(); }),
                    mc_rtc::gui::Label("Next state", [this]() { return next_state(); }),
                    mc_rtc::gui::Button("Start next state", [this]() { next(); }),
                    mc_rtc::gui::Form(
                        "Force transition", [this](const mc_rtc::Configuration & c) { this->resume(c("State")); },
                        mc_rtc::gui::FormDataComboInput("State", true, {"states"})));
  }
  std::string log_entry = "Executor";
  if(name_.size())
  {
    log_entry += "_" + name_;
  }
  else
  {
    log_entry += "_Main";
  }
  ctl.logger().addLogEntry(log_entry, this, [this]() { return curr_state_; });
  log_entry = "perf_" + log_entry;
  ctl.logger().addLogEntry(log_entry, this, [this]() {
    return state_create_dt_.count() + state_run_dt_.count() + state_teardown_dt_.count();
  });
  ctl.logger().addLogEntry(log_entry + "_create", this, [this]() { return state_create_dt_.count(); });
  ctl.logger().addLogEntry(log_entry + "_run", this, [this]() { return state_run_dt_.count(); });
  ctl.logger().addLogEntry(log_entry + "_teardown", this, [this]() { return state_teardown_dt_.count(); });
}

bool Executor::run(Controller & ctl, bool keep_state)
{
  if(interrupt_triggered_)
  {
    interrupt_triggered_ = false;
    if(state_)
    {
      state_->stop(ctl);
      mc_rtc::log::warning("Interrupted {}", curr_state_);
      complete(ctl, false);
    }
  }

  // FIXME ready is used to avoid a potential "yoyo" effect here, i.e. if a
  // state estimated it was completed once, the future estimations don't matter
  state_create_dt_ = duration_ms::zero();
  state_run_dt_ = duration_ms::zero();
  state_teardown_dt_ = duration_ms::zero();
  if(state_)
  {
    auto start_run = clock::now();
    if(!(state_->run(ctl) || ready_))
    {
      state_run_dt_ = clock::now() - start_run;
      return false;
    }
    state_run_dt_ = clock::now() - start_run;
    if(!ready_)
    {
      ready_ = true;
      state_output_ = state_->output();
      mc_rtc::log::success("Completed {} - output: {}", curr_state_, state_output_);
    }
    if(managed_)
    {
      complete_ = true;
      return complete(ctl, keep_state);
    }
    auto trans = transition_map_.transition(curr_state_, state_output_);
    if(!trans.first) // No more transition, execution complete
    {
      complete_ = true;
      return complete(ctl, keep_state);
    }
    next_state_ = trans.second.state;
    auto type = trans.second.type;
    if(type == Transition::Type::Auto || (type == Transition::Type::StepByStep && !step_by_step_)
       || transition_triggered_)
    {
      next(ctl);
      return true;
    }
    return complete(ctl, keep_state);
  }
  else if(transition_triggered_)
  {
    next(ctl);
  }
  return ready_;
}

void Executor::stop(Controller & ctl)
{
  if(state_)
  {
    state_->stop(ctl);
  }
}

void Executor::teardown(Controller & ctl)
{
  if(state_)
  {
    state_->teardown_(ctl);
    state_ = nullptr;
  }
  if(ctl.gui())
  {
    ctl.gui()->removeCategory(category_);
  }
  ctl.logger().removeLogEntries(this);
}

bool Executor::complete(Controller & ctl, bool keep_state)
{
  if(!keep_state)
  {
    auto start_teardown = clock::now();
    state_->teardown_(ctl);
    state_ = nullptr;
    state_teardown_dt_ = clock::now() - start_teardown;
  }
  ready_ = true;
  return true;
}

bool Executor::next()
{
  if(!managed_ && ready_)
  {
    transition_triggered_ = true;
    return true;
  }
  return false;
}

void Executor::next(Controller & ctl)
{
  if(!ready_ || next_state_.empty())
  {
    return;
  }
  ready_ = false;
  transition_triggered_ = false;
  if(state_)
  {
    auto state_teardown_start = clock::now();
    state_->teardown_(ctl);
    ctl.resetPostures();
    state_teardown_dt_ = clock::now() - state_teardown_start;
  }
  mc_rtc::log::success("Starting state {}", next_state_);
  if(config_.has("configs") && config_("configs").has(next_state_))
  {
    auto state_create_start = clock::now();
    state_ = ctl.factory().create(next_state_, ctl, config_("configs")(next_state_));
    state_create_dt_ = clock::now() - state_create_start;
  }
  else
  {
    auto state_create_start = clock::now();
    state_ = ctl.factory().create(next_state_, ctl);
    state_create_dt_ = clock::now() - state_create_start;
  }
  auto gui = ctl.gui();
  if(gui)
  {
    for(const auto & s : transition_map_.transitions(curr_state_))
    {
      gui->removeElement(category_, "Force transition to " + s);
    }
    for(const auto & s : transition_map_.transitions(next_state_))
    {
      gui->addElement(category_, mc_rtc::gui::Button("Force transition to " + s, [this, s]() { this->resume(s); }));
    }
  }
  curr_state_ = next_state_;
  next_state_ = "";
}

bool Executor::resume(const std::string & state)
{
  if(!interrupt_triggered_ && state_ && !ready_)
  {
    mc_rtc::log::warning("{} interrupted to  resume {}", curr_state_, state);
  }
  complete_ = false;
  interrupt_triggered_ = true;
  transition_triggered_ = true;
  next_state_ = state;
  return true;
}

bool Executor::read_msg(std::string & msg)
{
  return state_ && state_->read_msg(msg);
}

bool Executor::read_write_msg(std::string & msg, std::string & out)
{
  return state_ && state_->read_write_msg(msg, out);
}

} // namespace fsm

} // namespace mc_control


<|start_filename|>include/mc_observers/BodySensorObserver.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_observers/Observer.h>
#include <mc_observers/api.h>
#include <mc_rbdyn/Robot.h>

#include <SpaceVecAlg/SpaceVecAlg>

namespace mc_observers
{
/*! The BodySensorObserver is responsible for estimating the state of a robot's
 * floating base from sensor measurements provided by a BodySensor and the
 * kinematics beween this sensor and the floating base.
 * It is assumed here that the floating base sensor kinematics estimate are synchronized.
 *
 * \see BodySensorObserver::run() for usage requirements
 *
 * The default configuration estimates the floating base pose from the main bodysensor (typically an IMU).
 */
struct MC_OBSERVER_DLLAPI BodySensorObserver : public Observer
{
  BodySensorObserver(const std::string & type, double dt) : Observer(type, dt) {}

  /** Configure observer */
  void configure(const mc_control::MCController & ctl, const mc_rtc::Configuration & /*config*/) override;

  /** \brief Resets the observer.
   *
   * Default implementation ensures that the BodySensor provided in the
   * configuration exists and compute the initial floating base pose (calls run())
   *
   * \param ctl The controller running this observer
   */
  void reset(const mc_control::MCController & ctl) override;

  /*! \brief  Determines the pose of the floating base
   *
   * The pose of the floating base is determined depending on the update type
   * chosen:
   * - Update::Control: copies the floating base position from the control robot
   *   (no estimation)
   * - Update::Sensor: Computes the position of the floating base from a BodySensor and the kinematic chain
   *   between it and the floating base. If the BodySensor is not directly attached to the floating base link, this
   * estimator requires accurate estimation of the robot kinematics. A typical pipeline will achieve this by running the
   * EncoderObserver observer before the BodySensorObserver.
   *
   *   It is assumed here that the floating base sensor and encoders are synchronized.
   *
   * \param ctl The controller instance running this observer
   */
  bool run(const mc_control::MCController & ctl) override;

  /*! \brief Update the robot's floating base from its estimated pose
   *
   * \see run for usage requirements
   *
   * \note Calls rbd::forwardKinematics and rbd::forwardVelocity
   */
  void update(mc_control::MCController & ctl) override;

  /*! \brief Get floating-base pose in the world frame. */
  const sva::PTransformd & posW() const
  {
    return posW_;
  }

  /*! \brief Get floating-base velocity in the world frame. */
  const sva::MotionVecd & velW() const
  {
    return velW_;
  }

protected:
  void addToLogger(const mc_control::MCController &, mc_rtc::Logger &, const std::string & category) override;
  void addToGUI(const mc_control::MCController &,
                mc_rtc::gui::StateBuilder &,
                const std::vector<std::string> & category) override;

protected:
  enum class Update
  {
    Control, ///< Use the control robot floating base state
    Sensor ///< Use the body sensor to determine the floating base pose
  };
  Update updateFrom_ = Update::Sensor;
  std::string fbSensorName_;
  sva::PTransformd posW_ = sva::PTransformd::Identity();
  sva::MotionVecd velW_ = sva::MotionVecd::Zero();
  sva::MotionVecd accW_ = sva::MotionVecd::Zero();
  std::string robot_;
  std::string updateRobot_;
  bool updatePose_ = true;
  bool updateVel_ = true;

  bool logPos_ = true;
  bool logVel_ = true;
  bool logAcc_ = true;
  bool guiPos_ = false;
  bool guiVel_ = true;
  mc_rtc::gui::ArrowConfig guiVelConfig_;
  bool guiAcc_ = false;
  mc_rtc::gui::ArrowConfig guiAccConfig_;
  bool advancedGUI_ = false; ///< When true, displays an Advanced tab in the GUI
};

} // namespace mc_observers


<|start_filename|>include/mc_tasks/PositionBasedVisServoTask.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once
#include <mc_tasks/TrajectoryTaskGeneric.h>

namespace mc_tasks
{
/*! \brief Servo an end-effector depending on position error
 *
 * This task is thin wrapper around the appropriate tasks in Tasks.
 *
 */
struct MC_TASKS_DLLAPI PositionBasedVisServoTask : public TrajectoryTaskGeneric<tasks::qp::PositionBasedVisServoTask>
{
public:
  /*! \brief Constructor
   *
   * \param bodyName Name of the body to control
   *
   * \param X_b_s Transformation from the controlled body to the surface being controlled
   *
   * \param X_t_s Transformation from the surface to the target
   *
   * \param robots Robots controlled by this task
   *
   * \param robotIndex Index of the robot controlled by this task
   *
   * \param stiffness Task stiffness
   *
   * \param weight Task weight
   *
   */
  PositionBasedVisServoTask(const std::string & bodyName,
                            const sva::PTransformd & X_t_s,
                            const sva::PTransformd & X_b_s,
                            const mc_rbdyn::Robots & robots,
                            unsigned int robotIndex,
                            double stiffness = 2.0,
                            double weight = 500);

  /*! \brief Constructor (from mc_rbdyn::Surface information)
   *
   * \param surfaceName Name of the surface the control
   *
   * \param X_t_s Transformation from the surface to the target
   *
   * \param robots Robots controlled by this task
   *
   * \param robotIndex Index of the robot controlled by this task
   *
   * \param stiffness Task stiffness
   *
   * \param weight Task weight
   *
   */
  PositionBasedVisServoTask(const std::string & surfaceName,
                            const sva::PTransformd & X_t_s,
                            const mc_rbdyn::Robots & robots,
                            unsigned int robotIndex,
                            double stiffness = 2.0,
                            double weight = 500);

  /*! \brief Reset the task
   *
   * Set the task objective to the current body orientation
   */
  void reset() override;

  /*! \brief Set the current error
   *
   * \param sva::PTransformd Desired configuration in camera frame
   *
   */
  void error(const sva::PTransformd & X_t_s);

  void addToLogger(mc_rtc::Logger & logger) override;

private:
  sva::PTransformd X_t_s_;
};
} // namespace mc_tasks


<|start_filename|>include/mc_filter/LowPass.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 *
 * This file is  inspired by Stephane's Caron implementation as part of
 * lipm_walking_controller <https://github.com/stephane-caron/lipm_walking_controller>
 */

#pragma once

#include <mc_rtc/logging.h>
#include <algorithm>

namespace mc_filter
{

/** Low-pass filter from series of velocity measurements.
 *
 * Expects T to have:
 * - T::Zero() static method (e.g Eigen::Vector3d, etc)
 */
template<typename T>
struct LowPass
{
  /** Constructor with cutoff period.
   *
   * \param dt Sampling period.
   *
   * \param period Cutoff period.
   *
   */
  LowPass(double dt, double period = 0) : cutoffPeriod_(period), dt_(dt)
  {
    reset(T::Zero());
  }

  /** Get cutoff period. */
  double cutoffPeriod() const
  {
    return cutoffPeriod_;
  }

  /** Set cutoff period.
   *
   * \param period New cutoff period.
   *
   * \note period is explicitely enforced to respect the Nyquist–Shannon sampling theorem, that is T is at least
   * 2*timestep.
   */
  void cutoffPeriod(double period)
  {
    if(period < 2 * dt_)
    {
      mc_rtc::log::warning("Time constant must be at least twice the timestep (Nyquist–Shannon sampling theorem)");
      period = 2 * dt_;
    }
    cutoffPeriod_ = period;
  }

  /** Reset position to an initial rest value.
   *
   * \param pos New position.
   *
   */
  void reset(const T & value)
  {
    eval_ = value;
  }

  /** Update velocity estimate from new position value.
   *
   * \param newPos New observed position.
   *
   */
  void update(const T & newValue)
  {
    double x = (cutoffPeriod_ <= dt_) ? 1. : dt_ / cutoffPeriod_;
    eval_ = x * newValue + (1. - x) * eval_;
  }

  /** Get filtered velocity.
   *
   */
  const T & eval() const
  {
    return eval_;
  }

  /** Get sampling period.
   *
   */
  double dt() const
  {
    return dt_;
  }

  /** Set sampling period.
   *
   * \param dt Sampling period.
   *
   * \note the cutoff period is updated to satisfy the Nyquist–Shannon sampling theorem according the new sampling
   * period.
   */
  void dt(double dt)
  {
    dt_ = dt;
    cutoffPeriod(cutoffPeriod_);
  }

private:
  T eval_;
  double cutoffPeriod_ = 0.;

protected:
  double dt_ = 0.005; // [s]
};

} // namespace mc_filter


<|start_filename|>3rd-party/qhull/src/qhulltest/QhullHyperplane_test.cpp<|end_filename|>
/****************************************************************************
**
** Copyright (c) 2009-2020 <NAME>. All rights reserved.
** $Id: //main/2019/qhull/src/qhulltest/QhullHyperplane_test.cpp#4 $$Change: 3001 $
** $DateTime: 2020/07/24 20:43:28 $$Author: bbarber $
**
****************************************************************************/

//pre-compiled headers
#include <iostream>
#include "qhulltest/RoadTest.h" // QT_VERSION

#include "libqhullcpp/QhullHyperplane.h"
#include "libqhullcpp/QhullError.h"
#include "libqhullcpp/RboxPoints.h"
#include "libqhullcpp/QhullFacet.h"
#include "libqhullcpp/QhullFacetList.h"
#include "libqhullcpp/QhullFacetSet.h"
#include "libqhullcpp/Qhull.h"

#include <numeric>
#include <vector>

using std::cout;
using std::endl;
using std::ostringstream;
using std::ostream;
using std::string;

namespace orgQhull {

class QhullHyperplane_test : public RoadTest
{
    Q_OBJECT

#//!\name Test slots
private slots:
    void cleanup();
    void t_construct();
    void t_construct_qh();
    void t_convert();
    void t_readonly();
    void t_define();
    void t_value();
    void t_operator();
    void t_iterator();
    void t_const_iterator();
    void t_foreach();
    void t_qhullHyperplane_iterator();
    void t_java_iterator();
    void t_io();
};//QhullHyperplane_test

void
add_QhullHyperplane_test()
{
    new QhullHyperplane_test();  // RoadTest::s_testcases
}

//Executed after each testcase
void QhullHyperplane_test::
cleanup()
{
    RoadTest::cleanup();
}

void QhullHyperplane_test::
t_construct()
{
    QhullHyperplane h4;
    QVERIFY(!h4.isValid());
    QCOMPARE(h4.dimension(), 0);
    // Qhull.runQhull() constructs QhullFacets as facetT
    RboxPoints rcube("c");
    Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    QhullHyperplane h(q);
    QVERIFY(!h.isValid());
    QCOMPARE(h.dimension(), 0);
    QCOMPARE(h.coordinates(),static_cast<double *>(0));
    QhullFacet f= q.firstFacet();
    QhullHyperplane h2(f.hyperplane());
    QVERIFY(h2.isValid());
    QCOMPARE(h2.dimension(), 3);
    h= h2;
    QCOMPARE(h, h2);
    QhullHyperplane h3(q, h2.dimension(), h2.coordinates(), h2.offset());
    QCOMPARE(h2, h3);
    QhullHyperplane h5= h2; // copy constructor
    QVERIFY(h5==h2);
}//t_construct

void QhullHyperplane_test::
t_construct_qh()
{
    // Qhull.runQhull() constructs QhullFacets as facetT
    RboxPoints rcube("c");
    Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    QhullFacet f= q.firstFacet();
    QhullHyperplane h2(f.hyperplane());
    QVERIFY(h2.isValid());
    QCOMPARE(h2.dimension(), 3);
    // h= h2;  // copy assignment disabled, ambiguous
    QhullHyperplane h3(q.qh(), h2.dimension(), h2.coordinates(), h2.offset());
    QCOMPARE(h2, h3);
    QhullHyperplane h5= h2; // copy constructor
    QVERIFY(h5==h2);
}//t_construct_qh

void QhullHyperplane_test::
t_convert()
{
    RboxPoints rcube("c");
    Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    QhullHyperplane h= q.firstFacet().hyperplane();
    std::vector<double> fs= h.toStdVector();
    QCOMPARE(fs.size(), 4u);
    double offset= fs.back();
    fs.pop_back();
    QCOMPARE(offset, -0.5);

    double squareNorm= inner_product(fs.begin(), fs.end(), fs.begin(), 0.0);
    QCOMPARE(squareNorm, 1.0);
    QList<double> qs= h.toQList();
    QCOMPARE(qs.size(), 4);
    double offset2= qs.takeLast();
    QCOMPARE(offset2, -0.5);
    double squareNorm2= std::inner_product(qs.begin(), qs.end(), qs.begin(), 0.0);
    QCOMPARE(squareNorm2, 1.0);
}//t_convert

void QhullHyperplane_test::
t_readonly()
{
    RboxPoints rcube("c");
    {
        Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
        QhullFacetList fs= q.facetList();
        QhullFacetListIterator i(fs);
        while(i.hasNext()){
            QhullFacet f= i.next();
            QhullHyperplane h= f.hyperplane();
            int id= f.id();
            cout << "h" << id << endl;
            QVERIFY(h.isValid());
            QCOMPARE(h.dimension(),3);
            const coordT *c= h.coordinates();
            coordT *c2= h.coordinates();
            QCOMPARE(c, c2);
            const coordT *c3= h.begin();
            QCOMPARE(c, c3);
            QCOMPARE(h.offset(), -0.5);
            int j= h.end()-h.begin();
            QCOMPARE(j, 3);
            double squareNorm= std::inner_product(h.begin(), h.end(), h.begin(), 0.0);
            QCOMPARE(squareNorm, 1.0);
        }
        QhullHyperplane h2= fs.first().hyperplane();
        QhullHyperplane h3= fs.last().hyperplane();
        QVERIFY(h2!=h3);
        QVERIFY(h3.coordinates()!=h2.coordinates());
    }
}//t_readonly

void QhullHyperplane_test::
t_define()
{
    RboxPoints rcube("c");
    {
        Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
        QhullFacetList fs= q.facetList();
        QhullHyperplane h= fs.first().hyperplane();
        QhullHyperplane h2= h;
        QVERIFY(h==h2);
        QhullHyperplane h3= fs.last().hyperplane();
        QVERIFY(h2!=h3);

        QhullHyperplane h4= h3;
        h4.defineAs(h2);
        QVERIFY(h2==h4);
        QhullHyperplane p5= h3;
        p5.defineAs(h2.dimension(), h2.coordinates(), h2.offset());
        QVERIFY(h2==p5);
        QhullHyperplane h6= h3;
        h6.setCoordinates(h2.coordinates());
        QCOMPARE(h2.coordinates(), h6.coordinates());
        h6.setOffset(h2.offset());
        QCOMPARE(h2.offset(), h6.offset());
        QVERIFY(h2==h6);
        h6.setDimension(2);
        QCOMPARE(h6.dimension(), 2);
        QVERIFY(h2!=h6);
    }
}//t_define

void QhullHyperplane_test::
t_value()
{
    RboxPoints rcube("c G1");
    Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    QhullFacet f= q.firstFacet();
    QhullFacet f2= f.neighborFacets().at(0);
    const QhullHyperplane h= f.hyperplane();
    const QhullHyperplane h2= f2.hyperplane();   // At right angles
    double dist= h.distance(q.origin());
    QCOMPARE(dist, -1.0);
    double norm= h.norm();
    QCOMPARE(norm, 1.0);
    double angle= h.hyperplaneAngle(h2);
    cout << "angle " << angle << endl;
    QCOMPARE(angle+1.0, 1.0); // qFuzzyCompare does not work for 0.0
    QVERIFY(h==h);
    QVERIFY(h!=h2);
}//t_value

void QhullHyperplane_test::
t_operator()
{
    RboxPoints rcube("c");
    Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    const QhullHyperplane h= q.firstFacet().hyperplane();
    //operator== and operator!= tested elsewhere
    const coordT *c= h.coordinates();
    for(int k=h.dimension(); k--; ){
        QCOMPARE(c[k], h[k]);
    }
    //h[0]= 10.0; // compiler error, const
    QhullHyperplane h2= q.firstFacet().hyperplane();
    h2[0]= 10.0;  // Overwrites Hyperplane coordinate!
    QCOMPARE(h2[0], 10.0);
}//t_operator

void QhullHyperplane_test::
t_iterator()
{
    RboxPoints rcube("c");
    {
        Qhull q(rcube,"QR0");  // rotated unit cube
        QhullHyperplane h= q.firstFacet().hyperplane();
        QCOMPARE(h.count(), 3);
        QCOMPARE(h.size(), 3u);
        QhullHyperplane::Iterator i= h.begin();
        QhullHyperplane::iterator i2= h.begin();
        QVERIFY(i==i2);
        QVERIFY(i>=i2);
        QVERIFY(i<=i2);
        i= h.begin();
        QVERIFY(i==i2);
        i2= h.end();
        QVERIFY(i!=i2);
        double d3= *i;
        i2--;
        double d2= *i2;
        QCOMPARE(d3, h[0]);
        QCOMPARE(d2, h[2]);
        QhullHyperplane::Iterator i3(i2);
        QCOMPARE(*i2, *i3);

        (i3= i)++;
        QCOMPARE((*i3), h[1]);
        QVERIFY(i==i);
        QVERIFY(i!=i2);
        QVERIFY(i<i2);
        QVERIFY(i<=i2);
        QVERIFY(i2>i);
        QVERIFY(i2>=i);

        QhullHyperplane::ConstIterator i4= h.begin();
        QVERIFY(i==i4); // iterator COMP const_iterator
        QVERIFY(i<=i4);
        QVERIFY(i>=i4);
        QVERIFY(i4==i); // const_iterator COMP iterator
        QVERIFY(i4<=i);
        QVERIFY(i4>=i);
        QVERIFY(i>=i4);
        QVERIFY(i4<=i);
        QVERIFY(i2!=i4);
        QVERIFY(i2>i4);
        QVERIFY(i2>=i4);
        QVERIFY(i4!=i2);
        QVERIFY(i4<i2);
        QVERIFY(i4<=i2);
        ++i4;
        QVERIFY(i<i4);
        QVERIFY(i<=i4);
        QVERIFY(i4>i);
        QVERIFY(i4>=i);

        i= h.begin();
        i2= h.begin();
        QCOMPARE(i, i2++);
        QCOMPARE(*i2, h[1]);
        QCOMPARE(++i, i2);
        QCOMPARE(i, i2--);
        QCOMPARE(i2, h.begin());
        QCOMPARE(--i, i2);
        QCOMPARE(i2 += 3, h.end());
        QCOMPARE(i2 -= 3, h.begin());
        QCOMPARE(i2+0, h.begin());
        QCOMPARE(i2+3, h.end());
        i2 += 3;
        i= i2-0;
        QCOMPARE(i, i2);
        i= i2-3;
        QCOMPARE(i, h.begin());
        QCOMPARE(i2-i, 3);

        //h.begin end tested above

        // QhullHyperplane is const-only
    }
}//t_iterator

void QhullHyperplane_test::
t_const_iterator()
{
    RboxPoints rcube("c");
    {
        Qhull q(rcube,"QR0");  // rotated unit cube
        QhullHyperplane h= q.firstFacet().hyperplane();
        QhullHyperplane::ConstIterator i= h.begin();
        QhullHyperplane::const_iterator i2= h.begin();
        QVERIFY(i==i2);
        QVERIFY(i>=i2);
        QVERIFY(i<=i2);
        i= h.begin();
        QVERIFY(i==i2);
        i2= h.end();
        QVERIFY(i!=i2);
        double d3= *i;
        i2--;
        double d2= *i2;
        QCOMPARE(d3, h[0]);
        QCOMPARE(d2, h[2]);
        QhullHyperplane::ConstIterator i3(i2);
        QCOMPARE(*i2, *i3);

        (i3= i)++;
        QCOMPARE((*i3), h[1]);
        QVERIFY(i==i);
        QVERIFY(i!=i2);
        QVERIFY(i<i2);
        QVERIFY(i<=i2);
        QVERIFY(i2>i);
        QVERIFY(i2>=i);

        // See t_iterator for const_iterator COMP iterator

        i= h.begin();
        i2= h.constBegin();
        QCOMPARE(i, i2++);
        QCOMPARE(*i2, h[1]);
        QCOMPARE(++i, i2);
        QCOMPARE(i, i2--);
        QCOMPARE(i2, h.constBegin());
        QCOMPARE(--i, i2);
        QCOMPARE(i2+=3, h.constEnd());
        QCOMPARE(i2-=3, h.constBegin());
        QCOMPARE(i2+0, h.constBegin());
        QCOMPARE(i2+3, h.constEnd());
        i2 += 3;
        i= i2-0;
        QCOMPARE(i, i2);
        i= i2-3;
        QCOMPARE(i, h.constBegin());
        QCOMPARE(i2-i, 3);

        // QhullHyperplane is const-only
    }
}//t_const_iterator

void QhullHyperplane_test::
t_foreach()
{
    RboxPoints rcube("c");
    Qhull q(rcube, "QR0");  // rotated unit cube
    QhullHyperplane h= q.firstFacet().hyperplane();
    coordT c2= (h.coordinates())[1];
    
    bool isC2= false;
    int count= 0;
    foreach(coordT c, q.firstFacet().hyperplane()){
        ++count;
        if(c==c2){
            isC2= true;
            QCOMPARE(count, 2);
        }
    }
    QVERIFY(isC2);
    QCOMPARE(count, q.dimension());
    
    isC2= false;
    count= 0;
    for(coordT c : q.firstFacet().hyperplane()){
        ++count;
        if(c==c2){
            isC2= true;
            QCOMPARE(count, 2);
        }
    }
    QVERIFY(isC2);
    QCOMPARE(count, q.dimension());
}//t_foreach

void QhullHyperplane_test::
t_qhullHyperplane_iterator()
{
    RboxPoints rcube("c");
    Qhull q(rcube,"QR0");  // rotated unit cube
    QhullHyperplane h= q.firstFacet().hyperplane();
    QhullHyperplaneIterator i2(h);
    QCOMPARE(h.dimension(), 3);
    QhullHyperplaneIterator i= h;
    QVERIFY(i2.hasNext());
    QVERIFY(!i2.hasPrevious());
    QVERIFY(i.hasNext());
    QVERIFY(!i.hasPrevious());
    i2.toBack();
    i.toFront();
    QVERIFY(!i2.hasNext());
    QVERIFY(i2.hasPrevious());
    QVERIFY(i.hasNext());
    QVERIFY(!i.hasPrevious());

    // i at front, i2 at end/back, 3 coordinates
    QCOMPARE(i.peekNext(), h[0]);
    QCOMPARE(i2.peekPrevious(), h[2]);
    QCOMPARE(i2.previous(), h[2]);
    QCOMPARE(i2.previous(), h[1]);
    QCOMPARE(i2.previous(), h[0]);
    QVERIFY(!i2.hasPrevious());
    QCOMPARE(i.peekNext(), h[0]);
    // i.peekNext()= 1.0; // compiler error, i is const
    QCOMPARE(i.next(), h[0]);
    QCOMPARE(i.peekNext(), h[1]);
    QCOMPARE(i.next(), h[1]);
    QCOMPARE(i.next(), h[2]);
    QVERIFY(!i.hasNext());
    i.toFront();
    QCOMPARE(i.next(), h[0]);
}//t_qhullHyperplane_iterator

void QhullHyperplane_test::
t_java_iterator()
{
    RboxPoints rcube("c");
    Qhull q(rcube, "QR0");  // rotated unit cube
    QhullHyperplane h= q.firstFacet().hyperplane();
    coordT c2= (h.coordinates())[1];
    
    bool isC2= false;
    int count= 0;
    QhullHyperplaneIterator i(q.firstFacet().hyperplane());
    while(i.hasNext()){
        coordT c= i.next();
        ++count;
        if(c==c2){
            isC2= true;
            QCOMPARE(count, 2);
        }
    }
    QVERIFY(isC2);
    QCOMPARE(count, q.dimension());
}//t_java_iterator

void QhullHyperplane_test::
t_io()
{
    RboxPoints rcube("c");
    {
        Qhull q(rcube, "");
        QhullHyperplane h= q.firstFacet().hyperplane();
        ostringstream os;
        os << "Hyperplane:\n";
        os << h;
        os << h.print("message");
        os << h.print(" and a message ", " offset ");
        cout << os.str();
        QString s= QString::fromStdString(os.str());
        QCOMPARE(s.count("1"), 3);
        // QCOMPARE(s.count(QRegExp("f\\d")), 3*7 + 13*3*2);
    }
}//t_io


}//orgQhull

#include "moc/QhullHyperplane_test.moc"


<|start_filename|>include/mc_tasks/ImpedanceGains.h<|end_filename|>
/*
 * Copyright 2015-2021 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_tasks/api.h>

#include <mc_rtc/Configuration.h>

namespace mc_tasks
{

namespace force
{

namespace details
{

/** A wrapper around an sva::ImpedanceVecd that imposes (strictly) positive values */
template<bool StrictlyPositive>
struct ImpedanceVecd
{
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW

  ImpedanceVecd() : ImpedanceVecd(limit, limit) {}

  ImpedanceVecd(const sva::ImpedanceVecd & v)
  {
    vec(v);
  }

  ImpedanceVecd(double angular_, double linear_)
  {
    angular(angular_);
    linear(linear_);
  }

  ImpedanceVecd & operator=(const sva::ImpedanceVecd & v)
  {
    vec(v);
    return *this;
  }

  inline Eigen::Vector6d vector() const noexcept
  {
    return vec_.vector();
  }

  inline void vec(const sva::ImpedanceVecd & v) noexcept
  {
    angular(v.angular());
    linear(v.linear());
  }

  inline void vec(const Eigen::Vector6d & v) noexcept
  {
    angular(v.head<3>());
    linear(v.tail<3>());
  }

  inline void vec(const Eigen::Vector3d & a, const Eigen::Vector3d & l) noexcept
  {
    angular(a);
    linear(l);
  }

  inline void vec(double a, double l) noexcept
  {
    angular(a);
    linear(l);
  }

  inline const sva::ImpedanceVecd & vec() const noexcept
  {
    return vec_;
  }

  inline void angular(const Eigen::Vector3d & v) noexcept
  {
    vec_.angular() = v.cwiseMax(limit);
  }

  inline void angular(double angular) noexcept
  {
    vec_.angular().setConstant(std::max(angular, limit));
  }

  inline const Eigen::Vector3d & angular() const noexcept
  {
    return vec_.angular();
  }

  inline void linear(const Eigen::Vector3d & v) noexcept
  {
    vec_.linear() = v.cwiseMax(limit);
  }

  inline void linear(double linear) noexcept
  {
    vec_.linear().setConstant(std::max(linear, limit));
  }

  inline const Eigen::Vector3d & linear() const noexcept
  {
    return vec_.linear();
  }

private:
  static constexpr double limit = StrictlyPositive ? 1e-6 : 0.0;
  sva::ImpedanceVecd vec_;
};

// Repeat static constexpr declarations
// See also https://stackoverflow.com/q/8016780
template<bool b>
constexpr double ImpedanceVecd<b>::limit;

} // namespace details

/*! \brief Represent impedance gains for an \ref ImpedanceTask */
struct MC_TASKS_DLLAPI ImpedanceGains
{
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW

  /** Impedance mass parameter */
  inline const details::ImpedanceVecd<true> & M() const noexcept
  {
    return M_;
  }
  /** Impedance mass parameter */
  inline details::ImpedanceVecd<true> & M() noexcept
  {
    return M_;
  }
  /** Impedance mass parameter */
  inline const details::ImpedanceVecd<true> & mass() const noexcept
  {
    return M_;
  }
  /** Impedance mass parameter */
  inline details::ImpedanceVecd<true> & mass() noexcept
  {
    return M_;
  }

  /** Impedance damper parameter */
  inline const details::ImpedanceVecd<false> & D() const noexcept
  {
    return D_;
  }
  /** Impedance damper parameter */
  inline details::ImpedanceVecd<false> & D() noexcept
  {
    return D_;
  }
  /** Impedance damper parameter */
  inline const details::ImpedanceVecd<false> & damper() const noexcept
  {
    return D_;
  }
  /** Impedance damper parameter */
  inline details::ImpedanceVecd<false> & damper() noexcept
  {
    return D_;
  }

  /** Impedance spring parameter */
  inline const details::ImpedanceVecd<false> & K() const noexcept
  {
    return K_;
  }
  /** Impedance spring parameter */
  inline details::ImpedanceVecd<false> & K() noexcept
  {
    return K_;
  }
  /** Impedance spring parameter */
  inline const details::ImpedanceVecd<false> & spring() const noexcept
  {
    return K_;
  }
  /** Impedance spring parameter */
  inline details::ImpedanceVecd<false> & spring() noexcept
  {
    return K_;
  }

  /** Impedance wrench gain */
  inline const details::ImpedanceVecd<false> & wrench() const noexcept
  {
    return wrench_;
  }
  /** Impedance wrench gain */
  inline details::ImpedanceVecd<false> & wrench() noexcept
  {
    return wrench_;
  }

  /** Returns default gains for the ImpedanceTask */
  inline static ImpedanceGains Default()
  {
    ImpedanceGains out;
    out.mass().vec(10.0, 10.0);
    out.damper().vec(1000.0, 1000.0);
    out.spring().vec(1000.0, 1000.0);
    return out;
  }

protected:
  /** Impedance mass parameter */
  details::ImpedanceVecd<true> M_;
  /** Impedance damper parameter */
  details::ImpedanceVecd<false> D_;
  /** Impedance spring parameter */
  details::ImpedanceVecd<false> K_;
  /** Impedance wrench gain */
  details::ImpedanceVecd<false> wrench_;
};

} // namespace force

} // namespace mc_tasks

namespace mc_rtc
{

template<>
struct MC_TASKS_DLLAPI ConfigurationLoader<mc_tasks::force::ImpedanceGains>
{
  static mc_tasks::force::ImpedanceGains load(const mc_rtc::Configuration & config)
  {
    mc_tasks::force::ImpedanceGains out;
    if(config.has("mass"))
    {
      sva::ImpedanceVecd M = config("mass");
      out.M().vec(M);
    }
    if(config.has("damper"))
    {
      sva::ImpedanceVecd D = config("damper");
      out.D().vec(D);
    }
    if(config.has("spring"))
    {
      sva::ImpedanceVecd K = config("spring");
      out.K().vec(K);
    }
    if(config.has("wrench"))
    {
      sva::ImpedanceVecd wrench = config("wrench");
      out.wrench().vec(wrench);
    }
    return out;
  }
  static mc_rtc::Configuration save(const mc_tasks::force::ImpedanceGains & ig)
  {
    mc_rtc::Configuration out;
    out.add("mass", ig.M().vec());
    out.add("damper", ig.D().vec());
    out.add("spring", ig.K().vec());
    out.add("wrench", ig.wrench().vec());
    return out;
  }
};

} // namespace mc_rtc


<|start_filename|>src/mc_control/samples/Impedance/mc_impedance_controller.h<|end_filename|>
/*
 * Copyright 2015-2020 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_control/api.h>
#include <mc_control/mc_controller.h>

#include <mc_tasks/CoMTask.h>
#include <mc_tasks/ImpedanceTask.h>

namespace mc_control
{

struct MC_CONTROL_DLLAPI MCImpedanceController : public MCController
{
  MCImpedanceController(std::shared_ptr<mc_rbdyn::RobotModule> robot, double dt);
  void reset(const ControllerResetData & reset_data) override;
  bool run() override;
  void stop() override;

protected:
  void addGUI();
  Eigen::Vector3d circleTrajectory(double angle);

protected:
  std::shared_ptr<mc_tasks::CoMTask> comTask_;
  std::shared_ptr<mc_tasks::force::ImpedanceTask> impedanceTask_;
  double angle_ = 0.0;
  double radius_ = 0.2;
  double speed_ = 0.5;
  Eigen::Vector3d center_ = Eigen::Vector3d(0.3, 0.5, 1.0);
  Eigen::Matrix3d orientation_ = Eigen::Matrix3d::Identity();
};

} // namespace mc_control

SIMPLE_CONTROLLER_CONSTRUCTOR("Impedance", mc_control::MCImpedanceController)


<|start_filename|>tests/controllers/plugins/TestPlugin.cpp<|end_filename|>
/*
 * Copyright 2021 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include <mc_control/GlobalPluginMacros.h>

namespace mc_plugin
{

struct MC_CONTROL_DLLAPI TestPlugin : public mc_control::GlobalPlugin
{
  TestPlugin(const std::string & name) : name_(name) {}

  void init(mc_control::MCGlobalController & controller, const mc_rtc::Configuration & config) override
  {
    config_.load(config);
    reset(controller);
  }

  void reset(mc_control::MCGlobalController & controller) override
  {
    iter_before = 0;
    iter_after = 0;
    auto & ds = controller.controller().datastore();
    ds.make<size_t>(name_ + "::iter_before", iter_before);
    ds.make<size_t>(name_ + "::iter_after", iter_after);
    ds.make<void *>(name_ + "::address", this);
    ds.make<mc_rtc::Configuration>(name_ + "::config", config_);
  }

  void before(mc_control::MCGlobalController & controller) override
  {
    iter_before++;
    auto & ds = controller.controller().datastore();
    ds.assign(name_ + "::iter_before", iter_before);
  }

  void after(mc_control::MCGlobalController & controller) override
  {
    iter_after++;
    auto & ds = controller.controller().datastore();
    ds.assign(name_ + "::iter_after", iter_after);
  }

private:
  mc_rtc::Configuration config_;
  std::string name_;
  size_t iter_before = 0;
  size_t iter_after = 0;
};

} // namespace mc_plugin

extern "C"
{

  GLOBAL_PLUGIN_API void MC_RTC_GLOBAL_PLUGIN(std::vector<std::string> & names)
  {
    names = {"Plugin0", "Plugin1", "Plugin2", "Plugin3"};
  }

  GLOBAL_PLUGIN_API void destroy(mc_control::GlobalPlugin * ptr)
  {
    delete ptr;
  }

  GLOBAL_PLUGIN_API mc_control::GlobalPlugin * create(const std::string & name)
  {
    return new mc_plugin::TestPlugin(name);
  }
}


<|start_filename|>3rd-party/qhull/src/libqhullcpp/QhullUser.h<|end_filename|>
/****************************************************************************
**
** Copyright (c) 2008-2020 <NAME>. All rights reserved.
** $Id: //main/2019/qhull/src/libqhullcpp/QhullUser.h#7 $$Change: 3010 $
** $DateTime: 2020/07/30 22:14:11 $$Author: bbarber $
**
****************************************************************************/

#ifndef QhullUser_H
#define QhullUser_H

#include "libqhull_r/qhull_ra.h"
#include "libqhullcpp/QhullPoint.h"
#include "libqhullcpp/PointCoordinates.h"

#include <stdarg.h>
#include <string>
#include <vector>
#include <istream>
#include <ostream>
#include <sstream>

namespace orgQhull{

#//!\name Defined here
    //! QhullUser -- custom C++ interfaces into Qhull (via qh_fprintf)
    class QhullUser;
    // qh_fprintf is defined below.  It replaces libqhull_r/userprintf_r.c

#//!\name Used here
    class QhullQh;

    class QhullUser{

    private:
#//!\name Fields
        QhullQh *       qh_qh;          //!< QhullQh/qhT for access to libqhull_r
        void *          previous_user;  //!< previous qh.cpp_user, restored on deconstruction
        std::vector<std::vector<double> > doubles_vector;  //! vectors for capturing ints and doubles
        std::vector<std::vector<int> >  ints_vector;
        std::vector<int>                fprintf_ints;
        std::vector<double>             fprintf_doubles;
        std::vector<int>                fprintf_codes;
        std::vector<std::string>        fprintf_strings;
        int             num_facets;
        int             num_neighbors;
        int             num_numbers;
        int             num_points;
        int             num_results;
        int             num_ridges;
        int             num_vectors;
        int             num_vertices;
        int             qhull_dim;
        int             delaunay_dim;   //! ints for capturing fprintf fields

    public:
#//!\name Construct
                        QhullUser(QhullQh *qqh);
                        ~QhullUser();
    private:                // Disable default constructor, copy constructor, and assignment
                        QhullUser();
                        QhullUser(const QhullUser &);
        QhullUser &     operator=(const QhullUser &);
    private:

    public:
#//!\name GetSet
        void            appendCode(int msgCode) { fprintf_codes.push_back(msgCode); }
        void            appendDouble(double a) { fprintf_doubles.push_back(a); }
        void            appendInt(int i) { fprintf_ints.push_back(i); }
        void            appendAndClearDoubles() { doubles_vector.push_back(fprintf_doubles); fprintf_doubles.clear(); }
        void            appendAndClearInts() { ints_vector.push_back(fprintf_ints); fprintf_ints.clear(); }
        void            clear();
        void            clearDoubles() { fprintf_doubles.clear(); }
        void            clearDoublesVector() { doubles_vector.clear(); }
        void            clearInts() { fprintf_ints.clear(); }
        void            clearIntsVector() { ints_vector.clear(); }
        const std::vector<int> &codes() const { return fprintf_codes; }
        int             delaunayDim() const { return delaunay_dim; }
        const std::vector<std::vector<double> > &doublesVector() const { return doubles_vector; }
        const std::vector<double> &doubles() const { return fprintf_doubles; }
        int             firstCode() const { return (fprintf_codes.size() == 0 ? -1 : fprintf_codes[0]); }
        const std::vector<int> &ints() const { return fprintf_ints; }
        const std::vector<std::vector<int> > &intsVector() const { return ints_vector; }
        int             numDoubles() const { return (int)doubles_vector.size();  }
        int             numFacets() const { return num_facets; }
        int             numInts() const { return (int)ints_vector.size(); }
        int             numNeighbors() const { return num_neighbors; }
        int             numNumbers() const { return num_numbers; }
        int             numPoints() const { return num_points; }
        int             numResults() const { return num_results; }
        int             numRidges() const { return num_ridges; }
        int             numVectors() const { return num_vectors; }
        int             numVertices() const { return num_vertices; }
        QhullQh *       qh() const { return qh_qh; }
        int             qhullDim() const { return qhull_dim; }
        void            setDelaunayDim(int i) { delaunay_dim= i; }
        void            setNumFacets(int i) { num_facets= i; }
        void            setNumResults(int i) { num_results= i; }
        void            setNumRidges(int i) { num_ridges= i; }
        void            setNumNeighbors(int i) { num_neighbors= i; }
        void            setNumNumbers(int i) { num_numbers= i; }
        void            setNumPoints(int i) { num_points= i; }
        void            setNumVectors(int i) { num_vectors= i; }
        void            setNumVertices(int i) { num_vertices= i; }
        void            setQhullDim(int i) { qhull_dim= i; }

#//!\name Methods
        void            captureOn();
        void            captureOff();
};//class QhullUser

}//namespace orgQhull

#endif // QhullUser_H


<|start_filename|>include/mc_rbdyn/surface_hull.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#ifndef _H_MCRBDYNSURFACEHULL_H_
#define _H_MCRBDYNSURFACEHULL_H_

#include <mc_rtc/pragma.h>

MC_RTC_diagnostic_push
MC_RTC_diagnostic_ignored(GCC, "-Wsign-conversion")
#include <Eigen/Core>
MC_RTC_diagnostic_pop
#include <mc_rbdyn/api.h>

#include <SpaceVecAlg/SpaceVecAlg>

#include <vector>

namespace sch
{
class S_Object;
}

namespace mc_rbdyn
{

struct Surface;
struct PlanarSurface;
struct CylindricalSurface;
struct GripperSurface;

MC_RBDYN_DLLAPI sch::S_Object * surface_to_sch(const mc_rbdyn::Surface & surface,
                                               const double & depth = 0.01,
                                               const unsigned int & slice = 8);

MC_RBDYN_DLLAPI sch::S_Object * sch_polyhedron(const std::vector<sva::PTransformd> & points);

MC_RBDYN_DLLAPI sch::S_Object * planar_hull(const mc_rbdyn::PlanarSurface & surface, const double & depth);

MC_RBDYN_DLLAPI sch::S_Object * cylindrical_hull(const mc_rbdyn::CylindricalSurface & surface,
                                                 const unsigned int & slice);

MC_RBDYN_DLLAPI sch::S_Object * gripper_hull(const mc_rbdyn::GripperSurface & surface, const double & depth);

} // namespace mc_rbdyn

#endif


<|start_filename|>include/mc_rbdyn/ForceSensor.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_rbdyn/Device.h>

namespace mc_rbdyn
{

struct Robot;

namespace detail
{
struct ForceSensorCalibData;
}

/** This struct is intended to hold static information about a force sensor
 * and the current reading of said sensor. If the appropriate data is
 * provided, a gravity-free reading can be provided.
 */
struct MC_RBDYN_DLLAPI ForceSensor : public Device
{
public:
  /** Default constructor, this does not represent a valid force sensor */
  ForceSensor();

  /** Construct a valid force sensor based on static information, this
   * force sensor can then be used to provide sensing information to the
   * robot. However, filtering will have no effect
   *
   * @param name Name of the sensor
   *
   * @param parentBodyName Name of the sensor's parent body
   *
   * @param X_p_f Model transformation from the parent body to the model (not
   * real) sensor frame
   *
   */
  ForceSensor(const std::string & name, const std::string & parentBodyName, const sva::PTransformd & X_p_f);

  ForceSensor(const ForceSensor & fs);

  ForceSensor & operator=(const ForceSensor & fs);

  ForceSensor(ForceSensor &&) = default;
  ForceSensor & operator=(ForceSensor &&) = default;

  /** Destructor */
  ~ForceSensor() noexcept override;

  /** Return the sensor's parent body */
  inline const std::string & parentBody() const
  {
    return Device::parent();
  }

  /** Return the transformation from the parent body to the sensor (model) */
  inline const sva::PTransformd & X_p_f() const
  {
    return Device::X_p_s();
  }

  /** Return the sensor pose in the inertial frame (convenience function) */
  inline sva::PTransformd X_0_f(const mc_rbdyn::Robot & robot) const
  {
    return Device::X_0_s(robot);
  }

  /** Return the current wrench */
  inline const sva::ForceVecd & wrench() const
  {
    return wrench_;
  }

  /** Return the force reading
   *
   * Shortcut for wrench().force()
   */
  inline const Eigen::Vector3d & force() const
  {
    return wrench().force();
  }

  /** Return the couple reading
   *
   * Shortcut for wrench().couple()
   */
  inline const Eigen::Vector3d & couple() const
  {
    return wrench().couple();
  }

  /** Set the current wrench expressed in sensor frame
   *
   * @param wrench New wrench reading
   */
  inline void wrench(const sva::ForceVecd & wrench)
  {
    wrench_ = wrench;
  }

  /** Return a gravity-free wrench in sensor frame
   *
   * @param robot Robot that the sensor belongs to
   *
   * @returns A gravity-free reading of the wrench
   *
   */
  sva::ForceVecd wrenchWithoutGravity(const mc_rbdyn::Robot & robot) const;

  /** Return measured wrench in the inertial frame
   *
   * @param robot Robot that the sensor belongs to
   *
   * @returns w_0 Spatial force vector of measured wrench
   *
   */
  sva::ForceVecd worldWrench(const mc_rbdyn::Robot & robot) const;

  /** Return measured gravity-free wrench in the inertial frame
   *
   * @param robot Robot that the sensor belongs to
   *
   * @returns w_0 Spatial force vector of measured wrench
   *
   */
  sva::ForceVecd worldWrenchWithoutGravity(const mc_rbdyn::Robot & robot) const;

  /** @name Calibration
   *
   * These functions are related to the sensor's calibration
   *
   * @{
   */

  /** Load data from a file, using a gravity vector. The file should
   * contain 13 parameters in that order: mass (1), rpy for X_f_ds (3),
   * position for X_p_vb (3), wrench offset (6).
   *
   * @param calib_file Calibration file, the file should exist
   *
   * @param gravity Gravity vector, defaults to Z
   *
   */
  void loadCalibrator(const std::string & calib_file, const Eigen::Vector3d & gravity = {0., 0., 9.81});

  /** Copy the calibration data from another force sensor
   *
   * @param other Other force sensor from which the data is copied
   *
   */
  void copyCalibrator(const mc_rbdyn::ForceSensor & other);

  /**
   * Reset the force calibration to its default values such that the calibrator
   * has no effect on the sensor wrench
   **/
  void resetCalibrator();

  /** Return the local rotation associated to the sensor, i.e. the error
   * between the model forceSensor and real one
   */
  const sva::PTransformd & X_fsmodel_fsactual() const;

  /** Return the transform from the parent body to the real force sensor
   */
  const sva::PTransformd X_fsactual_parent() const;

  /** Return the mass of the sensor */
  double mass() const;

  /** Return the sensor offset */
  const sva::ForceVecd & offset() const;

  /** @} */
  /* End of Calibration group */

  DevicePtr clone() const override;

private:
  sva::ForceVecd wrench_;

  std::shared_ptr<detail::ForceSensorCalibData> calibration_;
};

} // namespace mc_rbdyn


<|start_filename|>src/mc_control/ControllerLoader.cpp<|end_filename|>
#include <mc_control/ControllerLoader.h>

namespace mc_control
{

std::unique_ptr<mc_rtc::ObjectLoader<MCController>> ControllerLoader::loader_;

mc_rtc::ObjectLoader<MCController> & ControllerLoader::loader()
{
  if(loader_)
  {
    return *loader_;
  }
  loader_.reset(new mc_rtc::ObjectLoader<MCController>("MC_RTC_CONTROLLER", {}, false, false));
  return *loader_;
}

} // namespace mc_control


<|start_filename|>tests/controllers/TestFSMStateOptions.cpp<|end_filename|>
/*
 * Copyright 2015-2020 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#ifdef BOOST_TEST_MAIN
#  undef BOOST_TEST_MAIN
#endif

#include <mc_control/fsm/Controller.h>

#include <mc_control/mc_controller.h>

#include <mc_rtc/logging.h>

#include <mc_tasks/CoMTask.h>

#include <boost/test/unit_test.hpp>

namespace mc_control
{

struct MC_CONTROL_DLLAPI TestFSMStateOptionsController : public fsm::Controller
{
public:
  TestFSMStateOptionsController(mc_rbdyn::RobotModulePtr rm, double dt, const mc_rtc::Configuration & conf)
  : fsm::Controller(rm, dt, conf)
  {
    // Check that the default constructor loads the robot + ground environment
    BOOST_CHECK_EQUAL(robots().robots().size(), 2);
    // Check that JVRC-1 was loaded
    BOOST_CHECK_EQUAL(robot().name(), "jvrc1");
    BOOST_REQUIRE(hasRobot("ground"));
    mc_rtc::log::success("Created TestFSMStateOptionsController");
  }

  bool run() override
  {
    bool ret = fsm::Controller::run();
    BOOST_REQUIRE(ret);
    if(iter_ == 0) // TestContactsManipulation
    {
      BOOST_REQUIRE(executor_.state() == "TestContactsManipulation");
      // The AddContacts/RemoveContacts actions should have swapped the contacts
      BOOST_REQUIRE(!hasContact("LeftFoot"));
      BOOST_REQUIRE(!hasContact("RightFoot"));
      BOOST_REQUIRE(hasContact("LeftFootCenter", 0.8));
      Eigen::Vector6d dof = Eigen::Vector6d::Ones();
      dof(2) = 0.0;
      BOOST_REQUIRE(hasContact("RightFootCenter", 0.8, dof));
    }
    if(iter_ == 1) // TestCollisionsManipulation
    {
      BOOST_REQUIRE(executor_.state() == "TestCollisionsManipulation");
      BOOST_REQUIRE(hasCollision("jvrc1", "ground", {"L_WRIST_Y_S", "ground", 0.05, 0.01, 0}));
      BOOST_REQUIRE(!hasCollision("jvrc1", "jvrc1", {"R_WRIST_Y_S", "R_HIP_Y_S", 0.05, 0.025, 0}));
    }
    else
    {
      BOOST_REQUIRE(!hasCollision("jvrc1", "ground", {"L_WRIST_Y_S", "ground", 0.05, 0.01, 0}));
      BOOST_REQUIRE(hasCollision("jvrc1", "jvrc1", {"R_WRIST_Y_S", "R_HIP_Y_S", 0.05, 0.025, 0}));
    }
    if(iter_ == 2) // TestRemovePostureTask
    {
      BOOST_REQUIRE(executor_.state() == "TestRemovePostureTask");
      // AddContactsAfter/RemoveContactsAfter should have put the contacts back
      BOOST_REQUIRE(hasContact("LeftFoot"));
      BOOST_REQUIRE(hasContact("RightFoot"));
      BOOST_REQUIRE(!hasContact("LeftFootCenter"));
      BOOST_REQUIRE(!hasContact("RightFootCenter"));
      // RemovePostureTask should have removed the posture task
      BOOST_REQUIRE(!getPostureTask("jvrc1")->inSolver());
    }
    else
    {
      BOOST_REQUIRE(getPostureTask("jvrc1")->inSolver());
    }
    if(iter_ > 2) // TestConstraintsAndTasks
    {
      BOOST_REQUIRE(executor_.state() == "TestConstraintsAndTasks");
      // There is now a constraint to set l_wrist speed to a constant
      auto bIndex = robot().bodyIndexByName("l_wrist");
      auto speed = robot().bodyVelW()[bIndex].vector();
      Eigen::Vector6d ref = Eigen::Vector6d::Zero();
      ref(5) = 0.001;
      BOOST_REQUIRE((speed - ref).norm() < 5e-4);
      BOOST_REQUIRE(hasTask<mc_tasks::CoMTask>());
    }
    else
    {
      BOOST_REQUIRE(!hasTask<mc_tasks::CoMTask>());
    }
    iter_++;
    return ret;
  }

  void reset(const ControllerResetData & reset_data) override
  {
    fsm::Controller::reset(reset_data);
    BOOST_REQUIRE(hasContact("LeftFoot"));
    BOOST_REQUIRE(hasContact("RightFoot"));
  }

  using fsm::Controller::hasContact;

  const fsm::Contact & contact(const fsm::Contact & c)
  {
    assert(hasContact(c));
    return *contacts().find(c);
  }

  bool hasContact(const std::string & s,
                  double friction = mc_rbdyn::Contact::defaultFriction,
                  const Eigen::Vector6d & dof = Eigen::Vector6d::Ones())
  {
    fsm::Contact c("jvrc1", "ground", s, "AllGround", friction, dof);
    if(!hasContact(c))
    {
      return false;
    }
    const auto & ref = contact(c);
    return ref.friction == c.friction && ref.dof == c.dof;
  }

  bool hasCollision(const std::string & r1, const std::string & r2, const mc_rbdyn::Collision & col)
  {
    if(!collision_constraints_.count({r1, r2}))
    {
      return false;
    }
    const auto & cols = collision_constraints_.at({r1, r2})->cols;
    return std::find(cols.begin(), cols.end(), col) != cols.end();
  }

  template<typename T>
  bool hasTask()
  {
    for(const auto & t : solver().tasks())
    {
      if(dynamic_cast<const T *>(t) != nullptr)
      {
        return true;
      }
    }
    return false;
  }

private:
  unsigned int iter_ = 0;
};

} // namespace mc_control

CONTROLLER_CONSTRUCTOR("TestFSMStateOptions", mc_control::TestFSMStateOptionsController)


<|start_filename|>src/mc_control/samples/EndEffector/mc_end_effector_controller.h<|end_filename|>
/*
 * Copyright 2015-2020 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_control/api.h>
#include <mc_control/mc_controller.h>

#include <mc_tasks/CoMTask.h>
#include <mc_tasks/EndEffectorTask.h>

namespace mc_control
{

struct MC_CONTROL_DLLAPI MCEndEffectorController : public MCController
{
public:
  MCEndEffectorController(std::shared_ptr<mc_rbdyn::RobotModule> robot,
                          double dt,
                          const mc_rtc::Configuration & config);

  void reset(const ControllerResetData & reset_data) override;

private:
  std::shared_ptr<mc_tasks::EndEffectorTask> efTask_;
  std::shared_ptr<mc_tasks::CoMTask> comTask_;
  std::vector<mc_tasks::MetaTaskPtr> tasks_;
};

} // namespace mc_control

CONTROLLER_CONSTRUCTOR("EndEffector", mc_control::MCEndEffectorController)


<|start_filename|>doc/_data/schemas/common/gains_spline_trajectory.json<|end_filename|>
{
  "type": "array",
  "description": "Gains can be either represented as Vector6d (dimensional gains) or double",
  "items":
  [
    {
      "type": "number", "description": "time"
    },
    {
      "$ref": "/../../Eigen/Vector6d.json"
    }
  ],
  "minItems": 1
}


<|start_filename|>doc/_data/schemas/common/fdqp_weights.json<|end_filename|>
{
  "type": "object",
  "title": "mc_rbdyn::lipm_stabilizer::FDQPWeights",
  "description": "Force distribution QP parameters",
  "properties":
  {
    "net_wrench": { "type": "number", "minimum": 0, "default": 10000 },
    "ankle_torque": { "type": "number", "minimum": 0, "default": 100 },
    "pressure": { "type": "number", "minimum": 0, "default": 1 }
  }
}


<|start_filename|>src/mc_robots/env.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_rbdyn/RobotModule.h>
#include <mc_rtc/config.h>

#include "api.h"

namespace mc_robots
{

struct MC_ROBOTS_DLLAPI EnvRobotModule : public mc_rbdyn::RobotModule
{
public:
  EnvRobotModule(const std::string & env_path, const std::string & env_name, bool fixed);
};

} // namespace mc_robots


<|start_filename|>doc/_data/schemas/common/contactTarget.json<|end_filename|>
{
  "type": "object",
  "title": "Stabilizer Contact Target",
  "description": "Stabilizer Contact Target (default: current contact surface pose)",
  "properties":
  {
    "rotation": { "$ref": "/../../Eigen/Vector3d.json" },
    "translation": { "$ref": "/../../Eigen/Vector3d.json" },
    "overwriteRPY": { "$ref": "/../../common/overwriteRPY.json" },
    "height": { "type": "number", "minimum": 0 }
  }
}


<|start_filename|>src/mc_rtc/debug.in.cpp<|end_filename|>
#include <mc_rtc/debug.h>

/* !!! WARNING !!!
 * debug.cpp is a generated file
 * if you wish to change this file, edit:
 * @PROJECT_SOURCE_DIR@/src/mc_rtc/debug.in.cpp
 */

namespace mc_rtc
{

bool debug() noexcept
{
  // clang-format off
  return $<CONFIG:debug> == 1;
  // clang-format on
}

} // namespace mc_rtc


<|start_filename|>include/mc_observers/Observer.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

/*! Interface used to load observers */

#include <mc_observers/api.h>
#include <mc_rtc/gui/StateBuilder.h>
#include <mc_rtc/log/Logger.h>

namespace mc_rtc
{
namespace gui
{
struct StateBuilder;
} // namespace gui
} // namespace mc_rtc

namespace mc_control
{
struct MCController;
} // namespace mc_control

namespace mc_rbdyn
{

struct Robots;
}

namespace mc_observers
{

/**
 * @brief State observation API
 *
 * All new observers must inherit from this Observer class, and implement the
 * required virtual functions (at least reset, run, updateRobots). The framework
 * provides a mechanism to load state observation pipelines (see ObserverPipeline) consisting
 * of multiple sequences of observers running sequentially, each estimating part
 * of the robot state. In that case, observers are loaded by the framework from
 * libraries, and must be exported using the EXPORT_OBSERVER_MODULE(NAME, TYPE)
 * function defined in ObserverMacros.h
 *
 * Each observer has access to the public members of its parent controller, and thus can
 * access properties of the robots, sensors, datastore, logger, gui,
 * and set its estimation results to the controller's realRobots instances.
 */
struct MC_OBSERVERS_DLLAPI Observer
{
  Observer(const std::string & type, double dt) : type_(type), dt_(dt) {}
  virtual ~Observer() = default;

  /**
   * @brief Configure observer
   *
   * @param config Configuration for this observer. Refer to the JSON Schema
   * documentation fo each observer for details on supported entries:
   * https://jrl-umi3218.github.io/mc_rtc/json.html#Observers-objects
   */
  virtual void configure(const mc_control::MCController & /*ctl*/, const mc_rtc::Configuration & /*config*/) {}

  /*! \brief Reset estimator.
   *
   * The reset function should make sure that the observer is started in a state
   * consistent with the current robot state.
   *
   * This function is called after MCController::reset()
   **/
  virtual void reset(const mc_control::MCController & ctl) = 0;

  /*! \brief Compute observer state
   *
   * This function is called before MCController::run()
   **/
  virtual bool run(const mc_control::MCController & ctl) = 0;

  /*! \brief Update the real robot state from the observed state
   *
   * This function is expected to write the estimated parameters to the
   * corresponding real robot instances ctl.realRobots(), and compute the necessary information
   * to ensure a consistent state (e.g calling forward velocity after
   * updating joint velocities, etc).
   *
   * This function is called after Observer::run() if the observer is declared
   * to update the real robot in the pipeline configuration.
   *
   * \param ctl Controller running this observer
   **/
  virtual void update(mc_control::MCController & ctl) = 0;

  /**
   * @brief Set the observer's name
   *
   * @param name Name of the observer
   */
  inline void name(const std::string & name)
  {
    name_ = name;
  }

  /**
   * @brief Returns the observer's name
   */
  inline const std::string & name() const noexcept
  {
    return name_;
  }

  /*! \brief Add observer entries to the logger under the categrory "category + name()". */
  void addToLogger_(const mc_control::MCController & ctl, mc_rtc::Logger & logger, const std::string & category = "")
  {
    addToLogger(ctl, logger, category + "_" + name_);
  }

  /*! \brief Remove observer from logger. */
  void removeFromLogger_(mc_rtc::Logger & logger, const std::string & category = "")
  {
    removeFromLogger(logger, category + "_" + name_);
  }

  /*! \brief Add observer to the gui under category {category, name()} */
  void addToGUI_(const mc_control::MCController & ctl,
                 mc_rtc::gui::StateBuilder & gui,
                 std::vector<std::string> category = {})
  {
    category.push_back(name_);
    addToGUI(ctl, gui, category);
  }

  /*! \brief Remove observer from gui
   *
   * Default implementation removes category {category, name()}
   */
  void removeFromGUI_(mc_rtc::gui::StateBuilder & gui, std::vector<std::string> category = {})
  {
    category.push_back(name_);
    removeFromGUI(gui, category);
  };

  /*! \brief Short description of the observer
   *
   * Used to display a short summary of the observers pipeline to the user, the description should be as short as
   * possible while showing the important information about the observer.
   *
   * The output might end up looking like:
   * Encoders (Position+Velocity) -> KinematicInertial (cutoff=0.01) -> BodySensor
   *
   * \returns Short description of the observer.
   */
  inline const std::string & desc() const noexcept
  {
    return desc_;
  }

  inline const std::string type() const noexcept
  {
    return type_;
  }

  /*! Informative message about the last error */
  inline const std::string & error() const noexcept
  {
    return error_;
  }

  /*! Controller timestep */
  inline double dt() const noexcept
  {
    return dt_;
  }

protected:
  /*! \brief Add observer from logger
   *
   * Default implementation does nothing, each observer implementation is
   * responsible for adding its own log entries
   *
   * @param category Category in which to log this observer
   */
  virtual void addToLogger(const mc_control::MCController &, mc_rtc::Logger &, const std::string & /* category */) {}

  /*! \brief Remove observer from logger
   *
   * The default implementation removes the entry that were registered by this
   *
   * @param category Category in which this observer entries are logged
   */
  virtual void removeFromLogger(mc_rtc::Logger &, const std::string & /* category */);

  /*! \brief Add observer information the GUI.
   *
   * Default implementation does nothing, each observer implementation is
   * responsible for adding its own elements to the GUI.
   *
   * @param category Category in which to add this observer
   */
  virtual void addToGUI(const mc_control::MCController &,
                        mc_rtc::gui::StateBuilder &,
                        const std::vector<std::string> & /* category */)
  {
  }

  /**
   * @brief Remove observer from GUI
   *
   * Default implementation removes the category
   *
   * @param gui
   * @param category Categroy in which the observer was added
   */
  virtual void removeFromGUI(mc_rtc::gui::StateBuilder &, const std::vector<std::string> & category);

protected:
  std::string name_; ///< Observer name
  std::string type_; ///< Observer type
  std::string desc_; ///< Short description of the observer and its configuration
  std::string error_; ///< Descriptive error message to show if the observer failed

  double dt_; ///< Timestep
};

using ObserverPtr = std::shared_ptr<mc_observers::Observer>;

} // namespace mc_observers

#ifdef WIN32
#  define OBSERVER_MODULE_API __declspec(dllexport)
#else
#  if __GNUC__ >= 4
#    define OBSERVER_MODULE_API __attribute__((visibility("default")))
#  else
#    define OBSERVER_MODULE_API
#  endif
#endif


<|start_filename|>src/mc_control/samples/ExternalForces/src/lib.cpp<|end_filename|>
/*
 * Copyright 2021 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include "mc_external_forces_controller.h"

CONTROLLER_CONSTRUCTOR("ExternalForces", ExternalForcesController)


<|start_filename|>doc/_data/schemas/mc_rbdyn/StabilizerConfiguration.json<|end_filename|>
{
  "allOf":
  [
    {
      "type": "object",
      "title": "mc_rbdyn::lipm_stabilizer::StabilizerConfiguration",
      "description": "Configuration of {% doxygen mc_tasks::lipm_stabilizer::StabilizerTask %} stabilization parameters (DCM tracking, admittance, tasks weights, etc). A default configuration is provided by each robot's {% doxygen mc_rbdyn::RobotModule %}."
    },
    {
      "$ref": "/../../common/StabilizerConfiguration.json"
    }
  ]
}


<|start_filename|>doc/_data/schemas/common/State.json<|end_filename|>
{
  "properties":
  {
    "AddContacts": { "type": "array", "items": { "$ref": "/../../common/fsm_contact.json" },
                     "description": "Add the specified contacts during the state's start"  },
    "RemoveContacts": { "type": "array", "items": { "$ref": "/../../common/fsm_contact.json" },
                        "description": "Remove the specified contacts during the state's start"  },
    "AddContactsAfter": { "type": "array", "items": { "$ref": "/../../common/fsm_contact.json" },
                          "description": "Add the specified contacts during the state's teardown"  },
    "RemoveContactsAfter": { "type": "array", "items": { "$ref": "/../../common/fsm_contact.json" },
                             "description": "Remove the specified contacts during the state's teardown"  },
    "AddCollisions": { "type": "array", "items": { "$ref": "/../../common/fsm_collision.json" },
                       "description": "Add the specified collisions during the state's start" },
    "RemoveCollisions": { "type": "array", "items": { "$ref": "/../../common/fsm_collision.json" },
                          "description": "Remove the specified collisions during the state's start" },
    "AddCollisionsAfter": { "type": "array", "items": { "$ref": "/../../common/fsm_collision.json" },
                            "description": "Add the specified collisions during the state's teardown" },
    "RemoveCollisionsAfter": { "type": "array", "items": { "$ref": "/../../common/fsm_collision.json" },
                               "description": "Remove the specified collisions during the state's teardown" },
    "DisablePostureTask":
    {
      "oneOf": [
        { "type": "boolean", "default": false, "description": "If true, disable all FSM posture tasks during this state execution." },
        { "type": "array", "items": { "type": "string" }, "description": "Disable the posture tasks for the specified robots during this state execution." }
      ]
    },
    "constraints": { "type": "object",
                     "description": "Constraints that will be added when the state starts and removed when the state stops.<br/>Keys are names for the constraints and values are <a href=\"{{site.baseurl}}/json.html#ConstraintSet\">ConstraintSet objects</a>." },
    "tasks": { "type": "object",
               "description": "Tasks that will be added when the state starts and removed when the state stops.<br/>Keys are names for the tasks and values are <a href=\"{{site.baseurl}}/json.html#MetaTask\">MetaTask objects</a>." }
  }
}


<|start_filename|>src/mc_solver/KinematicsConstraint.cpp<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include <mc_solver/KinematicsConstraint.h>

#include <Tasks/Bounds.h>

#include <array>

namespace mc_solver
{

KinematicsConstraint::KinematicsConstraint(const mc_rbdyn::Robots & robots, unsigned int robotIndex, double timeStep)
{
  const mc_rbdyn::Robot & robot = robots.robot(robotIndex);
  tasks::QBound qBound(robot.ql(), robot.qu());
  tasks::AlphaDBound aDBound(robot.al(), robot.au());
  tasks::AlphaDDBound aDDBound(robot.jl(), robot.ju());
  jointLimitsConstr.reset(new tasks::qp::JointLimitsConstr(robots.mbs(), static_cast<int>(robotIndex), qBound, aDBound,
                                                           aDDBound, timeStep));
}

KinematicsConstraint::KinematicsConstraint(const mc_rbdyn::Robots & robots,
                                           unsigned int robotIndex,
                                           double timeStep,
                                           const std::array<double, 3> & damper,
                                           double velocityPercent)
{
  const mc_rbdyn::Robot & robot = robots.robot(robotIndex);
  tasks::QBound qBound(robot.ql(), robot.qu());
  double percentInter = damper[0];
  double percentSecur = damper[1];
  double offset = damper[2];

  std::vector<std::vector<double>> vl = robot.vl();
  std::vector<std::vector<double>> vu = robot.vu();
  std::vector<std::vector<double>> al = robot.al();
  std::vector<std::vector<double>> au = robot.au();
  std::vector<std::vector<double>> jl = robot.jl();
  std::vector<std::vector<double>> ju = robot.ju();
  for(auto & vi : vl)
  {
    for(auto & v : vi)
    {
      v = v * velocityPercent;
    }
  }
  for(auto & vi : vu)
  {
    for(auto & v : vi)
    {
      v = v * velocityPercent;
    }
  }
  tasks::AlphaBound alphaBound(vl, vu);
  tasks::AlphaDBound alphaDBound(al, au);
  tasks::AlphaDDBound alphaDDBound(jl, ju);

  damperJointLimitsConstr.reset(new tasks::qp::DamperJointLimitsConstr(robots.mbs(), static_cast<int>(robotIndex),
                                                                       qBound, alphaBound, alphaDBound, alphaDDBound,
                                                                       percentInter, percentSecur, offset, timeStep));
}

void KinematicsConstraint::addToSolver(const std::vector<rbd::MultiBody> & mbs, tasks::qp::QPSolver & solver)
{
  if(damperJointLimitsConstr)
  {
    damperJointLimitsConstr->addToSolver(mbs, solver);
  }
  if(jointLimitsConstr)
  {
    jointLimitsConstr->addToSolver(mbs, solver);
  }
}

void KinematicsConstraint::removeFromSolver(tasks::qp::QPSolver & solver)
{
  if(damperJointLimitsConstr)
  {
    damperJointLimitsConstr->removeFromSolver(solver);
  }
  if(jointLimitsConstr)
  {
    jointLimitsConstr->removeFromSolver(solver);
  }
}

} // namespace mc_solver


<|start_filename|>src/mc_control/GlobalPluginLoader.cpp<|end_filename|>
#include <mc_control/GlobalPluginLoader.h>

namespace mc_control
{

std::unique_ptr<mc_rtc::ObjectLoader<GlobalPlugin>> GlobalPluginLoader::loader_;

mc_rtc::ObjectLoader<GlobalPlugin> & GlobalPluginLoader::loader()
{
  if(loader_)
  {
    return *loader_;
  }
  loader_.reset(new mc_rtc::ObjectLoader<GlobalPlugin>("MC_RTC_GLOBAL_PLUGIN", {}, false, false));
  return *loader_;
}

} // namespace mc_control


<|start_filename|>include/mc_control/GlobalPluginLoader.h<|end_filename|>
/*
 * Copyright 2015-2020 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

/*! Static interface used to load plugins, this is only used in static builds of mc_rtc */

#include <mc_control/GlobalPlugin.h>

#include <mc_rtc/config.h>
#include <mc_rtc/loader.h>

#include <map>
#include <memory>
#include <mutex>

namespace mc_control
{

/*! \class GlobalPluginLoader
 *
 * A wrapper around a singleton mc_rtc::ObjectLoader<GlobalPlugin>
 */
struct MC_CONTROL_DLLAPI GlobalPluginLoader
{
public:
  static mc_rtc::ObjectLoader<GlobalPlugin> & loader();

private:
  static std::unique_ptr<mc_rtc::ObjectLoader<GlobalPlugin>> loader_;
};

} // namespace mc_control


<|start_filename|>doc/_data/schemas/common/external_wrench.json<|end_filename|>
{
  "type": "object",
  "title": "mc_rbdyn::lipm_stabilizer::ExternalWrenchConfiguration",
  "description": "Parameters for external wrench compensation. See {% doxygen mc_rbdyn::lipm_stabilizer::ExternalWrenchConfiguration %} for detailed usage.",
  "properties":
  {
    "add_expected_com_offset": {
      "type": "boolean",
      "default": "false",
      "description": "Whether to add the CoM offset expected from the external wrenches."
    },
    "subtract_measured_value": {
      "type": "boolean",
      "default": "false",
      "description": "Use the measured external wrenches instead of target ones"
    },
    "modify_com_error": {
      "type": "boolean",
      "default": "false",
      "description": "Modify CoM depending on the error of the external wrenches in target and measurement"
    },
    "modify_zmp_error": {
      "type": "boolean",
      "default": "false",
      "description": "Modify ZMP depending on the error of the external wrenches in target and measurement"
    },
    "modify_zmp_error_d": {
      "type": "boolean",
      "default": "false",
      "description": "Modify ZMP velocity depending on the error of the external wrenches in target and measurement"
    },

    "com_offset_err_com_limit": {
      "type": "number",
      "minimum": 0,
      "default": 0.1,
      "description": "Limit of CoM offset error handled by CoM modification [m]"
    },
    "com_offset_err_zmp_limit": {
      "type": "number",
      "minimum": 0,
      "default": 0.1,
      "description": "Limit of ZMP offset error handled by ZMP modification [m]"
    },
    "ext_wrench_sum_cutoff": {
      "type": "number",
      "minimum": 0,
      "default": 0.05,
      "description": "Cutoff period [s] for the low-pass filter of the sum of the measured external wrenches"
    },
    "com_offset_cutoff": {
      "type": "number",
      "minimum": 0,
      "default": 0.05,
      "description": "Cutoff period [s] for the low-pass filter of CoM offset"
    },
    "com_offset_com_cutoff": {
      "type": "number",
      "minimum": 0,
      "default": 1.0,
      "description": "Cutoff period [s] for the low-pass filter of CoM offset to extract CoM modification"
    },
    "derivator_time_constant": {
      "type": "number",
      "minimum": 0,
      "default": 1.0,
      "description": "Time window [s] for the stationary offset filter of the CoM offset derivator"
    }
  }
}


<|start_filename|>doc/_includes/schema_categories/ConstraintSet.html<|end_filename|>
<p>These objects represent constraints that can be added to the solver.</p>

<p>They can be loaded from C++ with the following code:</p>

<div class="card bg-light card-source">
  <div class="card-body">
{% highlight cpp %}
// Access the constraint set loader
#include <mc_solver/ConstraintSetLoader.h>

// Get the constraint from a JSON file
auto constraint = mc_solver::ConstraintSetLoader::load(solver(), "/my/path/constraint.json");

// Get the constraint from a YAML file
auto constraint = mc_solver::ConstraintSetLoader::load(solver(), "/my/path/constraint.yaml");

// Actually you can get the constraint from any mc_rtc::Configuration entry
auto constraint = mc_solver::ConstraintSetLoader::load(solver(), config("constraint"));

// In all the cases above, constraint is an std::shared_ptr<mc_solver::ConstraintSet>
// but you can retrieve a more precise type, mc_rtc will check that the constraints
// that was retrieved from disk is compatible with the constraint you requested
auto constraint = mc_solver::ConstraintSetLoader::load<mc_solver::BoundedSpeedConstr>(solver(), config("constraint"));
{% endhighlight %}
  </div>
</div>

<p>This page shows what data is expected for each constraint available in mc_rtc.</p>


<|start_filename|>3rd-party/qhull/src/libqhullcpp/QhullSet.cpp<|end_filename|>
/****************************************************************************
**
** Copyright (c) 2008-2020 <NAME>. All rights reserved.
** $Id: //main/2019/qhull/src/libqhullcpp/QhullSet.cpp#4 $$Change: 3009 $
** $DateTime: 2020/07/30 19:25:22 $$Author: bbarber $
**
****************************************************************************/

#//! QhullSet -- Qhull's set structure, setT, as a C++ class

#include "libqhullcpp/QhullSet.h"

#include "libqhullcpp/Qhull.h"
#include "libqhullcpp/QhullError.h"

#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
#endif

namespace orgQhull {

#//!\name Class objects

setT QhullSetBase::
s_empty_set;

#//!\name Constructors

QhullSetBase::
QhullSetBase(const Qhull &q, setT *s)
: qh_set(s ? s : &s_empty_set)
, qh_qh(q.qh())
{
}

#//!\name Class methods

// Same code for qh_setsize [qset_r.c] and QhullSetBase::count [static]
countT QhullSetBase::
count(const setT *set)
{
    countT size;
    const setelemT *sizep;

    if(!set){
        return(0);
    }
    sizep= SETsizeaddr_(set);
    if((size= sizep->i)){
        size--;
        if(size > set->maxsize){
            // QH11022 FIX: How to add additional output to a error? -- qh_setprint(qhmem.ferr, "set: ", set);
            throw QhullError(10032, "QhullSet internal error: current set size %d is greater than maximum size %d\n",
                size, set->maxsize);
        }
    }else{
        size= set->maxsize;
    }
    return size;
}//count

}//namespace orgQhull



<|start_filename|>src/mc_control/samples/Admittance/src/mc_admittance_sample_controller.cpp<|end_filename|>
/*
 * Copyright 2020 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include "mc_admittance_sample_controller.h"

AdmittanceSampleController::AdmittanceSampleController(mc_rbdyn::RobotModulePtr rm,
                                                       double dt,
                                                       const mc_rtc::Configuration & config)
: mc_control::fsm::Controller(rm, dt, config)
{
  datastore().make_call("KinematicAnchorFrame::" + robot().name(), [](const mc_rbdyn::Robot & robot) {
    return sva::interpolate(robot.surfacePose("LeftFoot"), robot.surfacePose("RightFoot"), 0.5);
  });
}

void AdmittanceSampleController::reset(const mc_control::ControllerResetData & reset_data)
{
  Controller::reset(reset_data);
  auto handForceConfig = mc_rtc::gui::ForceConfig(mc_rtc::gui::Color(0., 1., 0.));
  handForceConfig.force_scale *= 10;
  gui()->addElement({"Forces"},
                    mc_rtc::gui::Force(
                        "RightGripper", handForceConfig, [this]() { return robot().surfaceWrench("RightGripper"); },
                        [this]() { return robot().surfacePose("RightGripper"); }));

  using Color = mc_rtc::gui::Color;
  gui()->addPlot(
      "Wrenches without gravity (t)", mc_rtc::gui::plot::X({"t", {t_ + 0, t_ + 10}}, [this]() { return t_; }),
      mc_rtc::gui::plot::Y(
          "Wrenches(z)", [this]() { return robot().surfaceWrench("RightGripper").force().z(); }, Color::Blue));
}

bool AdmittanceSampleController::run()
{
  t_ += timeStep;
  return Controller::run();
}


<|start_filename|>doc/_data/schemas/mc_rbdyn/S_ObjectPtr.json<|end_filename|>
{
  "title": "mc_rbdyn::S_ObjectPtr",
  "type": "object",
  "properties":
  {
    "type":
    {
      "enum": ["box", "capsule", "cone", "cylinder", "point", "polyhedron", "sphere", "stp-bv", "stp-bv-p", "superellipsoid"],
      "description": "Type of sch object"
    },
    "width":
    {
      "type": "number",
      "description": "Width of the box (if type == box)"
    },
    "height":
    {
      "type": "number",
      "description": "Height of the box (if type == box)<br/>Height of the cone (if type == cone)"
    },
    "depth":
    {
      "type": "number",
      "description": "Depth of the box (if type == box)"
    },
    "angle":
    {
      "type": "number",
      "description": "Angle of the cone (if type == cone)"
    },
    "p1":
    {
      "type": "array",
      "items":
      {
        "type": "number"
      },
      "minItems": 3,
      "maxItems": 3,
      "description": "Starting point of the capsule (if type == capsule)<br/>Starting point of the cylinder (if type == cylinder)"
    },
    "p2":
    {
      "type": "array",
      "items":
      {
        "type": "number"
      },
      "minItems": 3,
      "maxItems": 3,
      "description": "End point of the capsule (if type == capsule)<br/>End point of the cylinder (if type == cylinder)"
    },
    "radius":
    {
      "type": "number",
      "description": "Radius of the capsule (if type == capsule)<br/>Radius of the cylinder (if type == cylinder)<br/>Radius of the sphere (if type == sphere)"
    },
    "filename":
    {
      "type": "string",
      "description": "Path to the file with data for the object (if type == polyhedron or type == stp-bv or type == stp-bv-p"
    },
    "a":
    {
      "type": "number",
      "description": "First parameter of the superellipsoid (if type == superellipsoid"
    },
    "b":
    {
      "type": "number",
      "description": "Second parameter of the superellipsoid (if type == superellipsoid"
    },
    "c":
    {
      "type": "number",
      "description": "Third parameter of the superellipsoid (if type == superellipsoid"
    },
    "epsilon1":
    {
      "type": "number",
      "description": "Epsilon 1 parameter of the superellipsoid (if type == superellipsoid"
    },
    "epsilon2":
    {
      "type": "number",
      "description": "Epsilon 2 parameter of the superellipsoid (if type == superellipsoid"
    }
  }
}


<|start_filename|>include/mc_rtc/log/FlatLog.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_rtc/log/utils.h>
#include <mc_rtc/utils_api.h>

#include <SpaceVecAlg/SpaceVecAlg>

#include <stdexcept>
#include <string>
#include <unordered_map>

namespace mc_rtc
{

namespace log
{

/** From an on-disk binary log recorded by mc_rtc, return a flat structure */
struct MC_RTC_UTILS_DLLAPI FlatLog
{
  /** Default constructor, empty log */
  FlatLog() = default;

  /** Load a file into the log */
  FlatLog(const std::string & fpath);

  FlatLog(const FlatLog &) = delete;
  FlatLog & operator=(const FlatLog &) = delete;

  FlatLog(FlatLog &&) = default;
  FlatLog & operator=(FlatLog &&) = default;

  /** Load a file into the log, erase the current content of the flat log */
  void load(const std::string & fpath);

  /** Append a file into the flat log, the resulting content is the concatenation of the two logs*/
  void append(const std::string & fpath);

  /** Returns the size of the log */
  size_t size() const;

  /** Returns a sorted list of entries in the log */
  std::set<std::string> entries() const;

  /** Returns true if the log has the provided entry */
  bool has(const std::string & entry) const;

  /** Returns available types for an entry */
  std::set<LogType> types(const std::string & entry) const;

  /** Get the first non None type for an entry */
  LogType type(const std::string & entry) const;

  /** Get the type at index i */
  LogType type(const std::string & entry, size_t i) const;

  /** Get a type record entry.
   *
   * Get null pointer entry when the record data type does not match the
   * requested data type
   *
   * \param entry Entry to get
   *
   */
  template<typename T>
  std::vector<const T *> getRaw(const std::string & entry) const;

  /** Get a typed record entry
   *
   * Get a default value when the record data type does not match the requested
   * data type
   *
   * \param entry Entry to get
   *
   * \param def Default value when the record data does not match requested
   * data
   *
   */
  template<typename T>
  std::vector<T> get(const std::string & entry, const T & def) const;

  /** Get a typed record entry
   *
   * When the record data type does not match the requested data type, the
   * vector is automatically filled up with the last (or first if relevant)
   * recorded data
   *
   * \param entry Entry to get
   *
   */
  template<typename T>
  std::vector<T> get(const std::string & entry) const;

  /** Get a typed record entry at a given index
   *
   * When the record type does not match the requested data type, returns the default value provided.
   *
   * \param entry Entry to get
   *
   * \param i Index to get
   *
   * \param def Default value when the record data does not match requested
   * data
   *
   */
  template<typename T>
  T get(const std::string & entry, size_t i, const T & def) const;

  /** Get a typed raw entry at a given index
   *
   * Returns nullptr when the requested type does not match the record type
   *
   * \param entry Entry to get
   *
   * \param i Index to get
   *
   */
  template<typename T>
  const T * getRaw(const std::string & entry, size_t i) const;

  struct record
  {
    using unique_void_ptr = std::unique_ptr<void, void (*)(void const *)>;
    record();
    record(LogType t, unique_void_ptr && d) : type(t), data(std::move(d)) {}
    record(const record &) = delete;
    record & operator=(const record &) = delete;
    record(record &&) = default;
    record & operator=(record &&) = default;
    LogType type = mc_rtc::log::LogType::None;
    unique_void_ptr data;
  };
  struct entry
  {
    std::string name;
    std::vector<record> records;
  };

private:
  std::vector<entry> data_;

  /** Retrieve records for a given entry */
  const std::vector<record> & at(const std::string & entry) const;

  /** Retrieve the index of a given entry, creates the entry if it doesn't exist */
  size_t index(const std::string & entry, size_t size);

  /** Append a flat file to the log, all entries will be either double or strings */
  void appendFlat(const std::string & fpath);

  /** Append a binary file to the log */
  void appendBin(const std::string & fpath);
};

} // namespace log

} // namespace mc_rtc

#include "FlatLog.hpp"


<|start_filename|>include/mc_tasks/LookAtTask.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once
#include <mc_tasks/VectorOrientationTask.h>

namespace mc_tasks
{

/*! \brief Orient a "gaze" vector defined on a body to look towards a world
 * position. This task is a convenience wrapper around VectorOrientationTask
 */
struct MC_TASKS_DLLAPI LookAtTask : public VectorOrientationTask
{
  /*! \brief Constructor with user-specified target initialization
   * \param bodyName Name of the body to control
   * \param bodyVector Gaze vector for the body.
        For instance [1., 0, 0] will try to align the x axis of the body with
   the target direction.
   * \param targetPos Position of target frame to look towards in world frame
   * \param robots Robots controlled by this task
   * \param robotIndex Index of the robot controlled by this task
   * \param stiffness Task stiffness
   * \param weight Task weight
   *
   */
  LookAtTask(const std::string & bodyName,
             const Eigen::Vector3d & bodyVector,
             const Eigen::Vector3d & targetPos,
             const mc_rbdyn::Robots & robots,
             unsigned int robotIndex,
             double stiffness = 2.0,
             double weight = 500);

  /*! \brief Constructor with default target initialization */
  LookAtTask(const std::string & bodyName,
             const Eigen::Vector3d & bodyVector,
             const mc_rbdyn::Robots & robots,
             unsigned int robotIndex,
             double stiffness = 2.0,
             double weight = 500);

  /*! \brief Reset the task */
  void reset() override;

  /**
   * \brief Look towards the target frame position
   *
   * \param pos Target position in world frame
   */
  void target(const Eigen::Vector3d & pos);
  /**
   * @brief Gets the target frame position
   * See targetVector() to obtain the gaze vector
   *
   * @return Target vector in world frame
   */
  Eigen::Vector3d target() const;

private:
  void addToLogger(mc_rtc::Logger & logger) override;
  void addToGUI(mc_rtc::gui::StateBuilder & gui) override;

private:
  /*! Target position in world frame */
  Eigen::Vector3d target_pos_;
};

} // namespace mc_tasks


<|start_filename|>doc/_data/schemas/State/StabilizerStandingState.json<|end_filename|>
{
  "allOf":
  [
    {
      "title": "mc_control::fsm::StabilizerStandingState",
      "description": "This state creates a {% doxygen mc_tasks::lipm_stabilizer::StabilizerTask %}, and provides set-point position target to the CoM, from which a full dynamic trajectory (com, comd, comdd, zmp) is computed and provided as reference to the lipm_stabilizer::StabilizerTask. For further information, refer to the <a href=\"{{site.baseurl}}/tutorials/recipes/lipm-stabilizer.html#using-the-stabilizer-fsm\">tutorial</a> and the <a href=\"{{site.baseurl}}/tutorials/samples/list-of-samples.html#lipmstabilizer\">LIPMStabilizer sample controller</a>.",
      "type": "object",
      "properties":
      {
        "StabilizerConfig":
        {
          "$ref": "/../MetaTask/LIPMStabilizerTask.json"
        },
        "stiffness": { "type": "number", "minimum": 0, "default": 5, "description": "Stiffness of CoM target" },
        "damping": { "type": "number", "minimum": 0, "description": "Damping of CoM target (default=2*sqrt(stiffness))" },
        "above": { "type": "string", "description": "Put CoM over the special frames LeftAnkle, RightAnkle or Center, or any other valid surface on the robot"},
        "com": { "$ref": "/../../Eigen/Vector3d.json", "description": "Target CoM position"},
        "completion":
        {
          "type": "object",
          "title": "Completion criteria for the state",
          "description": "Completion criteria for the state",
          "properties":
          {
            "dcmEval": { "$ref": "/../../Eigen/Vector3d.json", "description": "Returns true with output 'OK' when the dcm error is under this threshold."}
          }
        },
        "anchorFrameFunction": { "type": "string", "default": "KinematicAnchorFrame::&lt;robot&gt;", "description": "Name of the datastore entry providing a kinematic anchor frame for both the control/real robots. This anchor frame may be used by observers to compute the state of the real robot instance." }
      },
      "required": []
    },
    {
      "$ref": "/../../common/State.json"
    }
  ]
}


<|start_filename|>3rd-party/qhull/src/libqhullcpp/QhullVertex.cpp<|end_filename|>
/****************************************************************************
**
** Copyright (c) 2008-2020 <NAME>. All rights reserved.
** $Id: //main/2019/qhull/src/libqhullcpp/QhullVertex.cpp#4 $$Change: 3001 $
** $DateTime: 2020/07/24 20:43:28 $$Author: bbarber $
**
****************************************************************************/

#//! QhullVertex -- Qhull's vertex structure, vertexT, as a C++ class

#include "libqhullcpp/QhullVertex.h"

#include "libqhullcpp/Qhull.h"
#include "libqhullcpp/QhullPoint.h"
#include "libqhullcpp/QhullFacetSet.h"
#include "libqhullcpp/QhullFacet.h"

#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
#pragma warning( disable : 4611)  // interaction between '_setjmp' and C++ object destruction is non-portable
#pragma warning( disable : 4996)  // function was declared deprecated(strcpy, localtime, etc.)
#endif

namespace orgQhull {

#//!\name Class objects
vertexT QhullVertex::
s_empty_vertex= {NULL,NULL,NULL,NULL,0,0,       // must match vertexT -Wmissing-field-initializers
                false,false,false,false,false,false};

#//!\name Constructors

QhullVertex::QhullVertex(const Qhull &q)
: qh_vertex(&s_empty_vertex)
, qh_qh(q.qh())
{
}//Default

QhullVertex::QhullVertex(const Qhull &q, vertexT *v)
: qh_vertex(v ? v : &s_empty_vertex)
, qh_qh(q.qh())
{
}//vertexT

QhullVertex::QhullVertex(const QhullVertex &other)
: qh_vertex(other.qh_vertex)
, qh_qh(other.qh_qh) 
{
}//copy constructor

#//!\name foreach

//! Return neighboring facets for a vertex
//! If neither merging nor Voronoi diagram, requires Qhull::defineVertexNeighborFacets() beforehand.
QhullFacetSet QhullVertex::
neighborFacets() const
{
    if(!neighborFacetsDefined()){
        throw QhullError(10034, "Qhull error: neighboring facets of vertex %d not defined.  Please call Qhull::defineVertexNeighborFacets() beforehand.", id());
    }
    return QhullFacetSet(qh_qh, qh_vertex->neighbors);
}//neighborFacets

}//namespace orgQhull

#//!\name Global functions

using std::endl;
using std::ostream;
using std::string;
using std::vector;
using orgQhull::QhullPoint;
using orgQhull::QhullFacet;
using orgQhull::QhullFacetSet;
using orgQhull::QhullFacetSetIterator;
using orgQhull::QhullVertex;

//! Duplicate of qh_printvertex [io_r.c]
ostream &
operator<<(ostream &os, const QhullVertex::PrintVertex &pr)
{
    QhullVertex v= *pr.vertex;
    QhullPoint p= v.point();
    if(*pr.print_message){
        os << pr.print_message << " ";
    }else{
        os << "- ";
    }
    os << "p" << p.id() << " (v" << v.id() << "): ";
    const realT *c= p.coordinates();
    for(int k= p.dimension(); k--; ){
        os << " " << *c++; // QH11010 FIX: %5.2g
    }
    if(v.getVertexT()->deleted){
        os << " deleted";
    }
    if(v.getVertexT()->delridge){
        os << " delridge";
    }
    if(v.getVertexT()->newfacet){
      os << " newfacet";
    }
    if(v.getVertexT()->seen && v.qh()->IStracing){
      os << " seen";
    }
    if(v.getVertexT()->seen2 && v.qh()->IStracing){
      os << " seen2";
    }
    os << endl;
    if(v.neighborFacetsDefined()){
        QhullFacetSetIterator i= v.neighborFacets();
        if(i.hasNext()){
            os << " neighborFacets:";
            countT count= 0;
            while(i.hasNext()){
                if(++count % 100 == 0){
                    os << endl << "     ";
                }
                QhullFacet f= i.next();
                os << " f" << f.id();
            }
            os << endl;
        }
    }
    return os;
}//<< PrintVertex



<|start_filename|>doc/_data/schemas/common/dcm_bias.json<|end_filename|>
{
  "type": "object",
  "title": "mc_rbdyn::lipm_stabilizer::DCMBiasEstimatorConfiguration",
  "description": "A humanoid robot can be modeled as an inverted pendulum. The dynamics can be linearized to obtain a dynamics with a convergent and a divergent component of motion (DCM). The DCM can be measured using the CoM and its velocity, but the CoM position can be biased. The dynamics of the DCM depends on the Zero Moment Point. This estimator uses Kalman Filtering to estimate this bias and give a delay-free filtering of the DCM. For more details please refer to LipmDcmEstimator class in <a href=\"https://jrl-umi3218.github.io/state-observation/doxygen/HEAD/classes.html#letter_l\">state-observation</a>",
  "properties":
  {

    "dcmMeasureErrorStd": {
      "type": "number",
      "minimum": 0,
      "default": 0.01,
      "description": "the standard deviation of the dcm estimation error, NOT including the bias (m)"
    },
    "zmpMeasureErrorStd": {
      "type": "number",
      "minimum": 0,
      "default": 0.05,
      "description": "the standard deviaiton of the zmp estimation error (m)"
    },
    "biasDriftPerSecondStd": {
      "type": "number",
      "minimum": 0,
      "default": 0.02,
      "description": "the standard deviation of the bias drift (m/s)"
    },
    "biasLimit": {
      "type": "number",
      "minimum": 0,
      "default": 0.02,
      "description": "the X and Y (expressed in local frame) largest accepted absolute values of the bias (zero means no limit)"
    },
    "withDCMBias": {
      "type": "boolean",
      "default": "false",
      "description": "whether the bias estimation is activated"
    },
    "withDCMFilter": {
      "type": "boolean",
      "default": "false",
      "description": "whether the DCM is filtered with this estimator"
    }
  }
}


<|start_filename|>include/mc_rtc/pragma.h<|end_filename|>
/*
 * Copyright 2015-2021 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

/** Preprocessor identity, used to force expansion (mainly to work around MSVC "feature" */
#define MC_RTC_PP_ID(x) x

/** Transform a expression to string*/
#define MC_RTC_STRINGIFY_(x) #x
#define MC_RTC_STRINGIFY(x) MC_RTC_STRINGIFY_(x)

#define MC_RTC_PRAGMA(x) _Pragma(MC_RTC_STRINGIFY(x))

/** Signal wrong number of arguments */
#define MC_RTC_WN(fun, ...)                                                                                          \
  MC_RTC_PRAGMA(message(__FILE__ "[" STRING(__LINE__) "] Incorrect number of arguments when invoking macro " STRING( \
      fun) ". Call is ignored"))

/** Sub-function in FOR_EACH macro reading arguments two by two */
#define MC_RTC_MAP2_0(fun)
#define MC_RTC_MAP2_1(fun, X, Y) fun(X, Y)
#define MC_RTC_MAP2_2(fun, X, Y, ...) fun(X, Y) MC_RTC_PP_ID(MC_RTC_MAP2_1(fun, __VA_ARGS__))
#define MC_RTC_MAP2_3(fun, X, Y, ...) fun(X, Y) MC_RTC_PP_ID(MC_RTC_MAP2_2(fun, __VA_ARGS__))
#define MC_RTC_MAP2_4(fun, X, Y, ...) fun(X, Y) MC_RTC_PP_ID(MC_RTC_MAP2_3(fun, __VA_ARGS__))
#define MC_RTC_MAP2_5(fun, X, Y, ...) fun(X, Y) MC_RTC_PP_ID(MC_RTC_MAP2_4(fun, __VA_ARGS__))
#define MC_RTC_MAP2_6(fun, X, Y, ...) fun(X, Y) MC_RTC_PP_ID(MC_RTC_MAP2_5(fun, __VA_ARGS__))
#define MC_RTC_MAP2_7(fun, X, Y, ...) fun(X, Y) MC_RTC_PP_ID(MC_RTC_MAP2_6(fun, __VA_ARGS__))
#define MC_RTC_MAP2_8(fun, X, Y, ...) fun(X, Y) MC_RTC_PP_ID(MC_RTC_MAP2_7(fun, __VA_ARGS__))
#define MC_RTC_MAP2_9(fun, X, Y, ...) fun(X, Y) MC_RTC_PP_ID(MC_RTC_MAP2_8(fun, __VA_ARGS__))
#define MC_RTC_MAP2_10(fun, X, Y, ...) fun(X, Y) MC_RTC_PP_ID(MC_RTC_MAP2_9(fun, __VA_ARGS__))

#define MC_RTC_GET_MACRO(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, \
                         _20, NAME, ...)                                                                           \
  NAME

/** Transform fun(arg1, arg2, arg3, arg4, ...) into fun(arg1, arg2), fun(arg3, arg4), ...*/
#define MC_RTC_MAP_TWO_ARGS(fun, ...)                                                                                \
  MC_RTC_PP_ID(MC_RTC_GET_MACRO(_0, __VA_ARGS__, MC_RTC_MAP2_10, MC_RTC_WN, MC_RTC_MAP2_9, MC_RTC_WN, MC_RTC_MAP2_8, \
                                MC_RTC_WN, MC_RTC_MAP2_7, MC_RTC_WN, MC_RTC_MAP2_6, MC_RTC_WN, MC_RTC_MAP2_5,        \
                                MC_RTC_WN, MC_RTC_MAP2_4, MC_RTC_WN, MC_RTC_MAP2_3, MC_RTC_WN, MC_RTC_MAP2_2,        \
                                MC_RTC_WN, MC_RTC_MAP2_1, MC_RTC_WN, MC_RTC_MAP2_0, MC_RTC_MAP2_0)(fun, __VA_ARGS__))

#ifdef __GNUG__
#  define MC_RTC_diagnostic_push _Pragma("GCC diagnostic push")
#  define MC_RTC_diagnostic_pop _Pragma("GCC diagnostic pop")
#  define MC_RTC_GCC_diagnostic_ignored(x) MC_RTC_PRAGMA(GCC diagnostic ignored x)
#  ifdef __clang__
#    define MC_RTC_ClangOnly_diagnostic_ignored(x) MC_RTC_PRAGMA(GCC diagnostic ignored x)
#    define MC_RTC_GCCOnly_diagnostic_ignored(x)
#  else
#    define MC_RTC_ClangOnly_diagnostic_ignored(x)
#    define MC_RTC_GCCOnly_diagnostic_ignored(x) MC_RTC_PRAGMA(GCC diagnostic ignored x)
#  endif
#else
#  define MC_RTC_GCC_diagnostic_ignored(x)
#  define MC_RTC_ClangOnly_diagnostic_ignored(x)
#  define MC_RTC_GCCOnly_diagnostic_ignored(x)
#endif

#ifdef _MSC_VER
#  define MC_RTC_diagnostic_push _Pragma("warning(push)")
#  define MC_RTC_diagnostic_pop _Pragma("warning(pop)")
#  define MC_RTC_MSVC_diagnostic_ignored(x) MC_RTC_PRAGMA(warning(disable : x))
#else
#  define MC_RTC_MSVC_diagnostic_ignored(x)
#endif

/** Helper macro for MC_RTC_diagnostic_ignored*/
#define MC_RTC_diagnostic_ignored_(c, w) MC_RTC_##c##_diagnostic_ignored(w)

/** Ignore warning by compiler. Used as MC_RTC_diagnostic_ignored(compiler1, warningID1, compiler2, warningID2, ...).*/
#define MC_RTC_diagnostic_ignored(...) MC_RTC_MAP_TWO_ARGS(MC_RTC_diagnostic_ignored_, __VA_ARGS__)

<|start_filename|>include/mc_tasks/GazeTask.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_tasks/TrajectoryTaskGeneric.h>

namespace mc_tasks
{

/*! \brief Control the Gaze of a body
 *
 * This task is thin wrapper around the appropriate tasks in Tasks.
 *
 */
struct MC_TASKS_DLLAPI GazeTask : public TrajectoryTaskGeneric<tasks::qp::GazeTask>
{
public:
  /*! \brief Constructor
   *
   * \param bodyName Name of the body to control
   *
   * \param point2d Position of the point in image frame
   *
   * \param depthEstimate Distance of the monitored point from the camera
   *
   * \param X_b_gaze Transformation between the camera link and the parent body
   *
   * \param robots Robots controlled by this task
   *
   * \param robotIndex Index of the robot controlled by this task
   *
   * \param stiffness Task stiffness
   *
   * \param weight Task weight
   *
   */
  GazeTask(const std::string & bodyName,
           const Eigen::Vector2d & point2d,
           double depthEstimate,
           const sva::PTransformd & X_b_gaze,
           const mc_rbdyn::Robots & robots,
           unsigned int robotIndex,
           double stiffness = 2.0,
           double weight = 500);

  /*! \brief Constructor
   *
   * \param bodyName Name of the body to control
   *
   * \param point3d Equal to (point2d[0], point2d[1], depthEstimate).
   * Depth estimate must be >0, the constructor will throw otherwise as this
   * would result in a division by zero.
   *
   * \param X_b_gaze Transformation between the camera link and the parent body
   *
   * \param robots Robots controlled by this task
   *
   * \param robotIndex Index of the robot controlled by this task
   *
   * \param stiffness Task stiffness
   *
   * \param weight Task weight
   *
   * \throws std::logic_error if point3d.z() <= 0
   *
   */
  GazeTask(const std::string & bodyName,
           const Eigen::Vector3d & point3d,
           const sva::PTransformd & X_b_gaze,
           const mc_rbdyn::Robots & robots,
           unsigned int robotIndex,
           double stiffness = 2.0,
           double weight = 500);

  /*! \brief Reset the task
   *
   * Set the task objective to the current body orientation
   */
  void reset() override;

  /*! \brief Set the current error
   *
   * \param point2d Target position in camera frame
   *
   * \param point2d_ref Desired position of the point in image frame
   *
   */
  void error(const Eigen::Vector2d & point2d, const Eigen::Vector2d & point2d_ref = Eigen::Vector2d::Zero());

  /*! \brief Set the current error
   *
   * \param point3d Target position in camera frame
   *
   * \param point2d_ref Desired position of the point in image frame
   *
   */
  void error(const Eigen::Vector3d & point3d, const Eigen::Vector2d & point2d_ref = Eigen::Vector2d::Zero());
};

} // namespace mc_tasks


<|start_filename|>tests/test_controller_restart.cpp<|end_filename|>
/*
 * Copyright 2021 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include <mc_control/mc_global_controller.h>

#include "utils.h"

#include <boost/filesystem.hpp>

static bool initialized = configureRobotLoader();

/** This tests checks that MCGlobalController can be deleted and constructed multiple time */

int main(int argc, char * argv[])
{
  if(argc < 3)
  {
    mc_rtc::log::critical("Wrong usage, expected: {} [conf] [nRuns]", argv[0]);
    return 1;
  }
  std::string config = argv[1];
  unsigned long nRuns = std::stoul(argv[2]);
  for(unsigned long i = 0; i < nRuns; ++i)
  {
    mc_control::MCGlobalController gc(config);
    for(size_t j = 0; j < 100; ++j)
    {
      gc.run();
    }
  }
}


<|start_filename|>3rd-party/qhull/src/libqhull/global.c<|end_filename|>

/*<html><pre>  -<a                             href="qh-globa.htm"
  >-------------------------------</a><a name="TOP">-</a>

   global.c
   initializes all the globals of the qhull application

   see README

   see libqhull.h for qh.globals and function prototypes

   see qhull_a.h for internal functions

   Copyright (c) 1993-2020 The Geometry Center.
   $Id: //main/2019/qhull/src/libqhull/global.c#16 $$Change: 3037 $
   $DateTime: 2020/09/03 17:28:32 $$Author: bbarber $
 */

#include "qhull_a.h"

/*========= qh definition -- globals defined in libqhull.h =======================*/

#if qh_QHpointer
qhT *qh_qh= NULL;       /* pointer to all global variables */
#else
qhT qh_qh;              /* all global variables.
                           Add "= {0}" if this causes a compiler error.
                           Also qh_qhstat in stat.c and qhmem in mem.c.  */
#endif

/*-<a                             href  ="qh-globa.htm#TOC"
  >--------------------------------</a><a name="version">-</a>

  qh_version
    version string by year and date
    qh_version2 for Unix users and -V

    the revision increases on code changes only

  notes:
    change date:    Changes.txt, Announce.txt, index.htm, README.txt,
                    qhull-news.html, Eudora signatures, CMakeLists.txt
    change version: README.txt, qh-get.htm, File_id.diz, Makefile.txt, CMakeLists.txt
    check that CMakeLists.txt @version is the same as qh_version2
    change year:    Copying.txt
    check download size
    recompile user_eg.c, rbox.c, libqhull.c, qconvex.c, qdelaun.c qvoronoi.c, qhalf.c, testqset.c
*/

const char qh_version[]= "2020.2 2020/08/31";
const char qh_version2[]= "qhull 8.0.2 (2020.2 2020/08/31)";

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="appendprint">-</a>

  qh_appendprint( printFormat )
    append printFormat to qh.PRINTout unless already defined
*/
void qh_appendprint(qh_PRINT format) {
  int i;

  for (i=0; i < qh_PRINTEND; i++) {
    if (qh PRINTout[i] == format && format != qh_PRINTqhull)
      break;
    if (!qh PRINTout[i]) {
      qh PRINTout[i]= format;
      break;
    }
  }
} /* appendprint */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="checkflags">-</a>

  qh_checkflags( commandStr, hiddenFlags )
    errors if commandStr contains hiddenFlags
    hiddenFlags starts and ends with a space and is space delimited (checked)

  notes:
    ignores first word (e.g., "qconvex i")
    use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces

  see:
    qh_initflags() initializes Qhull according to commandStr
*/
void qh_checkflags(char *command, char *hiddenflags) {
  char *s= command, *t, *chkerr; /* qh_skipfilename is non-const */
  char key, opt, prevopt;
  char chkkey[]=  "   ";    /* check one character options ('s') */
  char chkopt[]=  "    ";   /* check two character options ('Ta') */
  char chkopt2[]= "     ";  /* check three character options ('Q12') */
  boolT waserr= False;

  if (*hiddenflags != ' ' || hiddenflags[strlen(hiddenflags)-1] != ' ') {
    qh_fprintf(qh ferr, 6026, "qhull internal error (qh_checkflags): hiddenflags must start and end with a space: \"%s\"\n", hiddenflags);
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  if (strpbrk(hiddenflags, ",\n\r\t")) {
    qh_fprintf(qh ferr, 6027, "qhull internal error (qh_checkflags): hiddenflags contains commas, newlines, or tabs: \"%s\"\n", hiddenflags);
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  while (*s && !isspace(*s))  /* skip program name */
    s++;
  while (*s) {
    while (*s && isspace(*s))
      s++;
    if (*s == '-')
      s++;
    if (!*s)
      break;
    key= *s++;
    chkerr= NULL;
    if (key == 'T' && (*s == 'I' || *s == 'O')) {  /* TI or TO 'file name' */
      s= qh_skipfilename(++s);
      continue;
    }
    chkkey[1]= key;
    if (strstr(hiddenflags, chkkey)) {
      chkerr= chkkey;
    }else if (isupper(key)) {
      opt= ' ';
      prevopt= ' ';
      chkopt[1]= key;
      chkopt2[1]= key;
      while (!chkerr && *s && !isspace(*s)) {
        opt= *s++;
        if (isalpha(opt)) {
          chkopt[2]= opt;
          if (strstr(hiddenflags, chkopt))
            chkerr= chkopt;
          if (prevopt != ' ') {
            chkopt2[2]= prevopt;
            chkopt2[3]= opt;
            if (strstr(hiddenflags, chkopt2))
              chkerr= chkopt2;
          }
        }else if (key == 'Q' && isdigit(opt) && prevopt != 'b'
              && (prevopt == ' ' || islower(prevopt))) {
            if (isdigit(*s)) {  /* Q12 */
              chkopt2[2]= opt;
              chkopt2[3]= *s++;
              if (strstr(hiddenflags, chkopt2))
                chkerr= chkopt2;
            }else {
              chkopt[2]= opt;
              if (strstr(hiddenflags, chkopt))
                chkerr= chkopt;
            }
        }else {
          qh_strtod(s-1, &t);
          if (s < t)
            s= t;
        }
        prevopt= opt;
      }
    }
    if (chkerr) {
      *chkerr= '\'';
      chkerr[strlen(chkerr)-1]=  '\'';
      qh_fprintf(qh ferr, 6029, "qhull option error: option %s is not used with this program.\n             It may be used with qhull.\n", chkerr);
      waserr= True;
    }
  }
  if (waserr)
    qh_errexit(qh_ERRinput, NULL, NULL);
} /* checkflags */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="clear_outputflags">-</a>

  qh_clear_outputflags( )
    Clear output flags for QhullPoints
*/
void qh_clear_outputflags(void) {
  int i,k;

  qh ANNOTATEoutput= False;
  qh DOintersections= False;
  qh DROPdim= -1;
  qh FORCEoutput= False;
  qh GETarea= False;
  qh GOODpoint= 0;
  qh GOODpointp= NULL;
  qh GOODthreshold= False;
  qh GOODvertex= 0;
  qh GOODvertexp= NULL;
  qh IStracing= 0;
  qh KEEParea= False;
  qh KEEPmerge= False;
  qh KEEPminArea= REALmax;
  qh PRINTcentrums= False;
  qh PRINTcoplanar= False;
  qh PRINTdots= False;
  qh PRINTgood= False;
  qh PRINTinner= False;
  qh PRINTneighbors= False;
  qh PRINTnoplanes= False;
  qh PRINToptions1st= False;
  qh PRINTouter= False;
  qh PRINTprecision= True;
  qh PRINTridges= False;
  qh PRINTspheres= False;
  qh PRINTstatistics= False;
  qh PRINTsummary= False;
  qh PRINTtransparent= False;
  qh SPLITthresholds= False;
  qh TRACElevel= 0;
  qh TRInormals= False;
  qh USEstdout= False;
  qh VERIFYoutput= False;
  for (k=qh input_dim+1; k--; ) {  /* duplicated in qh_initqhull_buffers and qh_clear_outputflags */
    qh lower_threshold[k]= -REALmax;
    qh upper_threshold[k]= REALmax;
    qh lower_bound[k]= -REALmax;
    qh upper_bound[k]= REALmax;
  }

  for (i=0; i < qh_PRINTEND; i++) {
    qh PRINTout[i]= qh_PRINTnone;
  }

  if (!qh qhull_commandsiz2)
      qh qhull_commandsiz2= (int)strlen(qh qhull_command); /* WARN64 */
  else {
      qh qhull_command[qh qhull_commandsiz2]= '\0';
  }
  if (!qh qhull_optionsiz2)
    qh qhull_optionsiz2= (int)strlen(qh qhull_options);  /* WARN64 */
  else {
    qh qhull_options[qh qhull_optionsiz2]= '\0';
    qh qhull_optionlen= qh_OPTIONline;  /* start a new line */
  }
} /* clear_outputflags */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="clock">-</a>

  qh_clock()
    return user CPU time in 100ths (qh_SECtick)
    only defined for qh_CLOCKtype == 2

  notes:
    use first value to determine time 0
    from Stevens '92 8.15
*/
unsigned long qh_clock(void) {

#if (qh_CLOCKtype == 2)
  struct tms time;
  static long clktck;  /* initialized first call and never updated */
  double ratio, cpu;
  unsigned long ticks;

  if (!clktck) {
    if ((clktck= sysconf(_SC_CLK_TCK)) < 0) {
      qh_fprintf(qh ferr, 6030, "qhull internal error (qh_clock): sysconf() failed.  Use qh_CLOCKtype 1 in user.h\n");
      qh_errexit(qh_ERRqhull, NULL, NULL);
    }
  }
  if (times(&time) == -1) {
    qh_fprintf(qh ferr, 6031, "qhull internal error (qh_clock): times() failed.  Use qh_CLOCKtype 1 in user.h\n");
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  ratio= qh_SECticks / (double)clktck;
  ticks= time.tms_utime * ratio;
  return ticks;
#else
  qh_fprintf(qh ferr, 6032, "qhull internal error (qh_clock): use qh_CLOCKtype 2 in user.h\n");
  qh_errexit(qh_ERRqhull, NULL, NULL); /* never returns */
  return 0;
#endif
} /* clock */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="freebuffers">-</a>

  qh_freebuffers()
    free up global memory buffers

  notes:
    must match qh_initbuffers()
*/
void qh_freebuffers(void) {

  trace5((qh ferr, 5001, "qh_freebuffers: freeing up global memory buffers\n"));
  /* allocated by qh_initqhull_buffers */
  qh_setfree(&qh other_points);
  qh_setfree(&qh del_vertices);
  qh_setfree(&qh coplanarfacetset);
  qh_memfree(qh NEARzero, qh hull_dim * (int)sizeof(realT));
  qh_memfree(qh lower_threshold, (qh input_dim+1) * (int)sizeof(realT));
  qh_memfree(qh upper_threshold, (qh input_dim+1) * (int)sizeof(realT));
  qh_memfree(qh lower_bound, (qh input_dim+1) * (int)sizeof(realT));
  qh_memfree(qh upper_bound, (qh input_dim+1) * (int)sizeof(realT));
  qh_memfree(qh gm_matrix, (qh hull_dim+1) * qh hull_dim * (int)sizeof(coordT));
  qh_memfree(qh gm_row, (qh hull_dim+1) * (int)sizeof(coordT *));
  qh NEARzero= qh lower_threshold= qh upper_threshold= NULL;
  qh lower_bound= qh upper_bound= NULL;
  qh gm_matrix= NULL;
  qh gm_row= NULL;

  if (qh line)                /* allocated by qh_readinput, freed if no error */
    qh_free(qh line);
  if (qh half_space)
    qh_free(qh half_space);
  if (qh temp_malloc)
    qh_free(qh temp_malloc);
  if (qh feasible_point)      /* allocated by qh_readfeasible */
    qh_free(qh feasible_point);
  if (qh feasible_string)     /* allocated by qh_initflags */
    qh_free(qh feasible_string);
  qh line= qh feasible_string= NULL;
  qh half_space= qh feasible_point= qh temp_malloc= NULL;
  /* usually allocated by qh_readinput */
  if (qh first_point && qh POINTSmalloc) {
    qh_free(qh first_point);
    qh first_point= NULL;
  }
  if (qh input_points && qh input_malloc) { /* set by qh_joggleinput */
    qh_free(qh input_points);
    qh input_points= NULL;
  }
  trace5((qh ferr, 5002, "qh_freebuffers: finished\n"));
} /* freebuffers */


/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="freebuild">-</a>

  qh_freebuild( allmem )
    free global memory used by qh_initbuild and qh_buildhull
    if !allmem,
      does not free short memory (e.g., facetT, freed by qh_memfreeshort)

  design:
    free centrums
    free each vertex
    for each facet
      free ridges
      free outside set, coplanar set, neighbor set, ridge set, vertex set
      free facet
    free hash table
    free interior point
    free merge sets
    free temporary sets
*/
void qh_freebuild(boolT allmem) {
  facetT *facet, *previousfacet= NULL;
  vertexT *vertex, *previousvertex= NULL;
  ridgeT *ridge, **ridgep, *previousridge= NULL;
  mergeT *merge, **mergep;
  int newsize;
  boolT freeall;

  /* free qhT global sets first, includes references from qh_buildhull */
  trace5((qh ferr, 5004, "qh_freebuild: free global sets\n"));
  FOREACHmerge_(qh facet_mergeset)  /* usually empty */
    qh_memfree(merge, (int)sizeof(mergeT));
  FOREACHmerge_(qh degen_mergeset)  /* usually empty */
    qh_memfree(merge, (int)sizeof(mergeT));
  FOREACHmerge_(qh vertex_mergeset)  /* usually empty */
    qh_memfree(merge, (int)sizeof(mergeT));
  qh facet_mergeset= NULL;  /* temp set freed by qh_settempfree_all */
  qh degen_mergeset= NULL;  /* temp set freed by qh_settempfree_all */
  qh vertex_mergeset= NULL;  /* temp set freed by qh_settempfree_all */
  qh_setfree(&(qh hash_table));
  trace5((qh ferr, 5003, "qh_freebuild: free temporary sets (qh_settempfree_all)\n"));
  qh_settempfree_all();
  trace1((qh ferr, 1005, "qh_freebuild: free memory from qh_inithull and qh_buildhull\n"));
  if (qh del_vertices)
    qh_settruncate(qh del_vertices, 0);
  if (allmem) {
    while ((vertex= qh vertex_list)) {
      if (vertex->next)
        qh_delvertex(vertex);
      else {
        qh_memfree(vertex, (int)sizeof(vertexT)); /* sentinel */
        qh newvertex_list= qh vertex_list= NULL;
        break;
      }
      previousvertex= vertex; /* in case of memory fault */
      QHULL_UNUSED(previousvertex)
    }
  }else if (qh VERTEXneighbors) {
    FORALLvertices
      qh_setfreelong(&(vertex->neighbors));
  }
  qh VERTEXneighbors= False;
  qh GOODclosest= NULL;
  if (allmem) {
    FORALLfacets {
      FOREACHridge_(facet->ridges)
        ridge->seen= False;
    }
    while ((facet= qh facet_list)) {
      if (!facet->newfacet || !qh NEWtentative || qh_setsize(facet->ridges) > 1) { /* skip tentative horizon ridges */
        trace4((qh ferr, 4095, "qh_freebuild: delete the previously-seen ridges of f%d\n", facet->id));
        FOREACHridge_(facet->ridges) {
          if (ridge->seen)
            qh_delridge(ridge);
          else
            ridge->seen= True;
          previousridge= ridge; /* in case of memory fault */
          QHULL_UNUSED(previousridge)
        }
      }
      qh_setfree(&(facet->outsideset));
      qh_setfree(&(facet->coplanarset));
      qh_setfree(&(facet->neighbors));
      qh_setfree(&(facet->ridges));
      qh_setfree(&(facet->vertices));
      if (facet->next)
        qh_delfacet(facet);
      else {
        qh_memfree(facet, (int)sizeof(facetT));
        qh visible_list= qh newfacet_list= qh facet_list= NULL;
      }
      previousfacet= facet; /* in case of memory fault */
      QHULL_UNUSED(previousfacet)
    }
  }else {
    freeall= True;
    if (qh_setlarger_quick(qh hull_dim + 1, &newsize))
      freeall= False;
    FORALLfacets {
      qh_setfreelong(&(facet->outsideset));
      qh_setfreelong(&(facet->coplanarset));
      if (!facet->simplicial || freeall) {
        qh_setfreelong(&(facet->neighbors));
        qh_setfreelong(&(facet->ridges));
        qh_setfreelong(&(facet->vertices));
      }
    }
  }
  /* qh internal constants */
  qh_memfree(qh interior_point, qh normal_size);
  qh interior_point= NULL;
} /* freebuild */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="freeqhull">-</a>

  qh_freeqhull( allmem )
    see qh_freeqhull2
    if qh_QHpointer, frees qh_qh
*/
void qh_freeqhull(boolT allmem) {
    qh_freeqhull2(allmem);
#if qh_QHpointer
    qh_free(qh_qh);
    qh_qh= NULL;
#endif
}

/*-<a                             href="qh-globa.htm#TOC"
>-------------------------------</a><a name="freeqhull2">-</a>

qh_freeqhull2( allmem )
  free global memory and set qhT to 0
  if !allmem,
    does not free short memory (freed by qh_memfreeshort unless qh_NOmem)

notes:
  sets qh.NOerrexit in case caller forgets to
  Does not throw errors

see:
  see qh_initqhull_start2()

design:
  free global and temporary memory from qh_initbuild and qh_buildhull
  free buffers
  free statistics
*/
void qh_freeqhull2(boolT allmem) {

  qh NOerrexit= True;  /* no more setjmp since called at exit and ~QhullQh */
  trace1((qh ferr, 1006, "qh_freeqhull: free global memory\n"));
  qh_freebuild(allmem);
  qh_freebuffers();
  qh_freestatistics();
#if qh_QHpointer
  memset((char *)qh_qh, 0, sizeof(qhT));
  /* qh_qh freed by caller, qh_freeqhull() */
#else
  memset((char *)&qh_qh, 0, sizeof(qhT));
#endif
  qh NOerrexit= True;
} /* freeqhull2 */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="init_A">-</a>

  qh_init_A( infile, outfile, errfile, argc, argv )
    initialize memory and stdio files
    convert input options to option string (qh.qhull_command)

  notes:
    infile may be NULL if qh_readpoints() is not called

    errfile should always be defined.  It is used for reporting
    errors.  outfile is used for output and format options.

    argc/argv may be 0/NULL

    called before error handling initialized
    qh_errexit() may not be used
*/
void qh_init_A(FILE *infile, FILE *outfile, FILE *errfile, int argc, char *argv[]) {
  qh_meminit(errfile);
  qh_initqhull_start(infile, outfile, errfile);
  qh_init_qhull_command(argc, argv);
} /* init_A */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="init_B">-</a>

  qh_init_B( points, numpoints, dim, ismalloc )
    initialize globals for points array

    points has numpoints dim-dimensional points
      points[0] is the first coordinate of the first point
      points[1] is the second coordinate of the first point
      points[dim] is the first coordinate of the second point

    ismalloc=True
      Qhull will call qh_free(points) on exit or input transformation
    ismalloc=False
      Qhull will allocate a new point array if needed for input transformation

    qh.qhull_command
      is the option string.
      It is defined by qh_init_B(), qh_qhull_command(), or qh_initflags

  returns:
    if qh.PROJECTinput or (qh.DELAUNAY and qh.PROJECTdelaunay)
      projects the input to a new point array

        if qh.DELAUNAY,
          qh.hull_dim is increased by one
        if qh.ATinfinity,
          qh_projectinput adds point-at-infinity for Delaunay tri.

    if qh.SCALEinput
      changes the upper and lower bounds of the input, see qh_scaleinput

    if qh.ROTATEinput
      rotates the input by a random rotation, see qh_rotateinput
      if qh.DELAUNAY
        rotates about the last coordinate

  notes:
    called after points are defined
    qh_errexit() may be used
*/
void qh_init_B(coordT *points, int numpoints, int dim, boolT ismalloc) {
  qh_initqhull_globals(points, numpoints, dim, ismalloc);
  if (qhmem.LASTsize == 0)
    qh_initqhull_mem();
  /* mem.c and qset.c are initialized */
  qh_initqhull_buffers();
  qh_initthresholds(qh qhull_command);
  if (qh PROJECTinput || (qh DELAUNAY && qh PROJECTdelaunay))
    qh_projectinput();
  if (qh SCALEinput)
    qh_scaleinput();
  if (qh ROTATErandom >= 0) {
    qh_randommatrix(qh gm_matrix, qh hull_dim, qh gm_row);
    if (qh DELAUNAY) {
      int k, lastk= qh hull_dim-1;
      for (k=0; k < lastk; k++) {
        qh gm_row[k][lastk]= 0.0;
        qh gm_row[lastk][k]= 0.0;
      }
      qh gm_row[lastk][lastk]= 1.0;
    }
    qh_gram_schmidt(qh hull_dim, qh gm_row);
    qh_rotateinput(qh gm_row);
  }
} /* init_B */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="init_qhull_command">-</a>

  qh_init_qhull_command( argc, argv )
    build qh.qhull_command from argc/argv
    Calls qh_exit if qhull_command is too short

  returns:
    a space-delimited string of options (just as typed)

  notes:
    makes option string easy to input and output

    argc/argv may be 0/NULL
*/
void qh_init_qhull_command(int argc, char *argv[]) {

  if (!qh_argv_to_command(argc, argv, qh qhull_command, (int)sizeof(qh qhull_command))){
    /* Assumes qh.ferr is defined. */
    qh_fprintf(qh ferr, 6033, "qhull input error: more than %d characters in command line.\n",
          (int)sizeof(qh qhull_command));
    qh_exit(qh_ERRinput);  /* error reported, can not use qh_errexit */
  }
} /* init_qhull_command */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="initflags">-</a>

  qh_initflags( commandStr )
    set flags and initialized constants from commandStr
    calls qh_exit() if qh.NOerrexit

  returns:
    sets qh.qhull_command to command if needed

  notes:
    ignores first word (e.g., 'qhull' in "qhull d")
    use qh_strtol/strtod since strtol/strtod may or may not skip trailing spaces

  see:
    qh_initthresholds() continues processing of 'Pdn' and 'PDn'
    'prompt' in unix.c for documentation

  design:
    for each space-delimited option group
      if top-level option
        check syntax
        append appropriate option to option string
        set appropriate global variable or append printFormat to print options
      else
        for each sub-option
          check syntax
          append appropriate option to option string
          set appropriate global variable or append printFormat to print options
*/
void qh_initflags(char *command) {
  int k, i, lastproject;
  char *s= command, *t, *prev_s, *start, key, *lastwarning= NULL;
  boolT isgeom= False, wasproject;
  realT r;

  if(qh NOerrexit){
    qh_fprintf(qh ferr, 6245, "qhull internal error (qh_initflags): qh.NOerrexit was not cleared before calling qh_initflags().  It should be cleared after setjmp().  Exit qhull.\n");
    qh_exit(qh_ERRqhull);
  }
#ifdef qh_RANDOMdist
  qh RANDOMfactor= qh_RANDOMdist;
  qh_option("Random-qh_RANDOMdist", NULL, &qh RANDOMfactor);
  qh RANDOMdist= True;
#endif
  if (command <= &qh qhull_command[0] || command > &qh qhull_command[0] + sizeof(qh qhull_command)) {
    if (command != &qh qhull_command[0]) {
      *qh qhull_command= '\0';
      strncat(qh qhull_command, command, sizeof(qh qhull_command)-strlen(qh qhull_command)-1);
    }
    while (*s && !isspace(*s))  /* skip program name */
      s++;
  }
  if (qh_QHpointer)
    qh_option("qh_QHpointer", NULL, NULL);
  while (*s) {
    while (*s && isspace(*s))
      s++;
    if (*s == '-')
      s++;
    if (!*s)
      break;
    prev_s= s;
    switch (*s++) {
    case 'd':
      qh_option("delaunay", NULL, NULL);
      qh DELAUNAY= True;
      break;
    case 'f':
      qh_option("facets", NULL, NULL);
      qh_appendprint(qh_PRINTfacets);
      break;
    case 'i':
      qh_option("incidence", NULL, NULL);
      qh_appendprint(qh_PRINTincidences);
      break;
    case 'm':
      qh_option("mathematica", NULL, NULL);
      qh_appendprint(qh_PRINTmathematica);
      break;
    case 'n':
      qh_option("normals", NULL, NULL);
      qh_appendprint(qh_PRINTnormals);
      break;
    case 'o':
      qh_option("offFile", NULL, NULL);
      qh_appendprint(qh_PRINToff);
      break;
    case 'p':
      qh_option("points", NULL, NULL);
      qh_appendprint(qh_PRINTpoints);
      break;
    case 's':
      qh_option("summary", NULL, NULL);
      qh PRINTsummary= True;
      break;
    case 'v':
      qh_option("voronoi", NULL, NULL);
      qh VORONOI= True;
      qh DELAUNAY= True;
      break;
    case 'A':
      if (!isdigit(*s) && *s != '.' && *s != '-') {
        qh_fprintf(qh ferr, 7002, "qhull input warning: no maximum cosine angle given for option 'An'.  A1.0 is coplanar\n");
        lastwarning= s-1;
      }else {
        if (*s == '-') {
          qh premerge_cos= -qh_strtod(s, &s);
          qh_option("Angle-premerge-", NULL, &qh premerge_cos);
          qh PREmerge= True;
        }else {
          qh postmerge_cos= qh_strtod(s, &s);
          qh_option("Angle-postmerge", NULL, &qh postmerge_cos);
          qh POSTmerge= True;
        }
        qh MERGING= True;
      }
      break;
    case 'C':
      if (!isdigit(*s) && *s != '.' && *s != '-') {
        qh_fprintf(qh ferr, 7003, "qhull input warning: no centrum radius given for option 'Cn'\n");
        lastwarning= s-1;
      }else {
        if (*s == '-') {
          qh premerge_centrum= -qh_strtod(s, &s);
          qh_option("Centrum-premerge-", NULL, &qh premerge_centrum);
          qh PREmerge= True;
        }else {
          qh postmerge_centrum= qh_strtod(s, &s);
          qh_option("Centrum-postmerge", NULL, &qh postmerge_centrum);
          qh POSTmerge= True;
        }
        qh MERGING= True;
      }
      break;
    case 'E':
      if (*s == '-') {
        qh_fprintf(qh ferr, 6363, "qhull option error: expecting a positive number for maximum roundoff 'En'.  Got '%s'\n", s-1);
        qh_errexit(qh_ERRinput, NULL, NULL);
      }else if (!isdigit(*s)) {
        qh_fprintf(qh ferr, 7005, "qhull option warning: no maximum roundoff given for option 'En'\n");
        lastwarning= s-1;
      }else {
        qh DISTround= qh_strtod(s, &s);
        qh_option("Distance-roundoff", NULL, &qh DISTround);
        qh SETroundoff= True;
      }
      break;
    case 'H':
      start= s;
      qh HALFspace= True;
      qh_strtod(s, &t);
      while (t > s)  {
        if (*t && !isspace(*t)) {
          if (*t == ',')
            t++;
          else {
            qh_fprintf(qh ferr, 6364, "qhull option error: expecting 'Hn,n,n,...' for feasible point of halfspace intersection. Got '%s'\n", start-1);
            qh_errexit(qh_ERRinput, NULL, NULL);
          }
        }
        s= t;
        qh_strtod(s, &t);
      }
      if (start < t) {
        if (!(qh feasible_string= (char *)calloc((size_t)(t-start+1), (size_t)1))) {
          qh_fprintf(qh ferr, 6034, "qhull error: insufficient memory for 'Hn,n,n'\n");
          qh_errexit(qh_ERRmem, NULL, NULL);
        }
        strncpy(qh feasible_string, start, (size_t)(t-start));
        qh_option("Halfspace-about", NULL, NULL);
        qh_option(qh feasible_string, NULL, NULL);
      }else
        qh_option("Halfspace", NULL, NULL);
      break;
    case 'R':
      if (!isdigit(*s)) {
        qh_fprintf(qh ferr, 7007, "qhull option warning: missing random perturbation for option 'Rn'\n");
        lastwarning= s-1;
      }else {
        qh RANDOMfactor= qh_strtod(s, &s);
        qh_option("Random-perturb", NULL, &qh RANDOMfactor);
        qh RANDOMdist= True;
      }
      break;
    case 'V':
      if (!isdigit(*s) && *s != '-') {
        qh_fprintf(qh ferr, 7008, "qhull option warning: missing visible distance for option 'Vn'\n");
        lastwarning= s-1;
      }else {
        qh MINvisible= qh_strtod(s, &s);
        qh_option("Visible", NULL, &qh MINvisible);
      }
      break;
    case 'U':
      if (!isdigit(*s) && *s != '-') {
        qh_fprintf(qh ferr, 7009, "qhull option warning: missing coplanar distance for option 'Un'\n");
        lastwarning= s-1;
      }else {
        qh MAXcoplanar= qh_strtod(s, &s);
        qh_option("U-coplanar", NULL, &qh MAXcoplanar);
      }
      break;
    case 'W':
      if (*s == '-') {
        qh_fprintf(qh ferr, 6365, "qhull option error: expecting a positive number for outside width 'Wn'.  Got '%s'\n", s-1);
        qh_errexit(qh_ERRinput, NULL, NULL);
      }else if (!isdigit(*s)) {
        qh_fprintf(qh ferr, 7011, "qhull option warning: missing outside width for option 'Wn'\n");
        lastwarning= s-1;
      }else {
        qh MINoutside= qh_strtod(s, &s);
        qh_option("W-outside", NULL, &qh MINoutside);
        qh APPROXhull= True;
      }
      break;
    /************  sub menus ***************/
    case 'F':
      while (*s && !isspace(*s)) {
        switch (*s++) {
        case 'a':
          qh_option("Farea", NULL, NULL);
          qh_appendprint(qh_PRINTarea);
          qh GETarea= True;
          break;
        case 'A':
          qh_option("FArea-total", NULL, NULL);
          qh GETarea= True;
          break;
        case 'c':
          qh_option("Fcoplanars", NULL, NULL);
          qh_appendprint(qh_PRINTcoplanars);
          break;
        case 'C':
          qh_option("FCentrums", NULL, NULL);
          qh_appendprint(qh_PRINTcentrums);
          break;
        case 'd':
          qh_option("Fd-cdd-in", NULL, NULL);
          qh CDDinput= True;
          break;
        case 'D':
          qh_option("FD-cdd-out", NULL, NULL);
          qh CDDoutput= True;
          break;
        case 'F':
          qh_option("FFacets-xridge", NULL, NULL);
          qh_appendprint(qh_PRINTfacets_xridge);
          break;
        case 'i':
          qh_option("Finner", NULL, NULL);
          qh_appendprint(qh_PRINTinner);
          break;
        case 'I':
          qh_option("FIDs", NULL, NULL);
          qh_appendprint(qh_PRINTids);
          break;
        case 'm':
          qh_option("Fmerges", NULL, NULL);
          qh_appendprint(qh_PRINTmerges);
          break;
        case 'M':
          qh_option("FMaple", NULL, NULL);
          qh_appendprint(qh_PRINTmaple);
          break;
        case 'n':
          qh_option("Fneighbors", NULL, NULL);
          qh_appendprint(qh_PRINTneighbors);
          break;
        case 'N':
          qh_option("FNeighbors-vertex", NULL, NULL);
          qh_appendprint(qh_PRINTvneighbors);
          break;
        case 'o':
          qh_option("Fouter", NULL, NULL);
          qh_appendprint(qh_PRINTouter);
          break;
        case 'O':
          if (qh PRINToptions1st) {
            qh_option("FOptions", NULL, NULL);
            qh_appendprint(qh_PRINToptions);
          }else
            qh PRINToptions1st= True;
          break;
        case 'p':
          qh_option("Fpoint-intersect", NULL, NULL);
          qh_appendprint(qh_PRINTpointintersect);
          break;
        case 'P':
          qh_option("FPoint-nearest", NULL, NULL);
          qh_appendprint(qh_PRINTpointnearest);
          break;
        case 'Q':
          qh_option("FQhull", NULL, NULL);
          qh_appendprint(qh_PRINTqhull);
          break;
        case 's':
          qh_option("Fsummary", NULL, NULL);
          qh_appendprint(qh_PRINTsummary);
          break;
        case 'S':
          qh_option("FSize", NULL, NULL);
          qh_appendprint(qh_PRINTsize);
          qh GETarea= True;
          break;
        case 't':
          qh_option("Ftriangles", NULL, NULL);
          qh_appendprint(qh_PRINTtriangles);
          break;
        case 'v':
          /* option set in qh_initqhull_globals */
          qh_appendprint(qh_PRINTvertices);
          break;
        case 'V':
          qh_option("FVertex-average", NULL, NULL);
          qh_appendprint(qh_PRINTaverage);
          break;
        case 'x':
          qh_option("Fxtremes", NULL, NULL);
          qh_appendprint(qh_PRINTextremes);
          break;
        default:
          s--;
          qh_fprintf(qh ferr, 7012, "qhull option warning: unknown 'F' output option 'F%c', skip to next space\n", (int)s[0]);
          lastwarning= s-1;
          while (*++s && !isspace(*s));
          break;
        }
      }
      break;
    case 'G':
      isgeom= True;
      qh_appendprint(qh_PRINTgeom);
      while (*s && !isspace(*s)) {
        switch (*s++) {
        case 'a':
          qh_option("Gall-points", NULL, NULL);
          qh PRINTdots= True;
          break;
        case 'c':
          qh_option("Gcentrums", NULL, NULL);
          qh PRINTcentrums= True;
          break;
        case 'h':
          qh_option("Gintersections", NULL, NULL);
          qh DOintersections= True;
          break;
        case 'i':
          qh_option("Ginner", NULL, NULL);
          qh PRINTinner= True;
          break;
        case 'n':
          qh_option("Gno-planes", NULL, NULL);
          qh PRINTnoplanes= True;
          break;
        case 'o':
          qh_option("Gouter", NULL, NULL);
          qh PRINTouter= True;
          break;
        case 'p':
          qh_option("Gpoints", NULL, NULL);
          qh PRINTcoplanar= True;
          break;
        case 'r':
          qh_option("Gridges", NULL, NULL);
          qh PRINTridges= True;
          break;
        case 't':
          qh_option("Gtransparent", NULL, NULL);
          qh PRINTtransparent= True;
          break;
        case 'v':
          qh_option("Gvertices", NULL, NULL);
          qh PRINTspheres= True;
          break;
        case 'D':
          if (!isdigit(*s)) {
            qh_fprintf(qh ferr, 7004, "qhull option warning: missing dimension for option 'GDn'\n");
            lastwarning= s-2;
          }else {
            if (qh DROPdim >= 0) {
              qh_fprintf(qh ferr, 7013, "qhull option warning: can only drop one dimension.  Previous 'GD%d' ignored\n",
                   qh DROPdim);
              lastwarning= s-2;
            }
            qh DROPdim= qh_strtol(s, &s);
            qh_option("GDrop-dim", &qh DROPdim, NULL);
          }
          break;
        default:
          s--;
          qh_fprintf(qh ferr, 7014, "qhull option warning: unknown 'G' geomview option 'G%c', skip to next space\n", (int)s[0]);
          lastwarning= s-1;
          while (*++s && !isspace(*s));
          break;
        }
      }
      break;
    case 'P':
      while (*s && !isspace(*s)) {
        switch (*s++) {
        case 'd': case 'D':  /* see qh_initthresholds() */
          key= s[-1];
          i= qh_strtol(s, &s);
          r= 0;
          if (*s == ':') {
            s++;
            r= qh_strtod(s, &s);
          }
          if (key == 'd')
            qh_option("Pdrop-facets-dim-less", &i, &r);
          else
            qh_option("PDrop-facets-dim-more", &i, &r);
          break;
        case 'g':
          qh_option("Pgood-facets", NULL, NULL);
          qh PRINTgood= True;
          break;
        case 'G':
          qh_option("PGood-facet-neighbors", NULL, NULL);
          qh PRINTneighbors= True;
          break;
        case 'o':
          qh_option("Poutput-forced", NULL, NULL);
          qh FORCEoutput= True;
          break;
        case 'p':
          qh_option("Pprecision-ignore", NULL, NULL);
          qh PRINTprecision= False;
          break;
        case 'A':
          if (!isdigit(*s)) {
            qh_fprintf(qh ferr, 7006, "qhull option warning: missing facet count for keep area option 'PAn'\n");
            lastwarning= s-2;
          }else {
            qh KEEParea= qh_strtol(s, &s);
            qh_option("PArea-keep", &qh KEEParea, NULL);
            qh GETarea= True;
          }
          break;
        case 'F':
          if (!isdigit(*s)) {
            qh_fprintf(qh ferr, 7010, "qhull option warning: missing facet area for option 'PFn'\n");
            lastwarning= s-2;
          }else {
            qh KEEPminArea= qh_strtod(s, &s);
            qh_option("PFacet-area-keep", NULL, &qh KEEPminArea);
            qh GETarea= True;
          }
          break;
        case 'M':
          if (!isdigit(*s)) {
            qh_fprintf(qh ferr, 7090, "qhull option warning: missing merge count for option 'PMn'\n");
            lastwarning= s-2;
          }else {
            qh KEEPmerge= qh_strtol(s, &s);
            qh_option("PMerge-keep", &qh KEEPmerge, NULL);
          }
          break;
        default:
          s--;
          qh_fprintf(qh ferr, 7015, "qhull option warning: unknown 'P' print option 'P%c', skip to next space\n", (int)s[0]);
          lastwarning= s-1;
          while (*++s && !isspace(*s));
          break;
        }
      }
      break;
    case 'Q':
      lastproject= -1;
      while (*s && !isspace(*s)) {
        switch (*s++) {
        case 'a':
          qh_option("Qallow-short", NULL, NULL);
          qh ALLOWshort= True;
          break;
        case 'b': case 'B':  /* handled by qh_initthresholds */
          key= s[-1];
          if (key == 'b' && *s == 'B') {
            s++;
            r= qh_DEFAULTbox;
            qh SCALEinput= True;
            qh_option("QbBound-unit-box", NULL, &r);
            break;
          }
          if (key == 'b' && *s == 'b') {
            s++;
            qh SCALElast= True;
            qh_option("Qbbound-last", NULL, NULL);
            break;
          }
          k= qh_strtol(s, &s);
          r= 0.0;
          wasproject= False;
          if (*s == ':') {
            s++;
            if ((r= qh_strtod(s, &s)) == 0.0) {
              t= s;            /* need true dimension for memory allocation */
              while (*t && !isspace(*t)) {
                if (toupper(*t++) == 'B'
                 && k == qh_strtol(t, &t)
                 && *t++ == ':'
                 && qh_strtod(t, &t) == 0.0) {
                  qh PROJECTinput++;
                  trace2((qh ferr, 2004, "qh_initflags: project dimension %d\n", k));
                  qh_option("Qb-project-dim", &k, NULL);
                  wasproject= True;
                  lastproject= k;
                  break;
                }
              }
            }
          }
          if (!wasproject) {
            if (lastproject == k && r == 0.0)
              lastproject= -1;  /* doesn't catch all possible sequences */
            else if (key == 'b') {
              qh SCALEinput= True;
              if (r == 0.0)
                r= -qh_DEFAULTbox;
              qh_option("Qbound-dim-low", &k, &r);
            }else {
              qh SCALEinput= True;
              if (r == 0.0)
                r= qh_DEFAULTbox;
              qh_option("QBound-dim-high", &k, &r);
            }
          }
          break;
        case 'c':
          qh_option("Qcoplanar-keep", NULL, NULL);
          qh KEEPcoplanar= True;
          break;
        case 'f':
          qh_option("Qfurthest-outside", NULL, NULL);
          qh BESToutside= True;
          break;
        case 'g':
          qh_option("Qgood-facets-only", NULL, NULL);
          qh ONLYgood= True;
          break;
        case 'i':
          qh_option("Qinterior-keep", NULL, NULL);
          qh KEEPinside= True;
          break;
        case 'm':
          qh_option("Qmax-outside-only", NULL, NULL);
          qh ONLYmax= True;
          break;
        case 'r':
          qh_option("Qrandom-outside", NULL, NULL);
          qh RANDOMoutside= True;
          break;
        case 's':
          qh_option("Qsearch-initial-simplex", NULL, NULL);
          qh ALLpoints= True;
          break;
        case 't':
          qh_option("Qtriangulate", NULL, NULL);
          qh TRIangulate= True;
          break;
        case 'T':
          qh_option("QTestPoints", NULL, NULL);
          if (!isdigit(*s)) {
            qh_fprintf(qh ferr, 7091, "qhull option warning: missing number of test points for option 'QTn'\n");
            lastwarning= s-2;
          }else {
            qh TESTpoints= qh_strtol(s, &s);
            qh_option("QTestPoints", &qh TESTpoints, NULL);
          }
          break;
        case 'u':
          qh_option("QupperDelaunay", NULL, NULL);
          qh UPPERdelaunay= True;
          break;
        case 'v':
          qh_option("Qvertex-neighbors-convex", NULL, NULL);
          qh TESTvneighbors= True;
          break;
        case 'x':
          qh_option("Qxact-merge", NULL, NULL);
          qh MERGEexact= True;
          break;
        case 'z':
          qh_option("Qz-infinity-point", NULL, NULL);
          qh ATinfinity= True;
          break;
        case '0':
          qh_option("Q0-no-premerge", NULL, NULL);
          qh NOpremerge= True;
          break;
        case '1':
          if (!isdigit(*s)) {
            qh_option("Q1-angle-merge", NULL, NULL);
            qh ANGLEmerge= True;
            break;
          }
          switch (*s++) {
          case '0':
            qh_option("Q10-no-narrow", NULL, NULL);
            qh NOnarrow= True;
            break;
          case '1':
            qh_option("Q11-trinormals Qtriangulate", NULL, NULL);
            qh TRInormals= True;
            qh TRIangulate= True;
            break;
          case '2':
            qh_option("Q12-allow-wide", NULL, NULL);
            qh ALLOWwide= True;
            break;
          case '4':
#ifndef qh_NOmerge
            qh_option("Q14-merge-pinched-vertices", NULL, NULL);
            qh MERGEpinched= True;
#else
            /* ignore 'Q14' for q_benchmark testing of difficult cases for Qhull */
            qh_fprintf(qh ferr, 7099, "qhull option warning: option 'Q14-merge-pinched' disabled due to qh_NOmerge\n");
#endif
            break;
          case '7':
            qh_option("Q15-check-duplicates", NULL, NULL);
            qh CHECKduplicates= True;
            break;
          default:
            s--;
            qh_fprintf(qh ferr, 7016, "qhull option warning: unknown 'Q' qhull option 'Q1%c', skip to next space\n", (int)s[0]);
            lastwarning= s-1;
            while (*++s && !isspace(*s));
            break;
          }
          break;
        case '2':
          qh_option("Q2-no-merge-independent", NULL, NULL);
          qh MERGEindependent= False;
          goto LABELcheckdigit;
          break; /* no gcc warnings */
        case '3':
          qh_option("Q3-no-merge-vertices", NULL, NULL);
          qh MERGEvertices= False;
        LABELcheckdigit:
          if (isdigit(*s)) {
            qh_fprintf(qh ferr, 7017, "qhull option warning: can not follow '1', '2', or '3' with a digit.  'Q%c%c' skipped\n", *(s-1), *s);
            lastwarning= s-2;
            s++;
          }
          break;
        case '4':
          qh_option("Q4-avoid-old-into-new", NULL, NULL);
          qh AVOIDold= True;
          break;
        case '5':
          qh_option("Q5-no-check-outer", NULL, NULL);
          qh SKIPcheckmax= True;
          break;
        case '6':
          qh_option("Q6-no-concave-merge", NULL, NULL);
          qh SKIPconvex= True;
          break;
        case '7':
          qh_option("Q7-no-breadth-first", NULL, NULL);
          qh VIRTUALmemory= True;
          break;
        case '8':
          qh_option("Q8-no-near-inside", NULL, NULL);
          qh NOnearinside= True;
          break;
        case '9':
          qh_option("Q9-pick-furthest", NULL, NULL);
          qh PICKfurthest= True;
          break;
        case 'G':
          i= qh_strtol(s, &t);
          if (qh GOODpoint) {
            qh_fprintf(qh ferr, 7018, "qhull option warning: good point already defined for option 'QGn'.  Ignored\n");
            lastwarning= s-2;
          }else if (s == t) {
            qh_fprintf(qh ferr, 7019, "qhull option warning: missing good point id for option 'QGn'.  Ignored\n");
            lastwarning= s-2;
          }else if (i < 0 || *s == '-') {
            qh GOODpoint= i-1;
            qh_option("QGood-if-dont-see-point", &i, NULL);
          }else {
            qh GOODpoint= i+1;
            qh_option("QGood-if-see-point", &i, NULL);
          }
          s= t;
          break;
        case 'J':
          if (!isdigit(*s) && *s != '-')
            qh JOGGLEmax= 0.0;
          else {
            qh JOGGLEmax= (realT) qh_strtod(s, &s);
            qh_option("QJoggle", NULL, &qh JOGGLEmax);
          }
          break;
        case 'R':
          if (!isdigit(*s) && *s != '-') {
            qh_fprintf(qh ferr, 7020, "qhull option warning: missing random seed for option 'QRn'\n");
            lastwarning= s-2;
          }else {
            qh ROTATErandom= i= qh_strtol(s, &s);
            if (i > 0)
              qh_option("QRotate-id", &i, NULL );
            else if (i < -1)
              qh_option("QRandom-seed", &i, NULL );
          }
          break;
        case 'V':
          i= qh_strtol(s, &t);
          if (qh GOODvertex) {
            qh_fprintf(qh ferr, 7021, "qhull option warning: good vertex already defined for option 'QVn'.  Ignored\n");
            lastwarning= s-2;
          }else if (s == t) {
            qh_fprintf(qh ferr, 7022, "qhull option warning: no good point id given for option 'QVn'.  Ignored\n");
            lastwarning= s-2;
          }else if (i < 0) {
            qh GOODvertex= i - 1;
            qh_option("QV-good-facets-not-point", &i, NULL);
          }else {
            qh_option("QV-good-facets-point", &i, NULL);
            qh GOODvertex= i + 1;
          }
          s= t;
          break;
        case 'w':
          qh_option("Qwarn-allow", NULL, NULL);
          qh ALLOWwarning= True;
          break;
        default:
          s--;
          qh_fprintf(qh ferr, 7023, "qhull option warning: unknown 'Q' qhull option 'Q%c', skip to next space\n", (int)s[0]);
          lastwarning= s-1;
          while (*++s && !isspace(*s));
          break;
        }
      }
      break;
    case 'T':
      while (*s && !isspace(*s)) {
        if (isdigit(*s) || *s == '-')
          qh IStracing= qh_strtol(s, &s);
        else switch (*s++) {
        case 'a':
          qh_option("Tannotate-output", NULL, NULL);
          qh ANNOTATEoutput= True;
          break;
        case 'c':
          qh_option("Tcheck-frequently", NULL, NULL);
          qh CHECKfrequently= True;
          break;
        case 'f':
          qh_option("Tflush", NULL, NULL);
          qh FLUSHprint= True;
          break;
        case 's':
          qh_option("Tstatistics", NULL, NULL);
          qh PRINTstatistics= True;
          break;
        case 'v':
          qh_option("Tverify", NULL, NULL);
          qh VERIFYoutput= True;
          break;
        case 'z':
          if (qh ferr == qh_FILEstderr) {
            /* The C++ interface captures the output in qh_fprint_qhull() */
            qh_option("Tz-stdout", NULL, NULL);
            qh USEstdout= True;
          }else if (!qh fout) {
            qh_fprintf(qh ferr, 7024, "qhull option warning: output file undefined(stdout).  Option 'Tz' ignored.\n");
            lastwarning= s-2;
          }else {
            qh_option("Tz-stdout", NULL, NULL);
            qh USEstdout= True;
            qh ferr= qh fout;
            qhmem.ferr= qh fout;
          }
          break;
        case 'C':
          if (!isdigit(*s)) {
            qh_fprintf(qh ferr, 7025, "qhull option warning: missing point id for cone for trace option 'TCn'\n");
            lastwarning= s-2;
          }else {
            i= qh_strtol(s, &s);
            qh_option("TCone-stop", &i, NULL);
            qh STOPcone= i + 1;
          }
          break;
        case 'F':
          if (!isdigit(*s)) {
            qh_fprintf(qh ferr, 7026, "qhull option warning: missing frequency count for trace option 'TFn'\n");
            lastwarning= s-2;
          }else {
            qh REPORTfreq= qh_strtol(s, &s);
            qh_option("TFacet-log", &qh REPORTfreq, NULL);
            qh REPORTfreq2= qh REPORTfreq/2;  /* for tracemerging() */
          }
          break;
        case 'I':
          while (isspace(*s))
            s++;
          t= qh_skipfilename(s);
          {
            char filename[qh_FILENAMElen];

            qh_copyfilename(filename, (int)sizeof(filename), s, (int)(t-s));   /* WARN64 */
            s= t;
            if (!freopen(filename, "r", stdin)) {
              qh_fprintf(qh ferr, 6041, "qhull option error: cannot open 'TI' file \"%s\"\n", filename);
              qh_errexit(qh_ERRinput, NULL, NULL);
            }else {
              qh_option("TInput-file", NULL, NULL);
              qh_option(filename, NULL, NULL);
            }
          }
          break;
        case 'O':
          while (isspace(*s))
            s++;
          t= qh_skipfilename(s);
          {
            char filename[qh_FILENAMElen];

            qh_copyfilename(filename, (int)sizeof(filename), s, (int)(t-s));  /* WARN64 */
            if (!qh fout) {
              qh_fprintf(qh ferr, 7092, "qhull option warning: qh.fout was not set by caller of qh_initflags.  Cannot use option 'TO' to redirect output.  Ignoring option 'TO'\n");
              lastwarning= s-2;
            }else if (!freopen(filename, "w", qh fout)) {
              qh_fprintf(qh ferr, 6044, "qhull option error: cannot open file \"%s\" for writing as option 'TO'.  It is already in use or read-only\n", filename);
              qh_errexit(qh_ERRinput, NULL, NULL);
            }else {
              qh_option("TOutput-file", NULL, NULL);
              qh_option(filename, NULL, NULL);
            }
            s= t;
          }
          break;
        case 'A':
          if (!isdigit(*s)) {
            qh_fprintf(qh ferr, 7093, "qhull option warning: missing count of added points for trace option 'TAn'\n");
            lastwarning= s-2;
          }else {
            i= qh_strtol(s, &t);
            qh STOPadd= i + 1;
            qh_option("TA-stop-add", &i, NULL);
          }
          s= t;
          break;
        case 'P':
          if (*s == '-') {
            if (s[1] == '1' && !isdigit(s[2])) {
              s += 2;
              qh TRACEpoint= qh_IDunknown; /* qh_buildhull done */
              qh_option("Trace-point", &qh TRACEpoint, NULL);
            }else {
              qh_fprintf(qh ferr, 7100, "qhull option warning: negative point id for trace option 'TPn'.  Expecting 'TP-1' for tracing after qh_buildhull and qh_postmerge\n");
              lastwarning= s-2;
              while (isdigit(*(++s)))
                ; /* skip digits */
            }
          }else if (!isdigit(*s)) {
            qh_fprintf(qh ferr, 7029, "qhull option warning: missing point id or -1 for trace option 'TPn'\n");
            lastwarning= s-2;
          }else {
            qh TRACEpoint= qh_strtol(s, &s);
            qh_option("Trace-point", &qh TRACEpoint, NULL);
          }
          break;
        case 'M':
          if (!isdigit(*s)) {
            qh_fprintf(qh ferr, 7030, "qhull option warning: missing merge id for trace option 'TMn'\n");
            lastwarning= s-2;
          }else {
            qh TRACEmerge= qh_strtol(s, &s);
            qh_option("Trace-merge", &qh TRACEmerge, NULL);
          }
          break;
        case 'R':
          if (!isdigit(*s)) {
            qh_fprintf(qh ferr, 7031, "qhull option warning: missing rerun count for trace option 'TRn'\n");
            lastwarning= s-2;
          }else {
            qh RERUN= qh_strtol(s, &s);
            qh_option("TRerun", &qh RERUN, NULL);
          }
          break;
        case 'V':
          i= qh_strtol(s, &t);
          if (s == t) {
            qh_fprintf(qh ferr, 7032, "qhull option warning: missing furthest point id for trace option 'TVn'\n");
            lastwarning= s-2;
          }else if (i < 0) {
            qh STOPpoint= i - 1;
            qh_option("TV-stop-before-point", &i, NULL);
          }else {
            qh STOPpoint= i + 1;
            qh_option("TV-stop-after-point", &i, NULL);
          }
          s= t;
          break;
        case 'W':
          if (!isdigit(*s)) {
            qh_fprintf(qh ferr, 7033, "qhull option warning: missing max width for trace option 'TWn'\n");
            lastwarning= s-2;
          }else {
            qh TRACEdist= (realT) qh_strtod(s, &s);
            qh_option("TWide-trace", NULL, &qh TRACEdist);
          }
          break;
        default:
          s--;
          qh_fprintf(qh ferr, 7034, "qhull option warning: unknown 'T' trace option 'T%c', skip to next space\n", (int)s[0]);
          lastwarning= s-2;
          while (*++s && !isspace(*s));
          break;
        }
      }
      break;
    default:
      qh_fprintf(qh ferr, 7094, "qhull option warning: unknown option '%c'(%x)\n",
        (int)s[-1], (int)s[-1]);
      lastwarning= s-2;
      break;
    }
    if (s-1 == prev_s && *s && !isspace(*s)) {
      qh_fprintf(qh ferr, 7036, "qhull option warning: missing space after option '%c'(%x), reserved for sub-options, ignoring '%c' options to next space\n",
               (int)*prev_s, (int)*prev_s, (int)*prev_s);
      lastwarning= s-1;
      while (*s && !isspace(*s))
        s++;
    }
  }
  if (qh STOPcone && qh JOGGLEmax < REALmax/2) {
    qh_fprintf(qh ferr, 7078, "qhull option warning: 'TCn' (stopCone) ignored when used with 'QJn' (joggle)\n");
    lastwarning= command;
  }
  if (isgeom && !qh FORCEoutput && qh PRINTout[1]) {
    qh_fprintf(qh ferr, 7037, "qhull option warning: additional output formats ('Fc',etc.) are not compatible with Geomview ('G').  Use option 'Po' to override\n");
    lastwarning= command;
  }
  if (lastwarning && !qh ALLOWwarning) {
    qh_fprintf(qh ferr, 6035, "qhull option error: see previous warnings, use 'Qw' to override: '%s' (last offset %d)\n",
          command, (int)(lastwarning-command));
    qh_errexit(qh_ERRinput, NULL, NULL);
  }
  trace4((qh ferr, 4093, "qh_initflags: option flags initialized\n"));
  /* set derived values in qh_initqhull_globals */
} /* initflags */


/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="initqhull_buffers">-</a>

  qh_initqhull_buffers( )
    initialize global memory buffers

  notes:
    must match qh_freebuffers()
*/
void qh_initqhull_buffers(void) {
  int k;

  qh TEMPsize= (qhmem.LASTsize - (int)sizeof(setT))/SETelemsize;
  if (qh TEMPsize <= 0 || qh TEMPsize > qhmem.LASTsize)
    qh TEMPsize= 8;  /* e.g., if qh_NOmem */
  qh other_points= qh_setnew(qh TEMPsize);
  qh del_vertices= qh_setnew(qh TEMPsize);
  qh coplanarfacetset= qh_setnew(qh TEMPsize);
  qh NEARzero= (realT *)qh_memalloc(qh hull_dim * (int)sizeof(realT));
  qh lower_threshold= (realT *)qh_memalloc((qh input_dim+1) * (int)sizeof(realT));
  qh upper_threshold= (realT *)qh_memalloc((qh input_dim+1) * (int)sizeof(realT));
  qh lower_bound= (realT *)qh_memalloc((qh input_dim+1) * (int)sizeof(realT));
  qh upper_bound= (realT *)qh_memalloc((qh input_dim+1) * (int)sizeof(realT));
  for (k=qh input_dim+1; k--; ) {  /* duplicated in qh_initqhull_buffers and qh_clear_outputflags */
    qh lower_threshold[k]= -REALmax;
    qh upper_threshold[k]= REALmax;
    qh lower_bound[k]= -REALmax;
    qh upper_bound[k]= REALmax;
  }
  qh gm_matrix= (coordT *)qh_memalloc((qh hull_dim+1) * qh hull_dim * (int)sizeof(coordT));
  qh gm_row= (coordT **)qh_memalloc((qh hull_dim+1) * (int)sizeof(coordT *));
} /* initqhull_buffers */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="initqhull_globals">-</a>

  qh_initqhull_globals( points, numpoints, dim, ismalloc )
    initialize globals
    if ismalloc
      points were malloc'd and qhull should free at end

  returns:
    sets qh.first_point, num_points, input_dim, hull_dim and others
    seeds random number generator (seed=1 if tracing)
    modifies qh.hull_dim if ((qh.DELAUNAY and qh.PROJECTdelaunay) or qh.PROJECTinput)
    adjust user flags as needed
    also checks DIM3 dependencies and constants

  notes:
    do not use qh_point() since an input transformation may move them elsewhere
    qh_initqhull_start() sets default values for non-zero globals
    consider duplicate error checks in qh_readpoints.  It is called before qh_initqhull_globals

  design:
    initialize points array from input arguments
    test for qh.ZEROcentrum
      (i.e., use opposite vertex instead of cetrum for convexity testing)
    initialize qh.CENTERtype, qh.normal_size,
      qh.center_size, qh.TRACEpoint/level,
    initialize and test random numbers
    qh_initqhull_outputflags() -- adjust and test output flags
*/
void qh_initqhull_globals(coordT *points, int numpoints, int dim, boolT ismalloc) {
  int seed, pointsneeded, extra= 0, i, randi, k;
  realT randr;
  realT factorial;

  time_t timedata;

  trace0((qh ferr, 13, "qh_initqhull_globals: for %s | %s\n", qh rbox_command,
      qh qhull_command));
  if (numpoints < 1 || numpoints > qh_POINTSmax) {
    qh_fprintf(qh ferr, 6412, "qhull input error (qh_initqhull_globals): expecting between 1 and %d points.  Got %d %d-d points\n",
      qh_POINTSmax, numpoints, dim);
    qh_errexit(qh_ERRinput, NULL, NULL);
    /* same error message in qh_readpoints */
  }
  qh POINTSmalloc= ismalloc;
  qh first_point= points;
  qh num_points= numpoints;
  qh hull_dim= qh input_dim= dim;
  if (!qh NOpremerge && !qh MERGEexact && !qh PREmerge && qh JOGGLEmax > REALmax/2) {
    qh MERGING= True;
    if (qh hull_dim <= 4) {
      qh PREmerge= True;
      qh_option("_pre-merge", NULL, NULL);
    }else {
      qh MERGEexact= True;
      qh_option("Qxact-merge", NULL, NULL);
    }
  }else if (qh MERGEexact)
    qh MERGING= True;
  if (qh NOpremerge && (qh MERGEexact || qh PREmerge))
    qh_fprintf(qh ferr, 7095, "qhull option warning: 'Q0-no-premerge' ignored due to exact merge ('Qx') or pre-merge ('C-n' or 'A-n')\n");
  if (!qh NOpremerge && qh JOGGLEmax > REALmax/2) {
#ifdef qh_NOmerge
    qh JOGGLEmax= 0.0;
#endif
  }
  if (qh TRIangulate && qh JOGGLEmax < REALmax/2 && !qh PREmerge && !qh POSTmerge && qh PRINTprecision)
    qh_fprintf(qh ferr, 7038, "qhull option warning: joggle ('QJ') produces simplicial output (i.e., triangles in 2-D).  Unless merging is requested, option 'Qt' has no effect\n");
  if (qh JOGGLEmax < REALmax/2 && qh DELAUNAY && !qh SCALEinput && !qh SCALElast) {
    qh SCALElast= True;
    qh_option("Qbbound-last-qj", NULL, NULL);
  }
  if (qh MERGING && !qh POSTmerge && qh premerge_cos > REALmax/2
  && qh premerge_centrum == 0.0) {
    qh ZEROcentrum= True;
    qh ZEROall_ok= True;
    qh_option("_zero-centrum", NULL, NULL);
  }
  if (qh JOGGLEmax < REALmax/2 && REALepsilon > 2e-8 && qh PRINTprecision)
    qh_fprintf(qh ferr, 7039, "qhull warning: real epsilon, %2.2g, is probably too large for joggle('QJn')\nRecompile with double precision reals(see user.h).\n",
          REALepsilon);
#ifdef qh_NOmerge
  if (qh MERGING) {
    qh_fprintf(qh ferr, 6045, "qhull option error: merging not installed (qh_NOmerge) for 'Qx', 'Cn' or 'An')\n");
    qh_errexit(qh_ERRinput, NULL, NULL);
  }
#endif
  if (qh DELAUNAY && qh KEEPcoplanar && !qh KEEPinside) {
    qh KEEPinside= True;
    qh_option("Qinterior-keep", NULL, NULL);
  }
  if (qh VORONOI && !qh DELAUNAY) {
    qh_fprintf(qh ferr, 6038, "qhull internal error (qh_initqhull_globals): if qh.VORONOI is set, qh.DELAUNAY must be set.  Qhull constructs the Delaunay triangulation in order to compute the Voronoi diagram\n");
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  if (qh DELAUNAY && qh HALFspace) {
    qh_fprintf(qh ferr, 6046, "qhull option error: can not use Delaunay('d') or Voronoi('v') with halfspace intersection('H')\n");
    qh_errexit(qh_ERRinput, NULL, NULL);
    /* same error message in qh_readpoints */
  }
  if (!qh DELAUNAY && (qh UPPERdelaunay || qh ATinfinity)) {
    qh_fprintf(qh ferr, 6047, "qhull option error: use upper-Delaunay('Qu') or infinity-point('Qz') with Delaunay('d') or Voronoi('v')\n");
    qh_errexit(qh_ERRinput, NULL, NULL);
  }
  if (qh UPPERdelaunay && qh ATinfinity) {
    qh_fprintf(qh ferr, 6048, "qhull option error: can not use infinity-point('Qz') with upper-Delaunay('Qu')\n");
    qh_errexit(qh_ERRinput, NULL, NULL);
  }
  if (qh MERGEpinched && qh ONLYgood) {
    qh_fprintf(qh ferr, 6362, "qhull option error: can not use merge-pinched-vertices ('Q14') with good-facets-only ('Qg')\n");
    qh_errexit(qh_ERRinput, NULL, NULL);
  }
  if (qh MERGEpinched && qh hull_dim == 2) {
    trace2((qh ferr, 2108, "qh_initqhull_globals: disable qh.MERGEpinched for 2-d.  It has no effect"))
    qh MERGEpinched= False;
  }
  if (qh SCALElast && !qh DELAUNAY && qh PRINTprecision)
    qh_fprintf(qh ferr, 7040, "qhull option warning: option 'Qbb' (scale-last-coordinate) is normally used with 'd' or 'v'\n");
  qh DOcheckmax= (!qh SKIPcheckmax && (qh MERGING || qh APPROXhull));
  qh KEEPnearinside= (qh DOcheckmax && !(qh KEEPinside && qh KEEPcoplanar)
                          && !qh NOnearinside);
  if (qh MERGING)
    qh CENTERtype= qh_AScentrum;
  else if (qh VORONOI)
    qh CENTERtype= qh_ASvoronoi;
  if (qh TESTvneighbors && !qh MERGING) {
    qh_fprintf(qh ferr, 6049, "qhull option error: test vertex neighbors('Qv') needs a merge option\n");
    qh_errexit(qh_ERRinput, NULL ,NULL);
  }
  if (qh PROJECTinput || (qh DELAUNAY && qh PROJECTdelaunay)) {
    qh hull_dim -= qh PROJECTinput;
    if (qh DELAUNAY) {
      qh hull_dim++;
      if (qh ATinfinity)
        extra= 1;
    }
  }
  if (qh hull_dim <= 1) {
    qh_fprintf(qh ferr, 6050, "qhull error: dimension %d must be > 1\n", qh hull_dim);
    qh_errexit(qh_ERRinput, NULL, NULL);
  }
  for (k=2, factorial=1.0; k < qh hull_dim; k++)
    factorial *= k;
  qh AREAfactor= 1.0 / factorial;
  trace2((qh ferr, 2005, "qh_initqhull_globals: initialize globals.  input_dim %d, numpoints %d, malloc? %d, projected %d to hull_dim %d\n",
        qh input_dim, numpoints, ismalloc, qh PROJECTinput, qh hull_dim));
  qh normal_size= qh hull_dim * (int)sizeof(coordT);
  qh center_size= qh normal_size - (int)sizeof(coordT);
  pointsneeded= qh hull_dim+1;
  if (qh hull_dim > qh_DIMmergeVertex) {
    qh MERGEvertices= False;
    qh_option("Q3-no-merge-vertices-dim-high", NULL, NULL);
  }
  if (qh GOODpoint)
    pointsneeded++;
#ifdef qh_NOtrace
  if (qh IStracing || qh TRACEmerge || qh TRACEpoint != qh_IDnone || qh TRACEdist < REALmax/2) {
      qh_fprintf(qh ferr, 6051, "qhull option error: tracing is not installed (qh_NOtrace in user.h).  Trace options 'Tn', 'TMn', 'TPn' and 'TWn' mostly removed.  Continue with 'Qw' (allow warning)\n");
      if (!qh ALLOWwarning)
        qh_errexit(qh_ERRinput, NULL, NULL);
  }
#endif
  if (qh RERUN > 1) {
    qh TRACElastrun= qh IStracing; /* qh_build_withrestart duplicates next conditional */
    if (qh IStracing && qh IStracing != -1) {
      qh_fprintf(qh ferr, 8162, "qh_initqhull_globals: trace last of TR%d runs at level %d\n", qh RERUN, qh IStracing);
      qh IStracing= 0;
    }
  }else if (qh TRACEpoint != qh_IDnone || qh TRACEdist < REALmax/2 || qh TRACEmerge) {
    qh TRACElevel= (qh IStracing ? qh IStracing : 3);
    qh IStracing= 0;
  }
  if (qh ROTATErandom == 0 || qh ROTATErandom == -1) {
    seed= (int)time(&timedata);
    if (qh ROTATErandom  == -1) {
      seed= -seed;
      qh_option("QRandom-seed", &seed, NULL );
    }else
      qh_option("QRotate-random", &seed, NULL);
    qh ROTATErandom= seed;
  }
  seed= qh ROTATErandom;
  if (seed == INT_MIN)    /* default value */
    seed= 1;
  else if (seed < 0)
    seed= -seed;
  qh_RANDOMseed_(seed);
  randr= 0.0;
  for (i=1000; i--; ) {
    randi= qh_RANDOMint;
    randr += randi;
    if (randi > qh_RANDOMmax) {
      qh_fprintf(qh ferr, 8036, "\
qhull configuration error (qh_RANDOMmax in user.h): random integer %d > qh_RANDOMmax (%.8g)\n",
               randi, qh_RANDOMmax);
      qh_errexit(qh_ERRinput, NULL, NULL);
    }
  }
  qh_RANDOMseed_(seed);
  randr= randr/1000;
  if (randr < qh_RANDOMmax * 0.1
  || randr > qh_RANDOMmax * 0.9)
    qh_fprintf(qh ferr, 8037, "\
qhull configuration warning (qh_RANDOMmax in user.h): average of 1000 random integers (%.2g) is much different than expected (%.2g).  Is qh_RANDOMmax (%.2g) wrong?\n",
             randr, qh_RANDOMmax * 0.5, qh_RANDOMmax);
  qh RANDOMa= 2.0 * qh RANDOMfactor/qh_RANDOMmax;
  qh RANDOMb= 1.0 - qh RANDOMfactor;
  if (qh_HASHfactor < 1.1) {
    qh_fprintf(qh ferr, 6052, "qhull internal error (qh_initqhull_globals): qh_HASHfactor %d must be at least 1.1.  Qhull uses linear hash probing\n",
      qh_HASHfactor);
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  if (numpoints+extra < pointsneeded) {
    qh_fprintf(qh ferr, 6214, "qhull input error: not enough points(%d) to construct initial simplex (need %d)\n",
            numpoints, pointsneeded);
    qh_errexit(qh_ERRinput, NULL, NULL);
  }
  qh_initqhull_outputflags();
} /* initqhull_globals */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="initqhull_mem">-</a>

  qh_initqhull_mem( )
    initialize mem.c for qhull
    qh.hull_dim and qh.normal_size determine some of the allocation sizes
    if qh.MERGING,
      includes ridgeT
    calls qh_user_memsizes (user.c) to add up to 10 additional sizes for quick allocation
      (see numsizes below)

  returns:
    mem.c already for qh_memalloc/qh_memfree (errors if called beforehand)

  notes:
    qh_produceoutput() prints memsizes

*/
void qh_initqhull_mem(void) {
  int numsizes;
  int i;

  numsizes= 8+10;
  qh_meminitbuffers(qh IStracing, qh_MEMalign, numsizes,
                     qh_MEMbufsize, qh_MEMinitbuf);
  qh_memsize((int)sizeof(vertexT));
  if (qh MERGING) {
    qh_memsize((int)sizeof(ridgeT));
    qh_memsize((int)sizeof(mergeT));
  }
  qh_memsize((int)sizeof(facetT));
  i= (int)sizeof(setT) + (qh hull_dim - 1) * SETelemsize;  /* ridge.vertices */
  qh_memsize(i);
  qh_memsize(qh normal_size);        /* normal */
  i += SETelemsize;                 /* facet.vertices, .ridges, .neighbors */
  qh_memsize(i);
  qh_user_memsizes();
  qh_memsetup();
} /* initqhull_mem */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="initqhull_outputflags">-</a>

  qh_initqhull_outputflags
    initialize flags concerned with output

  returns:
    adjust user flags as needed

  see:
    qh_clear_outputflags() resets the flags

  design:
    test for qh.PRINTgood (i.e., only print 'good' facets)
    check for conflicting print output options
*/
void qh_initqhull_outputflags(void) {
  boolT printgeom= False, printmath= False, printcoplanar= False;
  int i;

  trace3((qh ferr, 3024, "qh_initqhull_outputflags: %s\n", qh qhull_command));
  if (!(qh PRINTgood || qh PRINTneighbors)) {
    if (qh DELAUNAY || qh KEEParea || qh KEEPminArea < REALmax/2 || qh KEEPmerge
        || (!qh ONLYgood && (qh GOODvertex || qh GOODpoint))) {
      qh PRINTgood= True;
      qh_option("Pgood", NULL, NULL);
    }
  }
  if (qh PRINTtransparent) {
    if (qh hull_dim != 4 || !qh DELAUNAY || qh VORONOI || qh DROPdim >= 0) {
      qh_fprintf(qh ferr, 6215, "qhull option error: transparent Delaunay('Gt') needs 3-d Delaunay('d') w/o 'GDn'\n");
      qh_errexit(qh_ERRinput, NULL, NULL);
    }
    qh DROPdim= 3;
    qh PRINTridges= True;
  }
  for (i=qh_PRINTEND; i--; ) {
    if (qh PRINTout[i] == qh_PRINTgeom)
      printgeom= True;
    else if (qh PRINTout[i] == qh_PRINTmathematica || qh PRINTout[i] == qh_PRINTmaple)
      printmath= True;
    else if (qh PRINTout[i] == qh_PRINTcoplanars)
      printcoplanar= True;
    else if (qh PRINTout[i] == qh_PRINTpointnearest)
      printcoplanar= True;
    else if (qh PRINTout[i] == qh_PRINTpointintersect && !qh HALFspace) {
      qh_fprintf(qh ferr, 6053, "qhull option error: option 'Fp' is only used for \nhalfspace intersection('Hn,n,n').\n");
      qh_errexit(qh_ERRinput, NULL, NULL);
    }else if (qh PRINTout[i] == qh_PRINTtriangles && (qh HALFspace || qh VORONOI)) {
      qh_fprintf(qh ferr, 6054, "qhull option error: option 'Ft' is not available for Voronoi vertices ('v') or halfspace intersection ('H')\n");
      qh_errexit(qh_ERRinput, NULL, NULL);
    }else if (qh PRINTout[i] == qh_PRINTcentrums && qh VORONOI) {
      qh_fprintf(qh ferr, 6055, "qhull option error: option 'FC' is not available for Voronoi vertices('v')\n");
      qh_errexit(qh_ERRinput, NULL, NULL);
    }else if (qh PRINTout[i] == qh_PRINTvertices) {
      if (qh VORONOI)
        qh_option("Fvoronoi", NULL, NULL);
      else
        qh_option("Fvertices", NULL, NULL);
    }
  }
  if (printcoplanar && qh DELAUNAY && qh JOGGLEmax < REALmax/2) {
    if (qh PRINTprecision)
      qh_fprintf(qh ferr, 7041, "qhull option warning: 'QJ' (joggle) will usually prevent coincident input sites for options 'Fc' and 'FP'\n");
  }
  if (printmath && (qh hull_dim > 3 || qh VORONOI)) {
    qh_fprintf(qh ferr, 6056, "qhull option error: Mathematica and Maple output is only available for 2-d and 3-d convex hulls and 2-d Delaunay triangulations\n");
    qh_errexit(qh_ERRinput, NULL, NULL);
  }
  if (printgeom) {
    if (qh hull_dim > 4) {
      qh_fprintf(qh ferr, 6057, "qhull option error: Geomview output is only available for 2-d, 3-d and 4-d\n");
      qh_errexit(qh_ERRinput, NULL, NULL);
    }
    if (qh PRINTnoplanes && !(qh PRINTcoplanar + qh PRINTcentrums
     + qh PRINTdots + qh PRINTspheres + qh DOintersections + qh PRINTridges)) {
      qh_fprintf(qh ferr, 6058, "qhull option error: no output specified for Geomview\n");
      qh_errexit(qh_ERRinput, NULL, NULL);
    }
    if (qh VORONOI && (qh hull_dim > 3 || qh DROPdim >= 0)) {
      qh_fprintf(qh ferr, 6059, "qhull option error: Geomview output for Voronoi diagrams only for 2-d\n");
      qh_errexit(qh_ERRinput, NULL, NULL);
    }
    /* can not warn about furthest-site Geomview output: no lower_threshold */
    if (qh hull_dim == 4 && qh DROPdim == -1 &&
        (qh PRINTcoplanar || qh PRINTspheres || qh PRINTcentrums)) {
      qh_fprintf(qh ferr, 7042, "qhull option warning: coplanars, vertices, and centrums output not available for 4-d output(ignored).  Could use 'GDn' instead.\n");
      qh PRINTcoplanar= qh PRINTspheres= qh PRINTcentrums= False;
    }
  }
  if (!qh KEEPcoplanar && !qh KEEPinside && !qh ONLYgood) {
    if ((qh PRINTcoplanar && qh PRINTspheres) || printcoplanar) {
      if (qh QHULLfinished) {
        qh_fprintf(qh ferr, 7072, "qhull output warning: ignoring coplanar points, option 'Qc' was not set for the first run of qhull.\n");
      }else {
        qh KEEPcoplanar= True;
        qh_option("Qcoplanar", NULL, NULL);
      }
    }
  }
  qh PRINTdim= qh hull_dim;
  if (qh DROPdim >=0) {    /* after Geomview checks */
    if (qh DROPdim < qh hull_dim) {
      qh PRINTdim--;
      if (!printgeom || qh hull_dim < 3)
        qh_fprintf(qh ferr, 7043, "qhull option warning: drop dimension 'GD%d' is only available for 3-d/4-d Geomview\n", qh DROPdim);
    }else
      qh DROPdim= -1;
  }else if (qh VORONOI) {
    qh DROPdim= qh hull_dim-1;
    qh PRINTdim= qh hull_dim-1;
  }
} /* qh_initqhull_outputflags */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="initqhull_start">-</a>

  qh_initqhull_start( infile, outfile, errfile )
    allocate memory if needed and call qh_initqhull_start2()
*/
void qh_initqhull_start(FILE *infile, FILE *outfile, FILE *errfile) {

#if qh_QHpointer
  if (qh_qh) {
    qh_fprintf(errfile, 6205, "qhull error (qh_initqhull_start): qh_qh already defined.  Call qh_save_qhull() first\n");
    qh_exit(qh_ERRqhull);  /* no error handler */
  }
  if (!(qh_qh= (qhT *)qh_malloc(sizeof(qhT)))) {
    qh_fprintf(errfile, 6060, "qhull error (qh_initqhull_start): insufficient memory\n");
    qh_exit(qh_ERRmem);  /* no error handler */
  }
#endif
  qh_initstatistics();
  qh_initqhull_start2(infile, outfile, errfile);
} /* initqhull_start */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="initqhull_start2">-</a>

  qh_initqhull_start2( infile, outfile, errfile )
    start initialization of qhull
    initialize statistics, stdio, default values for global variables
    assumes qh_qh is defined
  notes:
    report errors elsewhere, error handling and g_qhull_output [Qhull.cpp, QhullQh()] not in initialized
  see:
    qh_maxmin() determines the precision constants
    qh_freeqhull2()
*/
void qh_initqhull_start2(FILE *infile, FILE *outfile, FILE *errfile) {
  time_t timedata;
  int seed;

  qh_CPUclock; /* start the clock(for qh_clock).  One-shot. */
#if qh_QHpointer
  memset((char *)qh_qh, 0, sizeof(qhT));   /* every field is 0, FALSE, NULL */
#else
  memset((char *)&qh_qh, 0, sizeof(qhT));
#endif
  qh NOerrexit= True;
  qh DROPdim= -1;
  qh ferr= errfile;
  qh fin= infile;
  qh fout= outfile;
  qh furthest_id= qh_IDunknown;
#ifndef qh_NOmerge
  qh JOGGLEmax= REALmax;
#else
  qh JOGGLEmax= 0.0;  /* Joggle ('QJ') if qh_NOmerge */
#endif
  qh KEEPminArea= REALmax;
  qh last_low= REALmax;
  qh last_high= REALmax;
  qh last_newhigh= REALmax;
  qh lastcpu= 0.0;
  qh max_outside= 0.0;
  qh max_vertex= 0.0;
  qh MAXabs_coord= 0.0;
  qh MAXsumcoord= 0.0;
  qh MAXwidth= -REALmax;
  qh MERGEindependent= True;
  qh MINdenom_1= fmax_(1.0/REALmax, REALmin); /* used by qh_scalepoints */
  qh MINoutside= 0.0;
  qh MINvisible= REALmax;
  qh MAXcoplanar= REALmax;
  qh outside_err= REALmax;
  qh premerge_centrum= 0.0;
  qh premerge_cos= REALmax;
  qh PRINTprecision= True;
  qh PRINTradius= 0.0;
  qh postmerge_cos= REALmax;
  qh postmerge_centrum= 0.0;
  qh ROTATErandom= INT_MIN;
  qh MERGEvertices= True;
  qh totarea= 0.0;
  qh totvol= 0.0;
  qh TRACEdist= REALmax;
  qh TRACEpoint= qh_IDnone;    /* recompile to trace a point, or use 'TPn' */
  qh tracefacet_id= UINT_MAX;  /* recompile to trace a facet, set to UINT_MAX when done, see userprintf.c/qh_fprintf */
  qh traceridge_id= UINT_MAX;  /* recompile to trace a ridge, set to UINT_MAX when done, see userprintf.c/qh_fprintf */
  qh tracevertex_id= UINT_MAX; /* recompile to trace a vertex, set to UINT_MAX when done, see userprintf.c/qh_fprintf */
  seed= (int)time(&timedata);
  qh_RANDOMseed_(seed);
  qh run_id= qh_RANDOMint;
  if(!qh run_id)
      qh run_id++;  /* guarantee non-zero */
  qh_option("run-id", &qh run_id, NULL);
  strcat(qh qhull, "qhull");
} /* initqhull_start2 */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="initthresholds">-</a>

  qh_initthresholds( commandString )
    set thresholds for printing and scaling from commandString

  returns:
    sets qh.GOODthreshold or qh.SPLITthreshold if 'Pd0D1' used

  see:
    qh_initflags(), 'Qbk' 'QBk' 'Pdk' and 'PDk'
    qh_inthresholds()

  design:
    for each 'Pdn' or 'PDn' option
      check syntax
      set qh.lower_threshold or qh.upper_threshold
    set qh.GOODthreshold if an unbounded threshold is used
    set qh.SPLITthreshold if a bounded threshold is used
*/
void qh_initthresholds(char *command) {
  realT value;
  int idx, maxdim, k;
  char *s= command; /* non-const due to strtol */
  char *lastoption, *lastwarning= NULL;
  char key;

  maxdim= qh input_dim;
  if (qh DELAUNAY && (qh PROJECTdelaunay || qh PROJECTinput))
    maxdim++;
  while (*s) {
    if (*s == '-')
      s++;
    if (*s == 'P') {
      lastoption= s++;
      while (*s && !isspace(key= *s++)) {
        if (key == 'd' || key == 'D') {
          if (!isdigit(*s)) {
            qh_fprintf(qh ferr, 7044, "qhull option warning: no dimension given for Print option 'P%c' at: %s.  Ignored\n",
                    key, s-1);
            lastwarning= lastoption;
            continue;
          }
          idx= qh_strtol(s, &s);
          if (idx >= qh hull_dim) {
            qh_fprintf(qh ferr, 7045, "qhull option warning: dimension %d for Print option 'P%c' is >= %d.  Ignored\n",
                idx, key, qh hull_dim);
            lastwarning= lastoption;
            continue;
          }
          if (*s == ':') {
            s++;
            value= qh_strtod(s, &s);
            if (fabs((double)value) > 1.0) {
              qh_fprintf(qh ferr, 7046, "qhull option warning: value %2.4g for Print option 'P%c' is > +1 or < -1.  Ignored\n",
                      value, key);
              lastwarning= lastoption;
              continue;
            }
          }else
            value= 0.0;
          if (key == 'd')
            qh lower_threshold[idx]= value;
          else
            qh upper_threshold[idx]= value;
        }
      }
    }else if (*s == 'Q') {
      lastoption= s++;
      while (*s && !isspace(key= *s++)) {
        if (key == 'b' && *s == 'B') {
          s++;
          for (k=maxdim; k--; ) {
            qh lower_bound[k]= -qh_DEFAULTbox;
            qh upper_bound[k]= qh_DEFAULTbox;
          }
        }else if (key == 'b' && *s == 'b')
          s++;
        else if (key == 'b' || key == 'B') {
          if (!isdigit(*s)) {
            qh_fprintf(qh ferr, 7047, "qhull option warning: no dimension given for Qhull option 'Q%c'\n",
                    key);
            lastwarning= lastoption;
            continue;
          }
          idx= qh_strtol(s, &s);
          if (idx >= maxdim) {
            qh_fprintf(qh ferr, 7048, "qhull option warning: dimension %d for Qhull option 'Q%c' is >= %d.  Ignored\n",
                idx, key, maxdim);
            lastwarning= lastoption;
            continue;
          }
          if (*s == ':') {
            s++;
            value= qh_strtod(s, &s);
          }else if (key == 'b')
            value= -qh_DEFAULTbox;
          else
            value= qh_DEFAULTbox;
          if (key == 'b')
            qh lower_bound[idx]= value;
          else
            qh upper_bound[idx]= value;
        }
      }
    }else {
      while (*s && !isspace(*s))
        s++;
    }
    while (isspace(*s))
      s++;
  }
  for (k=qh hull_dim; k--; ) {
    if (qh lower_threshold[k] > -REALmax/2) {
      qh GOODthreshold= True;
      if (qh upper_threshold[k] < REALmax/2) {
        qh SPLITthresholds= True;
        qh GOODthreshold= False;
        break;
      }
    }else if (qh upper_threshold[k] < REALmax/2)
      qh GOODthreshold= True;
  }
  if (lastwarning && !qh ALLOWwarning) {
    qh_fprintf(qh ferr, 6036, "qhull option error: see previous warnings, use 'Qw' to override: '%s' (last offset %d)\n",
      command, (int)(lastwarning-command));
    qh_errexit(qh_ERRinput, NULL, NULL);
  }
} /* initthresholds */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="lib_check">-</a>

  qh_lib_check( qhullLibraryType, qhTsize, vertexTsize, ridgeTsize, facetTsize, setTsize, qhmemTsize )
    Report error if library does not agree with caller

  notes:
    NOerrors -- qh_lib_check can not call qh_errexit()
*/
void qh_lib_check(int qhullLibraryType, int qhTsize, int vertexTsize, int ridgeTsize, int facetTsize, int setTsize, int qhmemTsize) {
    int last_errcode= qh_ERRnone;

#if defined(_MSC_VER) && defined(_DEBUG) && defined(QHULL_CRTDBG)  /* user.h */
    /*_CrtSetBreakAlloc(744);*/  /* Break at memalloc {744}, or 'watch' _crtBreakAlloc */
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) );
    _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG );
    _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR );
    _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG );
    _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR );
    _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG );
    _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR );
#endif

    if (qhullLibraryType==QHULL_NON_REENTRANT) { /* 0 */
      if (qh_QHpointer) {
        qh_fprintf_stderr(6246, "qh_lib_check: Incorrect qhull library called.  Caller uses a static qhT while library uses a dynamic qhT via qh_QHpointer.  Both caller and qhull library are non-reentrant.\n");
        last_errcode= 6246;
      }
    }else if (qhullLibraryType==QHULL_QH_POINTER) { /* 1 */
      if (!qh_QHpointer) {
        qh_fprintf_stderr(6247, "qh_lib_check: Incorrect qhull library called.  Caller uses a dynamic qhT via qh_QHpointer while qhull library uses a static qhT.  Both caller and qhull library are non-reentrant.\n");
        last_errcode= 6247;
      }
    }else if (qhullLibraryType == QHULL_REENTRANT) { /* 2 */
      qh_fprintf_stderr(6248, "qh_lib_check: Incorrect qhull library called.  Caller uses reentrant Qhull while qhull library is non-reentrant\n");
      last_errcode= 6248;
    }else {
      qh_fprintf_stderr(6262, "qh_lib_check: Expecting qhullLibraryType QHULL_NON_REENTRANT(0), QHULL_QH_POINTER(1), or QHULL_REENTRANT(2).  Got %d\n", qhullLibraryType);
      last_errcode= 6262;
    }
    if (qhTsize != (int)sizeof(qhT)) {
      qh_fprintf_stderr(6249, "qh_lib_check: Incorrect qhull library called.  Size of qhT for caller is %d, but for qhull library is %d.\n", qhTsize, (int)sizeof(qhT));
      last_errcode= 6249;
    }
    if (vertexTsize != (int)sizeof(vertexT)) {
      qh_fprintf_stderr(6250, "qh_lib_check: Incorrect qhull library called.  Size of vertexT for caller is %d, but for qhull library is %d.\n", vertexTsize, (int)sizeof(vertexT));
      last_errcode= 6250;
    }
    if (ridgeTsize != (int)sizeof(ridgeT)) {
      qh_fprintf_stderr(6251, "qh_lib_check: Incorrect qhull library called.  Size of ridgeT for caller is %d, but for qhull library is %d.\n", ridgeTsize, (int)sizeof(ridgeT));
      last_errcode= 6251;
    }
    if (facetTsize != (int)sizeof(facetT)) {
      qh_fprintf_stderr(6252, "qh_lib_check: Incorrect qhull library called.  Size of facetT for caller is %d, but for qhull library is %d.\n", facetTsize, (int)sizeof(facetT));
      last_errcode= 6252;
    }
    if (setTsize && setTsize != (int)sizeof(setT)) {
      qh_fprintf_stderr(6253, "qh_lib_check: Incorrect qhull library called.  Size of setT for caller is %d, but for qhull library is %d.\n", setTsize, (int)sizeof(setT));
      last_errcode= 6253;
    }
    if (qhmemTsize && qhmemTsize != sizeof(qhmemT)) {
      qh_fprintf_stderr(6254, "qh_lib_check: Incorrect qhull library called.  Size of qhmemT for caller is %d, but for qhull library is %d.\n", qhmemTsize, sizeof(qhmemT));
      last_errcode= 6254;
    }
    if (last_errcode) {
      if(qh_QHpointer){
        qh_fprintf_stderr(6255, "qhull internal error (qh_lib_check): Cannot continue due to QH%d.  Library '%s' should use a dynamic qhT via qh_QHpointer (e.g., qhull_p.so).  It may be out-of-date.  Exit with %d\n",
              last_errcode, qh_version2, last_errcode - 6200);
      }else {
        qh_fprintf_stderr(6256, "qhull internal error (qh_lib_check): Cannot continue due to QH%d..  Library '%s' should use a static qhT (e.g., libqhull.so).  It may be out-of-date.  Exit with %d\n",
              last_errcode, qh_version2, last_errcode - 6200);
      }
      qh_exit(last_errcode - 6200);  /* can not use qh_errexit(), must be less than 255 */
    }
} /* lib_check */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="option">-</a>

  qh_option( option, intVal, realVal )
    add an option description to qh.qhull_options

  notes:
    NOerrors -- qh_option can not call qh_errexit() [qh_initqhull_start2]
    will be printed with statistics ('Ts') and errors
    strlen(option) < 40
*/
void qh_option(const char *option, int *i, realT *r) {
  char buf[200];
  int buflen, remainder;

  if (strlen(option) > sizeof(buf)-30-30) {
    qh_fprintf(qh ferr, 6408, "qhull internal error (qh_option): option (%d chars) has more than %d chars.  May overflow temporary buffer.  Option '%s'\n",
        (int)strlen(option), (int)sizeof(buf)-30-30, option);
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  sprintf(buf, "  %s", option);
  if (i)
    sprintf(buf+strlen(buf), " %d", *i);
  if (r)
    sprintf(buf+strlen(buf), " %2.2g", *r);
  buflen= (int)strlen(buf);   /* WARN64 */
  qh qhull_optionlen += buflen;
  remainder= (int)(sizeof(qh qhull_options) - strlen(qh qhull_options)) - 1;    /* WARN64 */
  maximize_(remainder, 0);
  if (qh qhull_optionlen >= qh_OPTIONline && remainder > 0) {
    strncat(qh qhull_options, "\n", (unsigned int)remainder);
    --remainder;
    qh qhull_optionlen= buflen;
  }
  if (buflen > remainder) {
    trace1((qh ferr, 1058, "qh_option: option would overflow qh.qhull_options. Truncated '%s'\n", buf));
  }
  strncat(qh qhull_options, buf, (unsigned int)remainder);
} /* option */

#if qh_QHpointer
/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="restore_qhull">-</a>

  qh_restore_qhull( oldqh )
    restores a previously saved qhull
    also restores qh_qhstat and qhmem.tempstack
    Sets *oldqh to NULL
  notes:
    errors if current qhull hasn't been saved or freed
    uses qhmem for error reporting

  NOTE 1998/5/11:
    Freeing memory after qh_save_qhull and qh_restore_qhull
    is complicated.  The procedures will be redesigned.

  see:
    qh_save_qhull(), UsingLibQhull
*/
void qh_restore_qhull(qhT **oldqh) {

  if (*oldqh && strcmp((*oldqh)->qhull, "qhull")) {
    qh_fprintf(qhmem.ferr, 6061, "qhull internal error (qh_restore_qhull): %p is not a qhull data structure\n",
                  *oldqh);
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  if (qh_qh) {
    qh_fprintf(qhmem.ferr, 6062, "qhull internal error (qh_restore_qhull): did not save or free existing qhull\n");
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  if (!*oldqh || !(*oldqh)->old_qhstat) {
    qh_fprintf(qhmem.ferr, 6063, "qhull internal error (qh_restore_qhull): did not previously save qhull %p\n",
                  *oldqh);
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  qh_qh= *oldqh;
  *oldqh= NULL;
  qh_qhstat= qh old_qhstat;
  qhmem.tempstack= qh old_tempstack;
  qh old_qhstat= 0;
  qh old_tempstack= 0;
  trace1((qh ferr, 1007, "qh_restore_qhull: restored qhull from %p\n", *oldqh));
} /* restore_qhull */

/*-<a                             href="qh-globa.htm#TOC"
  >-------------------------------</a><a name="save_qhull">-</a>

  qh_save_qhull(  )
    saves qhull for a later qh_restore_qhull
    also saves qh_qhstat and qhmem.tempstack

  returns:
    qh_qh=NULL

  notes:
    need to initialize qhull or call qh_restore_qhull before continuing

  NOTE 1998/5/11:
    Freeing memory after qh_save_qhull and qh_restore_qhull
    is complicated.  The procedures will be redesigned.

  see:
    qh_restore_qhull()
*/
qhT *qh_save_qhull(void) {
  qhT *oldqh;

  trace1((qhmem.ferr, 1045, "qh_save_qhull: save qhull %p\n", qh_qh));
  if (!qh_qh) {
    qh_fprintf(qhmem.ferr, 6064, "qhull internal error (qh_save_qhull): qhull not initialized\n");
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  qh old_qhstat= qh_qhstat;
  qh_qhstat= NULL;
  qh old_tempstack= qhmem.tempstack;
  qhmem.tempstack= NULL;
  oldqh= qh_qh;
  qh_qh= NULL;
  return oldqh;
} /* save_qhull */

#endif



<|start_filename|>doc/_includes/json_schema_category_description.html<|end_filename|>
{% capture collapseId %}collapse_{{include.id}}{% endcapture %}
<div id="{{collapseId}}" class="collapse collapseSchema" style="position:sticky; top: 65px;">
  <div class="card">
    <div class="card-header">
      <h6 class="text-primary font-weight-bold">{{include.id}}</h6>
    </div>
    <div class="card-body">
      {{include.description | liquefy}}
    </div>
  </div>
</div>


<|start_filename|>src/mc_tasks/lipm_stabilizer/ZMPCC.cpp<|end_filename|>
/*
 * Copyright 2015-2020 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include <mc_rtc/gui.h>
#include <mc_rtc/log/Logger.h>
#include <mc_tasks/lipm_stabilizer/ZMPCC.h>

namespace mc_tasks
{
namespace lipm_stabilizer
{
void ZMPCC::reset()
{
  error_.setZero();
  comVel_.setZero();
  comAccel_.setZero();
  comOffset_.setZero();
  integrator_.reset();
}

void ZMPCC::update(const Eigen::Vector3d & distribZMP,
                   const Eigen::Vector3d & measuredZMP,
                   const sva::PTransformd & zmpFrame,
                   double dt)
{
  if(enabled_)
  {
    error_ = distribZMP - measuredZMP;
    const Eigen::Matrix3d & R_0_c = zmpFrame.rotation();
    Eigen::Vector3d newVel =
        -R_0_c.transpose()
        * Eigen::Vector3d{config_.comAdmittance.x(), config_.comAdmittance.y(), 0}.cwiseProduct(R_0_c * error_);
    comAccel_ = (newVel - comVel_) / dt;
    comVel_ = newVel;
    integrator_.add(newVel, dt);
  }
  else
  {
    comAccel_.setZero();
    comVel_.setZero();
    integrator_.add(Eigen::Vector3d::Zero(), dt); // leak to zero
  }
  comOffset_ = integrator_.eval();
}

void ZMPCC::apply(Eigen::Vector3d & com, Eigen::Vector3d & comd, Eigen::Vector3d & comdd)
{
  com += comOffset_;
  comd += comVel_;
  comdd += comAccel_;
}

void ZMPCC::addToGUI(mc_rtc::gui::StateBuilder & gui, const std::vector<std::string> & category)
{
  using namespace mc_rtc::gui;
  gui.addElement(category,
                 ArrayInput(
                     "CoM admittance", {"Ax", "Ay"},
                     [this]() -> const Eigen::Vector2d & { return config_.comAdmittance; },
                     [this](const Eigen::Vector2d & a) { config_.comAdmittance = a; }),
                 NumberInput(
                     "CoM integrator leak rate [Hz]", [this]() { return integrator_.rate(); },
                     [this](double T) {
                       integrator_.rate(T);
                       config_.integratorLeakRate = T;
                     }));
}

void ZMPCC::removeFromGUI(mc_rtc::gui::StateBuilder & gui, const std::vector<std::string> & category)
{
  gui.removeElement(category, "CoM admittance");
  gui.removeElement(category, "CoM integrator leak rate [Hz]");
}

void ZMPCC::addToLogger(mc_rtc::Logger & logger, const std::string & name)
{
  logger.addLogEntry(name + "_zmpcc_comAdmittance", this,
                     [this]() -> const Eigen::Vector2d & { return config_.comAdmittance; });
  MC_RTC_LOG_HELPER(name + "_zmpcc_errorZMP", error_);
}

void ZMPCC::removeFromLogger(mc_rtc::Logger & logger, const std::string &)
{
  logger.removeLogEntries(this);
}

} // namespace lipm_stabilizer
} // namespace mc_tasks


<|start_filename|>3rd-party/qhull/eg/q_test.bat<|end_filename|>
echo off
echo q_test.bat -- writes output to q_test.x for comparison with eg/q_test-ok.txt
echo
echo Instructions:
echo
echo Launch QHULL-GO.bat
echo "  ..\eg\q_test.bat >q_test.x 2>&1  "
echo
echo Compare bin\q_test.x with ..\eg\q_test-ok.txt

echo "========================" >q_test.x
echo "== Run q_test.bat for Windows from the bin/ directory."
echo "== Skips 'make test' and 'make qtest'"
echo "== Align the next block with 'eg/q_test'"
echo "========================"
echo
echo "=============================="
echo "========= eg/q_test =========="
echo "== Check qhull programs ======"
echo "=============================="
echo
echo "=============================="
echo "== check user_eg, user_eg2, and user_eg3"
echo "== errors if 'user_eg' and 'user_eg2' not found"
echo "=============================="
echo user_eg
user_eg
echo "user_eg 'QR1 p n Qt' 'v p' Fp"
user_eg "QR1 p n Qt" "v p" Fp
echo user_eg2
user_eg2
echo "user_eg2 'QR1 p' 'v p' Fp"
user_eg2 "QR1 p" "v p" Fp
echo user_eg3
user_eg3
echo user_eg3 eg-100
user_eg3 eg-100
echo user_eg3 eg-convex
user_eg3 eg-convex
echo user_eg3 eg-delaunay
user_eg3 eg-delaunay
echo user_eg3 eg-voronoi
user_eg3 eg-voronoi
echo user_eg3 eg-fifo
user_eg3 eg-fifo
echo user_eg3 rbox qhull
user_eg3 rbox qhull
echo user_eg3 rbox qhull T1
user_eg3 rbox qhull T1
echo user_eg3 rbox qhull d
user_eg3 rbox qhull d
echo user_eg3 rbox y c
user_eg3 rbox y c
echo 'user_eg3 rbox D2 10 2 "s r 5"  qhull 's p' facets'
user_eg3 rbox D2 10 2 "s r 5"  qhull 's p' facets
echo user_eg3 rbox "10 D2" eg-convex
user_eg3 rbox "10 D2" eg-convex
echo 'user_eg3 rbox "10 D2" qhull s eg-convex'
user_eg3 rbox "10 D2" qhull s eg-convex
echo 'user_eg3 rbox 10 eg-delaunay qhull "d o"'
user_eg3 rbox 10 eg-delaunay qhull "d o"
echo user_eg3 rbox D5 c P2 qhull d eg-delaunay
user_eg3 rbox D5 c P2 qhull d eg-delaunay
echo user_eg3 rbox "10 D2" eg-voronoi
user_eg3 rbox "10 D2" eg-voronoi
echo user_eg3 rbox "D5 c P2" qhull v eg-voronoi o
user_eg3 rbox "D5 c P2" qhull v eg-voronoi o
echo user_eg3 rbox "10 D2" eg-fifo
user_eg3 rbox "10 D2" eg-fifo
echo user_eg3 rbox "10 D2" qhull v eg-fifo p Fi Fo
user_eg3 rbox "10 D2" qhull v eg-fifo p Fi Fo

echo === check front ends ==========================================================
echo "qconvex -"
qconvex -
echo "qconvex ."
qconvex .
echo "qconvex -?"
qconvex -? # [mar'19] isatty does not work for Git for Windows
echo "rbox c D3 | qconvex s n Qt"
rbox c D3 | qconvex s n Qt
echo "rbox c D2 | qconvex s i"
rbox c D2 | qconvex s i 
echo "rbox c D2 | qconvex s n"
rbox c D2 | qconvex s n 
echo "rbox c D2 | qconvex o"
rbox c D2 | qconvex o
echo "rbox 1000 s | qconvex s Tv FA"
rbox 1000 s | qconvex s Tv FA
echo "rbox c d D2 | qconvex s Qc Fx"
rbox c d D2 | qconvex s Qc Fx
echo "rbox y 1000 W0 | qconvex Qc s n"
rbox y 1000 W0 | qconvex Qc s n 
echo "rbox y 1000 W0 | qconvex s QJ"
rbox y 1000 W0 | qconvex s QJ
echo "rbox d G1 D12 | qconvex QR0 FA"
rbox d G1 D12 | qconvex QR0 FA
echo "rbox c D6 | qconvex s FA TF500"
rbox c D6 | qconvex s FA TF500
echo "rbox c D7 | qconvex s FA TF1000"
rbox c D7 | qconvex s FA TF1000
echo "rbox d G1 D12 | qconvex QR0 FA Pp"
rbox d G1 D12 | qconvex QR0 FA Pp
echo "rbox c P0 d D2 | qconvex p Fa Fc FP FI Fn FN FS Fv Fx"
rbox c P0 d D2 | qconvex p Fa Fc FP FI Fn FN FS Fv Fx
echo "rbox c d D2 | qconvex s i QV0"
rbox c d D2 | qconvex s i QV0
echo "rbox c | qconvex Q0"
rbox c | qconvex Q0
echo "qvoronoi -"
qvoronoi -
echo "qvoronoi ."
qvoronoi .
echo "qvoronoi -?"
qvoronoi -? # [mar'19] isatty does not work for Git for Windows
echo "rbox c P0 D2 | qvoronoi s o"
rbox c P0 D2 | qvoronoi s o
echo "rbox c P0 D2 | qvoronoi Fi Tv"
rbox c P0 D2 | qvoronoi Fi Tv
echo "rbox c P0 D2 | qvoronoi Fo Tv"
rbox c P0 D2 | qvoronoi Fo Tv
echo "rbox c P0 D2 | qvoronoi Fv"
rbox c P0 D2 | qvoronoi Fv
echo "rbox c P0 D2 | qvoronoi s Qu Fv"
rbox c P0 D2 | qvoronoi s Qu Fv
echo "rbox c P0 D2 | qvoronoi s Qu Qt Fv"
rbox c P0 D2 | qvoronoi s Qu Qt Fv
echo "rbox c P0 D2 | qvoronoi Qu Fo"
rbox c P0 D2 | qvoronoi Qu Fo
echo "rbox c G1 d D2 | qvoronoi s p"
rbox c G1 d D2 | qvoronoi s p 
echo "rbox c G1 d D2 | qvoronoi QJ p"
rbox c G1 d D2 | qvoronoi QJ p 
echo "rbox c P-0.1 P+0.1 P+0.1 D2 | qvoronoi s Fc FP FQ Fn FN"
rbox c P-0.1 P+0.1 P+0.1 D2 | qvoronoi s Fc FP FQ Fn FN 
echo "rbox P0 c D2 | qvoronoi s Fv QV0"
rbox P0 c D2 | qvoronoi s Fv QV0
echo "qdelaunay -"
qdelaunay -
echo "qdelaunay ."
qdelaunay .
echo "qdelaunay -?"
qdelaunay -? # [mar'19] isatty does not work for Git for Windows
echo "rbox c P0 D2 | qdelaunay s o"
rbox c P0 D2 | qdelaunay s o
echo "rbox c P0 D2 | qdelaunay i"
rbox c P0 D2 | qdelaunay i
echo "rbox c P0 D2 | qdelaunay Fv"
rbox c P0 D2 | qdelaunay Fv
echo "rbox c P0 D2 | qdelaunay s Qu Qt Fv"
rbox c P0 D2 | qdelaunay s Qu Qt Fv
echo "rbox c G1 d D2 | qdelaunay s i"
rbox c G1 d D2 | qdelaunay s i
echo "rbox c G1 d D2 | qhull d Qbb Ft"
rbox c G1 d D2 | qhull d Qbb Ft
echo "rbox c G1 d D2 | qhull d Qbb QJ s Ft"
rbox c G1 d D2 | qhull d Qbb QJ s Ft
echo "rbox M3,4 z 100 D2 | qdelaunay s"
rbox M3,4 z 100 D2 | qdelaunay s
echo "rbox c P-0.1 P+0.1 P+0.1 D2 | qdelaunay s Fx Fa Fc FP FQ Fn FN"
rbox c P-0.1 P+0.1 P+0.1 D2 | qdelaunay s Fx Fa Fc FP FQ Fn FN
echo "rbox P0 P0 c D2 | qdelaunay s FP QV0"
rbox P0 P0 c D2 | qdelaunay s FP QV0
echo "qhalf -"
qhalf -
echo "qhalf ."
qhalf .
echo "qhalf -?"
qhalf -? # [mar'19] isatty does not work for Git for Windows
echo "rbox d | qhull FQ n | qhalf s Qt H0,0,0 Fp"
rbox d | qhull FQ n | qhalf s Qt H0,0,0 Fp
echo "rbox c | qhull FQ FV n | qhalf s i"
rbox c | qhull FQ FV n | qhalf s i
echo "rbox c | qhull FQ FV n | qhalf o"
rbox c | qhull FQ FV n | qhalf o
echo "rbox d D2 | qhull FQ n | qhalf s H0 Fc FP Fn FN FQ Fv Fx"
rbox d D2 | qhull FQ n | qhalf s H0 Fc FP Fn FN FQ Fv Fx
echo "qhull - printed at end"
echo "qhull ."
qhull .
echo "qhull -?"
qhull -? # [mar'19] isatty does not work for Git for Windows
echo "rbox 1000 s | qhull Tv s FA"
rbox 1000 s | qhull Tv s FA
echo "rbox 10 D2 | qhull d QJ s i TO q_test.log.1"
rbox 10 D2 | qhull d QJ s i TO q_test.log.1
echo "type q_test.log.1"
type q_test.log.1
echo "rbox 10 D2 | qhull v Qbb Qt p"
rbox 10 D2 | qhull v Qbb Qt p
echo "rbox 10 D2 | qhull d Qu QJ m" 
rbox 10 D2 | qhull d Qu QJ m  
echo "rbox 10 D2 | qhull v Qu QJ o"
rbox 10 D2 | qhull v Qu QJ o
echo "rbox c d D2 | qhull Qc s f Fx"
rbox c d D2 | qhull Qc s f Fx
echo "rbox c | qhull FV n | qhull H Fp"
rbox c | qhull FV n | qhull H Fp
echo "rbox d D12 | qhull QR0 FA"
rbox d D12 | qhull QR0 FA
echo "rbox c D7 | qhull FA TF1000"
rbox c D7 | qhull FA TF1000
echo "rbox y 1000 W0 | qhull Qc"   
rbox y 1000 W0 | qhull Qc    
echo "rbox c | qhull n"
rbox c | qhull n
echo "rbox c | qhull TA1"
rbox c | qhull TA1
echo "rbox 10 s | qhull C1e-5 T1P-1f"
rbox 10 s | qhull C1e-5 T1P-1f
echo === check quality of Qhull for ${d:-`hostname`} ${d:-`date`}
echo "rbox 1000 W0 | qhull QR2 QJ s Fs Tv"
rbox 1000 W0 | qhull QR2 QJ s Fs Tv
echo "rbox 1000 W0 | qhull QR2 s Fs Tv"
rbox 1000 W0 | qhull QR2 s Fs Tv
echo "rbox 1000 s | qhull C0.02 Qc Tv"
rbox 1000 s | qhull C0.02 Qc Tv
echo "rbox 500 s D4 | qhull C0.01 Qc Tv"
rbox 500 s D4 | qhull C0.01 Qc Tv
echo "rbox 1000 s | qhull C-0.02 Qc Tv"
rbox 1000 s | qhull C-0.02 Qc Tv
echo "rbox 1000 s D4 | qhull C-0.01 Qc Tv"
rbox 1000 s D4 | qhull C-0.01 Qc Tv
echo "rbox 200 s D5 | qhull C-0.01 Qx Qc Tv"
rbox 200 s D5 | qhull C-0.01 Qx Qc Tv
echo "rbox 100 s D6 | qhull C-0.001 Qx Qc Tv"
rbox 100 s D6 | qhull C-0.001 Qx Qc Tv
echo "rbox 1000 W1e-4 | qhull C-1e-6 Qc Tv"
rbox 1000 W1e-4 | qhull C-1e-6 Qc Tv
echo "rbox 1000 W5e-4 D4 | qhull C-1e-5 Qc Tv"
rbox 1000 W5e-4 D4 | qhull C-1e-5 Qc Tv
echo "rbox 400 W1e-3 D5 | qhull C-1e-5 Qx Qc Tv"
rbox 400 W1e-3 D5 | qhull C-1e-5 Qx Qc Tv

echo === check input format etc. ${d:-`date`}
echo "=== test long and short input to Qhull by hand, see eg/q_test ==="
echo "rbox d h | qhull Fd FV n FD  Tcv | qhull Fd H Fp Tcv"
rbox d h | qhull Fd FV n FD Tcv | qhull Fd H Fp Tcv
echo "rbox 10 h | qhull Fd FD p Tcv | qhull Fd d Tcv"
rbox 10 h | qhull Fd FD p Tcv | qhull Fd d Tcv 

echo === check rbox ${d:-`date`}
echo "rbox 3 n D2"
rbox 3 n D2
echo "rbox 3 D2"
rbox 3 D2
echo "rbox 3 h D2"
rbox 3 h D2
echo "rbox 3 z D2"
rbox 3 z D2
echo "rbox 3 z h D2"
rbox 3 z h D2
echo "rbox 3 B10 D2"
rbox 3 B10 D2
echo "rbox 3 B10 D2 C2,1E-13,1"
rbox 3 B10 D2 C2,1E-13,1
echo "rbox 3 z B10 D2"
rbox 3 z B10 D2
echo "rbox 4 L2 r D2"
rbox 4 L2 r D2
echo "rbox 8 L2 r D2"
rbox 8 L2 r D2
echo "rbox 8 L2 D2 C1,1E-13,2"
rbox 8 L2 D2 C1,1E-13,2
echo "rbox 4 L4 r D3"
rbox 4 L4 r D3
echo "rbox 4 L4 s D5 W1e-3" 
rbox 4 L4 s D5 W1e-3 
echo "rbox 4 L4 s D5 W1e-3 C1,1E-12"
rbox 4 L4 s D5 W1e-3 C1,1E-12
echo "rbox y"
rbox y
echo "rbox 10 M3,4"
rbox 10 M3,4
echo "rbox 10 M3,4 C1,1E-14"
rbox 10 M3,4 C1,1E-14
echo "rbox 27 M1,0,1"
rbox 27 M1,0,1
echo "rbox 10 L4 r D3 | qhull Tcv"
rbox 10 L4 r D3 | qhull Tcv
echo "rbox 10 L4 s D5 W1e-3 | qhull Tcv"
rbox 10 L4 s D5 W1e-3 | qhull Tcv
echo "rbox 10 L4 s D5 W1e-3 C1,1E-12 | qhull Tcv"
rbox 10 L4 s D5 W1e-3 C1,1E-12 | qhull Tcv
echo "rbox 10 L6 D3 | qhull Tcv"
rbox 10 L6 D3 | qhull Tcv
echo "rbox 10 L1.1 s D4 | qhull Tcv"
rbox 10 L1.1 s D4 | qhull Tcv
echo "rbox y r 100 W0 O0.5 | qhull s p Tcv"
rbox y r 100 W0 O0.5 | qhull s p Tcv
echo "rbox x r 100 W0 O0.5 | qhull s Tcv"
rbox x r 100 W0 O0.5 | qhull s Tcv
echo "rbox 12 D8 | qhull Tcv"
rbox 12 D8 | qhull Tcv
echo "rbox 12 D9 | qhull Tcv"
rbox 12 D9 | qhull Tcv
echo "rbox 1000 D4 | qhull s i A-0.97 C0.2 A0.7 Tcv"
rbox 1000 D4 | qhull s i A-0.97 C0.2 A0.7 Tcv
echo "rbox 3 D2 | qhull Qb0B1:-2 p"
rbox 3 D2 | qhull Qb0B1:-2 p 
echo "rbox 100 r D2 | qhull Pd0:0.7 PD0:0.8 Pg n Tcv"
rbox 100 r D2 | qhull Pd0:0.7 PD0:0.8 Pg n Tcv
echo "rbox 1000 s | qhull  C0.05 Tcv"
rbox 1000 s | qhull  C0.05 Tcv
echo "rbox 1000 s t | qhull Qm C0.05 Tcv"
rbox 1000 s t | qhull Qm C0.05 Tcv
echo "rbox 500 D2 | qhull n A-0.95 C0.1 Tcv"
rbox 500 D2 | qhull n A-0.95 C0.1 Tcv
echo "rbox 500 s P1,1,1 | qhull QgG0 Pp Tcv"
rbox 500 s P1,1,1 | qhull QgG0 Pp Tcv
echo "rbox d | qhull m"
rbox d | qhull m
echo "rbox d | qhull FM"
rbox d | qhull FM
echo "rbox c D2 | qhull Tcv Q0"
rbox c D2 | qhull Tcv Q0
echo "rbox d D2 | qhull Tcv"
rbox d D2 | qhull Tcv
echo "rbox c D3 | qhull Tcv Q0"
rbox c D3 | qhull Tcv Q0
echo "rbox d D3 | qhull Tcv"
rbox d D3 | qhull Tcv
echo "rbox c D4 | qhull Tcv Q0"
rbox c D4 | qhull Tcv Q0
echo "rbox d D4 | qhull Tcv"
rbox d D4 | qhull Tcv
echo "rbox c D5 | qhull Tcv Q0"
rbox c D5 | qhull Tcv Q0
echo "rbox d D5 | qhull Tcv"
rbox d D5 | qhull Tcv
echo "rbox c D6 | qhull Tcv Q0"
rbox c D6 | qhull Tcv Q0
echo "rbox d D6 | qhull Tcv"
rbox d D6 | qhull Tcv
echo "rbox d D7 | qhull Tcv"
rbox d D7 | qhull Tcv
echo "rbox c D2 | qhull Tcv C-0"
rbox c D2 | qhull Tcv C-0
echo "rbox c D3 | qhull Tcv C-0"
rbox c D3 | qhull Tcv C-0
echo "rbox c D4 | qhull Tcv C-0"
rbox c D4 | qhull Tcv C-0
echo "rbox c D5 | qhull Tcv C-0"
rbox c D5 | qhull Tcv C-0
echo "rbox c D6 | qhull Tcv C-0"
rbox c D6 | qhull Tcv C-0
echo "rbox c D7 | qhull Tv C-0"
rbox c D7 | qhull Tv C-0
echo "rbox 20 l D3 | qhull Tcv"
rbox 20 l D3 | qhull Tcv
echo "rbox 100 s D2 | qhull Tcv"
rbox 100 s D2 | qhull Tcv
echo "rbox 100 s D3 | qhull Tcv"
rbox 100 s D3 | qhull Tcv
echo "rbox 100 s D4 | qhull Tcv"
rbox 100 s D4 | qhull Tcv
echo "rbox 100 s c D4 | qhull Tcv"
rbox 100 s c D4 | qhull Tcv
echo "rbox 100 s d G1.5 D4 | qhull Tcv"
rbox 100 s d G1.5 D4 | qhull Tcv
echo "rbox 100 s W1e-2 | qhull Tcv"
rbox 100 s W1e-2 | qhull Tcv
echo "rbox 100 | qhull Tcv"
rbox 100 | qhull Tcv
echo "rbox 100 W1e-3 | qhull Tcv"
rbox 100 W1e-3 | qhull Tcv
echo "rbox 100 r D2 | qhull Tcv"
rbox 100 r D2 | qhull Tcv
echo "rbox 100 r s Z1 | qhull Tcv"
rbox 100 r s Z1 | qhull Tcv
echo "rbox 100 r s Z1 G0.1 | qhull Tcv C-0"
rbox 100 r s Z1 G0.1 | qhull Tcv C-0
echo "rbox 100 s Z1 G0.1 | qhull Tcv"
rbox 100 s Z1 G0.1 | qhull Tcv 
echo "rbox 100 s Z1e-5 G0.1 | qhull Tc Pp"
rbox 100 s Z1e-5 G0.1 | qhull Tc Pp

echo === check qhull option errors ${d:-`date`}


echo "rbox 10 | qhull d H0"
rbox 10 | qhull d H0
echo "rbox 10 | qhull Zza"
rbox 10 | qhull Zza
echo "rbox 10 | qhull Zza Qw"
rbox 10 | qhull Zza Qw
echo "rbox 10 | qhull Q999"
rbox 10 | qhull Q999
echo "rbox 10 | qhull Q999 Qw"
rbox 10 | qhull Q999 Qw
echo "qhull TIxyzlkajdfhwh"
qhull TIxyzlkajdfhwh
echo "rbox 10 >r.x"
rbox 10 >r.x
echo "qhull TIr.x"
qhull TIr.x
echo "qhull p TI    r.x TO x.x"
qhull p TI    r.x TO x.x
echo "type x.x"
type x.x

echo === check qhull output formats ${d:-`date`}
echo "rbox 5 r s D2 | qhull Tcv"
rbox 5 r s D2 | qhull Tcv
echo "rbox 5 r s D2 | qhull s "
rbox 5 r s D2 | qhull s 
echo "rbox 5 r s D2 | qhull s o "
rbox 5 r s D2 | qhull s o 
echo "rbox 5 r s D2 | qhull f"
rbox 5 r s D2 | qhull f
echo "rbox 5 r s D2 | qhull i "
rbox 5 r s D2 | qhull i 
echo "rbox 5 r s D2 | qhull m "
rbox 5 r s D2 | qhull m 
echo "rbox 5 r s D2 | qhull FM "
rbox 5 r s D2 | qhull FM 
echo "rbox 5 r s D2 | qhull n "
rbox 5 r s D2 | qhull n 
echo "rbox 5 r s D2 | qhull p "
rbox 5 r s D2 | qhull p 
echo "rbox 5 r s D2 | qhull o "
rbox 5 r s D2 | qhull o 
echo "rbox 5 r s D2 | qhull Ft"
rbox 5 r s D2 | qhull Ft
echo "rbox 5 r s D2 | qhull Fx"
rbox 5 r s D2 | qhull Fx
echo "rbox 5 r s D2 | qhull p n i p p"
rbox 5 r s D2 | qhull p n i p p
echo "rbox 10 D3 | qhull f Tcv"
rbox 10 D3 | qhull f Tcv
echo "rbox 10 D3 | qhull i"
rbox 10 D3 | qhull i
echo "rbox 10 D3 | qhull p "
rbox 10 D3 | qhull p 
echo "rbox 10 D3 | qhull o "
rbox 10 D3 | qhull o 
echo "rbox 10 D3 | qhull Fx"
rbox 10 D3 | qhull Fx
echo "rbox 27 M1,0,1 | qhull Qc"
rbox 27 M1,0,1 | qhull Qc
echo "rbox 50 D3 s | qhull C0.1 Qc Pd0d1d2 Pg s p Tcv"
rbox 50 D3 s | qhull C0.1 Qc Pd0d1d2 Pg s p Tcv
echo "rbox 10 D2 P0 P1e-15 | qhull d Qc FP s Tcv"
rbox 10 D2 P0 P1e-15 | qhull d Qc FP s Tcv
echo "rbox 100 s | qhull C-0.003 Qc FP s"
rbox 100 s | qhull C-0.003 Qc FP s
echo "rbox 100 s D2 | qhull C0.1 i Fx Tcv"
rbox 100 s D2 | qhull C0.1 i Fx Tcv
echo "rbox 4 s D3 | qhull Qc Ghipv Tcv "
rbox 4 s D3 | qhull Qc Ghipv Tcv 
echo "rbox 6 D4 | qhull f Tcv"
rbox 6 D4 | qhull f Tcv
echo "rbox 6 D4 | qhull i"
rbox 6 D4 | qhull i
echo "rbox 6 D4 | qhull p "
rbox 6 D4 | qhull p 
echo "rbox 6 D4 | qhull o"
rbox 6 D4 | qhull o
echo "rbox 1000 s D2 | qhull FA Tcv"
rbox 1000 s D2 | qhull FA Tcv
echo "rbox 1000 s | qhull FA Tcv"
rbox 1000 s | qhull FA Tcv
echo "rbox c D4 | qhull FA Tcv"
rbox c D4 | qhull FA Tcv
echo "rbox c D5 | qhull FA Tcv"
rbox c D5 | qhull FA Tcv
echo "rbox c D5 | qhull FA Qt Tcv"
rbox c D5 | qhull FA Qt Tcv
echo "rbox 10 D2 | qhull d FA Tcv"
rbox 10 D2 | qhull d FA Tcv
echo "rbox 10 D2 | qhull d Qu FA Tcv"
rbox 10 D2 | qhull d Qu FA Tcv
echo "rbox 10 D2 | qhull FA Tcv"
rbox 10 D2 | qhull FA Tcv
echo "rbox 10 c D2 | qhull Fx Tcv"
rbox 10 c D2 | qhull Fx Tcv
echo "rbox 1000 s | qhull FS Tcv"
rbox 1000 s | qhull FS Tcv
echo "rbox 10 W0 D2 | qhull p Qc FcC Tcv"
rbox 10 W0 D2 | qhull p Qc FcC Tcv
echo "rbox 4 z h s D2 | qhull Fd s n FD Tcv"
rbox 4 z h s D2 | qhull Fd s n FD Tcv
echo "rbox 6 s D3 | qhull C-0.1 Qc FF s FQ Fi n Fo FQ FI Fm Fn FN FO FO FQ Fs FS FV Fv Tcv"
rbox 6 s D3 | qhull C-0.1 Qc FF s FQ Fi n Fo FQ FI Fm Fn FN FO FO FQ Fs FS FV Fv Tcv
echo "rbox P0.5,0.5 P0.5,0.5 W0 5 D2 | qhull d FN Qc"
rbox P0.5,0.5 P0.5,0.5 W0 5 D2 | qhull d FN Qc
echo "rbox 10 D3 | qhull Fa PA5"
rbox 10 D3 | qhull Fa PA5
echo "rbox 10 D3 | qhull Fa PF0.4"
rbox 10 D3 | qhull Fa PF0.4

echo === test Qt ${d:-`date`}
echo "rbox c | qhull Qt s o Tcv"
rbox c | qhull Qt s o Tcv
echo "rbox c | qhull Qt f i"
rbox c | qhull Qt f i
echo "rbox c | qhull Qt m FM n"
rbox c | qhull Qt m FM n
echo "rbox c | qhull Qt p o"
rbox c | qhull Qt p o
echo "rbox c | qhull Qt Fx"
rbox c | qhull Qt Fx
echo "rbox c | qhull Qt FA s Fa"
rbox c | qhull Qt FA s Fa
echo "rbox 6 r s c G0.1 D2 | qhull Qt d FA Tcv"
rbox 6 r s c G0.1 D2 | qhull Qt d FA Tcv
echo "rbox 6 r s c G0.1 D2 | qhull d FA Tcv"
rbox 6 r s c G0.1 D2 | qhull d FA Tcv
echo "rbox 6 r s c G0.1 D2 | qhull Qt v p Tcv"
rbox 6 r s c G0.1 D2 | qhull Qt v p Tcv
echo "rbox c | qhull Qt C-0.1 Qc FF s FQ Fi n Fo FQ FI Fm Fn FN FO FO FQ Fs FS FV Fv Tcv"
rbox c | qhull Qt C-0.1 Qc FF s FQ Fi n Fo FQ FI Fm Fn FN FO FO FQ Fs FS FV Fv Tcv
echo "rbox 6 r s c G0.1 D2 P0.1,0.1 | qhull s FP d FO Qt"
rbox 6 r s c G0.1 D2 P0.1,0.1 | qhull s FP d FO Qt
echo "rbox 100 W0 | qhull Tv Q11 FO"
rbox 100 W0 | qhull Tv Q11 FO

echo === test unbounded intersection
echo "rbox c | qhull PD0:0.5 n | qhull H0 Fp Tcv"
rbox c | qhull PD0:0.5 n | qhull H0 Fp Tcv
echo "rbox 1000 W1e-3 D3 | qhull PA8 Fa FS s n Tcv"
rbox 1000 W1e-3 D3 | qhull PA8 Fa FS s n Tcv 
echo "rbox 1000 W1e-3 D3 | qhull C-0.01 PM10 Fm n Tcv Qc"
rbox 1000 W1e-3 D3 | qhull C-0.01 PM10 Fm n Tcv Qc
echo "rbox 1000 W1e-3 D3 | qhull C-0.01 PA8 PG n Tcv Qc"
rbox 1000 W1e-3 D3 | qhull C-0.01 PA8 PG n Tcv Qc
echo "rbox 10 | qhull FO Tz TO q_test.log.1"
rbox 10 | qhull FO Tz TO q_test.log.1
echo "type q_test.log.1"
type q_test.log.1

echo === check Delaunay/Voronoi ${d:-`date`}
echo "rbox 10 D2 | qhull d Tcv"
rbox 10 D2 | qhull d Tcv
echo "rbox 10 D2 | qhull d Qz Tcv"
rbox 10 D2 | qhull d Qz Tcv
echo "rbox 10 D3 | qhull d Tcv"
rbox 10 D3 | qhull d Tcv
echo "rbox c | qhull d Qz Ft Tcv"
rbox c | qhull d Qz Ft Tcv
echo "rbox 10 s D2 c | qhull d Tcv"
rbox 10 s D2 c | qhull d Tcv
echo "rbox 10 s D2 | qhull d Tcv Qz Q8"
rbox 10 s D2 | qhull d Tcv Qz Q8
echo "rbox 10 D2 | qhull d Tcv p"
rbox 10 D2 | qhull d Tcv p
echo "rbox 10 D2 | qhull d Tcv i"
rbox 10 D2 | qhull d Tcv i
echo "rbox 10 D2 | qhull d Tcv o"
rbox 10 D2 | qhull d Tcv o
echo "rbox 10 D2 | qhull v Tcv o"
rbox 10 D2 | qhull v Tcv o
echo "rbox 10 D2 | qhull v Tcv p"
rbox 10 D2 | qhull v Tcv p
echo "rbox 10 D2 | qhull v Tcv G"
rbox 10 D2 | qhull v Tcv G
echo "rbox 10 D2 | qhull v Tcv Fv"
rbox 10 D2 | qhull v Tcv Fv
echo "rbox 10 D2 | qhull v Tcv Fi"
rbox 10 D2 | qhull v Tcv Fi
echo "rbox 10 D2 | qhull v Tcv Fo"
rbox 10 D2 | qhull v Tcv Fo
echo "rbox 10 D2 | qhull v Qu o Fv Fi Fo Tcv"
rbox 10 D2 | qhull v Qu o Fv Fi Fo Tcv
echo "rbox 10 D3 | qhull v Fv Tcv"
rbox 10 D3 | qhull v Fv Tcv
echo "rbox 10 D3 | qhull v fi Tcv"
rbox 10 D3 | qhull v Fi Tcv
echo "rbox 10 D3 | qhull v Fo Tcv"
rbox 10 D3 | qhull v Fo Tcv
echo "rbox 10 D3 | qhull v Qu o Fv Fi Fo Tcv"
rbox 10 D3 | qhull v Qu o Fv Fi Fo Tcv
echo "rbox 5 D2 | qhull v f FnN o"
rbox 5 D2 | qhull v f FnN o

echo === check Halfspace, showing the input ${d:-`date`}
echo === the Qhull pipeline recreates 100 4-D cospherical points with the same area and volume
echo "rbox 100 s D4 | qhull FA FV s"
rbox 100 s D4 | qhull FA FV s
echo "rbox 100 s D4 | qhull FQ FA FV n | qhull s H"
rbox 100 s D4 | qhull FQ FA FV n s | qhull s H
echo "rbox 100 s D4 | qhull FQ FA FV n s Tcv | qhull FQ s H Fp Tcv | qhull FA Tcv"
rbox 100 s D4 | qhull FQ FA FV n s Tcv | qhull FQ s H Fp Tcv | qhull FA Tcv
echo === the Qhull pipeline recreates a 3-D tetrahedron
echo "rbox d D3 | qhull n FD"
rbox d D3 | qhull n FD
echo "rbox d D3 | qhull s n FD Tcv | qhull s Fd H0.1,0.1 Fp Tcv"
rbox d D3 | qhull s n FD Tcv | qhull s Fd H0.1,0.1 Fp Tcv
echo "rbox 5 r D2 | qhull s n Tcv"
echo === the Qhull pipeline recreates a regular 2-D pentagon
rbox 5 r D2 | qhull FQ n
echo "rbox 5 r D2 | qhull s FQ n Tcv | qhull s H0 Fp Tcv"
rbox 5 r D2 | qhull s FQ n Tcv | qhull s H0 Fp Tcv

echo === check qhull ${d:-`date`}
echo "rbox 10 s D3 | qhull Tcv"
rbox 10 s D3 | qhull Tcv
echo "rbox 10 s D3 | qhull f Pd0:0.5 Pd2 Pg Tcv"
rbox 10 s D3 | qhull f Pd0:0.5 Pd2 Pg Tcv
echo "rbox 10 s D3 | qhull f Tcv PD2:-0.5 Pg"
rbox 10 s D3 | qhull f Tcv PD2:-0.5 Pg
echo "rbox 10 s D3 | qhull QR-1"
rbox 10 s D3 | qhull QR-1
echo "rbox 10 s D3 | qhull QR-40"
rbox 10 s D3 | qhull QR-40
echo "rbox 1000 D3 | qhull Tcvs"
rbox 1000 D3 | qhull Tcvs
echo "Test tracing 'Tn', combine stderr/stdout 'Tz', flush fprintf 'Tf'"
echo "rbox 100 D3 | qhull T1 Tz Tf TA1 TO q_test.log.1"
rbox 100 D3 | qhull T1 Tz Tf TA1 TO q_test.log.1
echo "type q_test.log.1"
type q_test.log.1
echo "del q_test.log.1"
del q_test.log.1
echo "rbox 100 s D3 | qhull TcvA10"
rbox 100 s D3 | qhull TcvA10
echo "rbox 100 s D3 | qhull TcvV-2"
rbox 100 s D3 | qhull TcvV-2
echo "rbox 100 s D3 | qhull TcvC2"
rbox 100 s D3 | qhull TcvC2
echo "rbox 100 s D3 | qhull TcvV2"
rbox 100 s D3 | qhull TcvV2
echo "rbox 100 s D3 | qhull T1cvV2P2"
rbox 100 s D3 | qhull T1cvV2P2
echo "rbox 100 s D3 | qhull TcvF100"
rbox 100 s D3 | qhull TcvF100
echo "rbox 100 s D3 | qhull Qf Tcv"
rbox 100 s D3 | qhull Qf Tcv
echo "rbox 100 D3 | qhull Tcv"
rbox 100 D3 | qhull Tcv
echo "rbox 100 D3 | qhull Qs Tcv"
rbox 100 D3 | qhull Qs Tcv
echo "rbox 100 D5 | qhull Qs Tcv"
rbox 100 D5 | qhull Qs Tcv
echo "rbox 100 D3 | qhull Qr Tcv"
rbox 100 D3 | qhull Qr Tcv
echo "rbox 100 D3 | qhull Qxv Tcv"
rbox 100 D3 | qhull Qxv Tcv
echo "rbox 100 D3 | qhull Qi f Pd0 Pd1 Pd2 Pg Tcv"
rbox 100 D3 | qhull Qi f Pd0 Pd1 Pd2 Pg Tcv
echo "rbox c d | qhull Qc f Tcv"
rbox c d | qhull Qc f Tcv
echo "rbox c d | qhull Qc p Tcv"
rbox c d | qhull Qc p Tcv
echo "rbox 100 D3 | qhull QbB FO Tcv"
rbox 100 D3 | qhull QbB FO Tcv
echo "rbox 1000 D2 B1e6 | qhull d Qbb FO Tcv"
rbox 1000 D2 B1e6 | qhull d Qbb FO Tcv
echo "rbox 10 D3 | qhull QbB p Tcv"
rbox 10 D3 | qhull QbB p Tcv
echo "rbox 10 D3 | qhull Qbb p Tcv"
rbox 10 D3 | qhull Qbb p Tcv
echo "rbox 10 D3 | qhull Qb0:-10B2:20 p Tcv"
rbox 10 D3 | qhull Qb0:-10B2:20 p Tcv
echo "rbox 10 D3 | qhull Qb0:-10B2:20 p Tcv | qhull QbB p Tcv"
rbox 10 D3 | qhull Qb0:-10B2:20 p Tcv | qhull QbB p Tcv
echo "rbox 10 D3 | qhull Qb1:0B1:0 d Tcv Q8"
rbox 10 D3 | qhull Qb1:0B1:0 d Tcv Q8
echo "rbox 10 D3 | qhull Qb1:0B1:0B2:0 d Tcv Q8"
rbox 10 D3 | qhull Qb1:0B1:0B2:0 d Tcv Q8
echo "rbox 10 D3 | qhull Qb1:0 d Tcv"
rbox 10 D3 | qhull Qb1:0 d Tcv 
echo "rbox 10 D3 | qhull Qb1:0B1:0  Tcv"
rbox 10 D3 | qhull Qb1:0B1:0  Tcv
echo "== next command will error ${d:-`date`} =="
echo "rbox 10 D2 | qhull Qb1:1B1:1 Tcv"
rbox 10 D2 | qhull Qb1:1B1:1 Tcv
echo "rbox 200 L20 D2 t | qhull FO Tcv C-0"
rbox 200 L20 D2 t | qhull FO Tcv C-0
echo "rbox 1000 L20 t | qhull FO Tcv C-0"
rbox 1000 L20 t | qhull FO Tcv C-0
echo "rbox 200 L20 D4 t | qhull FO Tcv C-0"
rbox 200 L20 D4 t | qhull FO Tcv C-0
echo "rbox 200 L20 D5 t | qhull FO Tcv Qx"
rbox 200 L20 D5 t | qhull FO Tcv Qx
echo "rbox 1000 W1e-3 s D2 t | qhull d FO Tcv Qu Q0"
rbox 1000 W1e-3 s D2 t | qhull d FO Tcv Qu Q0
echo "rbox 1000 W1e-3 s D2 t | qhull d FO Tcv Qu C-0"
rbox 1000 W1e-3 s D2 t | qhull d FO Tcv Qu C-0

echo === check joggle and TRn ${d:-`date`}
echo "rbox 100 W0 | qhull QJ1e-14 Qc TR100 Tv"
rbox 100 W0 | qhull QJ1e-14 Qc TR100 Tv
echo "rbox 100 W0 | qhull QJ1e-13 Qc TR100 Tv"
rbox 100 W0 | qhull QJ1e-13 Qc TR100 Tv
echo "rbox 100 W0 | qhull QJ1e-12 Qc TR100 Tv"
rbox 100 W0 | qhull QJ1e-12 Qc TR100 Tv
echo "rbox 100 W0 | qhull QJ1e-11 Qc TR100 Tv"
rbox 100 W0 | qhull QJ1e-11 Qc TR100 Tv
echo "rbox 100 W0 | qhull QJ1e-10 Qc TR100 Tv"
rbox 100 W0 | qhull QJ1e-10 Qc TR100 Tv
echo "rbox 100 | qhull d QJ Qb0:1e4 QB0:1e5 Qb1:1e4 QB1:1e6 Qb2:1e5 QB2:1e7 FO Tv"
rbox 100 | qhull d QJ Qb0:1e4 QB0:1e5 Qb1:1e4 QB1:1e6 Qb2:1e5 QB2:1e7 FO Tv

echo === check precision options ${d:-`date`}
echo "rbox 100 D3 s | qhull E0.01 Qx Tcv FO"
rbox 100 D3 s | qhull E0.01 Qx Tcv FO
echo "rbox 100 D3 W1e-1 | qhull W1e-3 Tcv"
rbox 100 D3 W1e-1 | qhull W1e-3 Tcv
echo "rbox 100 D3 W1e-1 | qhull W1e-2 Tcv Q0"
rbox 100 D3 W1e-1 | qhull W1e-2 Tcv Q0
echo "rbox 100 D3 W1e-1 | qhull W1e-2 Tcv"
rbox 100 D3 W1e-1 | qhull W1e-2 Tcv
echo "rbox 100 D3 W1e-1 | qhull W1e-1 Tcv"
rbox 100 D3 W1e-1 | qhull W1e-1 Tcv
echo "rbox 15 D2 P0 P1e-14,1e-14 | qhull d Quc Tcv"
rbox 15 D2 P0 P1e-14,1e-14 | qhull d Quc Tcv
echo "rbox 15 D3 P0 P1e-12,1e-14,1e-14 | qhull d Qcu Tcv"
rbox 15 D3 P0 P1e-12,1e-14,1e-14 | qhull d Qcu Tcv
echo "rbox 1000 s D3 | qhull C-0.01 Tcv Qc"
rbox 1000 s D3 | qhull C-0.01 Tcv Qc
echo "rbox 1000 s D3 | qhull C-0.01 V0 Qc Tcv"
rbox 1000 s D3 | qhull C-0.01 V0 Qc Tcv
echo "rbox 1000 s D3 | qhull C-0.01 U0 Qc Tcv"
rbox 1000 s D3 | qhull C-0.01 U0 Qc Tcv
echo "rbox 1000 s D3 | qhull C-0.01 V0 Qcm Tcv"
rbox 1000 s D3 | qhull C-0.01 V0 Qcm Tcv
echo "rbox 1000 s D3 | qhull C-0.01 Qcm Tcv"
rbox 1000 s D3 | qhull C-0.01 Qcm Tcv
echo "rbox 1000 s D3 | qhull C-0.01 Q1 FO Tcv Qc"
rbox 1000 s D3 | qhull C-0.01 Q1 FO Tcv Qc
echo "rbox 1000 s D3 | qhull C-0.01 Q2 FO Tcv Qc"
rbox 1000 s D3 | qhull C-0.01 Q2 FO Tcv Qc
echo "rbox 1000 s D3 | qhull C-0.01 Q3 FO Tcv Qc"
rbox 1000 s D3 | qhull C-0.01 Q3 FO Tcv Qc
echo "rbox 1000 s D3 | qhull C-0.01 Q4 FO Tcv Qc"
rbox 1000 s D3 | qhull C-0.01 Q4 FO Tcv Qc
echo === this may generate an error ${d:-`date`}
echo "rbox 1000 s D3 | qhull C-0.01 Q5 FO Tcv"
rbox 1000 s D3 | qhull C-0.01 Q5 FO Tcv 
echo === this should generate an error ${d:-`date`}
echo "rbox 1000 s D3 | qhull C-0.01 Q6 FO Po Tcv Qc"
rbox 1000 s D3 | qhull C-0.01 Q6 FO Po Tcv Qc
echo "rbox 1000 s D3 | qhull C-0.01 Q7 FO Tcv Qc"
rbox 1000 s D3 | qhull C-0.01 Q7 FO Tcv Qc
echo "rbox 1000 s D3 | qhull C-0.01 Qx Tcv Qc"
rbox 1000 s D3 | qhull C-0.01 Qx Tcv Qc
echo "=== this may generate an error e.g., t1263080158 ${d:-`date`}"
echo "rbox 100 s D3 t | qhull R1e-3 Tcv Qc"
rbox 100 s D3 t | qhull R1e-3 Tcv Qc
echo "rbox 100 s D3 t | qhull R1e-2 Tcv Qc"
rbox 100 s D3 t | qhull R1e-2 Tcv Qc
echo "rbox 500 s D3 t | qhull R0.05 A-1 Tcv Qc"
rbox 500 s D3 t | qhull R0.05 A-1 Tcv Qc
echo "rbox 100 W0 D3 t | qhull R1e-3 Tcv Qc"
rbox 100 W0 D3 t | qhull R1e-3 Tcv Qc
echo "rbox 100 W0 D3 t | qhull R1e-3 Qx Tcv Qc"
rbox 100 W0 D3 t | qhull R1e-3 Qx Tcv Qc
echo "rbox 100 W0 D3 t | qhull R1e-2 Tcv Qc"
rbox 100 W0 D3 t | qhull R1e-2 Tcv Qc
echo "rbox 100 W0 D3 t | qhull R1e-2 Qx Tcv Qc"
rbox 100 W0 D3 t | qhull R1e-2 Qx Tcv Qc
echo "rbox 500 W0 D3 t | qhull R0.05 A-1 Tcv Qc"
rbox 500 W0 D3 t | qhull R0.05 A-1 Tcv Qc
echo "rbox 500 W0 D3 t | qhull R0.05 Qx Tcv Qc"
rbox 500 W0 D3 t | qhull R0.05 Qx Tcv Qc
echo "rbox 1000 W1e-20 t | qhull Tcv Qc"
rbox 1000 W1e-20 t | qhull Tcv Qc
echo "rbox 1000 W1e-20 D4 t | qhull Tcv Qc"
rbox 1000 W1e-20 D4 t | qhull Tcv Qc
echo "rbox 500 W1e-20 D5 t | qhull Tv Qc"
rbox 500 W1e-20 D5 t | qhull Tv Qc
echo "rbox 100 W1e-20 D6 t | qhull Tv Qc"
rbox 100 W1e-20 D6 t | qhull Tv Qc
echo "rbox 50 W1e-20 D6 t | qhull Qv Tv Qc"
rbox 50 W1e-20 D6 t | qhull Qv Tv Qc
echo "rbox 10000 D4 t | qhull QR0 Qc C-0.01 A0.3 Tv"
rbox 10000 D4 t | qhull QR0 Qc C-0.01 A0.3 Tv
echo "rbox 1000 D2 t | qhull d QR0 Qc C-1e-8 Qu Tv"
rbox 1000 D2 t | qhull d QR0 Qc C-1e-8 Qu Tv
echo "rbox 300 D5 t |qhull A-0.999 Qx Qc Tcv"
rbox 300 D5 t |qhull A-0.999 Qx Qc Tcv
echo "rbox 100 D6 t |qhull A-0.9999 Qx Qc Tcv"
rbox 100 D6 t |qhull A-0.9999 Qx Qc Tcv
echo "rbox 50 D7 t |qhull A-0.99999 Qx Qc Tcv W0.1"
rbox 50 D7 t |qhull A-0.99999 Qx Qc Tcv W0.1

echo === check bad cases for Qhull.  May cause errors ${d:-`date`}
echo "rbox 1000 L100000 s G1e-6 t | qhull Tv"
rbox 1000 L100000 s G1e-6 t | qhull Tv
echo "rbox 1000 L100000 s G1e-6 t | qhull Tv Q10"
rbox 1000 L100000 s G1e-6 t | qhull Tv Q10
echo "rbox 1000 s Z1 G1e-13 t | qhull Tv"
rbox 1000 s Z1 G1e-13 t | qhull Tv
echo "rbox 1000 s W1e-13 P0 t | qhull d Qbb Qc Q12 Tv"
rbox 1000 s W1e-13 P0 t | qhull d Qbb Qc Q12 Tv
echo "rbox 1000 s W1e-13 t | qhull d Q12 Tv"
rbox 1000 s W1e-13 t | qhull d Q12 Tv
echo "rbox 1000 s W1e-13 t D2 | qhull d Tv"
rbox 1000 s W1e-13 t D2 | qhull d Tv

echo =======================================================
echo =======================================================
echo === The following commands may cause errors ===========
echo =======================================================
echo =======================================================
echo "rbox c D7 | qhull Q0 Tcv"
rbox c D7 | qhull Q0 Tcv
echo "rbox 100 s D3 | qhull Q0 E1e-3 Tc Po"
rbox 100 s D3 | qhull Q0 E1e-3 Tc Po
echo "rbox 100 s D3 | qhull Q0 E1e-2 Tc Po"
rbox 100 s D3 | qhull Q0 E1e-2 Tc Po
echo "rbox 100 s D3 | qhull Q0 E1e-1 Tc Po"
rbox 100 s D3 | qhull Q0 E1e-1 Tc Po
echo "rbox 100 s D3 | qhull Q0 R1e-3 Tc Po"
rbox 100 s D3 | qhull Q0 R1e-3 Tc Po
echo "rbox 100 s D3 | qhull Q0 R1e-2 Tc Po"
rbox 100 s D3 | qhull Q0 R1e-2 Tc Po
echo "rbox 100 s D3 | qhull Q0 R0.05 Tc"
rbox 100 s D3 | qhull Q0 R0.05 Tc
echo "rbox 100 s D3 | qhull Q0 R0.05 Tc Po"
rbox 100 s D3 | qhull Q0 R0.05 Tc Po
echo "rbox 1000 W1e-7 | qhull Q0 Tc Po"
rbox 1000 W1e-7 | qhull Q0 Tc Po
echo "rbox 50 s | qhull Q0 V0.05 W0.01 Tc Po"
rbox 50 s | qhull Q0 V0.05 W0.01 Tc Po
echo "rbox 100 s D5 | qhull Q0 R1e-2 Tc Po"
rbox 100 s D5 | qhull Q0 R1e-2 Tc Po
echo "rbox L100 10000 D4 s C1,1e-13 t2 | qhull"
rbox L100 10000 D4 s C1,1e-13 t2 | qhull
echo "rbox L100 10000 D4 s C1,1e-13 t2 | qhull Q12"
rbox L100 10000 D4 s C1,1e-13 t2 | qhull Q12
echo "rbox 50 C1,1E-13 t1447644703 | qhull d"
rbox 50 C1,1E-13 t1447644703 | qhull d
echo "rbox 50 C1,1E-13 t1447644703 | qhull d Q12"
rbox 50 C1,1E-13 t1447644703 | qhull d Q12
echo "rbox 50 C1,1E-13 t1447644703 | qhull d Q14"
rbox 50 C1,1E-13 t1447644703 | qhull d Q14

echo "======================================================="
echo "=== Testing done.  Print documentation"
echo "======================================================="
echo "qhull -"
qhull -
echo "rbox"
rbox

echo "# end of q_test"


<|start_filename|>3rd-party/qhull/src/qhulltest/PointCoordinates_test.cpp<|end_filename|>
/****************************************************************************
**
** Copyright (c) 2009-2020 <NAME>. All rights reserved.
** $Id: //main/2019/qhull/src/qhulltest/PointCoordinates_test.cpp#5 $$Change: 3006 $
** $DateTime: 2020/07/29 18:28:16 $$Author: bbarber $
**
****************************************************************************/

//pre-compiled headers
#include <iostream>
#include "qhulltest/RoadTest.h" // QT_VERSION

#include "libqhullcpp/PointCoordinates.h"
#include "libqhullcpp/QhullError.h"
#include "libqhullcpp/RboxPoints.h"
#include "libqhullcpp/Qhull.h"

using std::cout;
using std::endl;
using std::ostringstream;
using std::ostream;
using std::string;
using std::stringstream;

namespace orgQhull {

class PointCoordinates_test : public RoadTest
{
    Q_OBJECT

#//!\name Test slots
private slots:
    void t_construct_q();
    void t_construct_qh();
    void t_convert();
    void t_getset();
    void t_element();
    void t_foreach();
    void t_search();
    void t_modify();
    void t_append_points();
    void t_io();
};//PointCoordinates_test

void
add_PointCoordinates_test()
{
    new PointCoordinates_test();  // RoadTest::s_testcases
}

void PointCoordinates_test::
t_construct_q()
{
    Qhull q;
    PointCoordinates pc(q);
    QCOMPARE(pc.size(), 0U);
    QCOMPARE(pc.coordinateCount(), 0);
    QCOMPARE(pc.dimension(), 0);
    QCOMPARE(pc.coordinates(), (coordT *)0);
    QVERIFY(pc.isEmpty());
    pc.checkValid();
    PointCoordinates pc7(q, 2, "test explicit dimension");
    QCOMPARE(pc7.dimension(), 2);
    QCOMPARE(pc7.count(), 0);
    QVERIFY(pc7.isEmpty());
    QCOMPARE(pc7.comment(), std::string("test explicit dimension"));
    pc7.checkValid();
    PointCoordinates pc2(q, "Test pc2");
    QCOMPARE(pc2.count(), 0);
    QVERIFY(pc2.isEmpty());
    QCOMPARE(pc2.comment(), std::string("Test pc2"));
    pc2.checkValid();
    PointCoordinates pc3(q, 3, "Test 3-d pc3");
    QCOMPARE(pc3.dimension(), 3);
    QVERIFY(pc3.isEmpty());
    pc3.checkValid();
    coordT c[]= { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 };
    PointCoordinates pc4(q, 2, "Test 2-d pc4", 6, c);
    QCOMPARE(pc4.dimension(), 2);
    QCOMPARE(pc4.count(), 3);
    QCOMPARE(pc4.size(), 3u);
    QVERIFY(!pc4.isEmpty());
    pc4.checkValid();
    QhullPoint p= pc4[2];
    QCOMPARE(p[1], 5.0);
    // QhullPoint refers to PointCoordinates
    p[1] += 1.0;
    QCOMPARE(pc4[2][1], 6.0);
    PointCoordinates pc5(q, 4, "Test 4-d pc5 with insufficient coordinates", 6, c);
    QCOMPARE(pc5.dimension(), 4);
    QCOMPARE(pc5.count(), 1);
    QCOMPARE(pc5.extraCoordinatesCount(), 2);
    QCOMPARE(pc5.extraCoordinates()[1], 5.0);
    QVERIFY(!pc5.isEmpty());;
    std::vector<coordT> vc;
    vc.push_back(3.0);
    vc.push_back(4.0);
    vc.push_back(5.0);
    vc.push_back(6.0);
    vc.push_back(7.0);
    vc.push_back(9.0);
    pc5.append(2, &vc[3]); // Copy of vc[]
    pc5.checkValid();
    QhullPoint p5(q, 4, &vc[1]);
    QCOMPARE(pc5[1], p5);
    PointCoordinates pc6(pc5); // Makes copy of point_coordinates
    QCOMPARE(pc6[1], p5);
    QVERIFY(pc6==pc5);
    QhullPoint p6= pc5[1];  // Refers to pc5.coordinates
    pc5[1][0] += 1.0;
    QCOMPARE(pc5[1], p6);
    QVERIFY(pc5[1]!=p5);
    QVERIFY(pc6!=pc5);
    pc6= pc5;
    QVERIFY(pc6==pc5);
    PointCoordinates pc8(q);
    pc6= pc8;
    QVERIFY(pc6!=pc5);
    QVERIFY(pc6.isEmpty());
}//t_construct_q

void PointCoordinates_test::
t_construct_qh()
{
    QhullQh qh;
    PointCoordinates pc(&qh);
    QCOMPARE(pc.size(), 0U);
    QCOMPARE(pc.coordinateCount(), 0);
    QCOMPARE(pc.dimension(), 0);
    QCOMPARE(pc.coordinates(), (coordT *)0);
    QVERIFY(pc.isEmpty());
    pc.checkValid();
    PointCoordinates pc7(&qh, 2, "test explicit dimension");
    QCOMPARE(pc7.dimension(), 2);
    QCOMPARE(pc7.count(), 0);
    QVERIFY(pc7.isEmpty());
    QCOMPARE(pc7.comment(), std::string("test explicit dimension"));
    pc7.checkValid();
    PointCoordinates pc2(&qh, "Test pc2");
    QCOMPARE(pc2.count(), 0);
    QVERIFY(pc2.isEmpty());
    QCOMPARE(pc2.comment(), std::string("Test pc2"));
    pc2.checkValid();
    PointCoordinates pc3(&qh, 3, "Test 3-d pc3");
    QCOMPARE(pc3.dimension(), 3);
    QVERIFY(pc3.isEmpty());
    pc3.checkValid();
    coordT c[]= { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 };
    PointCoordinates pc4(&qh, 2, "Test 2-d pc4", 6, c);
    QCOMPARE(pc4.dimension(), 2);
    QCOMPARE(pc4.count(), 3);
    QCOMPARE(pc4.size(), 3u);
    QVERIFY(!pc4.isEmpty());
    pc4.checkValid();
    QhullPoint p= pc4[2];
    QCOMPARE(p[1], 5.0);
    // QhullPoint refers to PointCoordinates
    p[1] += 1.0;
    QCOMPARE(pc4[2][1], 6.0);
    PointCoordinates pc5(&qh, 4, "Test 4-d pc5 with insufficient coordinates", 6, c);
    QCOMPARE(pc5.dimension(), 4);
    QCOMPARE(pc5.count(), 1);
    QCOMPARE(pc5.extraCoordinatesCount(), 2);
    QCOMPARE(pc5.extraCoordinates()[1], 5.0);
    QVERIFY(!pc5.isEmpty());;
    std::vector<coordT> vc;
    vc.push_back(3.0);
    vc.push_back(4.0);
    vc.push_back(5.0);
    vc.push_back(6.0);
    vc.push_back(7.0);
    vc.push_back(9.0);
    pc5.append(2, &vc[3]); // Copy of vc[]
    pc5.checkValid();
    QhullPoint p5(&qh, 4, &vc[1]);
    QCOMPARE(pc5[1], p5);
    PointCoordinates pc6(pc5); // Makes copy of point_coordinates
    QCOMPARE(pc6[1], p5);
    QVERIFY(pc6==pc5);
    QhullPoint p6= pc5[1];  // Refers to pc5.coordinates
    pc5[1][0] += 1.0;
    QCOMPARE(pc5[1], p6);
    QVERIFY(pc5[1]!=p5);
    QVERIFY(pc6!=pc5);
    pc6= pc5;
    QVERIFY(pc6==pc5);
    PointCoordinates pc8(&qh);
    pc6= pc8;
    QVERIFY(pc6!=pc5);
    QVERIFY(pc6.isEmpty());
}//t_construct_qh

void PointCoordinates_test::
t_convert()
{
    Qhull q;
    //defineAs tested above
    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    PointCoordinates ps(q, 3, "two 3-d points", 6, c);
    QCOMPARE(ps.dimension(), 3);
    QCOMPARE(ps.size(), 2u);
    const coordT *c2= ps.constData();
    QVERIFY(c!=c2);
    QCOMPARE(c[0], c2[0]);
    const coordT *c3= ps.data();
    QCOMPARE(c3, c2);
    coordT *c4= ps.data();
    QCOMPARE(c4, c2);
    std::vector<coordT> vs= ps.toStdVector();
    QCOMPARE(vs.size(), 6u);
    QCOMPARE(vs[5], 5.0);
    QList<coordT> qs= ps.toQList();
    QCOMPARE(qs.size(), 6);
    QCOMPARE(qs[5], 5.0);
}//t_convert

void PointCoordinates_test::
t_getset()
{
    // See t_construct() for test of coordinates, coordinateCount, dimension, empty, isEmpty, ==, !=
    // See t_construct() for test of checkValid, comment, setDimension
    Qhull q;
    PointCoordinates pc(q, "Coordinates c");
    pc.setComment("New comment");
    QCOMPARE(pc.comment(), std::string("New comment"));
    pc.checkValid();
    pc.makeValid();  // A no-op
    pc.checkValid();
    Coordinates cs= pc.getCoordinates();
    QVERIFY(cs.isEmpty());
    PointCoordinates pc2(pc);
    pc.setDimension(3);
    QVERIFY(pc2!=pc);
    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    pc.append(6, c);
    pc.checkValid();
    pc.makeValid();  // A no-op
    QhullPoint p= pc[0];
    QCOMPARE(p[2], 2.0);
    try{
        pc.setDimension(2);
        QFAIL("setDimension(2) did not fail for 3-d.");
    }catch (const std::exception &e) {
        const char *s= e.what();
        cout << "INFO   : Caught " << s;
    }
}//t_getset

void PointCoordinates_test::
t_element()
{
    Qhull q;
    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    PointCoordinates pc(q, 2, "2-d points", 6, c);
    QhullPoint p= pc.at(0);
    QCOMPARE(p, pc[0]);
    QCOMPARE(p, pc.first());
    QCOMPARE(p, pc.value(0));
    p= pc.back();
    QCOMPARE(p, pc[2]);
    QCOMPARE(p, pc.last());
    QCOMPARE(p, pc.value(2));
    QhullPoints ps= pc.mid(1, 2);
    QCOMPARE(ps[1], p);
}//t_element

void PointCoordinates_test::
t_foreach()
{
    Qhull q;
    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    PointCoordinates pc(q, 2, "2-d points", 6, c);
    QhullPoints::Iterator i= pc.begin();
    QhullPoint p= pc[0];
    QCOMPARE(*i, p);
    QCOMPARE((*i)[0], 0.0);
    QhullPoint p3= pc[2];
    i= pc.end();
    QCOMPARE(i[-1], p3);
    const PointCoordinates pc2(q, 2, "2-d points", 6, c);
    QhullPoints::ConstIterator i2= pc.begin();
    const QhullPoint p0= pc2[0];
    QCOMPARE(*i2, p0);
    QCOMPARE((*i2)[0], 0.0);
    QhullPoints::ConstIterator i3= i2;
    QCOMPARE(i3, i2);
    QCOMPARE((*i3)[0], 0.0);
    i3= pc.constEnd();
    --i3;
    QhullPoint p2= pc2[2];
    QCOMPARE(*i3, p2);
    i= pc.end();
    QVERIFY(i-1==i3);
    i2= pc2.end();
    QVERIFY(i2-1!=i3);
    QCOMPARE(*(i2-1), *i3);
    Coordinates::ConstIterator i4= pc.beginCoordinates();
    QCOMPARE(*i4, 0.0);
    Coordinates::Iterator i5= pc.beginCoordinates();
    QCOMPARE(*i5, 0.0);
    i4= pc.beginCoordinates(1);
    QCOMPARE(*i4, 2.0);
    i5= pc.beginCoordinates(1);
    QCOMPARE(*i5, 2.0);
    i4= pc.endCoordinates();
    QCOMPARE(*--i4, 5.0);
    i5= pc.endCoordinates();
    QCOMPARE(*--i5, 5.0);

    // Qt's 'foreach' makes a copy of std::point_coordinates.  It should not be used.
    bool isP3= false;
    int count= false;
    isP3= false;
    count= false;
    for(QhullPoint pt : pc){
        ++count;
        QVERIFY(pt[0] >= 0.0);
        QVERIFY(pt[0] <= 5.0);
        if(pt==p3){
            isP3= true;
            QCOMPARE(count, 3);
        }
    }
    QVERIFY(isP3);
    QCOMPARE(count, pc.count());
}//t_foreach

void PointCoordinates_test::
t_search()
{
    Qhull q;
    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    PointCoordinates pc(q, 2, "2-d points", 6, c);
    QhullPoint p0= pc[0];
    QhullPoint p2= pc[2];
    QVERIFY(pc.contains(p0));
    QVERIFY(pc.contains(p2));
    QCOMPARE(pc.count(p0), 1);
    QCOMPARE(pc.indexOf(p2), 2);
    QCOMPARE(pc.lastIndexOf(p0), 0);
}//t_search

void PointCoordinates_test::
t_modify()
{
    Qhull q;
    coordT c[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    PointCoordinates pc(q, 2, "2-d points", 6, c);
    coordT c3[]= {0.0, 1.0, 2.0, 3.0, 4.0, 5.0};
    PointCoordinates pc5(q, 2, "test explicit dimension");
    pc5.append(6, c3); // 0-5
    QVERIFY(pc5==pc);
    PointCoordinates pc2(q, 2, "2-d");
    coordT c2[]= {6.0, 7.0, 8.0, 9.0, 10.0, 11.0};
    pc2.append(6, c2);
    QCOMPARE(pc2.count(), 3);
    pc2.append(14.0);
    QCOMPARE(pc2.count(), 3);
    QCOMPARE(pc2.extraCoordinatesCount(), 1);
    pc2.append(15.0); // 6-11, 14,15
    QCOMPARE(pc2.count(), 4);
    QCOMPARE(pc2.extraCoordinatesCount(), 0);
    QhullPoint p(pc[0]);
    pc2.append(p); // 6-11, 14,15, 0,1
    QCOMPARE(pc2.count(), 5);
    QCOMPARE(pc2.extraCoordinatesCount(), 0);
    QCOMPARE(pc2.lastIndexOf(p), 4);
    pc.append(pc2); // Invalidates p
    QCOMPARE(pc.count(), 8); // 0-11, 14,15, 0,1
    QCOMPARE(pc.extraCoordinatesCount(), 0);
    QCOMPARE(pc.lastIndexOf(pc[0]), 7);
    pc.appendComment(" operators");
    QCOMPARE(pc.comment(), std::string("2-d points operators"));
    pc.checkValid();
    // see t_append_points for appendPoints
    PointCoordinates pc3= pc+pc2;
    pc3.checkValid();
    QCOMPARE(pc3.count(), 13);
    QCOMPARE(pc3[6][0], 14.0);
    QCOMPARE(pc3[8][0], 6.0);
    pc3 += pc;
    QCOMPARE(pc3.count(), 21);
    QCOMPARE(pc3[14][0], 2.0);
    pc3 += 12.0;
    pc3 += 14.0;
    QCOMPARE(pc3.count(), 22);
    QCOMPARE(pc3.last()[0], 12.0);
    // QhullPoint p3= pc3.first(); // += throws error because append may move the data
    QhullPoint p3= pc2.first();
    pc3 += p3;
    QCOMPARE(pc3.count(), 23);
    QCOMPARE(pc3.last()[0], 6.0);
    pc3 << pc;
    QCOMPARE(pc3.count(), 31);
    QCOMPARE(pc3.last()[0], 0.0);
    pc3 << 12.0 << 14.0;
    QCOMPARE(pc3.count(), 32);
    QCOMPARE(pc3.last()[0], 12.0);
    PointCoordinates pc4(pc3);
    pc4.reserveCoordinates(100);
    QVERIFY(pc3==pc4);
}//t_modify

void PointCoordinates_test::
t_append_points()
{
    Qhull q;
    PointCoordinates pc(q, 2, "stringstream");
    stringstream s("2 3 1 2 3 4 5 6");
    pc.appendPoints(s);
    QCOMPARE(pc.count(), 3);
}//t_append_points

void PointCoordinates_test::
t_io()
{
    Qhull q;
    PointCoordinates c(q);
    ostringstream os;
    os << "PointCoordinates 0-d\n" << c;
    c.setDimension(2);
    c << 1.0 << 2.0 << 3.0 << 1.0 << 2.0 << 3.0;
    os << "PointCoordinates 1,2 3,1 2,3\n" << c;
    cout << os.str();
    QString s= QString::fromStdString(os.str());
    QCOMPARE(s.count("0"), 3);
    QCOMPARE(s.count("2"), 5);
}//t_io

}//orgQhull

#include "moc/PointCoordinates_test.moc"


<|start_filename|>3rd-party/qhull/html/qhull.htm<|end_filename|>
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>

<head>
<title>qhull -- convex hull and related structures</title>
</head>

<body>
<!-- Navigation links -->
<p><b><a name="TOP">Up</a></b><b>:</b> <a href="http://www.qhull.org">Home page</a> for Qhull (<a href="../index.htm">local</a>)<br>
<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: contents<br>
<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="http://www.qhull.org/src/libqhull_r/index.htm">Functions</a> (<a href="../src/libqhull_r/index.htm">local</a>)<br>
<b>To:</b> <a href="#synopsis">sy</a>nopsis &#149; <a href="#input">in</a>put
&#149; <a href="#outputs">ou</a>tputs &#149; <a href="#controls">co</a>ntrols
&#149; <a href="#options">op</a>tions
<hr>
<!-- Main text of document -->
<h1><a
href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/cone.html"><img
src="qh--cone.gif" alt="[cone]" align="middle" width="100"
height="100"></a>qhull -- convex hull and related structures</h1>

<p>The convex hull of a set of points is the smallest convex set
containing the points.  The Delaunay triangulation and furthest-site
Delaunay triangulation are equivalent to a convex hull in one
higher dimension.  Halfspace intersection about a point is
equivalent to a convex hull by polar duality.

<p>The <tt>qhull</tt> program provides options to build these
structures and to experiment with the process.  Use the
<a href=qconvex.htm>qconvex</a>,
<a href=qdelaun.htm>qdelaunay</a>, <a href=qhalf.htm>qhalf</a>,
and <a href=qvoronoi.htm>qvoronoi</a> programs
to build specific structures.  You may use <tt>qhull</tt> instead.
It takes the same options and uses the same code.
<blockquote>
<dl>
    <dt><b>Example:</b> rbox 1000 D3 | qhull
         <a href="qh-optc.htm#Cn">C-1e-4</a>
         <a href="qh-optf.htm#FO">FO</a>
         <a href="qh-optt.htm#Ts">Ts</a>
         </dt>
    <dd>Compute the 3-d convex hull of 1000 random
             points.
                 Centrums must be 10^-4 below neighboring
                 hyperplanes.  Print the options and precision constants.
                 When done, print statistics.  These options may be
                 used with any of the Qhull programs.</dd>
    <dt>&nbsp;</dt>
    <dt><b>Example:</b> rbox 1000 D3 | qhull <a href=qhull.htm#outputs>d</a>
         <a href="qh-optq.htm#Qbb">Qbb</a>
         <a href="qh-optc.htm#Rn">R1e-4</a>
         <a href="qh-optq.htm#Q0">Q0</a></dt>
    <dd>Compute the 3-d Delaunay triangulation of 1000 random
             points.  Randomly perturb all calculations by
                 [0.9999,1.0001].  Do not correct precision problems.
                 This leads to serious precision errors.</dd>
</dl>
</blockquote>
<p>Use the following equivalences when calling <tt>qhull</tt>:
<blockquote>
<ul>
<li>
<a href="qconvex.htm">qconvex</a> == qhull
<li>
<a href=qdelaun.htm>qdelaunay</a> == qhull d <a href="qh-optq.htm#Qbb">Qbb</a>
<li>
<a href=qhalf.htm>qhalf</a> == qhull H
<li>
<a href=qvoronoi.htm>qvoronoi</a> == qhull v <a href="qh-optq.htm#Qbb">Qbb</a>
</ul>
</blockquote>

<p>By default, Qhull merges coplanar facets.  For example, the convex
hull of a cube's vertices has six facets.

<p>If you use '<a href="qh-optq.htm#Qt">Qt</a>' (triangulated output),
all facets will be simplicial (e.g., triangles in 2-d).  For the cube
example, it will have 12 facets.  Some facets may be
degenerate and have zero area.

<p>If you use '<a href="qh-optq.htm#QJn">QJ</a>' (joggled input),
all facets will be simplicial.  The corresponding vertices will be
slightly perturbed.  Joggled input is less accurate that triangulated
output.See <a
href="qh-impre.htm#joggle">Merged facets or joggled input</a>. </p>

<p>The output for 4-d convex hulls may be confusing if the convex
hull contains non-simplicial facets (e.g., a hypercube). See
<a href=qh-faq.htm#extra>Why
are there extra points in a 4-d or higher convex hull?</a><br>
</p>

<p><b>Copyright &copy; 1995-2020 <NAME></b></p>

<hr>

<h3><a href="#TOP">&#187;</a><a name="synopsis">qhull synopsis</a></h3>
<pre>
qhull -- compute convex hulls and related structures.
    input (stdin): dimension, number of points, point coordinates
    comments start with a non-numeric character
    halfspace: use dim+1 and put offsets after coefficients

options:
    d    - Delaunay triangulation by lifting points to a paraboloid
    d Qu - furthest-site Delaunay triangulation (upper convex hull)
    v    - Voronoi diagram as the dual of the Delaunay triangulation
    v Qu - furthest-site Voronoi diagram
    H1,1 - Halfspace intersection about [1,1,0,...] via polar duality
    Qt   - triangulated output
    QJ   - joggled input instead of merged facets
    Tv   - verify result: structure, convexity, and point inclusion
    .    - concise list of all options
    -    - one-line description of each option
    -?   - this message
    -V   - version

Output options (subset):
    s    - summary of results (default)
    i    - vertices incident to each facet
    n    - normals with offsets
    p    - vertex coordinates (if 'Qc', includes coplanar points)
           if 'v', Voronoi vertices
    FA   - report total area and volume
    Fp   - halfspace intersections
    FS   - total area and volume
    Fx   - extreme points (convex hull vertices)
    G    - Geomview output (2-d, 3-d and 4-d)
    m    - Mathematica output (2-d and 3-d)
    o    - OFF format (if 'v', outputs Voronoi regions)
    QVn  - print facets that include point n, -n if not
    TI file - input file, may be enclosed in single quotes
    TO file - output file, may be enclosed in single quotes

examples:
    rbox D4 | qhull Tv                        rbox 1000 s | qhull Tv s FA
    rbox 10 D2 | qhull d QJ s i TO result     rbox 10 D2 | qhull v Qbb Qt p
    rbox 10 D2 | qhull d Qu QJ m              rbox 10 D2 | qhull v Qu QJ o
    rbox c d D2 | qhull Qc s f Fx | more      rbox c | qhull FV n | qhull H Fp
    rbox d D12 | qhull QR0 FA                 rbox c D7 | qhull FA TF1000
    rbox y 1000 W0 | qhull Qc                 rbox c | qhull n
</pre>

<h3><a href="#TOP">&#187;</a><a name="input">qhull input</a></h3>
<blockquote>

<p>The input data on <tt>stdin</tt> consists of:</p>
<ul>
    <li>dimension
    <li>number of points</li>
    <li>point coordinates</li>
</ul>

<p>Use I/O redirection (e.g., qhull &lt; data.txt), a pipe (e.g., rbox 10 | qhull),
or the '<a href="qh-optt.htm#TI">TI</a>' option (e.g., qhull TI data.txt).

<p>Comments start with a non-numeric character.  Error reporting is
simpler if there is one point per line.  Dimension
and number of points may be reversed.  For halfspace intersection,
an interior point may be prepended (see <a href=qhalf.htm#input>qhalf input</a>).

<p>Here is the input for computing the convex
hull of the unit cube.  The output is the normals, one
per facet.</p>

<blockquote>
    <p>rbox c &gt; data </p>
    <pre>
3 RBOX c
8
  -0.5   -0.5   -0.5
  -0.5   -0.5    0.5
  -0.5    0.5   -0.5
  -0.5    0.5    0.5
   0.5   -0.5   -0.5
   0.5   -0.5    0.5
   0.5    0.5   -0.5
   0.5    0.5    0.5
</pre>
    <p>qhull s n &lt; data</p>
    <pre>

Convex hull of 8 points in 3-d:

  Number of vertices: 8
  Number of facets: 6
  Number of non-simplicial facets: 6

Statistics for: RBOX c | QHULL s n

  Number of points processed: 8
  Number of hyperplanes created: 11
  Number of distance tests for qhull: 35
  Number of merged facets: 6
  Number of distance tests for merging: 84
  CPU seconds to compute hull (after input): 0.081

4
6
     0      0     -1   -0.5
     0     -1      0   -0.5
     1      0      0   -0.5
    -1      0      0   -0.5
     0      1      0   -0.5
     0      0      1   -0.5
</pre>
</blockquote>

</blockquote>
<h3><a href="#TOP">&#187;</a><a name="outputs">qhull outputs</a></h3>
<blockquote>

<p>These options control the output of qhull. They may be used
individually or together.</p>
<blockquote>
<dl compact>
    <dt>&nbsp;</dt>
    <dd><b>General</b></dd>
    <dt><a>qhull</a></dt>
    <dd>compute the convex hull of the input points.
        See <a href=qconvex.htm>qconvex</a>.</dd>
    <dt><a name="d">qhull d Qbb</a></dt>
    <dd>compute the Delaunay triangulation by lifting the points
        to a paraboloid.  Use option '<a href="qh-optq.htm#Qbb">Qbb</a>'
        to scale the paraboloid and improve numeric precision.
        See <a href=qdelaun.htm>qdelaunay</a>.</dd>
    <dt><a name="v">qhull v Qbb</a></dt>
    <dd>compute the Voronoi diagram by computing the Delaunay
        triangulation.    Use option '<a href="qh-optq.htm#Qbb">Qbb</a>'
        to scale the paraboloid and improve numeric precision.
        See <a href=qvoronoi.htm>qvoronoi</a>.</dd>
    <dt><a name="H">qhull H</a></dt>
    <dd>compute the halfspace intersection about a point via polar
        duality.  The point is below the hyperplanes that defines the halfspace.
        See <a href=qhalf.htm>qhalf</a>.</dd>
</dl>
</blockquote>

<p>For a full list of output options see
<blockquote>
<ul>
            <li><a href="qh-opto.htm#output">Output</a> formats</li>
            <li><a href="qh-optf.htm#format">Additional</a> I/O
                formats</li>
            <li><a href="qh-optg.htm#geomview">Geomview</a>
                output options</li>
            <li><a href="qh-optp.htm#print">Print</a> options</li>
</ul>
</blockquote>

</blockquote>
<h3><a href="#TOP">&#187;</a><a name="controls">qhull controls</a></h3>
<blockquote>

<p>For a full list of control options see
<blockquote>
<ul>
            <li><a href="qh-optq.htm#qhull">Qhull</a> control
                options</li>
            <li><a href="qh-optc.htm#prec">Precision</a> options</li>
            <li><a href="qh-optt.htm#trace">Trace</a> options</li>
</ul>
</blockquote>

</blockquote>
<h3><a href="#TOP">&#187;</a><a name="options">qhull options</a></h3>

<pre>
qhull -- compute convex hulls and related structures.
    http://www.qhull.org

input (stdin):
    first lines: dimension and number of points (or vice-versa).
    other lines: point coordinates, best if one point per line
    comments:    start with a non-numeric character
    halfspaces:  use dim plus one and put offset after coefficients.
                 May be preceded by a single interior point ('H').

options:
    d    - Delaunay triangulation by lifting points to a paraboloid
    d Qu - furthest-site Delaunay triangulation (upper convex hull)
    Hn,n,... - halfspace intersection about point [n,n,0,...]
    Qc   - keep coplanar points with nearest facet
    Qi   - keep interior points with nearest facet
    QJ   - joggled input instead of merged facets
    Qt   - triangulated output
    v    - Voronoi diagram (dual of the Delaunay triangulation)
    v Qu - furthest-site Voronoi diagram

Qhull control options:
    Qa   - allow input with fewer or more points than coordinates
    Qbk:n   - scale coord k so that low bound is n
      QBk:n - scale coord k so that upper bound is n (QBk is 0.5)
    QbB  - scale input to unit cube centered at the origin
    Qbb  - scale last coordinate to [0,m] for Delaunay triangulations
    Qbk:0Bk:0 - remove k-th coordinate from input
    QJn  - randomly joggle input in range [-n,n]
    QRn  - random rotation (n=seed, n=0 time, n=-1 time/no rotate)
    Qs   - search all points for the initial simplex
    Qu   - for 'd' or 'v', compute upper hull without point at-infinity
              returns furthest-site Delaunay triangulation
    QVn  - good facet if it includes point n, -n if not
    Qx   - exact pre-merges (skips coplanar and angle-coplanar facets)
    Qz   - add point-at-infinity to Delaunay triangulation

Qhull extra options:
    Qf   - partition point to furthest outside facet
    Qg   - only build good facets (needs 'QGn', 'QVn', or 'PdD')
    QGn  - good facet if visible from point n, -n for not visible
    Qm   - only process points that would increase max_outside
    Qr   - process random outside points instead of furthest ones
    Qv   - test vertex neighbors for convexity
    Qw   - allow option warnings
    Q0   - turn off default premerge with 'C-0'/'Qx'
    Q1   - merge by mergetype/angle instead of mergetype/distance
    Q2   - merge all coplanar facets instead of merging independent sets
    Q3   - do not merge redundant vertices
    Q4   - avoid old->new merges
    Q5   - do not correct outer planes at end of qhull
    Q6   - do not pre-merge concave or coplanar facets
    Q7   - depth-first processing instead of breadth-first
    Q8   - do not process near-inside points
    Q9   - process furthest of furthest points
    Q10  - no special processing for narrow distributions
    Q11  - copy normals and recompute centrums for tricoplanar facets
    Q12  - allow wide facets and wide dupridge
    Q14  - merge pinched vertices that create a dupridge
    Q15  - check for duplicate ridges with the same vertices

T options:
    TFn  - report summary when n or more facets created
    TI file - input file, may be enclosed in single quotes
    TO file - output file, may be enclosed in single quotes
    Ts   - statistics
    Tv   - verify result: structure, convexity, and point inclusion
    Tz   - send all output to stdout

Trace options:
    T4   - trace at level n, 4=all, 5=mem/gauss, -1= events
    Ta   - annotate output with message codes
    TAn  - stop qhull after adding n vertices
     TCn - stop qhull after building cone for point n
     TVn - stop qhull after adding point n, -n for before
    Tc   - check frequently during execution
    Tf   - flush each qh_fprintf for debugging segfaults
    TPn  - turn on tracing when point n added to hull
     TP-1  turn on tracing after qh_buildhull and qh_postmerge
     TMn - turn on tracing at merge n
     TWn - trace merge facets when width > n
    TRn  - rerun qhull n times for statistics to adjust 'QJn'

Precision options:
    Cn   - radius of centrum (roundoff added).  Merge facets if non-convex
     An  - cosine of maximum angle.  Merge facets if cosine > n or non-convex
           C-0 roundoff, A-0.99/C-0.01 pre-merge, A0.99/C0.01 post-merge
    En   - max roundoff error for distance computation
    Rn   - randomly perturb computations by a factor of [1-n,1+n]
    Vn   - min distance above plane for a visible facet (default 3C-n or En)
    Un   - max distance below plane for a new, coplanar point (default Vn)
    Wn   - min facet width for outside point (before roundoff, default 2Vn)

Output formats (may be combined; if none, produces a summary to stdout):
    f    - facet dump
    G    - Geomview output (see below)
    i    - vertices incident to each facet
    m    - Mathematica output (2-d and 3-d)
    n    - normals with offsets
    o    - OFF format (dim, points and facets; Voronoi regions)
    p    - vertex coordinates or Voronoi vertices (coplanar points if 'Qc')
    s    - summary (stderr)

More formats:
    Fa   - area for each facet
    FA   - compute total area and volume for option 's'
    Fc   - count plus coplanar points for each facet
           use 'Qc' (default) for coplanar and 'Qi' for interior
    FC   - centrum or Voronoi center for each facet
    Fd   - use cdd format for input (homogeneous with offset first)
    FD   - use cdd format for numeric output (offset first)
    FF   - facet dump without ridges
    Fi   - inner plane for each facet
           for 'v', separating hyperplanes for bounded Voronoi regions
    FI   - ID of each facet
    Fm   - merge count for each facet (511 max)
    FM   - Maple output (2-d and 3-d)
    Fn   - count plus neighboring facets for each facet
    FN   - count plus neighboring facets for each point
    Fo   - outer plane (or max_outside) for each facet
           for 'v', separating hyperplanes for unbounded Voronoi regions
    FO   - options and precision constants
    Fp   - dim, count, and intersection coordinates (halfspace only)
    FP   - nearest vertex and distance for each coplanar point
    FQ   - command used for qhull
    Fs   - summary: #int (8), dimension, #points, tot vertices, tot facets,
                      output: #vertices, #facets, #coplanars, #nonsimplicial
                    #real (2), max outer plane, min vertex
    FS   - sizes:   #int (0)
                    #real (2) tot area, tot volume
    Ft   - triangulation with centrums for non-simplicial facets (OFF format)
    Fv   - count plus vertices for each facet
           for 'v', Voronoi diagram as Voronoi vertices for pairs of sites
    FV   - average of vertices (a feasible point for 'H')
    Fx   - extreme points (in order for 2-d)

Geomview output (2-d, 3-d, and 4-d; 2-d Voronoi)
    Ga   - all points as dots
     Gp  -  coplanar points and vertices as radii
     Gv  -  vertices as spheres
    Gc   - centrums
    GDn  - drop dimension n in 3-d and 4-d output
    Gh   - hyperplane intersections
    Gi   - inner planes only
     Gn  -  no planes
     Go  -  outer planes only
    Gr   - ridges
    Gt   - for 3-d 'd', transparent outer ridges

Print options:
    PAn  - keep n largest facets by area
    Pdk:n - drop facet if normal[k] <= n (default 0.0)
    PDk:n - drop facet if normal[k] >= n
    PFn  - keep facets whose area is at least n
    Pg   - print good facets (needs 'QGn' or 'QVn')
    PG   - print neighbors of good facets
    PMn  - keep n facets with most merges
    Po   - force output.  If error, output neighborhood of facet
    Pp   - do not report precision problems

    .    - list of all options
    -    - one line descriptions of all options
    -?   - help with examples
    -V   - version
</pre>

<!-- Navigation links -->
<hr>

<p><b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull (<a href="../index.htm">local</a>)<br>
<b>Up:</b> <a href="index.htm#TOC">Qhull manual</a>: contents<br>
<b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="http://www.qhull.org/src/libqhull_r/index.htm">Functions</a> (<a href="../src/libqhull_r/index.htm">local</a>)<br>
<b>To:</b> <a href="#synopsis">sy</a>nopsis &#149; <a href="#input">in</a>put
&#149; <a href="#outputs">ou</a>tputs &#149; <a href="#controls">co</a>ntrols
&#149; <a href="#options">op</a>tions
<!-- GC common information -->
<hr>

<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
align="middle" width="40" height="40"></a><i>The Geometry Center
Home Page </i></p>

<p>Comments to: <a href=mailto:<EMAIL>><EMAIL></a>
<br>
Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
</body>
</html>


<|start_filename|>3rd-party/qhull/src/libqhullcpp/QhullVertexSet.cpp<|end_filename|>
/****************************************************************************
**
** Copyright (c) 2009-2020 <NAME>. All rights reserved.
** $Id: //main/2019/qhull/src/libqhullcpp/QhullVertexSet.cpp#3 $$Change: 2953 $
** $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $
**
****************************************************************************/

#//! QhullVertexSet -- Qhull's linked Vertexs, as a C++ class

#include "libqhullcpp/QhullVertexSet.h"

#include "libqhullcpp/QhullVertex.h"
#include "libqhullcpp/QhullPoint.h"
#include "libqhullcpp/QhullRidge.h"
#include "libqhullcpp/QhullVertex.h"
#include "libqhullcpp/Qhull.h"

using std::string;
using std::vector;

#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
#pragma warning( disable : 4611)  /* interaction between '_setjmp' and C++ object destruction is non-portable */
                                    /* setjmp should not be implemented with 'catch' */
#endif

namespace orgQhull {

QhullVertexSet::
QhullVertexSet(const Qhull &q, facetT *facetlist, setT *facetset, bool allfacets)
: QhullSet<QhullVertex>(q.qh(), 0)
, qhsettemp_defined(false)
{
    QH_TRY_(q.qh()){ // no object creation -- destructors skipped on longjmp()
        setT *vertices= qh_facetvertices(q.qh(), facetlist, facetset, allfacets);
        defineAs(vertices);
        qhsettemp_defined= true;
    }
    q.qh()->NOerrexit= true;
    q.qh()->maybeThrowQhullMessage(QH_TRY_status);
}//QhullVertexSet facetlist facetset

//! Return tempory QhullVertexSet of vertices for a list and/or a set of facets
//! Sets qhsettemp_defined (disallows copy constructor and assignment to prevent double-free)
QhullVertexSet::
QhullVertexSet(QhullQh *qqh, facetT *facetlist, setT *facetset, bool allfacets)
: QhullSet<QhullVertex>(qqh, 0)
, qhsettemp_defined(false)
{
    QH_TRY_(qh()){ // no object creation -- destructors skipped on longjmp()
        setT *vertices= qh_facetvertices(qh(), facetlist, facetset, allfacets);
        defineAs(vertices);
        qhsettemp_defined= true;
    }
    qh()->NOerrexit= true;
    qh()->maybeThrowQhullMessage(QH_TRY_status);
}//QhullVertexSet facetlist facetset

//! Copy constructor for argument passing and returning a result
//! Only copies a pointer to the set.
//! Throws an error if qhsettemp_defined, otherwise have a double-free
//!\todo Convert QhullVertexSet to a shared pointer with reference counting
QhullVertexSet::
QhullVertexSet(const QhullVertexSet &other)
: QhullSet<QhullVertex>(other)
, qhsettemp_defined(false)
{
    if(other.qhsettemp_defined){
        throw QhullError(10077, "QhullVertexSet: Cannot use copy constructor since qhsettemp_defined (e.g., QhullVertexSet for a set and/or list of QhFacet).  Contains %d vertices", other.count());
    }
}//copy constructor

//! Copy assignment only copies a pointer to the set.
//! Throws an error if qhsettemp_defined, otherwise have a double-free
QhullVertexSet & QhullVertexSet::
operator=(const QhullVertexSet &other)
{
    QhullSet<QhullVertex>::operator=(other);
    qhsettemp_defined= false;
    if(other.qhsettemp_defined){
        throw QhullError(10078, "QhullVertexSet: Cannot use copy constructor since qhsettemp_defined (e.g., QhullVertexSet for a set and/or list of QhFacet).  Contains %d vertices", other.count());
    }
    return *this;
}//assignment

void QhullVertexSet::
freeQhSetTemp()
{
    if(qhsettemp_defined){
        qhsettemp_defined= false;
        QH_TRY_(qh()){ // no object creation -- destructors skipped on longjmp()
            qh_settempfree(qh(), referenceSetT()); // errors if not top of tempstack or if qhmem corrupted
        }
        qh()->NOerrexit= true;
        qh()->maybeThrowQhullMessage(QH_TRY_status, QhullError::NOthrow);
    }
}//freeQhSetTemp

QhullVertexSet::
~QhullVertexSet()
{
    freeQhSetTemp();
}//~QhullVertexSet

#ifndef QHULL_NO_STL
std::vector<QhullVertex> QhullVertexSet::
toStdVector() const
{
    QhullSetIterator<QhullVertex> i(*this);
    std::vector<QhullVertex> vs;
    while(i.hasNext()){
        QhullVertex v= i.next();
        vs.push_back(v);
    }
    return vs;
}//toStdVector
#endif //QHULL_NO_STL

#//!\name Class functions

}//namespace orgQhull

#//!\name Global functions

using std::endl;
using std::ostream;
using orgQhull::QhullPoint;
using orgQhull::QhullVertex;
using orgQhull::QhullVertexSet;
using orgQhull::QhullVertexSetIterator;

//! Print Vertex identifiers to stream.  Space prefix.  From qh_printVertexheader [io_r.c]
ostream &
operator<<(ostream &os, const QhullVertexSet::PrintIdentifiers &pr)
{
    os << pr.print_message;
    for(QhullVertexSet::const_iterator i= pr.vertex_set->begin(); i!=pr.vertex_set->end(); ++i){
        const QhullVertex v= *i;
        os << " v" << v.id();
    }
    os << endl;
    return os;
}//<<QhullVertexSet::PrintIdentifiers

//! Duplicate of printvertices [io_r.c]
ostream &
operator<<(ostream &os, const QhullVertexSet::PrintVertexSet &pr){

    os << pr.print_message;
    const QhullVertexSet *vs= pr.vertex_set;
    QhullVertexSetIterator i= *vs;
    while(i.hasNext()){
        const QhullVertex v= i.next();
        const QhullPoint p= v.point();
        os << " p" << p.id() << "(v" << v.id() << ")";
    }
    os << endl;

    return os;
}//<< PrintVertexSet




<|start_filename|>src/mc_observers/KinematicInertialObserver.cpp<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include <mc_observers/KinematicInertialObserver.h>
#include <mc_observers/ObserverMacros.h>

#include <mc_control/MCController.h>

#include <mc_rtc/gui/Arrow.h>

namespace mc_observers
{

void KinematicInertialObserver::configure(const mc_control::MCController & ctl, const mc_rtc::Configuration & config)
{
  KinematicInertialPoseObserver::configure(ctl, config);
  if(config.has("gui"))
  {
    config("gui")("velocity", showVelocity_);
    config("gui")("velocityArrow", velocityArrowConfig_);
  }
  if(config.has("log"))
  {
    auto lConfig = config("log");
    lConfig("velocity", logVelocity_);
  }
  double cutoff = config("cutoff", 2 * ctl.timeStep);
  velFilter_.cutoffPeriod(cutoff);
  desc_ = fmt::format("{} (sensor={}, cutoff={:.3f})", name_, imuSensor_, velFilter_.cutoffPeriod());
}

void KinematicInertialObserver::reset(const mc_control::MCController & ctl)
{
  reset(ctl, ctl.realRobot(robot_).velW());
}

void KinematicInertialObserver::reset(const mc_control::MCController & ctl, const sva::MotionVecd & velW)
{
  KinematicInertialPoseObserver::reset(ctl);
  posWPrev_ = KinematicInertialPoseObserver::posW();
  velW_ = velW;
  velFilter_.reset(velW);
}

bool KinematicInertialObserver::run(const mc_control::MCController & ctl)
{
  bool res = KinematicInertialPoseObserver::run(ctl);
  const sva::PTransformd posW = KinematicInertialPoseObserver::posW();
  if(!anchorFrameJumped_)
  {
    sva::MotionVecd errVel = sva::transformError(posWPrev_, posW) / ctl.timeStep;
    velFilter_.update(errVel);
    velW_ = velFilter_.eval();
  }
  else
  {
    mc_rtc::log::warning("[{}] Skipping velocity update (anchor frame jumped)", name());
  }
  posWPrev_ = posW;
  return res;
}

void KinematicInertialObserver::update(mc_control::MCController & ctl)
{
  KinematicInertialPoseObserver::update(ctl);
  ctl.realRobot(robot_).velW(velW_);
}

const sva::MotionVecd & KinematicInertialObserver::velW() const
{
  return velW_;
}

void KinematicInertialObserver::addToLogger(const mc_control::MCController & ctl,
                                            mc_rtc::Logger & logger,
                                            const std::string & category)
{
  KinematicInertialPoseObserver::addToLogger(ctl, logger, category);
  if(logVelocity_)
  {
    logger.addLogEntry(category + "_velW", this, [this]() -> const sva::MotionVecd & { return velW_; });
    logger.addLogEntry(category + "_filter_cutoffPeriod", this, [this]() { return velFilter_.cutoffPeriod(); });
  }
}

void KinematicInertialObserver::addToGUI(const mc_control::MCController & ctl,
                                         mc_rtc::gui::StateBuilder & gui,
                                         const std::vector<std::string> & category)
{
  KinematicInertialPoseObserver::addToGUI(ctl, gui, category);
  auto showHideVel = [this, category, &gui]() {
    std::string name = "Velocity";
    auto cat = category;
    cat.push_back("Markers");
    gui.removeElement(cat, name);
    if(showVelocity_)
    {
      gui.addElement(cat, mc_rtc::gui::Arrow(
                              name, velocityArrowConfig_,
                              [this]() -> const Eigen::Vector3d & { return posW().translation(); },
                              [this]() -> Eigen::Vector3d {
                                const Eigen::Vector3d p = posW().translation();
                                Eigen::Vector3d end = p + velW().linear();
                                return end;
                              }));
    }
  };

  gui.addElement(category, mc_rtc::gui::Checkbox(
                               "Show velocity", [this]() { return showVelocity_; },
                               [this, showHideVel]() {
                                 showVelocity_ = !showVelocity_;
                                 showHideVel();
                               }));

  gui.addElement(
      category,
      mc_rtc::gui::NumberInput(
          "Cutoff Period", [this]() -> double { return velFilter_.cutoffPeriod(); },
          [this, &ctl](double cutoff) {
            if(cutoff < 2 * ctl.timeStep)
            {
              mc_rtc::log::warning(
                  "[{}] cutoff period must be at least twice the timestep (>={}), keeping the current value ({})",
                  name(), 2 * ctl.timeStep, velFilter_.cutoffPeriod());
              return;
            }
            velFilter_.cutoffPeriod(cutoff);
          }));

  showHideVel();
}

} // namespace mc_observers

EXPORT_OBSERVER_MODULE("KinematicInertial", mc_observers::KinematicInertialObserver)


<|start_filename|>3rd-party/qhull/src/libqhull/qh-stat.htm<|end_filename|>
<!-- Do not edit with Front Page, it adds too many spaces -->
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=iso-8859-1">
<title>stat.c -- statistical operations</title>
</head>

<body>
<!-- Navigation links -->
<p><a name="TOP"><b>Up:</b></a> <a
href="http://www.qhull.org">Home page</a> for Qhull (<a href="../index.htm">local</a>)<br>
<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: contents <br>
<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
&#149; <a href="../../html/qh-quick.htm#options">Options</a>
&#149; <a href="../../html/qh-opto.htm#output">Output</a>
&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
&#149; <a href="../../html/qh-optp.htm#print">Print</a>
&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
&#149; <a href="http://www.qhull.org/src/libqhull/index.htm">Functions</a> (<a href="index.htm">local</a>)<br>
<b>Up:</b> <a href="../../html/qh-code.htm">Qhull code</a><br>
<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149; <a href="qh-globa.htm">Global</a>
&#149; <a href="qh-io.htm">Io</a> &#149; <a href="qh-mem.htm">Mem</a>
&#149; <a href="qh-merge.htm">Merge</a> &#149; <a href="qh-poly.htm">Poly</a>
&#149; <a href="qh-qhull.htm">Qhull</a> &#149; <a href="qh-set.htm">Set</a>
&#149; <a href="qh-stat.htm#TOC">Stat</a> &#149; <a href="qh-user.htm">User</a>
</p>
<hr>

<h2>stat.c -- statistical operations</h2>
<blockquote>
<p>Qhull records many statistics. These functions and
macros make it inexpensive to add a statistic.
<p>As with Qhull's global variables, the statistics data structure is
accessed by a macro, 'qhstat'.  If qh_QHpointer is defined, the macro
is 'qh_qhstat->', otherwise the macro is 'qh_qhstat.'.
Statistics
may be turned off in user.h. If so, all but the 'zz'
statistics are ignored.</p>
</blockquote>
<p><b>Copyright &copy; 1995-2020 <NAME></b></p>
<hr>
<p><a href="#TOP">&#187;</a> <a href="qh-geom.htm#TOC">Geom</a>
<a name="TOC">&#149;</a> <a href="qh-globa.htm#TOC">Global</a>
&#149; <a href="qh-io.htm#TOC">Io</a> &#149; <a href="qh-mem.htm#TOC">Mem</a>
&#149; <a href="qh-merge.htm#TOC">Merge</a> &#149; <a href="qh-poly.htm#TOC">Poly</a>
&#149; <a href="qh-qhull.htm#TOC">Qhull</a> &#149; <a href="qh-set.htm#TOC">Set</a>
&#149; <b>Stat</b> &#149; <a href="qh-user.htm#TOC">User</a>
</p>
<h3>Index to <a href="stat.c">stat.c</a> and
<a href="stat.h">stat.h</a></h3>
<ul>
<li><a href="#ttype">stat.h types</a> </li>
<li><a href="#tconst">stat.h constants</a> </li>
<li><a href="#tmacro">stat.h macros</a> </li>
<li><a href="#tfunc">stat.c functions</a> </li>
</ul>

<h3><a href="qh-stat.htm#TOC">&#187;</a><a name="ttype">stat.h types</a></h3>
<ul>
<li><a href="stat.h#intrealT">intrealT</a> union of
integer and real</li>
<li><a href="stat.h#qhstat">qhstat</a> global data
structure for statistics </li>
</ul>
<h3><a href="qh-stat.htm#TOC">&#187;</a><a name="tconst">stat.h
constants</a></h3>
<ul>
<li><a href="stat.h#KEEPstatistics">qh_KEEPstatistics</a> 0 turns off most statistics</li>
<li><a href="stat.h#statistics">Z..., W...</a> integer (Z) and real (W) statistics
</li>
<li><a href="stat.h#ZZstat">ZZstat</a> Z.../W... statistics that
remain defined if qh_KEEPstatistics=0
</li>
<li><a href="stat.h#ztype">ztype</a> zdoc, zinc, etc.
for definining statistics </li>
</ul>
<h3><a href="qh-stat.htm#TOC">&#187;</a><a name="tmacro">stat.h macros</a></h3>
<ul>
<li><a href="stat.h#MAYdebugx">MAYdebugx</a> called
frequently for error trapping </li>
<li><a href="stat.h#zadd_">zadd_/wadd_</a> add value
to an integer or real statistic </li>
<li><a href="stat.h#zdef_">zdef_</a> define a
statistic </li>
<li><a href="stat.h#zinc_">zinc_</a> increment an
integer statistic </li>
<li><a href="stat.h#zmax_">zmax_/wmax_</a> update
integer or real maximum statistic </li>
<li><a href="stat.h#zmin_">zmin_/wmin_</a> update
integer or real minimum statistic </li>
<li><a href="stat.h#zval_">zval_/wval_</a> set or
return value of a statistic </li>
</ul>

<h3><a href="qh-stat.htm#TOC">&#187;</a><a name="tfunc">stat.c
functions</a></h3>
<ul>
<li><a href="stat.c#allstatA">qh_allstatA</a> define
statistics in groups of 20 </li>
<li><a href="stat.c#allstatistics">qh_allstatistics</a>
reset printed flag for all statistics </li>
<li><a href="stat.c#collectstatistics">qh_collectstatistics</a>
collect statistics for qh.facet_list </li>
<li><a href="stat.c#freestatistics">qh_freestatistics</a>
free memory used for statistics </li>
<li><a href="stat.c#initstatistics">qh_initstatistics</a>
allocate and initialize statistics </li>
<li><a href="stat.c#newstats">qh_newstats</a> returns
True if statistics for zdoc </li>
<li><a href="stat.c#nostatistic">qh_nostatistic</a>
true if no statistic to print </li>
<li><a href="stat.c#printallstatistics">qh_printallstatistics</a>
print all statistics </li>
<li><a href="stat.c#printstatistics">qh_printstatistics</a>
print statistics to a file </li>
<li><a href="stat.c#printstatlevel">qh_printstatlevel</a>
print level information for a statistic </li>
<li><a href="stat.c#printstats">qh_printstats</a>
print statistics for a zdoc group </li>
<li><a href="stat.c#stddev">qh_stddev</a> compute the
standard deviation and average from statistics </li>
</ul>

<p><!-- Navigation links --> </p>
<hr>
<p><b>Up:</b>
<a href="http://www.qhull.org">Home page</a> for
Qhull (<a href="../index.htm">local</a>)<br>
<b>Up:</b> <a href="../../html/index.htm#TOC">Qhull manual</a>: contents<br>
<b>Up:</b> <a href="../../html/qh-quick.htm#programs">Programs</a>
&#149; <a href="../../html/qh-quick.htm#options">Options</a>
&#149; <a href="../../html/qh-opto.htm#output">Output</a>
&#149; <a href="../../html/qh-optf.htm#format">Formats</a>
&#149; <a href="../../html/qh-optg.htm#geomview">Geomview</a>
&#149; <a href="../../html/qh-optp.htm#print">Print</a>
&#149; <a href="../../html/qh-optq.htm#qhull">Qhull</a>
&#149; <a href="../../html/qh-optc.htm#prec">Precision</a>
&#149; <a href="../../html/qh-optt.htm#trace">Trace</a>
&#149; <a href="http://www.qhull.org/src/libqhull/index.htm">Functions</a> (<a href="index.htm">local</a>)<br>
<b>Up:</b> <a href="../../html/qh-code.htm">Qhull code</a> <br>
<b>To:</b> <a href="qh-geom.htm">Geom</a> &#149;
<a href="qh-globa.htm">Global</a> &#149; <a href="qh-io.htm">Io</a>
&#149; <a href="qh-mem.htm">Mem</a> &#149; <a href="qh-merge.htm">Merge</a>
&#149; <a href="qh-poly.htm">Poly</a> &#149; <a href="qh-qhull.htm#TOC">Qhull</a>
&#149; <a href="qh-set.htm">Set</a> &#149; <a href="qh-stat.htm">Stat</a>
&#149; <a href="qh-user.htm">User</a><br>
</p>
<p><!-- GC common information --> </p>
<hr>
<p><a href="http://www.geom.uiuc.edu/"><img
src="../../html/qh--geom.gif" align="middle" width="40" height="40"></a><i>The
Geometry Center Home Page </i></p>
<p>Comments to: <a href=mailto:<EMAIL>><EMAIL></a>
<br>
Created: May 2, 1997 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
</body>
</html>


<|start_filename|>tests/controllers/TestPluginController.cpp<|end_filename|>
/*
 * Copyright 2021 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#ifdef BOOST_TEST_MAIN
#  undef BOOST_TEST_MAIN
#endif
#include <mc_control/api.h>
#include <mc_control/mc_controller.h>
#include <mc_rtc/logging.h>

#include <boost/test/unit_test.hpp>

namespace mc_control
{

struct MC_CONTROL_DLLAPI TestPluginController : public MCController
{
public:
  TestPluginController(const std::string & name, mc_rbdyn::RobotModulePtr rm, double dt)
  : MCController(rm, dt), name_(name)
  {
    solver().addConstraintSet(contactConstraint);
    solver().addConstraintSet(dynamicsConstraint);
    solver().addTask(postureTask.get());
    solver().setContacts(
        {mc_rbdyn::Contact(robots(), "LeftFoot", "AllGround"), mc_rbdyn::Contact(robots(), "RightFoot", "AllGround")});
    mc_rtc::log::success("Created {}", name_);
  }

  bool run() override
  {
    // The static map here is used to pass data across controller boundaries
    static std::map<std::string, void *> plugin_addresses;
    if(nrIter == 0)
    {
      for(const auto & plugin : common_plugins_)
      {
        BOOST_REQUIRE(datastore().has(plugin + "::address"));
        BOOST_REQUIRE(plugin_addresses.count(plugin));
        void * p_addr = datastore().get<void *>(plugin + "::address");
        BOOST_REQUIRE(plugin_addresses[plugin] == p_addr);
      }
      for(const auto & plugin : expected_plugins_)
      {
        BOOST_REQUIRE(datastore().has(plugin + "::address"));
        void * p_addr = datastore().get<void *>(plugin + "::address");
        plugin_addresses[plugin] = p_addr;
        BOOST_REQUIRE(datastore().has(plugin + "::config"));
        auto plugin_c = datastore().get<mc_rtc::Configuration>(plugin + "::config");
        BOOST_REQUIRE(plugin_c.has("common"));
        std::string plugin_c_common = plugin_c("common");
        BOOST_REQUIRE_EQUAL(plugin_c_common, plugin);
        BOOST_REQUIRE(plugin_c.has(name_));
        BOOST_REQUIRE(plugin_c(name_));
        BOOST_REQUIRE(plugin_c.has("exclusive_to_TestPluginController_1_2"));
        bool exclusive = plugin_c("exclusive_to_TestPluginController_1_2");
        if(name_ == "TestPluginController_1_2")
        {
          BOOST_REQUIRE_EQUAL(exclusive, plugin == "Plugin1");
        }
        else
        {
          BOOST_REQUIRE_EQUAL(exclusive, false);
        }
      }
    }
    bool ret = MCController::run();
    nrIter++;
    for(const auto & plugin : expected_plugins_)
    {
      BOOST_REQUIRE(datastore().has(plugin + "::iter_before"));
      BOOST_REQUIRE(datastore().get<size_t>(plugin + "::iter_before") == nrIter);
      BOOST_REQUIRE(datastore().has(plugin + "::iter_after"));
      BOOST_REQUIRE(datastore().get<size_t>(plugin + "::iter_after") == nrIter - 1);
    }
    return ret;
  }

  // The plugin that we expect to be running in this controller
  std::vector<std::string> expected_plugins_ = {};
  // The plugin we expect to have kept from the previous controller
  std::vector<std::string> common_plugins_ = {};

private:
  std::string name_;
  size_t nrIter = 0;
};

} // namespace mc_control

extern "C"
{

  CONTROLLER_MODULE_API void MC_RTC_CONTROLLER(std::vector<std::string> & names)
  {
    CONTROLLER_CHECK_VERSION("TestPluginController")
    names = {"TestPluginController_1_2", "TestPluginController_2_3"};
  }

  CONTROLLER_MODULE_API void destroy(mc_control::MCController * ptr)
  {
    delete ptr;
  }

  CONTROLLER_MODULE_API unsigned int create_args_required()
  {
    return 4;
  }

  CONTROLLER_MODULE_API mc_control::MCController * create(const std::string & name,
                                                          const mc_rbdyn::RobotModulePtr & rm,
                                                          const double & dt,
                                                          const mc_rtc::Configuration &)
  {
    auto out = new mc_control::TestPluginController(name, rm, dt);
    if(name == "TestPluginController_1_2")
    {
      out->expected_plugins_ = {"Plugin0", "Plugin1", "Plugin2"};
    }
    else if(name == "TestPluginController_2_3")
    {
      out->expected_plugins_ = {"Plugin0", "Plugin2", "Plugin3"};
      out->common_plugins_ = {"Plugin0", "Plugin2"};
    }
    else
    {
      mc_rtc::log::critical("UNREACHABLE");
      return nullptr;
    }
    return out;
  }
}


<|start_filename|>include/mc_rtc/debug.h<|end_filename|>
/*
 * Copyright 2015-2020 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_rtc/utils_api.h>

namespace mc_rtc
{

/** Returns true if mc_rtc was compiled in debug mode */
bool MC_RTC_UTILS_DLLAPI debug() noexcept;

} // namespace mc_rtc


<|start_filename|>src/mc_rtc/MessagePackBuilder.cpp<|end_filename|>
#include <mc_rtc/Configuration.h>
#include <mc_rtc/MessagePackBuilder.h>
#include <mc_rtc/logging.h>

#include "mpack.h"

#if not EIGEN_VERSION_AT_LEAST(3, 2, 90)
namespace Eigen
{
using Index = Eigen::DenseIndex;
}
#endif

namespace mc_rtc
{

struct MessagePackBuilderImpl : mpack_writer_t
{
};

/** Inspired by mpack.c @ version 1.0 */
static void mpack_std_vector_writer_flush(mpack_writer_t * writer, const char * data, size_t count)
{
  auto & buffer = *static_cast<std::vector<char> *>(writer->context);
  // This is an intrusive flush function which modifies the writer's buffer
  // in response to a flush instead of emptying it in order to add more
  // capacity for data. This removes the need to copy data from a fixed buffer
  // into a growable one, improving performance.
  //
  // There are three ways flush can be called:
  //   - flushing the buffer during writing (used is zero, count is all data, data is buffer)
  //   - flushing extra data during writing (used is all flushed data, count is extra data, data is not buffer)
  //   - flushing during teardown (used and count are both all flushed data, data is buffer)
  //
  // In the first two cases, we grow the buffer by at least double, enough
  // to ensure that new data will fit. We ignore the teardown flush.

  if(data == writer->buffer)
  {

    // teardown, do nothing
    if(mpack_writer_buffer_used(writer) == count) return;

    // otherwise leave the data in the buffer and just grow
    writer->current = writer->buffer + count;
    count = 0;
  }

  size_t used = mpack_writer_buffer_used(writer);
  size_t size = mpack_writer_buffer_size(writer);

  mpack_log("flush size %i used %i data %p buffer %p\n", (int)count, (int)used, data, writer->buffer);

  mpack_assert(data == writer->buffer || used + count > size,
               "extra flush for %i but there is %i space left in the buffer! (%i/%i)", (int)count,
               (int)mpack_writer_buffer_left(writer), (int)used, (int)size);

  // grow to fit the data
  // TODO: this really needs to correctly test for overflow
  size_t new_size = size * 2;
  while(new_size < used + count) new_size *= 2;

  mpack_log("flush growing buffer size from %i to %i\n", (int)size, (int)new_size);

  // grow the buffer
  buffer.resize(new_size);
  char * new_buffer = buffer.data();
  if(new_buffer == NULL)
  {
    mpack_writer_flag_error(writer, mpack_error_memory);
    return;
  }
  writer->current = new_buffer + used;
  writer->buffer = new_buffer;
  writer->end = writer->buffer + new_size;

  // append the extra data
  if(count > 0)
  {
    mpack_memcpy(writer->current, data, count);
    writer->current += count;
  }

  mpack_log("new buffer %p, used %i\n", new_buffer, (int)mpack_writer_buffer_used(writer));
}

MessagePackBuilder::MessagePackBuilder(std::vector<char> & buffer) : impl_(new MessagePackBuilderImpl())
{
  if(buffer.size() == 0)
  {
    buffer.resize(MPACK_BUFFER_SIZE);
  }
  mpack_writer_init(impl_.get(), buffer.data(), buffer.size());
  mpack_writer_set_context(impl_.get(), &buffer);
  mpack_writer_set_flush(impl_.get(), mpack_std_vector_writer_flush);
}

MessagePackBuilder::~MessagePackBuilder() {}

void MessagePackBuilder::write()
{
  mpack_write_nil(impl_.get());
}

void MessagePackBuilder::write(bool b)
{
  mpack_write_bool(impl_.get(), b);
}
void MessagePackBuilder::write(int8_t i)
{
  mpack_write_i8(impl_.get(), i);
}
void MessagePackBuilder::write(int16_t i)
{
  mpack_write_i16(impl_.get(), i);
}
void MessagePackBuilder::write(int32_t i)
{
  mpack_write_i32(impl_.get(), i);
}
void MessagePackBuilder::write(int64_t i)
{
  mpack_write_i64(impl_.get(), i);
}
void MessagePackBuilder::write(uint8_t i)
{
  mpack_write_u8(impl_.get(), i);
}
void MessagePackBuilder::write(uint16_t i)
{
  mpack_write_u16(impl_.get(), i);
}
void MessagePackBuilder::write(uint32_t i)
{
  mpack_write_u32(impl_.get(), i);
}
void MessagePackBuilder::write(uint64_t i)
{
  mpack_write_u64(impl_.get(), i);
}
void MessagePackBuilder::write(float f)
{
  mpack_write_float(impl_.get(), f);
}
void MessagePackBuilder::write(double d)
{
  mpack_write_double(impl_.get(), d);
}
void MessagePackBuilder::write(const std::string & s)
{
  mpack_write_str(impl_.get(), s.c_str(), static_cast<uint32_t>(s.size()));
}
void MessagePackBuilder::write(const char * s)
{
  mpack_write_cstr(impl_.get(), s);
}

void MessagePackBuilder::write(const char * s, size_t len)
{
  mpack_write_str(impl_.get(), s, static_cast<uint32_t>(len));
}

namespace
{

template<typename T>
inline void write_vector(mpack_writer_t * writer, const T & v)
{
  for(Eigen::Index i = 0; i < v.size(); ++i)
  {
    mpack_write_double(writer, v(i));
  }
}

template<typename T>
inline void write_matrix(mpack_writer_t * writer, const T & m)
{
  for(Eigen::Index i = 0; i < m.rows(); ++i)
  {
    for(Eigen::Index j = 0; j < m.cols(); ++j)
    {
      mpack_write_double(writer, m(i, j));
    }
  }
}

} // namespace

void MessagePackBuilder::write(const Eigen::Vector2d & v)
{
  start_array(2);
  write_vector(impl_.get(), v);
  finish_array();
}

void MessagePackBuilder::write(const Eigen::Vector3d & v)
{
  start_array(3);
  write_vector(impl_.get(), v);
  finish_array();
}

void MessagePackBuilder::write(const Eigen::Vector6d & v)
{
  start_array(6);
  write_vector(impl_.get(), v);
  finish_array();
}

void MessagePackBuilder::write(const Eigen::VectorXd & v)
{
  start_array(static_cast<size_t>(v.size()));
  write_vector(impl_.get(), v);
  finish_array();
}

void MessagePackBuilder::write(const Eigen::Quaterniond & q)
{
  start_array(4);
  write(q.w());
  write(q.x());
  write(q.y());
  write(q.z());
  finish_array();
}

void MessagePackBuilder::write(const Eigen::Matrix3d & m)
{
  start_array(9);
  write_matrix(impl_.get(), m);
  finish_array();
}

void MessagePackBuilder::write(const sva::PTransformd & pt)
{
  start_array(12);
  write_matrix(impl_.get(), pt.rotation());
  write_vector(impl_.get(), pt.translation());
  finish_array();
}

void MessagePackBuilder::write(const sva::ForceVecd & fv)
{
  start_array(6);
  write_vector(impl_.get(), fv.couple());
  write_vector(impl_.get(), fv.force());
  finish_array();
}

void MessagePackBuilder::write(const sva::MotionVecd & mv)
{
  start_array(6);
  write_vector(impl_.get(), mv.angular());
  write_vector(impl_.get(), mv.linear());
  finish_array();
}

void MessagePackBuilder::write(const sva::ImpedanceVecd & iv)
{
  start_array(6);
  write_vector(impl_.get(), iv.angular());
  write_vector(impl_.get(), iv.linear());
  finish_array();
}

void MessagePackBuilder::write(const mc_rtc::Configuration & config)
{
  config.toMessagePack(*this);
}

void MessagePackBuilder::start_array(size_t s)
{
  mpack_start_array(impl_.get(), static_cast<uint32_t>(s));
}

void MessagePackBuilder::finish_array()
{
  mpack_finish_array(impl_.get());
}

void MessagePackBuilder::start_map(size_t s)
{
  mpack_start_map(impl_.get(), static_cast<uint32_t>(s));
}

void MessagePackBuilder::finish_map()
{
  mpack_finish_map(impl_.get());
}

void MessagePackBuilder::write_object(const char * data, size_t s)
{
  mpack_write_object_bytes(impl_.get(), data, s);
}

size_t MessagePackBuilder::finish()
{
  if(mpack_writer_destroy(impl_.get()) != mpack_ok)
  {
    log::error("Failed to convert to MessagePack");
    return 0;
  }
  return mpack_writer_buffer_used(impl_.get());
}

} // namespace mc_rtc


<|start_filename|>doc/_data/schemas/State/Posture.json<|end_filename|>
{
  "allOf":
  [
    {
      "title": "mc_control::fsm::PostureState",
      "description": "Change configuration of the global posture task of a robot.",
      "type": "object",
      "properties":
      {
        "robot": { "type": "string", "default": "MainRobot", "description": "Name of the robot whose posture task is modified" },
        "postureTask":
        {
          "type": "object",
          "title": "PostureTaskConfig",
          "description": "Configuration for the gains and targets of the posture task",
          "allOf":[ { "$ref": "/../../common/PostureTask_targets_properties.json" } ]
        },
        "completion": { "$ref": "/../../common/completion_criteria.json" }
      }
    },
    {
      "$ref": "/../../common/State.json"
    }
  ]
}


<|start_filename|>include/mc_rtc/gui/Robot.h<|end_filename|>
/*
 * Copyright 2015-2020 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_rtc/gui/details/traits.h>
#include <mc_rtc/gui/elements.h>
#include <mc_rtc/gui/types.h>

#include <mc_rbdyn/Robot.h>

namespace mc_rtc
{

namespace gui
{

namespace details
{

/** Robot should display a robot model in the environment
 *
 * The element provides the following data to the client:
 * - the parameters passed to RobotLoader to get the RobotModule (vector<string>)
 * - the current robot configuration (vector<vector<double>>)
 *
 * \tparam GetT Should return an mc_rbdyn::Robot
 */
template<typename GetT>
struct RobotImpl : public Element
{
  static constexpr auto type = Elements::Robot;

  RobotImpl(const std::string & name, GetT get_fn) : Element(name), get_fn_(get_fn)
  {
    static_assert(CheckReturnType<GetT, mc_rbdyn::Robot>::value, "Robot element must return an mc_rbdyn::Robot");
  }

  static constexpr size_t write_size()
  {
    return Element::write_size() + 3;
  }

  void write(mc_rtc::MessagePackBuilder & builder)
  {
    const mc_rbdyn::Robot & robot = get_fn_();
    Element::write(builder);
    builder.write(robot.module().parameters());
    builder.write(robot.mbc().q);
    builder.write(robot.posW());
  }

private:
  GetT get_fn_;
};

} // namespace details

/** Helper function to create a RobotImpl */
template<typename GetT>
details::RobotImpl<GetT> Robot(const std::string & name, GetT get_fn)
{
  return details::RobotImpl<GetT>(name, get_fn);
}

} // namespace gui

} // namespace mc_rtc


<|start_filename|>src/mc_tasks/MomentumTask.cpp<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include <mc_rbdyn/rpy_utils.h>
#include <mc_tasks/MetaTaskLoader.h>
#include <mc_tasks/MomentumTask.h>

#include <mc_rtc/gui/ArrayLabel.h>
#include <mc_rtc/gui/Transform.h>

namespace mc_tasks
{

MomentumTask::MomentumTask(const mc_rbdyn::Robots & robots, unsigned int robotIndex, double stiffness, double weight)
: TrajectoryTaskGeneric<tasks::qp::MomentumTask>(robots, robotIndex, stiffness, weight)
{
  auto & robot = robots.robot(robotIndex);
  auto momentum = rbd::computeCentroidalMomentum(robot.mb(), robot.mbc(), robot.com());
  finalize(robots.mbs(), static_cast<int>(rIndex), momentum);

  type_ = "momentum";
  name_ = "momentum_" + robots.robot(robotIndex).name();
}

void MomentumTask::reset()
{
  TrajectoryTaskGeneric::reset();
  const auto & robot = robots.robot(rIndex);
  errorT->momentum(rbd::computeCentroidalMomentum(robot.mb(), robot.mbc(), robot.com()));
}

/*! \brief Load parameters from a Configuration object */
void MomentumTask::load(mc_solver::QPSolver & solver, const mc_rtc::Configuration & config)
{
  if(config.has("momentum"))
  {
    this->momentum(config("momentum"));
  }
  TrajectoryBase::load(solver, config);
}

sva::ForceVecd MomentumTask::momentum() const
{
  return errorT->momentum();
}

void MomentumTask::momentum(const sva::ForceVecd & m)
{
  errorT->momentum(m);
}

void MomentumTask::addToLogger(mc_rtc::Logger & logger)
{
  TrajectoryBase::addToLogger(logger);
  logger.addLogEntry(name_ + "_target_momentum", this, [this]() { return momentum(); });
  // FIXME Not correct with dimWeight
  logger.addLogEntry(name_ + "_momentum", this,
                     [this]() { return sva::ForceVecd(momentum().vector() - errorT->eval()); });
}

void MomentumTask::addToGUI(mc_rtc::gui::StateBuilder & gui)
{
  TrajectoryTaskGeneric<tasks::qp::MomentumTask>::addToGUI(gui);
  gui.addElement({"Tasks", name_},
                 mc_rtc::gui::ArrayInput(
                     "target", {"cx", "cy", "cz", "fx", "fy", "fz"}, [this]() { return this->momentum(); },
                     [this](const sva::ForceVecd & m) { this->momentum(m); }),
                 mc_rtc::gui::ArrayLabel("momentum", {"cx", "cy", "cz", "fx", "fy", "fz"}, [this]() -> Eigen::Vector6d {
                   return this->momentum().vector() - this->errorT->eval();
                 }));
}

} // namespace mc_tasks

namespace
{

static auto registered = mc_tasks::MetaTaskLoader::register_load_function(
    "momentum",
    [](mc_solver::QPSolver & solver, const mc_rtc::Configuration & config) {
      auto robotIndex = robotIndexFromConfig(config, solver.robots(), "MomentumTask");
      auto t = std::make_shared<mc_tasks::MomentumTask>(solver.robots(), robotIndex);
      t->load(solver, config);
      return t;
    });
}


<|start_filename|>tests/controllers/TestPostureController.cpp<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#ifdef BOOST_TEST_MAIN
#  undef BOOST_TEST_MAIN
#endif
#include <mc_control/api.h>
#include <mc_control/mc_controller.h>
#include <mc_rtc/logging.h>

#include <boost/test/unit_test.hpp>

namespace mc_control
{

struct MC_CONTROL_DLLAPI TestPostureController : public MCController
{
public:
  TestPostureController(std::shared_ptr<mc_rbdyn::RobotModule> rm, double dt) : MCController(rm, dt)
  {
    // Check that the default constructor loads the robot + ground environment
    BOOST_CHECK_EQUAL(robots().robots().size(), 2);
    // Check that JVRC-1 was loaded
    BOOST_CHECK_EQUAL(robot().name(), "jvrc1");
    qpsolver->addConstraintSet(contactConstraint);
    qpsolver->addConstraintSet(kinematicsConstraint);
    postureTask->stiffness(200.0);
    qpsolver->addTask(postureTask.get());
    qpsolver->setContacts({});
    BOOST_CHECK(robot().hasJoint("NECK_P"));
    BOOST_CHECK_NO_THROW(head_joint_index = robot().jointIndexByName("NECK_P"));
    head_joint_target = std::min(std::abs(robot().ql()[head_joint_index][0]), robot().qu()[head_joint_index][0]) - 0.1;
    mc_rtc::log::success("Created TestPostureController");
  }

  virtual bool run() override
  {
    bool ret = MCController::run();
    nrIter++;
    BOOST_CHECK(ret);
    if(nrIter == 250)
    {
      BOOST_CHECK_SMALL(robot().mbc().q[head_joint_index][0] - head_joint_target, 0.05);

      neckTask_ = std::make_shared<mc_tasks::PostureTask>(solver(), 0);
      neckTask_->stiffness(200.0);
      neckTask_->selectActiveJoints(solver(), {"NECK_P"});
      neckTask_->name("neck_posture");
      postureTask->target({{"NECK_P", {head_joint_target}}});
      solver().addTask(neckTask_);

      postureTask->target({{"NECK_P", {0}}});
      postureTask->selectUnactiveJoints(solver(), {"NECK_P"});
    }
    if(nrIter == 500)
    {
      BOOST_CHECK_SMALL(robot().mbc().q[head_joint_index][0] - head_joint_target, 0.001);
      neckTask_->target({{"NECK_P", {0}}});
    }
    if(nrIter == 750)
    {
      BOOST_CHECK_SMALL(robot().mbc().q[head_joint_index][0], 0.001);
    }
    return ret;
  }

  virtual void reset(const ControllerResetData & reset_data) override
  {
    MCController::reset(reset_data);
    postureTask->target({{"NECK_P", {head_joint_target}}});
  }

private:
  unsigned int nrIter = 0;
  unsigned int head_joint_index;
  double head_joint_target;
  std::shared_ptr<mc_tasks::PostureTask> neckTask_;
};

} // namespace mc_control

SIMPLE_CONTROLLER_CONSTRUCTOR("TestPostureController", mc_control::TestPostureController)


<|start_filename|>tests/test_interpolation.cpp<|end_filename|>

/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include <mc_rtc/logging.h>
#include <mc_trajectory/SequenceInterpolator.h>
#include <boost/test/unit_test.hpp>
#include <Eigen/Core>

BOOST_AUTO_TEST_CASE(TestSequenceInterpolator)
{
  constexpr double epsilon = 1e-10;
  using namespace mc_trajectory;
  LinearInterpolation<double> interpolator;
  using SequenceInterpolator = SequenceInterpolator<double, LinearInterpolation<double>>;

  using values_t = std::vector<std::pair<double, double>>;
  auto values = values_t{{0., 0.}, {1., 1.}, {2., 2.}};
  constexpr double dt = 0.005;

  BOOST_REQUIRE_THROW(SequenceInterpolator{}.compute(0), std::runtime_error);
  BOOST_REQUIRE_THROW(SequenceInterpolator{{}}.compute(0), std::runtime_error);
  BOOST_REQUIRE(!SequenceInterpolator{}.hasValues());
  BOOST_REQUIRE_NO_THROW(SequenceInterpolator{values});
  SequenceInterpolator interpolation{values};
  // Check interpolation in first interval
  int i = 0;
  double time = 0;
  for(; i < 1 / dt; i++)
  {
    BOOST_REQUIRE_CLOSE(interpolation.compute(time), interpolator(0, 1, time), epsilon);
    time += dt;
  }
  // Check interpolation in second interval
  for(; i < 2 / dt; i++)
  {
    BOOST_REQUIRE_CLOSE(interpolation.compute(time), interpolator(1, 2, time - 1), epsilon);
    time += dt;
  }
  // Check access out of bounds (returns last value)
  for(; i < 3 / dt; i++)
  {
    BOOST_REQUIRE_CLOSE(interpolation.compute(time), 2., epsilon);
    time += dt;
  }

  BOOST_REQUIRE_THROW(interpolation.values(values_t{{2, 0}, {1.999, 0}}),
                      std::runtime_error); // values not correctly ordered
  BOOST_REQUIRE_THROW(interpolation.values(values_t{{2, 0}, {1.5, 0}}),
                      std::runtime_error); // values not correctly ordered

  // Change values
  values = values_t{{0.5, 0.5}, {1., 1.}, {2., 2.}};
  BOOST_REQUIRE_NO_THROW(interpolation.values(values));
  BOOST_REQUIRE_THROW(interpolation.values({{3.5, 0.5}, {1., 1.}, {2., 2.}}), std::runtime_error);
  time = 0.5;
  BOOST_REQUIRE_CLOSE(interpolation.compute(time), 0.5, epsilon);

  BOOST_REQUIRE_NO_THROW(interpolation.values({{1.2, 2.3}}));
  time = 1.2;
  BOOST_REQUIRE_CLOSE(interpolation.compute(time), 2.3, epsilon);

  // test starting at second interval
  values = values_t{{0.0, 0.0}, {1., 1.}, {2., 2.}};
  BOOST_REQUIRE_NO_THROW(interpolation.values(values));
  BOOST_REQUIRE_CLOSE(interpolation.compute(1.2), interpolator(1, 2, 0.2), epsilon);

  // test starting at any time
  values = values_t{{0.0, 0.0}, {1., 1.}, {2., 2.}};
  BOOST_REQUIRE_NO_THROW(interpolation.values(values));
  BOOST_REQUIRE_CLOSE(interpolation.compute(1.2), interpolator(1, 2, 0.2), epsilon);
  BOOST_REQUIRE_CLOSE(interpolation.compute(1.3), interpolator(1, 2, 0.3), epsilon);
  BOOST_REQUIRE_CLOSE(interpolation.compute(0.5), interpolator(0, 1, 0.5), epsilon);
  BOOST_REQUIRE_CLOSE(interpolation.compute(2.00001), 2.0, epsilon);
  BOOST_REQUIRE_CLOSE(interpolation.compute(-2.00001), 0.0, epsilon);

  // single value
  interpolation.values({{1., 1.}});
  BOOST_REQUIRE_CLOSE(interpolation.compute(0), 1., epsilon);
  BOOST_REQUIRE_CLOSE(interpolation.compute(1), 1., epsilon);
  BOOST_REQUIRE_CLOSE(interpolation.compute(2), 1., epsilon);

  // negative value
  BOOST_REQUIRE_NO_THROW(interpolation.values({{-1., -1.}, {0., 0.}, {1., 1.}}));
  BOOST_REQUIRE_CLOSE(interpolation.compute(-0.2), -0.2, epsilon);

  // Realistic case: trajectory gains
  interpolation.values({{0, 20}, {3., 20}, {3.8, 50}, {4., 200}});
  BOOST_REQUIRE_CLOSE(interpolation.compute(0.), 20, epsilon);

  // Random access
  values = {{0, 0}, {3., 20}, {3.8, 50}, {4., 200}};
  interpolation.values(values);
  BOOST_REQUIRE_CLOSE(interpolation.compute(6), 200, epsilon);
  BOOST_REQUIRE_CLOSE(interpolation.compute(-1), 0, epsilon);
  BOOST_REQUIRE_CLOSE(interpolation.compute(3), 20, epsilon);
  BOOST_REQUIRE_CLOSE(interpolation.compute(3.8), 50, epsilon);
  BOOST_REQUIRE_CLOSE(interpolation.compute(2), 20 / 3. * 2, epsilon);
  BOOST_REQUIRE_CLOSE(interpolation.compute(3.9), interpolator(50, 200, 0.5), epsilon);
}


<|start_filename|>3rd-party/qhull/src/libqhullcpp/QhullRidge.cpp<|end_filename|>
/****************************************************************************
**
** Copyright (c) 2008-2020 <NAME>. All rights reserved.
** $Id: //main/2019/qhull/src/libqhullcpp/QhullRidge.cpp#2 $$Change: 2953 $
** $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $
**
****************************************************************************/

#//! QhullRidge -- Qhull's ridge structure, ridgeT, as a C++ class

#include "libqhullcpp/QhullRidge.h"

#include "libqhullcpp/QhullSets.h"
#include "libqhullcpp/QhullVertex.h"
#include "libqhullcpp/Qhull.h"

#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
#pragma warning( disable : 4611)  // interaction between '_setjmp' and C++ object destruction is non-portable
#pragma warning( disable : 4996)  // function was declared deprecated(strcpy, localtime, etc.)
#endif

namespace orgQhull {

#//!\name Class objects
ridgeT QhullRidge::
s_empty_ridge= {NULL,NULL,NULL,0,    // must match ridgeT -Wmissing-field-initializers
                false,false,false,false,false,false,false};

#//!\name Constructors

QhullRidge::QhullRidge(const Qhull &q)
: qh_ridge(&s_empty_ridge)
, qh_qh(q.qh())
{
}//Default

QhullRidge::QhullRidge(const Qhull &q, ridgeT *r)
: qh_ridge(r ? r : &s_empty_ridge)
, qh_qh(q.qh())
{
}//ridgeT

#//!\name foreach

//! Return True if nextRidge3d
//! Simplicial facets may have incomplete ridgeSets
//! Does not use qh_errexit()
bool QhullRidge::
hasNextRidge3d(const QhullFacet &f) const
{
    if(!qh_qh){
        return false;
    }
    vertexT *v= 0;
    // Does not call qh_errexit(), TRY_QHULL_ not needed
    ridgeT *ridge= qh_nextridge3d(getRidgeT(), f.getFacetT(), &v);
    return (ridge!=0);
}//hasNextRidge3d

//! Return next ridge and optional vertex for a 3d facet and ridge
//! Does not use qh_errexit()
QhullRidge QhullRidge::
nextRidge3d(const QhullFacet &f, QhullVertex *nextVertex) const
{
    vertexT *v= 0;
    ridgeT *ridge= 0;
    if(qh_qh){
        // Does not call qh_errexit(), TRY_QHULL_ not needed
        ridge= qh_nextridge3d(getRidgeT(), f.getFacetT(), &v);
        if(!ridge){
            throw QhullError(10030, "Qhull error nextRidge3d:  missing next ridge for facet %d ridge %d.  Does facet contain ridge?", f.id(), id());
        }
    }
    if(nextVertex!=0){
        *nextVertex= QhullVertex(qh_qh, v);
    }
    return QhullRidge(qh_qh, ridge);
}//nextRidge3d

}//namespace orgQhull

#//!\name Global functions

using std::endl;
using std::ostream;
using orgQhull::QhullRidge;
using orgQhull::QhullVertex;

ostream &
operator<<(ostream &os, const QhullRidge &r)
{
    os << r.print("");
    return os;
}//<< QhullRidge

//! Duplicate of qh_printridge [io_r.c]
ostream &
operator<<(ostream &os, const QhullRidge::PrintRidge &pr)
{
    if(*pr.print_message){
        os << pr.print_message << " ";
    }else{
        os << "     - ";
    }
    QhullRidge r= *pr.ridge;
    os << "r" << r.id();
    if(r.getRidgeT()->tested){
        os << " tested";
    }
    if(r.getRidgeT()->nonconvex){
        os << " nonconvex";
    }
    if(r.getRidgeT()->mergevertex){
      os << " mergevertex";
    }
    if(r.getRidgeT()->mergevertex2){
      os << " mergevertex2";
    }
    if(r.getRidgeT()->simplicialtop){
      os << " simplicialtop";
    }
    if(r.getRidgeT()->simplicialbot){
      os << " simplicialbot";
    }
    os << endl;
    os << r.vertices().print("           vertices:");
    if(r.getRidgeT()->top && r.getRidgeT()->bottom){
        os << "           between f" << r.topFacet().id() << " and f" << r.bottomFacet().id() << endl;
    }else if(r.getRidgeT()->top){
        os << "           top f" << r.topFacet().id() << endl;
    }else if(r.getRidgeT()->bottom){
        os << "           bottom f" << r.bottomFacet().id() << endl;
    }

    return os;
}//<< PrintRidge


<|start_filename|>3rd-party/qhull/src/libqhull/stat.c<|end_filename|>
/*<html><pre>  -<a                             href="qh-stat.htm"
  >-------------------------------</a><a name="TOP">-</a>

   stat.c
   contains all statistics that are collected for qhull

   see qh-stat.htm and stat.h

   Copyright (c) 1993-2020 The Geometry Center.
   $Id: //main/2019/qhull/src/libqhull/stat.c#9 $$Change: 2953 $
   $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $
*/

#include "qhull_a.h"

/*============ global data structure ==========*/

#if qh_QHpointer
qhstatT *qh_qhstat=NULL;  /* global data structure */
#else
qhstatT qh_qhstat;   /* add "={0}" if this causes a compiler error */
#endif

/*========== functions in alphabetic order ================*/

/*-<a                             href="qh-stat.htm#TOC"
  >-------------------------------</a><a name="allstatA">-</a>

  qh_allstatA()
    define statistics in groups of 20

  notes:
    (otherwise, 'gcc -O2' uses too much memory)
    uses qhstat.next
*/
void qh_allstatA(void) {

   /* zdef_(type,name,doc,average) */
  zzdef_(zdoc, Zdoc2, "precision statistics", -1);
  zdef_(zinc, Znewvertex, NULL, -1);
  zdef_(wadd, Wnewvertex, "ave. distance of a new vertex to a facet", Znewvertex);
  zzdef_(wmax, Wnewvertexmax, "max. distance of a new vertex to a facet", -1);
  zdef_(wmax, Wvertexmax, "max. distance of an output vertex to a facet", -1);
  zdef_(wmin, Wvertexmin, "min. distance of an output vertex to a facet", -1);
  zdef_(wmin, Wmindenom, "min. denominator in hyperplane computation", -1);

  qhstat precision= qhstat next;  /* usually call qh_joggle_restart, printed if Q0 or QJn */
  zzdef_(zdoc, Zdoc3, "precision problems (corrected unless 'Q0' or an error)", -1);
  zzdef_(zinc, Zcoplanarridges, "coplanar half ridges in output", -1);
  zzdef_(zinc, Zconcaveridges, "concave half ridges in output", -1);
  zzdef_(zinc, Zflippedfacets, "flipped facets", -1);
  zzdef_(zinc, Zcoplanarhorizon, "coplanar horizon facets for new vertices", -1);
  zzdef_(zinc, Zcoplanarpart, "coplanar points during partitioning", -1);
  zzdef_(zinc, Zminnorm, "degenerate hyperplanes recomputed with gaussian elimination", -1);
  zzdef_(zinc, Znearlysingular, "nearly singular or axis-parallel hyperplanes", -1);
  zzdef_(zinc, Zback0, "zero divisors during back substitute", -1);
  zzdef_(zinc, Zgauss0, "zero divisors during gaussian elimination", -1);
  zzdef_(zinc, Zmultiridge, "dupridges with multiple neighbors", -1);
  zzdef_(zinc, Zflipridge, "dupridges with flip facet into good neighbor", -1);
  zzdef_(zinc, Zflipridge2, "dupridges with flip facet into good flip neighbor", -1);
}
void qh_allstatB(void) {
  zzdef_(zdoc, Zdoc1, "summary information", -1);
  zdef_(zinc, Zvertices, "number of vertices in output", -1);
  zdef_(zinc, Znumfacets, "number of facets in output", -1);
  zdef_(zinc, Znonsimplicial, "number of non-simplicial facets in output", -1);
  zdef_(zinc, Znowsimplicial, "simplicial facets that were non-simplicial", -1);
  zdef_(zinc, Znumridges, "number of ridges in output", -1);
  zdef_(zadd, Znumridges, "average number of ridges per facet", Znumfacets);
  zdef_(zmax, Zmaxridges, "maximum number of ridges", -1);
  zdef_(zadd, Znumneighbors, "average number of neighbors per facet", Znumfacets);
  zdef_(zmax, Zmaxneighbors, "maximum number of neighbors", -1);
  zdef_(zadd, Znumvertices, "average number of vertices per facet", Znumfacets);
  zdef_(zmax, Zmaxvertices, "maximum number of vertices", -1);
  zdef_(zadd, Znumvneighbors, "average number of neighbors per vertex", Zvertices);
  zdef_(zmax, Zmaxvneighbors, "maximum number of neighbors", -1);
  zdef_(wadd, Wcpu, "cpu seconds for qhull after input", -1);
  zdef_(zinc, Ztotvertices, "vertices created altogether", -1);
  zzdef_(zinc, Zsetplane, "facets created altogether", -1);
  zdef_(zinc, Ztotridges, "ridges created altogether", -1);
  zdef_(zinc, Zpostfacets, "facets before post merge", -1);
  zdef_(zadd, Znummergetot, "average merges per facet (at most 511)", Znumfacets);
  zdef_(zmax, Znummergemax, "  maximum merges for a facet (at most 511)", -1);
  zdef_(zinc, Zangle, NULL, -1);
  zdef_(wadd, Wangle, "average cosine (angle) of facet normals for all ridges", Zangle);
  zdef_(wmax, Wanglemax, "  maximum cosine of facet normals (flatest) across a ridge", -1);
  zdef_(wmin, Wanglemin, "  minimum cosine of facet normals (sharpest) across a ridge", -1);
  zdef_(wadd, Wareatot, "total area of facets", -1);
  zdef_(wmax, Wareamax, "  maximum facet area", -1);
  zdef_(wmin, Wareamin, "  minimum facet area", -1);
}
void qh_allstatC(void) {
  zdef_(zdoc, Zdoc9, "build hull statistics", -1);
  zzdef_(zinc, Zprocessed, "points processed", -1);
  zzdef_(zinc, Zretry, "retries due to precision problems", -1);
  zdef_(wmax, Wretrymax, "  max. random joggle", -1);
  zdef_(zmax, Zmaxvertex, "max. vertices at any one time", -1);
  zdef_(zinc, Ztotvisible, "ave. visible facets per iteration", Zprocessed);
  zdef_(zinc, Zinsidevisible, "  ave. visible facets without an horizon neighbor", Zprocessed);
  zdef_(zadd, Zvisfacettot,  "  ave. facets deleted per iteration", Zprocessed);
  zdef_(zmax, Zvisfacetmax,  "    maximum", -1);
  zdef_(zadd, Zvisvertextot, "ave. visible vertices per iteration", Zprocessed);
  zdef_(zmax, Zvisvertexmax, "    maximum", -1);
  zdef_(zinc, Ztothorizon, "ave. horizon facets per iteration", Zprocessed);
  zdef_(zadd, Znewfacettot,  "ave. new or merged facets per iteration", Zprocessed);
  zdef_(zmax, Znewfacetmax,  "    maximum (includes initial simplex)", -1);
  zdef_(wadd, Wnewbalance, "average new facet balance", Zprocessed);
  zdef_(wadd, Wnewbalance2, "  standard deviation", -1);
  zdef_(wadd, Wpbalance, "average partition balance", Zpbalance);
  zdef_(wadd, Wpbalance2, "  standard deviation", -1);
  zdef_(zinc, Zpbalance,  "  count", -1);
  zdef_(zinc, Zsearchpoints, "searches of all points for initial simplex", -1);
  zdef_(zinc, Zdetfacetarea, "determinants for facet area", -1);
  zdef_(zinc, Znoarea, "  determinants not computed because vertex too low", -1);
  zdef_(zinc, Zdetsimplex, "determinants for initial hull or voronoi vertices", -1);
  zdef_(zinc, Znotmax, "points ignored (!above max_outside)", -1);
  zdef_(zinc, Zpinchedapex, "points ignored (pinched apex)", -1);
  zdef_(zinc, Znotgood, "points ignored (!above a good facet)", -1);
  zdef_(zinc, Znotgoodnew, "points ignored (didn't create a good new facet)", -1);
  zdef_(zinc, Zgoodfacet, "good facets found", -1);
  zzdef_(zinc, Znumvisibility, "distance tests for facet visibility", -1);
  zdef_(zinc, Zdistvertex, "distance tests to report minimum vertex", -1);
  zzdef_(zinc, Ztotcheck, "points checked for facets' outer planes", -1);
  zzdef_(zinc, Zcheckpart, "  ave. distance tests per check", Ztotcheck);
}
void qh_allstatD(void) {
  zdef_(zinc, Zvisit, "resets of visit_id", -1);
  zdef_(zinc, Zvvisit, "  resets of vertex_visit", -1);
  zdef_(zmax, Zvisit2max, "  max visit_id/2", -1);
  zdef_(zmax, Zvvisit2max, "  max vertex_visit/2", -1);

  zdef_(zdoc, Zdoc4, "partitioning statistics (see previous for outer planes)", -1);
  zzdef_(zadd, Zdelvertextot, "total vertices deleted", -1);
  zdef_(zmax, Zdelvertexmax, "    maximum vertices deleted per iteration", -1);
  zdef_(zinc, Zfindbest, "calls to findbest", -1);
  zdef_(zadd, Zfindbesttot, " ave. facets tested", Zfindbest);
  zdef_(zmax, Zfindbestmax, " max. facets tested", -1);
  zdef_(zadd, Zfindcoplanar, " ave. coplanar search", Zfindbest);
  zdef_(zinc, Zfindnew, "calls to findbestnew", -1);
  zdef_(zadd, Zfindnewtot, " ave. facets tested", Zfindnew);
  zdef_(zmax, Zfindnewmax, " max. facets tested", -1);
  zdef_(zinc, Zfindnewjump, " ave. clearly better", Zfindnew);
  zdef_(zinc, Zfindnewsharp, " calls due to qh_sharpnewfacets", -1);
  zdef_(zinc, Zfindhorizon, "calls to findhorizon", -1);
  zdef_(zadd, Zfindhorizontot, " ave. facets tested", Zfindhorizon);
  zdef_(zmax, Zfindhorizonmax, " max. facets tested", -1);
  zdef_(zinc, Zfindjump,       " ave. clearly better", Zfindhorizon);
  zdef_(zinc, Znewbesthorizon, " new bestfacets during qh_findbesthorizon", -1);
  zdef_(zinc, Zpartangle, "angle tests for repartitioned coplanar points", -1);
  zdef_(zinc, Zpartcorner, "  repartitioned coplanar points above a corner facet", -1);
  zdef_(zinc, Zparthidden, "  repartitioned coplanar points above a hidden facet", -1);
  zdef_(zinc, Zparttwisted, "  repartitioned coplanar points above a twisted facet", -1);
}
void qh_allstatE(void) {
  zdef_(zinc, Zpartinside, "inside points", -1);
  zdef_(zinc, Zpartnear, "  near inside points kept with a facet", -1);
  zdef_(zinc, Zcoplanarinside, "  inside points that were coplanar with a facet", -1);
  zdef_(zinc, Zbestlower, "calls to findbestlower", -1);
  zdef_(zinc, Zbestlowerv, "  with search of vertex neighbors", -1);
  zdef_(zinc, Zbestlowerall, "  with rare search of all facets", -1);
  zdef_(zmax, Zbestloweralln, "  facets per search of all facets", -1);
  zdef_(wadd, Wmaxout, "difference in max_outside at final check", -1);
  zzdef_(zinc, Zpartitionall, "distance tests for initial partition", -1);
  zdef_(zinc, Ztotpartition, "partitions of a point", -1);
  zzdef_(zinc, Zpartition, "distance tests for partitioning", -1);
  zzdef_(zinc, Zdistcheck, "distance tests for checking flipped facets", -1);
  zzdef_(zinc, Zdistconvex, "distance tests for checking convexity", -1);
  zdef_(zinc, Zdistgood, "distance tests for checking good point", -1);
  zdef_(zinc, Zdistio, "distance tests for output", -1);
  zdef_(zinc, Zdiststat, "distance tests for statistics", -1);
  zzdef_(zinc, Zdistplane, "total number of distance tests", -1);
  zdef_(zinc, Ztotpartcoplanar, "partitions of coplanar points or deleted vertices", -1);
  zzdef_(zinc, Zpartcoplanar, "   distance tests for these partitions", -1);
  zdef_(zinc, Zcomputefurthest, "distance tests for computing furthest", -1);
}
void qh_allstatE2(void) {
  zdef_(zdoc, Zdoc5, "statistics for matching ridges", -1);
  zdef_(zinc, Zhashlookup, "total lookups for matching ridges of new facets", -1);
  zdef_(zinc, Zhashtests, "average number of tests to match a ridge", Zhashlookup);
  zdef_(zinc, Zhashridge, "total lookups of subridges (duplicates and boundary)", -1);
  zdef_(zinc, Zhashridgetest, "average number of tests per subridge", Zhashridge);
  zdef_(zinc, Zdupsame, "duplicated ridges in same merge cycle", -1);
  zdef_(zinc, Zdupflip, "duplicated ridges with flipped facets", -1);

  zdef_(zdoc, Zdoc6, "statistics for determining merges", -1);
  zdef_(zinc, Zangletests, "angles computed for ridge convexity", -1);
  zdef_(zinc, Zbestcentrum, "best merges used centrum instead of vertices",-1);
  zzdef_(zinc, Zbestdist, "distance tests for best merge", -1);
  zzdef_(zinc, Zcentrumtests, "distance tests for centrum convexity", -1);
  zzdef_(zinc, Zvertextests, "distance tests for vertex convexity", -1);
  zzdef_(zinc, Zdistzero, "distance tests for checking simplicial convexity", -1);
  zdef_(zinc, Zcoplanarangle, "coplanar angles in getmergeset", -1);
  zdef_(zinc, Zcoplanarcentrum, "coplanar centrums or vertices in getmergeset", -1);
  zdef_(zinc, Zconcaveridge, "concave ridges in getmergeset", -1);
  zdef_(zinc, Zconcavecoplanarridge, "concave-coplanar ridges in getmergeset", -1);
  zdef_(zinc, Ztwistedridge, "twisted ridges in getmergeset", -1);
}
void qh_allstatF(void) {
  zdef_(zdoc, Zdoc7, "statistics for merging", -1);
  zdef_(zinc, Zpremergetot, "merge iterations", -1);
  zdef_(zadd, Zmergeinittot, "ave. initial non-convex ridges per iteration", Zpremergetot);
  zdef_(zadd, Zmergeinitmax, "  maximum", -1);
  zdef_(zadd, Zmergesettot, "  ave. additional non-convex ridges per iteration", Zpremergetot);
  zdef_(zadd, Zmergesetmax, "  maximum additional in one pass", -1);
  zdef_(zadd, Zmergeinittot2, "initial non-convex ridges for post merging", -1);
  zdef_(zadd, Zmergesettot2, "  additional non-convex ridges", -1);
  zdef_(wmax, Wmaxoutside, "max distance of vertex or coplanar point above facet (w/roundoff)", -1);
  zdef_(wmin, Wminvertex, "max distance of vertex below facet (or roundoff)", -1);
  zdef_(zinc, Zwidefacet, "centrums frozen due to a wide merge", -1);
  zdef_(zinc, Zwidevertices, "centrums frozen due to extra vertices", -1);
  zzdef_(zinc, Ztotmerge, "total number of facets or cycles of facets merged", -1);
  zdef_(zinc, Zmergesimplex, "merged a simplex", -1);
  zdef_(zinc, Zonehorizon, "simplices merged into coplanar horizon", -1);
  zzdef_(zinc, Zcyclehorizon, "cycles of facets merged into coplanar horizon", -1);
  zzdef_(zadd, Zcyclefacettot, "  ave. facets per cycle", Zcyclehorizon);
  zdef_(zmax, Zcyclefacetmax, "  max. facets", -1);
  zdef_(zinc, Zmergeintocoplanar, "new facets merged into coplanar horizon", -1);
  zdef_(zinc, Zmergeintohorizon, "new facets merged into horizon", -1);
  zdef_(zinc, Zmergenew, "new facets merged", -1);
  zdef_(zinc, Zmergehorizon, "horizon facets merged into new facets", -1);
  zdef_(zinc, Zmergevertex, "vertices deleted by merging", -1);
  zdef_(zinc, Zcyclevertex, "vertices deleted by merging into coplanar horizon", -1);
  zdef_(zinc, Zdegenvertex, "vertices deleted by degenerate facet", -1);
  zdef_(zinc, Zmergeflipdup, "merges due to flipped facets in duplicated ridge", -1);
  zdef_(zinc, Zredundant, "merges due to redundant neighbors", -1);
  zdef_(zinc, Zredundantmerge, "  detected by qh_test_nonsimplicial_merge instead of qh_test_redundant_neighbors", -1);
  zdef_(zadd, Ztestvneighbor, "non-convex vertex neighbors", -1);
}
void qh_allstatG(void) {
  zdef_(zinc, Zacoplanar, "merges due to angle coplanar facets", -1);
  zdef_(wadd, Wacoplanartot, "  average merge distance", Zacoplanar);
  zdef_(wmax, Wacoplanarmax, "  maximum merge distance", -1);
  zdef_(zinc, Zcoplanar, "merges due to coplanar facets", -1);
  zdef_(wadd, Wcoplanartot, "  average merge distance", Zcoplanar);
  zdef_(wmax, Wcoplanarmax, "  maximum merge distance", -1);
  zdef_(zinc, Zconcave, "merges due to concave facets", -1);
  zdef_(wadd, Wconcavetot, "  average merge distance", Zconcave);
  zdef_(wmax, Wconcavemax, "  maximum merge distance", -1);
  zdef_(zinc, Zconcavecoplanar, "merges due to concave-coplanar facets", -1);
  zdef_(wadd, Wconcavecoplanartot, "  average merge distance", Zconcavecoplanar);
  zdef_(wmax, Wconcavecoplanarmax, "  maximum merge distance", -1);
  zdef_(zinc, Zavoidold, "coplanar/concave merges due to avoiding old merge", -1);
  zdef_(wadd, Wavoidoldtot, "  average merge distance", Zavoidold);
  zdef_(wmax, Wavoidoldmax, "  maximum merge distance", -1);
  zdef_(zinc, Zdegen, "merges due to degenerate facets", -1);
  zdef_(wadd, Wdegentot, "  average merge distance", Zdegen);
  zdef_(wmax, Wdegenmax, "  maximum merge distance", -1);
  zdef_(zinc, Zflipped, "merges due to removing flipped facets", -1);
  zdef_(wadd, Wflippedtot, "  average merge distance", Zflipped);
  zdef_(wmax, Wflippedmax, "  maximum merge distance", -1);
  zdef_(zinc, Zduplicate, "merges due to dupridges", -1);
  zdef_(wadd, Wduplicatetot, "  average merge distance", Zduplicate);
  zdef_(wmax, Wduplicatemax, "  maximum merge distance", -1);
  zdef_(zinc, Ztwisted, "merges due to twisted facets", -1);
  zdef_(wadd, Wtwistedtot, "  average merge distance", Ztwisted);
  zdef_(wmax, Wtwistedmax, "  maximum merge distance", -1);
}
void qh_allstatH(void) {
  zdef_(zdoc, Zdoc8, "statistics for vertex merges", -1);
  zzdef_(zinc, Zpinchduplicate, "merge pinched vertices for a duplicate ridge", -1);
  zzdef_(zinc, Zpinchedvertex, "merge pinched vertices for a dupridge", -1);
  zdef_(zinc, Zrenameshare, "renamed vertices shared by two facets", -1);
  zdef_(zinc, Zrenamepinch, "renamed vertices in a pinched facet", -1);
  zdef_(zinc, Zrenameall, "renamed vertices shared by multiple facets", -1);
  zdef_(zinc, Zfindfail, "rename failures due to duplicated ridges", -1);
  zdef_(zinc, Znewvertexridge, "  found new vertex in ridge", -1);
  zdef_(zinc, Zdelridge, "deleted ridges due to renamed vertices", -1);
  zdef_(zinc, Zdropneighbor, "dropped neighbors due to renamed vertices", -1);
  zdef_(zinc, Zdropdegen, "merge degenerate facets due to dropped neighbors", -1);
  zdef_(zinc, Zdelfacetdup, "  facets deleted because of no neighbors", -1);
  zdef_(zinc, Zremvertex, "vertices removed from facets due to no ridges", -1);
  zdef_(zinc, Zremvertexdel, "  deleted", -1);
  zdef_(zinc, Zretryadd, "retry qh_addpoint after merge pinched vertex", -1);
  zdef_(zadd, Zretryaddtot, "  tot. merge pinched vertex due to dupridge", -1);
  zdef_(zmax, Zretryaddmax, "  max. merge pinched vertex for a qh_addpoint", -1);
  zdef_(zinc, Zintersectnum, "vertex intersections for locating redundant vertices", -1);
  zdef_(zinc, Zintersectfail, "intersections failed to find a redundant vertex", -1);
  zdef_(zinc, Zintersect, "intersections found redundant vertices", -1);
  zdef_(zadd, Zintersecttot, "   ave. number found per vertex", Zintersect);
  zdef_(zmax, Zintersectmax, "   max. found for a vertex", -1);
  zdef_(zinc, Zvertexridge, NULL, -1);
  zdef_(zadd, Zvertexridgetot, "  ave. number of ridges per tested vertex", Zvertexridge);
  zdef_(zmax, Zvertexridgemax, "  max. number of ridges per tested vertex", -1);

  zdef_(zdoc, Zdoc10, "memory usage statistics (in bytes)", -1);
  zdef_(zadd, Zmemfacets, "for facets and their normals, neighbor and vertex sets", -1);
  zdef_(zadd, Zmemvertices, "for vertices and their neighbor sets", -1);
  zdef_(zadd, Zmempoints, "for input points, outside and coplanar sets, and qhT",-1);
  zdef_(zadd, Zmemridges, "for ridges and their vertex sets", -1);
} /* allstat */

void qh_allstatI(void) {
  qhstat vridges= qhstat next; /* printed in qh_produce_output2 if non-zero Zridge or Zridgemid */
  zzdef_(zdoc, Zdoc11, "Voronoi ridge statistics", -1);
  zzdef_(zinc, Zridge, "non-simplicial Voronoi vertices for all ridges", -1);
  zzdef_(wadd, Wridge, "  ave. distance to ridge", Zridge);
  zzdef_(wmax, Wridgemax, "  max. distance to ridge", -1);
  zzdef_(zinc, Zridgemid, "bounded ridges", -1);
  zzdef_(wadd, Wridgemid, "  ave. distance of midpoint to ridge", Zridgemid);
  zzdef_(wmax, Wridgemidmax, "  max. distance of midpoint to ridge", -1);
  zzdef_(zinc, Zridgeok, "bounded ridges with ok normal", -1);
  zzdef_(wadd, Wridgeok, "  ave. angle to ridge", Zridgeok);
  zzdef_(wmax, Wridgeokmax, "  max. angle to ridge", -1);
  zzdef_(zinc, Zridge0, "bounded ridges with near-zero normal", -1);
  zzdef_(wadd, Wridge0, "  ave. angle to ridge", Zridge0);
  zzdef_(wmax, Wridge0max, "  max. angle to ridge", -1);

  zdef_(zdoc, Zdoc12, "Triangulation statistics ('Qt')", -1);
  zdef_(zinc, Ztricoplanar, "non-simplicial facets triangulated", -1);
  zdef_(zadd, Ztricoplanartot, "  ave. new facets created (may be deleted)", Ztricoplanar);
  zdef_(zmax, Ztricoplanarmax, "  max. new facets created", -1);
  zdef_(zinc, Ztrinull, "null new facets deleted (duplicated vertex)", -1);
  zdef_(zinc, Ztrimirror, "mirrored pairs of new facets deleted (same vertices)", -1);
  zdef_(zinc, Ztridegen, "degenerate new facets in output (same ridge)", -1);
} /* allstat */

/*-<a                             href="qh-stat.htm#TOC"
  >-------------------------------</a><a name="allstatistics">-</a>

  qh_allstatistics()
    reset printed flag for all statistics
*/
void qh_allstatistics(void) {
  int i;

  for(i=ZEND; i--; )
    qhstat printed[i]= False;
} /* allstatistics */

#if qh_KEEPstatistics
/*-<a                             href="qh-stat.htm#TOC"
  >-------------------------------</a><a name="collectstatistics">-</a>

  qh_collectstatistics()
    collect statistics for qh.facet_list

*/
void qh_collectstatistics(void) {
  facetT *facet, *neighbor, **neighborp;
  vertexT *vertex, **vertexp;
  realT dotproduct, dist;
  int sizneighbors, sizridges, sizvertices, i;

  qh old_randomdist= qh RANDOMdist;
  qh RANDOMdist= False;
  zval_(Zmempoints)= qh num_points * qh normal_size + (int)sizeof(qhT) + (int)sizeof(qhstatT);
  zval_(Zmemfacets)= 0;
  zval_(Zmemridges)= 0;
  zval_(Zmemvertices)= 0;
  zval_(Zangle)= 0;
  wval_(Wangle)= 0.0;
  zval_(Znumridges)= 0;
  zval_(Znumfacets)= 0;
  zval_(Znumneighbors)= 0;
  zval_(Znumvertices)= 0;
  zval_(Znumvneighbors)= 0;
  zval_(Znummergetot)= 0;
  zval_(Znummergemax)= 0;
  zval_(Zvertices)= qh num_vertices - qh_setsize(qh del_vertices);
  if (qh MERGING || qh APPROXhull || qh JOGGLEmax < REALmax/2)
    wmax_(Wmaxoutside, qh max_outside);
  if (qh MERGING)
    wmin_(Wminvertex, qh min_vertex);
  if (!qh_checklists(qh facet_list)) {
    qh_fprintf(qh ferr, 6373, "qhull internal error: qh_checklists failed on qh_collectstatistics\n");
    if (!qh ERREXITcalled)
      qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  FORALLfacets
    facet->seen= False;
  if (qh DELAUNAY) {
    FORALLfacets {
      if (facet->upperdelaunay != qh UPPERdelaunay)
        facet->seen= True; /* remove from angle statistics */
    }
  }
  FORALLfacets {
    if (facet->visible && qh NEWfacets)
      continue;
    sizvertices= qh_setsize(facet->vertices);
    sizneighbors= qh_setsize(facet->neighbors);
    sizridges= qh_setsize(facet->ridges);
    zinc_(Znumfacets);
    zadd_(Znumvertices, sizvertices);
    zmax_(Zmaxvertices, sizvertices);
    zadd_(Znumneighbors, sizneighbors);
    zmax_(Zmaxneighbors, sizneighbors);
    zadd_(Znummergetot, facet->nummerge);
    i= facet->nummerge; /* avoid warnings */
    zmax_(Znummergemax, i);
    if (!facet->simplicial) {
      if (sizvertices == qh hull_dim) {
        zinc_(Znowsimplicial);
      }else {
        zinc_(Znonsimplicial);
      }
    }
    if (sizridges) {
      zadd_(Znumridges, sizridges);
      zmax_(Zmaxridges, sizridges);
    }
    zadd_(Zmemfacets, (int)sizeof(facetT) + qh normal_size + 2*(int)sizeof(setT)
       + SETelemsize * (sizneighbors + sizvertices));
    if (facet->ridges) {
      zadd_(Zmemridges,
        (int)sizeof(setT) + SETelemsize * sizridges + sizridges *
         ((int)sizeof(ridgeT) + (int)sizeof(setT) + SETelemsize * (qh hull_dim-1))/2);
    }
    if (facet->outsideset)
      zadd_(Zmempoints, (int)sizeof(setT) + SETelemsize * qh_setsize(facet->outsideset));
    if (facet->coplanarset)
      zadd_(Zmempoints, (int)sizeof(setT) + SETelemsize * qh_setsize(facet->coplanarset));
    if (facet->seen) /* Delaunay upper envelope */
      continue;
    facet->seen= True;
    FOREACHneighbor_(facet) {
      if (neighbor == qh_DUPLICATEridge || neighbor == qh_MERGEridge
          || neighbor->seen || !facet->normal || !neighbor->normal)
        continue;
      dotproduct= qh_getangle(facet->normal, neighbor->normal);
      zinc_(Zangle);
      wadd_(Wangle, dotproduct);
      wmax_(Wanglemax, dotproduct)
      wmin_(Wanglemin, dotproduct)
    }
    if (facet->normal) {
      FOREACHvertex_(facet->vertices) {
        zinc_(Zdiststat);
        qh_distplane(vertex->point, facet, &dist);
        wmax_(Wvertexmax, dist);
        wmin_(Wvertexmin, dist);
      }
    }
  }
  FORALLvertices {
    if (vertex->deleted)
      continue;
    zadd_(Zmemvertices, (int)sizeof(vertexT));
    if (vertex->neighbors) {
      sizneighbors= qh_setsize(vertex->neighbors);
      zadd_(Znumvneighbors, sizneighbors);
      zmax_(Zmaxvneighbors, sizneighbors);
      zadd_(Zmemvertices, (int)sizeof(vertexT) + SETelemsize * sizneighbors);
    }
  }
  qh RANDOMdist= qh old_randomdist;
} /* collectstatistics */
#endif /* qh_KEEPstatistics */

/*-<a                             href="qh-stat.htm#TOC"
  >-------------------------------</a><a name="freestatistics">-</a>

  qh_freestatistics(  )
    free memory used for statistics
*/
void qh_freestatistics(void) {

#if qh_QHpointer
  qh_free(qh_qhstat);
  qh_qhstat= NULL;
#endif
} /* freestatistics */

/*-<a                             href="qh-stat.htm#TOC"
  >-------------------------------</a><a name="initstatistics">-</a>

  qh_initstatistics( )
    allocate and initialize statistics

  notes:
    uses qh_malloc() instead of qh_memalloc() since mem.c not set up yet
    NOerrors -- qh_initstatistics can not use qh_errexit(), qh_fprintf, or qh.ferr
    On first call, only qhmem.ferr is defined.  qh_memalloc is not setup.
    Also invoked by QhullQh().
*/
void qh_initstatistics(void) {
  int i;
  realT realx;
  int intx;
  
#if qh_QHpointer
  if(qh_qhstat){  /* qh_initstatistics may be called from Qhull::resetStatistics() */
    qh_free(qh_qhstat);
    qh_qhstat= 0;
  }
  if (!(qh_qhstat= (qhstatT *)qh_malloc(sizeof(qhstatT)))) {
    qh_fprintf_stderr(6183, "qhull error (qh_initstatistics): insufficient memory\n");
    qh_exit(qh_ERRmem);  /* can not use qh_errexit() */
  }
#endif

  qh_allstatistics();
  qhstat next= 0;
  qh_allstatA();
  qh_allstatB();
  qh_allstatC();
  qh_allstatD();
  qh_allstatE();
  qh_allstatE2();
  qh_allstatF();
  qh_allstatG();
  qh_allstatH();
  qh_allstatI();
  if (qhstat next > (int)sizeof(qhstat id)) {
    qh_fprintf_stderr(6184, "qhull internal error (qh_initstatistics): increase size of qhstat.id[].  qhstat.next %d should be <= sizeof(qhstat id) %d\n", 
          qhstat next, (int)sizeof(qhstat id));
#if 0 /* for locating error, Znumridges should be duplicated */
    for(i=0; i < ZEND; i++) {
      int j;
      for(j=i+1; j < ZEND; j++) {
        if (qhstat id[i] == qhstat id[j]) {
          qh_fprintf_stderr(6185, "qhull error (qh_initstatistics): duplicated statistic %d at indices %d and %d\n",
              qhstat id[i], i, j);
        }
      }
    }
#endif
    qh_exit(qh_ERRqhull);  /* can not use qh_errexit() */
  }
  qhstat init[zinc].i= 0;
  qhstat init[zadd].i= 0;
  qhstat init[zmin].i= INT_MAX;
  qhstat init[zmax].i= INT_MIN;
  qhstat init[wadd].r= 0;
  qhstat init[wmin].r= REALmax;
  qhstat init[wmax].r= -REALmax;
  for(i=0; i < ZEND; i++) {
    if (qhstat type[i] > ZTYPEreal) {
      realx= qhstat init[(unsigned char)(qhstat type[i])].r;
      qhstat stats[i].r= realx;
    }else if (qhstat type[i] != zdoc) {
      intx= qhstat init[(unsigned char)(qhstat type[i])].i;
      qhstat stats[i].i= intx;
    }
  }
} /* initstatistics */

/*-<a                             href="qh-stat.htm#TOC"
  >-------------------------------</a><a name="newstats">-</a>

  qh_newstats( )
    returns True if statistics for zdoc

  returns:
    next zdoc
*/
boolT qh_newstats(int idx, int *nextindex) {
  boolT isnew= False;
  int start, i;

  if (qhstat type[qhstat id[idx]] == zdoc)
    start= idx+1;
  else
    start= idx;
  for(i= start; i < qhstat next && qhstat type[qhstat id[i]] != zdoc; i++) {
    if (!qh_nostatistic(qhstat id[i]) && !qhstat printed[qhstat id[i]])
        isnew= True;
  }
  *nextindex= i;
  return isnew;
} /* newstats */

/*-<a                             href="qh-stat.htm#TOC"
  >-------------------------------</a><a name="nostatistic">-</a>

  qh_nostatistic( index )
    true if no statistic to print
*/
boolT qh_nostatistic(int i) {

  if ((qhstat type[i] > ZTYPEreal
       &&qhstat stats[i].r == qhstat init[(unsigned char)(qhstat type[i])].r)
      || (qhstat type[i] < ZTYPEreal
          &&qhstat stats[i].i == qhstat init[(unsigned char)(qhstat type[i])].i))
    return True;
  return False;
} /* nostatistic */

#if qh_KEEPstatistics
/*-<a                             href="qh-stat.htm#TOC"
  >-------------------------------</a><a name="printallstatistics">-</a>

  qh_printallstatistics( fp, string )
    print all statistics with header 'string'
*/
void qh_printallstatistics(FILE *fp, const char *string) {

  qh_allstatistics();
  qh_collectstatistics();
  qh_printstatistics(fp, string);
  qh_memstatistics(fp);
}


/*-<a                             href="qh-stat.htm#TOC"
  >-------------------------------</a><a name="printstatistics">-</a>

  qh_printstatistics( fp, string )
    print statistics to a file with header 'string'
    skips statistics with qhstat.printed[] (reset with qh_allstatistics)

  see:
    qh_printallstatistics()
*/
void qh_printstatistics(FILE *fp, const char *string) {
  int i, k;
  realT ave; /* ignored */

  if (qh num_points != qh num_vertices || zval_(Zpbalance) == 0) {
    wval_(Wpbalance)= 0.0;
    wval_(Wpbalance2)= 0.0;
  }else
    wval_(Wpbalance2)= qh_stddev(zval_(Zpbalance), wval_(Wpbalance),
                                 wval_(Wpbalance2), &ave);
  if (zval_(Zprocessed) == 0)
    wval_(Wnewbalance2)= 0.0;
  else
    wval_(Wnewbalance2)= qh_stddev(zval_(Zprocessed), wval_(Wnewbalance),
                                 wval_(Wnewbalance2), &ave);
  qh_fprintf(fp, 9350, "\n\
%s\n\
qhull invoked by: %s | %s\n  %s with options:\n%s\n", 
    string, qh rbox_command, qh qhull_command, qh_version, qh qhull_options);

  qh_fprintf(fp, 9351, "\nprecision constants:\n\
 %6.2g max. abs. coordinate in the (transformed) input ('Qbd:n')\n\
 %6.2g max. roundoff error for distance computation ('En')\n\
 %6.2g max. roundoff error for angle computations\n\
 %6.2g min. distance for outside points ('Wn')\n\
 %6.2g min. distance for visible facets ('Vn')\n\
 %6.2g max. distance for coplanar facets ('Un')\n\
 %6.2g max. facet width for recomputing centrum and area\n\
",
  qh MAXabs_coord, qh DISTround, qh ANGLEround, qh MINoutside,
        qh MINvisible, qh MAXcoplanar, qh WIDEfacet);
  if (qh KEEPnearinside)
    qh_fprintf(fp, 9352, "\
 %6.2g max. distance for near-inside points\n", qh NEARinside);
  if (qh premerge_cos < REALmax/2) qh_fprintf(fp, 9353, "\
 %6.2g max. cosine for pre-merge angle\n", qh premerge_cos);
  if (qh PREmerge) qh_fprintf(fp, 9354, "\
 %6.2g radius of pre-merge centrum\n", qh premerge_centrum);
  if (qh postmerge_cos < REALmax/2) qh_fprintf(fp, 9355, "\
 %6.2g max. cosine for post-merge angle\n", qh postmerge_cos);
  if (qh POSTmerge) qh_fprintf(fp, 9356, "\
 %6.2g radius of post-merge centrum\n", qh postmerge_centrum);
  qh_fprintf(fp, 9357, "\
 %6.2g max. distance for merging two simplicial facets\n\
 %6.2g max. roundoff error for arithmetic operations\n\
 %6.2g min. denominator for division\n\
  zero diagonal for Gauss: ", qh ONEmerge, REALepsilon, qh MINdenom);
  for(k=0; k < qh hull_dim; k++)
    qh_fprintf(fp, 9358, "%6.2e ", qh NEARzero[k]);
  qh_fprintf(fp, 9359, "\n\n");
  for(i=0 ; i < qhstat next; )
    qh_printstats(fp, i, &i);
} /* printstatistics */
#endif /* qh_KEEPstatistics */

/*-<a                             href="qh-stat.htm#TOC"
  >-------------------------------</a><a name="printstatlevel">-</a>

  qh_printstatlevel( fp, id )
    print level information for a statistic

  notes:
    nop if id >= ZEND, printed, or same as initial value
*/
void qh_printstatlevel(FILE *fp, int id) {

  if (id >= ZEND || qhstat printed[id])
    return;
  if (qhstat type[id] == zdoc) {
    qh_fprintf(fp, 9360, "%s\n", qhstat doc[id]);
    return;
  }
  if (qh_nostatistic(id) || !qhstat doc[id])
    return;
  qhstat printed[id]= True;
  if (qhstat count[id] != -1
      && qhstat stats[(unsigned char)(qhstat count[id])].i == 0)
    qh_fprintf(fp, 9361, " *0 cnt*");
  else if (qhstat type[id] >= ZTYPEreal && qhstat count[id] == -1)
    qh_fprintf(fp, 9362, "%7.2g", qhstat stats[id].r);
  else if (qhstat type[id] >= ZTYPEreal && qhstat count[id] != -1)
    qh_fprintf(fp, 9363, "%7.2g", qhstat stats[id].r/ qhstat stats[(unsigned char)(qhstat count[id])].i);
  else if (qhstat type[id] < ZTYPEreal && qhstat count[id] == -1)
    qh_fprintf(fp, 9364, "%7d", qhstat stats[id].i);
  else if (qhstat type[id] < ZTYPEreal && qhstat count[id] != -1)
    qh_fprintf(fp, 9365, "%7.3g", (realT) qhstat stats[id].i / qhstat stats[(unsigned char)(qhstat count[id])].i);
  qh_fprintf(fp, 9366, " %s\n", qhstat doc[id]);
} /* printstatlevel */


/*-<a                             href="qh-stat.htm#TOC"
  >-------------------------------</a><a name="printstats">-</a>

  qh_printstats( fp, index, nextindex )
    print statistics for a zdoc group

  returns:
    next zdoc if non-null
*/
void qh_printstats(FILE *fp, int idx, int *nextindex) {
  int j, nexti;

  if (qh_newstats(idx, &nexti)) {
    qh_fprintf(fp, 9367, "\n");
    for (j=idx; j<nexti; j++)
      qh_printstatlevel(fp, qhstat id[j]);
  }
  if (nextindex)
    *nextindex= nexti;
} /* printstats */

#if qh_KEEPstatistics

/*-<a                             href="qh-stat.htm#TOC"
  >-------------------------------</a><a name="stddev">-</a>

  qh_stddev( num, tot, tot2, ave )
    compute the standard deviation and average from statistics

    tot2 is the sum of the squares
  notes:
    computes r.m.s.:
      (x-ave)^2
      == x^2 - 2x tot/num +   (tot/num)^2
      == tot2 - 2 tot tot/num + tot tot/num
      == tot2 - tot ave
*/
realT qh_stddev(int num, realT tot, realT tot2, realT *ave) {
  realT stddev;

  if (num <= 0) {
    qh_fprintf(qh ferr, 7101, "qhull warning (qh_stddev): expecting num > 0.  Got num %d, tot %4.4g, tot2 %4.4g.  Returning 0.0\n",
      num, tot, tot2);
    return 0.0;
  }
  *ave= tot/num;
  stddev= sqrt(fabs(tot2/num - *ave * *ave));
  return stddev;
} /* stddev */
#else
realT qh_stddev(int num, realT tot, realT tot2, realT *ave) { /* for qhull-exports.def */
  QHULL_UNUSED(num)
  QHULL_UNUSED(tot)
  QHULL_UNUSED(tot2)
  QHULL_UNUSED(ave)

  return 0.0;
}
#endif /* qh_KEEPstatistics */

#if !qh_KEEPstatistics
void    qh_collectstatistics(void) {}
void    qh_printallstatistics(FILE *fp, const char *string) {}
void    qh_printstatistics(FILE *fp, const char *string) {}
#endif



<|start_filename|>include/mc_rtc/io_utils.h<|end_filename|>
/*
 * Copyright 2015-2020 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once
#include <algorithm>
#include <functional>
#include <limits>
#include <sstream>
#include <string>

namespace mc_rtc
{
namespace io
{

/** Converts a container to a string
 *
 * @tparam Container A container whose underlying type is convertible to
 * std::string. The container must define Container::value_type.
 * @param c Container to convert
 * @param delimiter Separator between the elements. By default they are
 * coma-separated
 *
 * @return A string of all the container elements.
 *
 * @anchor to_string
 */
template<typename Container,
         typename std::enable_if<!std::is_arithmetic<typename Container::value_type>::value, int>::type = 0>
std::string to_string(const Container & c, const std::string & delimiter = ", ")
{
  if(c.cbegin() == c.cend())
  {
    return "";
  }
  std::string out = *c.cbegin();
  for(auto it = std::next(c.cbegin()); it != c.cend(); ++it)
  {
    out += delimiter;
    out += *it;
  }
  return out;
}

/**
 * @brief Variant of \ref to_string that converts numeric types to string
 *
 * @param precision Number of digits to keep. The default precision corresponds
 * to the number of decimal digits that can be represented without change.
 */
template<typename Container,
         typename std::enable_if<std::is_arithmetic<typename Container::value_type>::value, int>::type = 0>
std::string to_string(const Container & c,
                      const std::string & delimiter = ", ",
                      const unsigned precision = std::numeric_limits<typename Container::value_type>::digits10)
{
  if(c.cbegin() == c.cend())
  {
    return "";
  }
  std::ostringstream out;
  out.precision(precision);
  out << std::fixed << *c.cbegin();
  for(auto it = std::next(c.cbegin()); it != c.cend(); ++it)
  {
    out << delimiter;
    out << std::fixed << *it;
  }
  return out.str();
}

/** Converts a container to a string
 *
 * Example:
 *\code{.cpp}
 * const auto availabeRobots = mc_rtc::io::to_string(robots(), [](const mc_rbdyn::Robot & r) -> const std::string & {
 *return r.name(); });
 \endcode
 *
 * @tparam Container An iterable container whose unerlying type is convertible
 * to std::string. The container must define Container::value_type.
 *
 * @param c Container to convert
 * @param get_value Lambda or functor that converts a single element from the container (of type Container::value_type)
 to std::string
 *
 * @return A string of all the container elements.
 */
template<typename Container,
         typename Callback,
         typename std::enable_if<!std::is_convertible<Callback, std::string>::value, int>::type = 0>
std::string to_string(const Container & c, Callback && get_value, const std::string & delimiter = ", ")
{
  if(c.cbegin() == c.cend())
  {
    return "";
  }
  std::string out = get_value(*c.cbegin());
  for(auto it = std::next(c.cbegin()); it != c.cend(); ++it)
  {
    out += delimiter;
    out += get_value(*it);
  }
  return out;
}

} // namespace io
} // namespace mc_rtc


<|start_filename|>doc/_data/schemas/MetaTask/LIPMStabilizerTask.json<|end_filename|>
{
  "allOf":
  [
    {
      "type": "object",
      "title": "mc_tasks::lipm_stabilizer::StabilizerTask",
      "description": "The stabilizer task attempts to make the real system track as best as possible the desired state of the CoM (position, velocity, acceleration) and ZMP based on the LIPM model. The reference desired dynamic state must be valid, and continuous (i.e obtained from a walking MPC). A suitable <a href=\"{{site.baseurl}}/tutorials/recipes/observers.html\">state observation pipeline</a> is required to observe the CoM position and velocity and ZMP of the real system (e.g [Encoder, KinematicInertialObserver]). Default gains are defined in the robot module ({% doxygen mc_rbdyn::RobotModule %}) and may be overriden from configuration (the default gains reported in this schemas are those of mc_rtc in case none is provided by the RobotModule).<br />See the <a href=\"{{site.baseurl}}/tutorials/recipes/lipm-stabilizer.html\">LIPM Stabilizer tutorial</a> and the API Documentation {% doxygen mc_tasks::lipm_stabilizer::StabilizerTask %} for further information.",
      "properties":
      {
        "type": { "enum": ["lipm_stabilizer"] },
        "name": { "type": "string" },
        "robot": { "$ref": "/../../common/MetaTask_robot.json" }
      }
    },
    {
      "$ref": "/../../common/LIPMStabilizerTaskConfiguration.json"
    },
    {
      "allOf":
      [
        {
          "properties":
          {
            "*" :
            {
              "type": "object",
              "title": "Robot-specific stabilizer configuration",
              "description": "You may specify different configuration entries on a per-robot basis. In this case the key is the robot name and the values are the configuration parameters you want to modify for that robot"
            }
          }
        },
        {
          "properties":
          {
            "*" :
            {
              "$ref": "/../../common/LIPMStabilizerTaskConfiguration.json"
            }
          }
        }
      ]
    }
  ],
  "required": ["type", "contacts"]
}


<|start_filename|>doc/_data/schemas/Observers/ObserverPipeline.json<|end_filename|>
{
  "type": "object",
  "title": "mc_observers::ObserverPipeline",
  "description": "State observation pipeline.<br/>Observers are responsible for estimating some of the robot properties from sensor measurements and/or fusing several source of information (e.g the Encoder observer estimates the joint position and velocity based on joint sensors, the KinematicInertial observers the state of the floating base from kinematics and IMU information, etc).<br/>These observers may be grouped into a \"State observation pipeline\", that will run each observer sequentially. Some observers may be used to update the state of the real robots instances used by the controller, while others may only be used for logging estimated values for comparison purposes.",
  "properties":
  {
    "name": {"type":"string", "description":"Name of this pipeline"},
    "run": {"type": "boolean", "default": true, "description": "When true, run each observer in this pipeline"},
    "update": {"type": "boolean", "default": true, "description": "When true, update the state of real robots instances (depending on each observer's configuration). Requires \"run\" to be true."},
    "log": {"type": "boolean", "default": true, "description": "When true, log estimated values"},
    "gui": {"type": "boolean", "default": false, "description": "When true, show this pipeline and its observers in the gui. All observers will be displayed acording to their configuration in \"observers\" (each observer is visible by default)."},
    "observers":
    {
      "type": "array",
      "description": "Sequence of observers",
      "items":
      {
        "type": "object",
        "title": "Observer",
        "properties":
        {
          "type": {"type": "string", "description": "Name of the observer. This is the name exported by each observer library, for default observers see the entries in \"Observer objects\". The main default observers are:<br /><ul><li><b>Encoder</b>: estimates a robot's joint state (position and velocity)</li><li><b>KinematicInertial</b>: estimates a robot's floating base state based on IMU and a kinematic information</li><li><b>BodySensor</b>: sets the floating base state based on a BodySensor (typically used to obtain groundtruth state from a simulator)</li></ul>"},
          "name": { "type": "string", "description": "Name of the observer (optional unless there are multiple observers with the same name type in this pipeline)" },
          "update": {"type": "boolean", "default": true, "description": "When true, update the state of real robots instances (requires the observer's run to have succeeded)"},
          "log": {"type": "boolean", "default": true, "description": "When true, log estimated values"},
          "gui": {"type": "boolean", "default": true, "description": "When true, show observer in the gui"},
          "required": {"type": "boolean", "default": true, "description": "When false, ignore this observer if it could not be loaded"},
          "successRequired": {"type": "boolean", "default": true, "description": "When true, the whole pipeline is considered to have failed if this observer's run() returns false"},
          "config":
          {
            "type": "object",
            "title": "Observer Configuration",
            "description": "Configuration options for the observer \"type\" (<a href=\"http://jrl-umi3218.github.io/mc_rtc/json.html#Observers/Encoder\">Encoder</a>, <a href=\"http://jrl-umi3218.github.io/mc_rtc/json.html#Observers/KinematicInertial\">KinematicInertial</a>, <a href=\"http://jrl-umi3218.github.io/mc_rtc/json.html#Observers/BodySensor\">BodySensor</a>, etc)"
          }
        },
        "required": ["type"]
      }
    }
  },
  "required": ["name"]
}


<|start_filename|>doc/_data/schemas/SpaceVecAlg/ImpedanceVecd.json<|end_filename|>
{
  "title": "sva::ImpedanceVecd",
  "type": "object",
  "properties":
  {
    "angular": { "$ref": "/../../Eigen/Vector3d.json" },
    "linear": { "$ref": "/../../Eigen/Vector3d.json" }
  },
  "required": ["angular", "linear"],
  "additionalProperties": false
}


<|start_filename|>include/mc_rtc/gui/Form.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_rtc/gui/details/traits.h>
#include <mc_rtc/gui/elements.h>

namespace mc_rtc
{

namespace gui
{

/** Create a user-defined form
 *
 * A form is composed of 1 or more elements (FormInput) that can be required for the form completion (this must be
 * checked on client side)
 *
 * \tparam Callback Will be called when the form is completed on client side
 *
 */
template<typename Callback>
struct FormImpl : public CallbackElement<Element, Callback>
{
  static constexpr auto type = Elements::Form;

  template<typename... Args>
  FormImpl(const std::string & name, Callback cb, Args &&... args) : CallbackElement<Element, Callback>(name, cb)
  {
    mc_rtc::MessagePackBuilder builder(data_);
    count_ = sizeof...(Args);
    write_elements(builder, std::forward<Args>(args)...);
    data_size_ = builder.finish();
  }

  template<typename T>
  void addElement(T && element)
  {
    count_ += 1;
    using ElementT = typename std::decay<T>::type;
    if(ElementT::is_dynamic())
    {
      addDynamicElement(std::forward<T>(element));
    }
    else
    {
      std::vector<char> data = data_;
      mc_rtc::MessagePackBuilder builder(data_);
      builder.write_object(data.data(), data_size_);
      builder.start_array(element.write_size());
      element.write(builder);
      builder.finish_array();
      data_size_ = builder.finish();
    }
  }

  static constexpr size_t write_size()
  {
    return CallbackElement<Element, Callback>::write_size() + 1;
  }

  void write(mc_rtc::MessagePackBuilder & builder)
  {
    CallbackElement<Element, Callback>::write(builder);
    builder.start_array(count_);
    for(const auto & el : dynamic_elements_)
    {
      el(builder);
    }
    builder.write_object(data_.data(), data_size_);
    for(size_t i = dynamic_elements_.size() + 1; i < count_; ++i)
    {
      builder.write_object(nullptr, 0);
    }
    builder.finish_array();
  }

  /** Invalid element */
  FormImpl() {}

private:
  template<typename... Args>
  void write_elements(mc_rtc::MessagePackBuilder &, Args &&...)
  {
  }

  template<typename Arg, typename... Args>
  void write_elements(mc_rtc::MessagePackBuilder & builder, Arg && element, Args &&... args)
  {
    using ElementT = typename std::decay<Arg>::type;
    if(ElementT::is_dynamic())
    {
      addDynamicElement(std::forward<Arg>(element));
    }
    else
    {
      builder.start_array(element.write_size());
      element.write(builder);
      builder.finish_array();
    }
    write_elements(builder, std::forward<Args>(args)...);
  }

  template<typename T>
  void addDynamicElement(T && element)
  {
    auto callback = [element](mc_rtc::MessagePackBuilder & builder) mutable {
      builder.start_array(element.write_size());
      element.write(builder);
      builder.finish_array();
    };
    dynamic_elements_.push_back(callback);
  }

  size_t count_;
  std::vector<std::function<void(mc_rtc::MessagePackBuilder &)>> dynamic_elements_;
  std::vector<char> data_;
  size_t data_size_;
};

template<typename Derived, Elements element>
struct FormElement
{
  static constexpr auto type = element;

  static constexpr size_t write_size()
  {
    return 3 + Derived::write_size_();
  }

  static constexpr bool is_dynamic()
  {
    return false;
  }

  void write(mc_rtc::MessagePackBuilder & builder)
  {
    builder.write(name_);
    builder.write(static_cast<typename std::underlying_type<Elements>::type>(type));
    builder.write(required_);
    static_cast<Derived &>(*this).write_(builder);
  }

  /** Invalid element */
  FormElement() {}

protected:
  FormElement(const std::string & name, bool required) : name_(name), required_(required) {}

  std::string name_;
  bool required_;
};

namespace internal
{

/** This helper class avoids forming reference to void arguments */
template<typename T, typename Callback>
struct CallbackOrValue
{
  static_assert(details::CheckReturnType<Callback, T>::value, "Callback should return the right type of value");

  Callback callback;

  void write(mc_rtc::MessagePackBuilder & builder)
  {
    builder.write(callback());
  }
};

template<typename T>
struct CallbackOrValue<T, void>
{
  T value;

  void write(mc_rtc::MessagePackBuilder & builder)
  {
    builder.write(value);
  }
};

template<typename T, Elements element, typename DataCallback = void>
struct FormDataInput : public FormElement<FormDataInput<T, element, DataCallback>, element>
{
  FormDataInput(const std::string & name, bool required, CallbackOrValue<T, DataCallback> def)
  : FormElement<FormDataInput<T, element, DataCallback>, element>(name, required), def_(def), has_def_(true)
  {
  }

  FormDataInput(const std::string & name, bool required) : FormDataInput<T, element>(name, required, {})
  {
    has_def_ = false;
  }

  static constexpr size_t write_size_()
  {
    return 2;
  }

  static constexpr bool is_dynamic()
  {
    return !std::is_same<DataCallback, void>::value;
  }

  void write_(mc_rtc::MessagePackBuilder & builder)
  {
    def_.write(builder);
    builder.write(has_def_);
  }

  /** Invalid element */
  FormDataInput() {}

private:
  CallbackOrValue<T, DataCallback> def_;
  bool has_def_;
};

} // namespace internal

#define MAKE_DATA_INPUT_HELPER(DATAT, ELEMENT, FNAME)                                                        \
  inline internal::FormDataInput<DATAT, ELEMENT> FNAME(const std::string & name, bool required)              \
  {                                                                                                          \
    return {name, required};                                                                                 \
  }                                                                                                          \
                                                                                                             \
  inline internal::FormDataInput<DATAT, ELEMENT> FNAME(const std::string & name, bool required, DATAT value) \
  {                                                                                                          \
    return {name, required, internal::CallbackOrValue<DATAT, void>{value}};                                  \
  }                                                                                                          \
                                                                                                             \
  template<typename Callback, typename std::enable_if<details::is_getter<Callback>(), int>::type = 0>        \
  inline internal::FormDataInput<DATAT, ELEMENT, Callback> FNAME(const std::string & name, bool required,    \
                                                                 Callback callback)                          \
  {                                                                                                          \
    return {name, required, internal::CallbackOrValue<DATAT, Callback>{callback}};                           \
  }

MAKE_DATA_INPUT_HELPER(bool, Elements::Checkbox, FormCheckbox)
MAKE_DATA_INPUT_HELPER(int, Elements::IntegerInput, FormIntegerInput)
MAKE_DATA_INPUT_HELPER(double, Elements::NumberInput, FormNumberInput)
MAKE_DATA_INPUT_HELPER(std::string, Elements::StringInput, FormStringInput)

#undef MAKE_DATA_INPUT_HELPER

namespace internal
{

template<typename T, typename DataCallback = void>
struct FormArrayInput : public FormElement<FormArrayInput<T, DataCallback>, Elements::ArrayInput>
{
  FormArrayInput(const std::string & name, bool required, CallbackOrValue<T, DataCallback> def, bool fixed_size = true)
  : FormElement<FormArrayInput<T, DataCallback>, Elements::ArrayInput>(name, required), def_(def),
    fixed_size_(fixed_size), has_def_(true)
  {
  }

  FormArrayInput(const std::string & name, bool required, bool fixed_size = false)
  : FormArrayInput<T>(name, required, {}, fixed_size)
  {
    has_def_ = false;
  }

  static constexpr size_t write_size_()
  {
    return 3;
  }

  static constexpr bool is_dynamic()
  {
    return !std::is_same<DataCallback, void>::value;
  }

  void write_(mc_rtc::MessagePackBuilder & builder)
  {
    def_.write(builder);
    builder.write(fixed_size_);
    builder.write(has_def_);
  }

  /** Invalid element */
  FormArrayInput() {}

private:
  CallbackOrValue<T, DataCallback> def_;
  bool fixed_size_;
  bool has_def_;
};

} // namespace internal

template<typename T>
inline internal::FormArrayInput<T> FormArrayInput(const std::string & name, bool required, bool fixed_size = false)
{
  return {name, required, fixed_size};
}

template<typename T, typename std::enable_if<!details::is_getter<T>(), int>::type = 0>
inline internal::FormArrayInput<T> FormArrayInput(const std::string & name,
                                                  bool required,
                                                  const T & value,
                                                  bool fixed_size = true)
{
  return {name, required, internal::CallbackOrValue<T, void>{value}, fixed_size};
}

template<typename Callback, typename std::enable_if<details::is_getter<Callback>(), int>::type = 0>
inline internal::FormArrayInput<details::ReturnTypeT<Callback>, Callback> FormArrayInput(const std::string & name,
                                                                                         bool required,
                                                                                         Callback callback,
                                                                                         bool fixed_size = true)
{
  using ReturnT = details::ReturnTypeT<Callback>;
  return {name, required, internal::CallbackOrValue<ReturnT, Callback>{callback}, fixed_size};
}

struct FormComboInput : public FormElement<FormComboInput, Elements::ComboInput>
{
  inline FormComboInput(const std::string & name,
                        bool required,
                        const std::vector<std::string> & values,
                        bool send_index = false,
                        int def = -1)
  : FormElement<FormComboInput, Elements::ComboInput>(name, required), values_(values), send_index_(send_index),
    def_(def)
  {
  }

  static constexpr size_t write_size_()
  {
    return 3;
  }

  inline void write_(mc_rtc::MessagePackBuilder & builder)
  {
    builder.write(values_);
    builder.write(send_index_);
    builder.write(def_);
  }

  /** Invalid element */
  inline FormComboInput() {}

private:
  std::vector<std::string> values_;
  bool send_index_ = false;
  int def_;
};

struct FormDataComboInput : public FormElement<FormDataComboInput, Elements::DataComboInput>
{
  static constexpr auto type = Elements::DataComboInput;

  inline FormDataComboInput(const std::string & name,
                            bool required,
                            const std::vector<std::string> & ref,
                            bool send_index = false)
  : FormElement<FormDataComboInput, Elements::DataComboInput>(name, required), ref_(ref), send_index_(send_index)
  {
  }

  static constexpr size_t write_size_()
  {
    return 2;
  }

  inline void write_(mc_rtc::MessagePackBuilder & builder)
  {
    builder.write(ref_);
    builder.write(send_index_);
  }

  /** Invalid element */
  inline FormDataComboInput() {}

private:
  std::vector<std::string> ref_;
  bool send_index_;
};

/** Helper to create a Form element */
template<typename Callback, typename... Args>
FormImpl<Callback> Form(const std::string & name, Callback cb, Args &&... args)
{
  return FormImpl<Callback>(name, cb, std::forward<Args>(args)...);
}

} // namespace gui

} // namespace mc_rtc


<|start_filename|>tests/testSchemaExamples.in.cpp<|end_filename|>
/*
 * Copyright 2015-2021 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include <boost/filesystem.hpp>
#include <boost/mpl/list.hpp>
#include <boost/test/unit_test.hpp>
namespace bfs = boost::filesystem;

#include <mc_tasks/AddRemoveContactTask.h>
#include <mc_tasks/AdmittanceTask.h>
#include <mc_tasks/BSplineTrajectoryTask.h>
#include <mc_tasks/CoMTask.h>
#include <mc_tasks/CoPTask.h>
#include <mc_tasks/ComplianceTask.h>
#include <mc_tasks/EndEffectorTask.h>
#include <mc_tasks/ExactCubicTrajectoryTask.h>
#include <mc_tasks/GazeTask.h>
#include <mc_tasks/LookAtSurfaceTask.h>
#include <mc_tasks/LookAtTask.h>
#include <mc_tasks/MomentumTask.h>
#include <mc_tasks/OrientationTask.h>
#include <mc_tasks/PositionBasedVisServoTask.h>
#include <mc_tasks/PositionTask.h>
#include <mc_tasks/PostureTask.h>
#include <mc_tasks/RelativeEndEffectorTask.h>
#include <mc_tasks/SurfaceTransformTask.h>
#include <mc_tasks/VectorOrientationTask.h>
#include <mc_tasks/lipm_stabilizer/StabilizerTask.h>

#include <mc_tasks/MetaTaskLoader.h>

#include "utils.h"

static bool configured = configureRobotLoader();
/* Create Robots with one robot and an environment for the purpose of the test */
static auto rm = mc_rbdyn::RobotLoader::get_robot_module("JVRC1");
static auto em =
    mc_rbdyn::RobotLoader::get_robot_module("env", std::string(mc_rtc::MC_ENV_DESCRIPTION_PATH), std::string("ground"));
static auto robots = mc_rbdyn::loadRobotAndEnv(*rm, *em);
static std::unique_ptr<mc_solver::QPSolver> solver_ptr = [](std::shared_ptr<mc_rbdyn::Robots> robots) {
  std::unique_ptr<mc_solver::QPSolver> solver(new mc_solver::QPSolver(robots, 0.005));
  solver->logger(std::make_shared<mc_rtc::Logger>(mc_rtc::Logger::Policy::NON_THREADED, ".", ""));
  solver->logger()->start("schema-examples", 0.005);
  solver->gui(std::make_shared<mc_rtc::gui::StateBuilder>());
  solver->updateNrVars();
  solver->updateConstrSize();
  return solver;
}(robots);
static mc_solver::QPSolver & solver = *solver_ptr;

static const bfs::path EXAMPLE_PATH = "@EXAMPLE_PATH@";
static const bfs::path JSON_EXAMPLES = EXAMPLE_PATH / "json" / "MetaTask";
static const bfs::path YAML_EXAMPLES = EXAMPLE_PATH / "yaml" / "MetaTask";

template<typename T>
struct TaskExamples
{
  static_assert(sizeof(T) == 0, "This must be specialized before being used");
};

#define TEST_TASK(TaskT, TaskN)                                         \
  template<>                                                            \
  struct TaskExamples<TaskT>                                            \
  {                                                                     \
    static const bfs::path json()                                       \
    {                                                                   \
      return JSON_EXAMPLES / #TaskN ".json";                            \
    }                                                                   \
    static const bfs::path yaml()                                       \
    {                                                                   \
      return YAML_EXAMPLES / #TaskN ".yaml";                            \
    }                                                                   \
  };                                                                    \
  BOOST_AUTO_TEST_CASE(TaskN)                                           \
  {                                                                     \
    {                                                                   \
      mc_rtc::Configuration json(TaskExamples<TaskT>::json().string()); \
      auto task = mc_tasks::MetaTaskLoader::load<TaskT>(solver, json);  \
      size_t nLogEntriesBefore = solver.logger()->size();               \
      size_t nGUIEntriesBefore = solver.gui()->size();                  \
      solver.addTask(task);                                             \
      solver.removeTask(task);                                          \
      BOOST_REQUIRE(nLogEntriesBefore == solver.logger()->size());      \
      BOOST_REQUIRE(nGUIEntriesBefore == solver.gui()->size());         \
    }                                                                   \
    {                                                                   \
      mc_rtc::Configuration yaml(TaskExamples<TaskT>::yaml().string()); \
      auto task = mc_tasks::MetaTaskLoader::load<TaskT>(solver, yaml);  \
    }                                                                   \
  }

TEST_TASK(mc_tasks::AddContactTask, AddContactTask)
TEST_TASK(mc_tasks::RemoveContactTask, RemoveContactTask)
TEST_TASK(mc_tasks::force::AdmittanceTask, AdmittanceTask)
TEST_TASK(mc_tasks::BSplineTrajectoryTask, BSplineTrajectoryTask)
TEST_TASK(mc_tasks::force::ComplianceTask, ComplianceTask)
TEST_TASK(mc_tasks::CoMTask, CoMTask)
TEST_TASK(mc_tasks::force::CoPTask, CoPTask)
TEST_TASK(mc_tasks::EndEffectorTask, EndEffectorTask)
TEST_TASK(mc_tasks::ExactCubicTrajectoryTask, ExactCubicTrajectoryTask)
TEST_TASK(mc_tasks::GazeTask, GazeTask)
TEST_TASK(mc_tasks::lipm_stabilizer::StabilizerTask, LIPMStabilizerTask)
TEST_TASK(mc_tasks::LookAtSurfaceTask, LookAtSurfaceTask)
TEST_TASK(mc_tasks::LookAtTask, LookAtTask)
TEST_TASK(mc_tasks::MomentumTask, MomentumTask)
TEST_TASK(mc_tasks::OrientationTask, OrientationTask)
TEST_TASK(mc_tasks::PositionBasedVisServoTask, PBVSTask)
TEST_TASK(mc_tasks::PositionTask, PositionTask)
TEST_TASK(mc_tasks::PostureTask, PostureTask)
TEST_TASK(mc_tasks::RelativeEndEffectorTask, RelativeEndEffectorTask)
TEST_TASK(mc_tasks::SurfaceTransformTask, SurfaceTransformTask)
TEST_TASK(mc_tasks::VectorOrientationTask, VectorOrientationTask)


<|start_filename|>include/mc_tasks/VectorOrientationTask.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_tasks/TrajectoryTaskGeneric.h>

namespace mc_tasks
{

/*! \brief Control the orientation of a body
 *
 * This task is thin wrapper around the appropriate tasks in Tasks.
 *
 */
struct MC_TASKS_DLLAPI VectorOrientationTask : public TrajectoryTaskGeneric<tasks::qp::VectorOrientationTask>
{
  /*! \brief Constructor with user-specified target
   *
   * \param bodyName Name of the body to control
   *
   * \param bodyVector Vector to be controlled, expressed in the body frame
   *
   * \param targetVector Target for the controlled vector, expressed in the
   * world frame
   *
   * \param robots Robots controlled by this task
   *
   * \param robotIndex Index of the robot controlled by this task
   *
   * \param stiffness Task stiffness
   *
   * \param weight Task weight
   *
   */
  VectorOrientationTask(const std::string & bodyName,
                        const Eigen::Vector3d & bodyVector,
                        const Eigen::Vector3d & targetVector,
                        const mc_rbdyn::Robots & robots,
                        unsigned int robotIndex,
                        double stiffness = 2.0,
                        double weight = 500);

  /*! \brief Constructor with default target */
  VectorOrientationTask(const std::string & bodyName,
                        const Eigen::Vector3d & bodyVector,
                        const mc_rbdyn::Robots & robots,
                        unsigned int robotIndex,
                        double stiffness = 2.0,
                        double weight = 500);

  /*! \brief Reset the task
   *
   * Set the task objective (i.e. target vector in the world frame) to the
   * current body vector
   */
  void reset() override;

  /*! \brief Set the body vector to be controlled
   *
   * \param vector Vector to be controlled in the body frame
   *
   */
  void bodyVector(const Eigen::Vector3d & vector);

  /*! \brief Get the current controlled vector in the body frame
   *
   * \returns The body orientation target in world frame
   *
   */
  const Eigen::Vector3d & bodyVector() const;

  /*! \brief Set world target for the controlled vector
   *
   * \param vector Target vector in the world frame
   *
   */
  void targetVector(const Eigen::Vector3d & vector);

  /**
   * @brief Get the target orientation
   *
   * @return The target orientation in world frame
   */
  const Eigen::Vector3d & targetVector() const;

  /**
   * @brief Get the current body orientation
   *
   * @return The current body orientation vector in world frame
   */
  const Eigen::Vector3d & actual() const;

  /*! \brief Return the controlled body */
  std::string body()
  {
    return bodyName;
  }

protected:
  void addToGUI(mc_rtc::gui::StateBuilder & gui) override;
  void addToLogger(mc_rtc::Logger & logger) override;

protected:
  std::string bodyName;
  unsigned int bIndex;
};

} // namespace mc_tasks


<|start_filename|>doc/_data/schemas/MetaTask/ImpedanceTask.json<|end_filename|>
{
  "type": "object",
  "title": "mc_tasks::force::ImpedanceTask",
  "description": "This task is similar to AdmittanceTask and DampingTask. Unlike those, the robot can adapt to an external force while following a target pose.",
  "allOf":
  [
    {
      "$ref": "/../../common/MetaTask_common.json"
    },
    {
      "$ref": "/../../common/MetaTask_surface.json"
    },
    {
      "$ref": "/../../common/SurfaceTransformTask.json"
    },
    {
      "$ref": "/../../common/TrajectoryTaskGeneric.json"
    },
    {
      "properties":
      {
        "type": { "enum": ["impedance"] },
        "weight": { "default": 1000},
        "stiffness": { "default": 5 },
        "target": { "$ref" : "/../../SpaceVecAlg/PTransformd.json" },
        "wrench": { "$ref": "/../../SpaceVecAlg/ForceVecd.json", "description": "<b>Wrench target</b> (desired force-torque)" },
        "gains": { "$ref": "/../../common/ImpedanceGains.json", "description": "Impedance gains" },
        "completion": { "$ref": "/../../common/completion_wrench.json" }
      }
    },
    {
      "properties":
      {
        "dimWeight": { "$ref": "/../../Eigen/Vector6d.json" }
      }
    }
  ],
  "required": ["type", "surface"]
}


<|start_filename|>include/mc_tasks/MomentumTask.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_tasks/TrajectoryTaskGeneric.h>

namespace mc_tasks
{

/*! \brief Control the momentum of a robot

 * This task is thin wrapper around the appropriate tasks in Tasks.
 *
 */
struct MC_TASKS_DLLAPI MomentumTask : public TrajectoryTaskGeneric<tasks::qp::MomentumTask>
{
  using TrajectoryBase = TrajectoryTaskGeneric<tasks::qp::MomentumTask>;

public:
  /*! \brief Constructor
   *
   * \param robots Robots controlled by this task
   *
   * \param robotIndex Index of the robot controlled by this task
   *
   * \param stiffness Task stiffness
   *
   * \param weight Task weight
   *
   */
  MomentumTask(const mc_rbdyn::Robots & robots, unsigned int robotIndex, double stiffness = 2.0, double weight = 500);

  /*! \brief Reset the task
   *
   * Set the task target to the current momentum and reset desired velocity and acceleration to zero.
   *
   */
  void reset() override;

  /*! \brief Get the current target momentum */
  sva::ForceVecd momentum() const;

  /*! \brief Set the target momentum
   *
   * \param target Target momentum
   *
   */
  void momentum(const sva::ForceVecd & mom);

  void addToLogger(mc_rtc::Logger & logger) override;

  /*! \brief Load parameters from a Configuration object */
  void load(mc_solver::QPSolver & solver, const mc_rtc::Configuration & config) override;

protected:
  void addToGUI(mc_rtc::gui::StateBuilder & gui) override;
};

} // namespace mc_tasks


<|start_filename|>include/mc_rtc/MessagePackBuilder.h<|end_filename|>
#pragma once

#include <mc_rtc/utils_api.h>

#include <SpaceVecAlg/SpaceVecAlg>

#include <Eigen/Core>
#include <array>
#include <exception>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>

namespace mc_rtc
{

struct Configuration;

struct MessagePackBuilderImpl;

/** Helper class to build a MessagePack message
 *
 * After a message has been built, the builder object must be discarded to
 * create a new message.
 */
struct MC_RTC_UTILS_DLLAPI MessagePackBuilder
{
  /** Constructor
   *
   * \param buffer Buffer used to store the data, it may grow depending on the needs
   *
   */
  MessagePackBuilder(std::vector<char> & buffer);

  /** Destructor */
  ~MessagePackBuilder();

  /** @name Add data to the MessagePack (basic)
   *
   * These overload set allows to write basic data to the MessagePack
   *
   * @{
   */

  /** Write null */
  void write();
  /** Write a bool */
  void write(bool b);
  /** Write a int8_t */
  void write(int8_t i);
  /** Write a int16_t */
  void write(int16_t i);
  /** Write a int32_t */
  void write(int32_t i);
  /** Write a int64_t */
  void write(int64_t i);
  /** Write a uint8_t */
  void write(uint8_t i);
  /** Write a uint16_t */
  void write(uint16_t i);
  /** Write a uint32_t */
  void write(uint32_t i);
  /** Write a uint64_t */
  void write(uint64_t i);
  /** Write a float */
  void write(float f);
  /** Write a double */
  void write(double d);
  /** Write an std::string */
  void write(const std::string & s);
  /** Write a C-style string */
  void write(const char * s);
  /** Write a C-style string with available size */
  void write(const char * s, size_t len);

  /** @} */
  /* End Add data to the MessagePack section */

  /** @name Add data to the MessagePack (extended)
   *
   * These functions serialize common types used throughout mc_rtc. This does
   * not use MessagePack extension mechanism. Instead, this serializes to
   * simple primitives, if type retrieval is desired, one should implement a
   * specific mechanism.
   *
   * @{
   */

  /** Write an Eigen::Vector2d
   *
   * Serializes as an array of size 2
   */
  void write(const Eigen::Vector2d & v);

  /** Write an Eigen::Vector3d
   *
   * Serializes as an array of size 3
   */
  void write(const Eigen::Vector3d & v);

  /** Write an Eigen::Vector6d
   *
   * Serializes as an array of size 6
   */
  void write(const Eigen::Vector6d & v);

  /** Write an Eigen::VectorXd
   *
   * Serializes as an array of size X
   */
  void write(const Eigen::VectorXd & v);

  /** Write an Eigen::Quaterniond
   *
   * Serializes as an array of size X
   */
  void write(const Eigen::Quaterniond & q);

  /** Write an Eigen::Matrix3d
   *
   * Serializes as an array of size 9
   */
  void write(const Eigen::Matrix3d & m);

  /** Write an sva::PTransformd
   *
   * Serializes as an array of size 12 (Matrix3d + Vector3d)
   */
  void write(const sva::PTransformd & pt);

  /** Write an sva::ForceVecd
   *
   * Serialized as an array of size 6 (Torque + Force)
   */
  void write(const sva::ForceVecd & fv);

  /** Write an sva::MotionVecd
   *
   * Serialized as an array of size 6 (Angular + Linear)
   */
  void write(const sva::MotionVecd & mv);

  /** Write an sva::ImpedanceVecd
   *
   * Serialized as an array of size 6 (Angular + Linear)
   */
  void write(const sva::ImpedanceVecd & mv);

  /** Write an mc_rtc::Configuration
   *
   * Serialied as the JSON data it holds
   */
  void write(const mc_rtc::Configuration & config);

  /** @} */
  /* End Add data to the MessagePack section (advanced) */

  /** @name Add data to the MessagePack (containers)
   *
   * These functions support the serialization of standard containers of
   * serializable objects.
   *
   * @{
   */

  /** Write an std::vector<T, A> */
  template<typename T, typename A>
  void write(const std::vector<T, A> & v)
  {
    start_array(v.size());
    for(const auto & value : v)
    {
      write(value);
    }
    finish_array();
  }

  /** Write an std::array<T, N> */
  template<typename T, std::size_t N>
  void write(const std::array<T, N> & a)
  {
    start_array(N);
    for(const auto & value : a)
    {
      write(value);
    }
    finish_array();
  }

  /** Write an std::pair<T1, T2> */
  template<typename T1, typename T2>
  void write(const std::pair<T1, T2> & p)
  {
    start_array(2);
    write(p.first);
    write(p.second);
    finish_array();
  }

  /** Write an std::map<KeyT, T, C, A> */
  template<typename KeyT, typename T, typename C, typename A>
  void write(const std::map<KeyT, T, C, A> & m)
  {
    start_map(m.count());
    for(const auto & p : m)
    {
      write(p.first);
      write(p.second);
    }
    finish_map();
  }

  /** Write an std::set<T, C, A> */
  template<typename T, typename C, typename A>
  void write(const std::set<T, C, A> & s)
  {
    start_array(s.size());
    for(const auto & value : s)
    {
      write(value);
    }
    finish_array();
  }

  /** Write an std::tuple<Args...> */
  template<typename... Args>
  void write(const std::tuple<Args...> & t)
  {
    start_array(sizeof...(Args));
    write_impl<0>(t);
    finish_array();
  }

  /** Write an Eigen::Ref */
  template<typename Type, int Options, typename StrideType>
  void write(const Eigen::Ref<Type, Options, StrideType> & v)
  {
#if not EIGEN_VERSION_AT_LEAST(3, 2, 90)
    using Index = Eigen::DenseIndex;
#else
    using Index = Eigen::Index;
#endif
    start_array(v.size());
    for(Index i = 0; i < v.size(); ++i)
    {
      write(v(i));
    }
    finish_array();
  }

  /** @} */
  /* End Add data to the MessagePack section (containers) */

  /** Start serializing an array
   *
   * \param size Size of the array
   */
  void start_array(size_t size);

  /** Finished serializing an array */
  void finish_array();

  /** Start serializing a map
   *
   * \param size Size of the map, the map must then contains 2*size elements
   */
  void start_map(size_t size);

  /** Finished serializing a map */
  void finish_map();

  /** Write an existing object into the object being constructed
   *
   * \param data Data written into the object
   *
   * \param size Size of the data
   *
   */
  void write_object(const char * data, size_t s);

  /** Finish building the message
   *
   * Afterwards, data cannot be appended to the builder
   *
   * \returns Effective size of MessagePack data, note that buffer.size() is likely different
   *
   */
  size_t finish();

private:
  /** Hide MessagePack implementation choice in pimpl pattern */
  std::unique_ptr<MessagePackBuilderImpl> impl_;

  template<size_t i,
           typename... Args,
           typename std::enable_if<i<sizeof...(Args), int>::type = 0> void write_impl(const std::tuple<Args...> & t)
  {
    write(std::get<i>(t));
    write_impl<i + 1>(t);
  }

  template<size_t i, typename... Args, typename std::enable_if<i >= sizeof...(Args), int>::type = 0>
  void write_impl(const std::tuple<Args...> &)
  {
  }
};

} // namespace mc_rtc


<|start_filename|>src/mc_control/samples/ExternalForces/src/mc_external_forces_controller.h<|end_filename|>
/*
 * Copyright 2021 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_control/api.h>
#include <mc_control/fsm/Controller.h>
#include <mc_control/mc_controller.h>

struct MC_CONTROL_DLLAPI ExternalForcesController : public mc_control::fsm::Controller
{
  ExternalForcesController(mc_rbdyn::RobotModulePtr rm, double dt, const mc_rtc::Configuration & config);

  void reset(const mc_control::ControllerResetData & reset_data) override;
  bool run() override;

  void supported_robots(std::vector<std::string> & out) const override
  {
    out = {"jvrc1"};
  }

  inline double t() const noexcept
  {
    return t_;
  }

protected:
  double t_ = 0; ///< Elapsed time since the controller started
};


<|start_filename|>3rd-party/qhull/src/libqhullcpp/Qhull.h<|end_filename|>
/****************************************************************************
**
** Copyright (c) 2008-2020 <NAME>. All rights reserved.
** $Id: //main/2019/qhull/src/libqhullcpp/Qhull.h#5 $$Change: 2956 $
** $DateTime: 2020/05/23 21:08:29 $$Author: bbarber $
**
****************************************************************************/

#ifndef QHULLCPP_H
#define QHULLCPP_H

#include "libqhullcpp/QhullPoint.h"
#include "libqhullcpp/QhullVertex.h"
#include "libqhullcpp/QhullFacet.h"

namespace orgQhull {

/***
   Compile qhullcpp and libqhull with the same compiler.  setjmp() and longjmp() must be the same.

   #define QHULL_NO_STL
      Do not supply conversions to STL
      Coordinates.h requires <vector>.  It could be rewritten for another vector class such as QList
   #define QHULL_USES_QT
      Supply conversions to QT
      qhulltest requires QT.  It is defined in RoadTest.h

  #define QHULL_ASSERT
      Defined by QhullError.h
      It invokes assert()
*/

#//!\name Used here
    class QhullFacetList;
    class QhullPoints;
    class QhullQh;
    class RboxPoints;

#//!\name Defined here
    class Qhull;

//! Interface to Qhull from C++
class Qhull {

private:
#//!\name Members and friends
    QhullQh *           qh_qh;          //! qhT for this instance
    Coordinates         origin_point;   //! origin for qh_qh->hull_dim.  Set by runQhull()
    bool                run_called;     //! True at start of runQhull.  Errors if call again.
    Coordinates         feasible_point;  //! feasible point for half-space intersection (alternative to qh.feasible_string for qh.feasible_point)

public:
#//!\name Constructors
                        Qhull();      //!< call runQhull() next
                        Qhull(const RboxPoints &rboxPoints, const char *qhullCommand2);
                        Qhull(const char *inputComment2, int pointDimension, int pointCount, const realT *pointCoordinates, const char *qhullCommand2);
                        ~Qhull() throw();
private:                //! Disable copy constructor and assignment.  Qhull owns QhullQh.
                        Qhull(const Qhull &);
    Qhull &             operator=(const Qhull &);

private:
    void                allocateQhullQh();

public:

#//!\name GetSet
    void                checkIfQhullInitialized();
    int                 dimension() const { return qh_qh->input_dim; } //!< Dimension of input and result
    void                disableOutputStream() { qh_qh->disableOutputStream(); }
    void                enableOutputStream() { qh_qh->enableOutputStream(); }
    countT              facetCount() const { return qh_qh->num_facets; }
    Coordinates         feasiblePoint() const; 
    int                 hullDimension() const { return qh_qh->hull_dim; } //!< Dimension of the computed hull
    bool                hasOutputStream() const { return qh_qh->hasOutputStream(); }
    bool                initialized() const { return (qh_qh->hull_dim>0); }
    const char *        inputComment() const { return qh_qh->rbox_command; }
    QhullPoint          inputOrigin();
    bool                isDelaunay() const { return qh_qh->DELAUNAY; }
                        //! non-const due to QhullPoint
    QhullPoint          origin() { QHULL_ASSERT(initialized()); return QhullPoint(qh_qh, origin_point.data()); }
    QhullQh *           qh() const { return qh_qh; }
    const char *        qhullCommand() const { return qh_qh->qhull_command; }
    const char *        rboxCommand() const { return qh_qh->rbox_command; }
    int                 rotateRandom() const { return qh_qh->ROTATErandom; } //!< Return QRn for repeating QR0 runs
    void                setFeasiblePoint(const Coordinates &c) { feasible_point= c; } //!< Sets qh.feasible_point via initializeFeasiblePoint
    countT              vertexCount() const { return qh_qh->num_vertices; }

#//!\name Delegated to QhullQh
    double              angleEpsilon() const { return qh_qh->angleEpsilon(); } //!< Epsilon for hyperplane angle equality
    void                appendQhullMessage(const std::string &s) { qh_qh->appendQhullMessage(s); }
    void                clearQhullMessage() { qh_qh->clearQhullMessage(); }
    double              distanceEpsilon() const { return qh_qh->distanceEpsilon(); } //!< Epsilon for distance to hyperplane
    double              factorEpsilon() const { return qh_qh->factorEpsilon(); }  //!< Factor for angleEpsilon and distanceEpsilon
    std::string         qhullMessage() const { return qh_qh->qhullMessage(); }
    bool                hasQhullMessage() const { return qh_qh->hasQhullMessage(); }
    int                 qhullStatus() const { return qh_qh->qhullStatus(); }
    void                setErrorStream(std::ostream *os) { qh_qh->setErrorStream(os); }
    void                setFactorEpsilon(double a) { qh_qh->setFactorEpsilon(a); }
    void                setOutputStream(std::ostream *os) { qh_qh->setOutputStream(os); }

#//!\name ForEach
    QhullFacet          beginFacet() const { return QhullFacet(qh_qh, qh_qh->facet_list); }
    QhullVertex         beginVertex() const { return QhullVertex(qh_qh, qh_qh->vertex_list); }
    void                defineVertexNeighborFacets(); //!< Automatically called if merging facets or Voronoi diagram
    QhullFacet          endFacet() const { return QhullFacet(qh_qh, qh_qh->facet_tail); }
    QhullVertex         endVertex() const { return QhullVertex(qh_qh, qh_qh->vertex_tail); }
    QhullFacetList      facetList() const;
    QhullFacet          firstFacet() const { return beginFacet(); }
    QhullVertex         firstVertex() const { return beginVertex(); }
    QhullPoints         points() const;
    QhullPointSet       otherPoints() const;
                        //! Same as points().coordinates()
    coordT *            pointCoordinateBegin() const { return qh_qh->first_point; }
    coordT *            pointCoordinateEnd() const { return qh_qh->first_point + qh_qh->num_points*qh_qh->hull_dim; }
    QhullVertexList     vertexList() const;

#//!\name Methods
    double              area();
    void                outputQhull();
    void                outputQhull(const char * outputflags);
    void                prepareVoronoi(bool *isLower, int *voronoiVertexCount);
    void                runQhull(const RboxPoints &rboxPoints, const char *qhullCommand2);
    void                runQhull(const char *inputComment2, int pointDimension, int pointCount, const realT *pointCoordinates, const char *qhullCommand2);
    double              volume();

#//!\name Helpers
private:
    void                initializeFeasiblePoint(int hulldim);
};//Qhull

}//namespace orgQhull

#endif // QHULLCPP_H


<|start_filename|>include/mc_tasks/CoPTask.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_tasks/DampingTask.h>

namespace mc_tasks
{

namespace force
{

/*! \brief Track center-of-pressure (CoP) references at contact
 *
 * The CoPTask is basically an AdmittanceTask where contact wrenches are
 * expressed in terms of center of pressure (a.k.a. ZMP, see e.g. [1]) rather
 * than torques. This is better-suited to locomotion applications.
 *
 * The CoP is well-defined only when the contact pressure is strictly positive.
 * When there is no contact pressure, the CoPTask will automatically disable
 * torque tracking in the underlying AdmittanceTask.
 *
 * [1] https://scaron.info/teaching/zero-tilting-moment-point.html
 *
 */
struct MC_TASKS_DLLAPI CoPTask : DampingTask
{
public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW

  /*! \brief Initialize a new CoP task.
   *
   * \param robotSurface Name of the surface frame to control, in which the
   * desired wrench will also be expressed
   *
   * \param robots Robots where the task will be applied
   *
   * \param robotIndex Which robot among the robots
   *
   * \param stiffness Stiffness of the underlying SurfaceTransform task
   *
   * \param weight Weight of the underlying SurfaceTransform task
   *
   * \throws If the body the task is attempting to control does not have a
   * sensor attached to it
   *
   */
  CoPTask(const std::string & robotSurface,
          const mc_rbdyn::Robots & robots,
          unsigned robotIndex,
          double stiffness = 5.0,
          double weight = 1000.0);

  /*! \brief Reset the task
   *
   * Set the end effector objective to the current position of the
   * end-effector, disable CoP tracking and reset admittance and target CoP to
   * zero.
   *
   */
  void reset() override;

  /** Add support for the following entries
   *
   * - copError Threshold for (targetCoP - measureCoP).norm()
   * - force Force threshold, similar to wrench for AdmittanceTask
   *
   */
  std::function<bool(const mc_tasks::MetaTask &, std::string &)> buildCompletionCriteria(
      double dt,
      const mc_rtc::Configuration & config) const override;

  /*! \brief Measured CoP in surface frame.
   *
   */
  Eigen::Vector2d measuredCoP() const
  {
    return robots_.robot(rIndex_).cop(surface_.name());
  }

  /*! \brief Measured CoP in world frame.
   *
   */
  Eigen::Vector3d measuredCoPW() const
  {
    return robots_.robot(rIndex_).copW(surface_.name());
  }

  /*! \brief Set targent wrench to zero.
   *
   */
  void setZeroTargetWrench()
  {
    AdmittanceTask::targetWrench(sva::ForceVecd{Eigen::Vector6d::Zero()});
    targetCoP(Eigen::Vector2d::Zero());
  }

  /*! \brief Get target CoP in the surface frame.
   *
   */
  const Eigen::Vector2d & targetCoP() const
  {
    return targetCoP_;
  }

  /*! \brief Get target CoP in the world frame.
   *
   */
  Eigen::Vector3d targetCoPW() const
  {
    Eigen::Vector3d cop_s;
    cop_s << targetCoP_, 0.;
    sva::PTransformd X_0_s = robots_.robot(rIndex_).surfacePose(surface_.name());
    return X_0_s.translation() + X_0_s.rotation().transpose() * cop_s;
  }

  /*! \brief Set target CoP in the surface frame.
   *
   * \param targetCoP 2D vector of CoP coordinates in the surface frame
   *
   */
  void targetCoP(const Eigen::Vector2d & targetCoP)
  {
    targetCoP_ = targetCoP;
  }

  /*! \brief Get target force in the surface frame
   *
   */
  const Eigen::Vector3d & targetForce() const
  {
    return targetForce_;
  }

  /*! \brief Set target force in the surface frame
   *
   * \param targetForce 3D vector of target force in the surface frame
   *
   */
  void targetForce(const Eigen::Vector3d & targetForce)
  {
    targetForce_ = targetForce;
  }

  /*! \brief Set target force in the world frame
   *
   * \param targetForce 3D vector of target force in the world frame
   */
  void targetForceW(const Eigen::Vector3d & targetForceW)
  {
    const auto & X_0_rh = robots_.robot(rIndex_).surface(surface_.name()).X_0_s(robots_.robot(rIndex_));
    targetForce(X_0_rh.dualMul(sva::ForceVecd(Eigen::Vector3d::Zero(), targetForceW)).force());
  }

  /*! \brief Get target wrench in the surface frame
   *
   */
  const sva::ForceVecd & targetWrench() const
  {
    return AdmittanceTask::targetWrench();
  }

protected:
  void addToLogger(mc_rtc::Logger & logger) override;
  void addToGUI(mc_rtc::gui::StateBuilder & gui) override;

private:
  Eigen::Vector2d targetCoP_ = Eigen::Vector2d::Zero();
  Eigen::Vector3d targetForce_ = Eigen::Vector3d::Zero();

  void update(mc_solver::QPSolver &) override;

  using AdmittanceTask::targetWrench;
  using AdmittanceTask::targetWrenchW;
};

} // namespace force

} // namespace mc_tasks


<|start_filename|>include/mc_rtc/gui/Visual.h<|end_filename|>
/*
 * Copyright 2015-2021 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_rtc/gui/details/traits.h>
#include <mc_rtc/gui/elements.h>
#include <mc_rtc/gui/types.h>

#include <mc_rbdyn/configuration_io.h>
#include "mc_rtc/MessagePackBuilder.h"

namespace mc_rtc
{

namespace gui
{

namespace details
{

/** Visual display shows the provided rbd::parsers::Visual element at the provided location
 *
 * \tparam GetVisual Must return an rbd::parsers::Visual
 *
 * \tparam GetPos Must return an sva::PTransformd or an Eigen::Vector3d
 *
 */
template<typename GetVisual, typename GetPos>
struct VisualImpl : public Element
{
  static constexpr auto type = Elements::Visual;

  VisualImpl(const std::string & name, GetVisual get_visual_fn, GetPos get_pos_fn)
  : Element(name), get_visual_fn_(get_visual_fn), get_pos_fn_(get_pos_fn)
  {
    static_assert(details::CheckReturnType<GetVisual, rbd::parsers::Visual>::value,
                  "Visual element visual callback must return an rbd::parsers::Visual");
    static_assert(details::CheckReturnType<GetPos, sva::PTransformd, Eigen::Vector3d>::value,
                  "Visual element position callback must return an sva::PTransformd or an Eigen::Vector3d");
  }

  /** Invalid element */
  VisualImpl() {}

  constexpr static size_t write_size()
  {
    return Element::write_size() + 2;
  }

  void write(mc_rtc::MessagePackBuilder & builder)
  {
    Element::write(builder);
    visual_.add("data", get_visual_fn_());
    builder.write(visual_("data"));
    builder.write(get_pos_fn_());
  }

private:
  GetVisual get_visual_fn_;
  GetPos get_pos_fn_;
  mc_rtc::Configuration visual_;
};

} // namespace details

/** Helper function to create a details::VisualImpl */
template<typename GetVisual, typename GetPos>
details::VisualImpl<GetVisual, GetPos> Visual(const std::string & name, GetVisual get_visual_fn, GetPos get_pos_fn)
{
  return details::VisualImpl<GetVisual, GetPos>(name, get_visual_fn, get_pos_fn);
}

} // namespace gui

} // namespace mc_rtc


<|start_filename|>3rd-party/qhull/src/libqhullcpp/functionObjects.h<|end_filename|>
/****************************************************************************
**
** Copyright (c) 2008-2020 <NAME>. All rights reserved.
** $Id: //main/2019/qhull/src/libqhullcpp/functionObjects.h#2 $$Change: 2953 $
** $DateTime: 2020/05/21 22:05:32 $$Author: bbarber $
**
****************************************************************************/

#ifndef QHFUNCTIONOBJECTS_H
#define QHFUNCTIONOBJECTS_H

#include <stdlib.h>
#include <math.h>

namespace orgQhull {

#//!\name Defined here

    //! Sum of absolute values of the elements in a container
    class AbsoluteSumOf;
    //! Sum of the elements in a container
    class SumOf;
    //! Sum of squares of the elements in a container
    class SumSquaresOf;

#//!\name Class

//! Absolute sum of the elements in a container
class AbsoluteSumOf
{
private:
    double sum;
public:
    inline AbsoluteSumOf() : sum(0.0) {}
    inline void operator()(double v) { sum += fabs(v); }
    inline operator double() { return sum; }
};//AbsoluteSumOf

//! Sum of the elements in a container
class SumOf
{
private:
    double sum;
public:
    inline SumOf() : sum(0.0) {}
    inline void operator()(double v) { sum += v; }
    inline operator double() { return sum; }
};//SumOf


//! Sum of squares of the elements in a container
class SumSquaresOf
{
private:
    double sum;
public:
    inline SumSquaresOf() : sum(0.0) {}
    inline void operator()(double v) { sum += v*v; }
    inline operator double() { return sum; }
};//SumSquaresOf


}//orgQhull


#endif //QHFUNCTIONOBJECTS_H



<|start_filename|>3rd-party/qhull/src/libqhullcpp/QhullUser.cpp<|end_filename|>
/****************************************************************************
**
** Copyright (c) 2008-2020 <NAME>. All rights reserved.
** $Id: //main/2019/qhull/src/libqhullcpp/QhullUser.cpp#10 $$Change: 3008 $
** $DateTime: 2020/07/30 13:54:27 $$Author: bbarber $
**
****************************************************************************/

#include "libqhullcpp/QhullUser.h"

#include "libqhullcpp/QhullError.h"

#include <iostream>
#include <stdint.h>

using std::cerr;
using std::endl;
using std::istream;
using std::ostream;
using std::ostringstream;
using std::string;
using std::vector;
using std::ws;

#ifdef _MSC_VER  // Microsoft Visual C++ -- warning level 4
#pragma warning( disable : 4996)  // function was declared deprecated(strcpy, localtime, etc.)
#endif

namespace orgQhull{

#//! QhullUser -- user-defined interface to qhull via qh_fprintf


#//!\name Constructors
QhullUser::
QhullUser(QhullQh *qqh)
    : qh_qh(qqh)
    , previous_user(NULL)
    , doubles_vector()
    , ints_vector()
    , fprintf_ints()
    , fprintf_doubles()
    , fprintf_codes()
    , fprintf_strings()
    , num_facets(0)
    , num_neighbors(0)
    , num_numbers(0)
    , num_points(0)
    , num_results(0)
    , num_ridges(0)
    , num_vectors(0)
    , num_vertices(0)
    , qhull_dim(0)
    , delaunay_dim(0)
{
    previous_user= qh()->cpp_user;
    qh()->cpp_user= NULL;
    captureOn();
}//constructor

QhullUser::
~QhullUser()
{
    captureOff();
    qh()->cpp_user= previous_user;
}//destructor

#//!\name Get/Set

//! Clear working fields of QhullUser
//! Retains qh_qh and previous_user as initialized
//! Updates qhull_dim from qh_qh (does not change)
//! Updates delaunay_dim if qh_qh.ISdelaunay
void QhullUser::
clear()
{
    doubles_vector.clear();
    ints_vector.clear();
    fprintf_ints.clear();
    fprintf_doubles.clear();
    fprintf_codes.clear();
    fprintf_strings.clear();
    num_facets= 0;
    num_neighbors= 0;
    num_numbers= 0;
    num_points= 0;
    num_results= 0;
    num_ridges= 0;
    num_vectors= 0;
    num_vertices= 0;
}//clear

#//!\name Methods

void QhullUser::
captureOff()
{
    if(qh()->cpp_user==NULL){
        throw QhullError(10080, "Qhull error: QhullUser::captureOn not call before QhullUser::captureOff for QhullUser 0x%llx", 0, 0, 0.0, this);
    }
    if(qh()->cpp_user!=this){
        throw QhullError(10081, "Qhull error: conflicting QhullUser (0x%llx) for QhullUser::captureOff().  Does not match 'this' (0x...%X)", int(0xffff&(intptr_t)this), 0, 0.0, qh()->cpp_user);
    }
    qh()->cpp_user= NULL;
}//captureOff

void QhullUser::
captureOn()
{
    if(qh()->cpp_user){
        throw QhullError(10079, "Qhull error: conflicting user of cpp_user for QhullUser::captureOn() or corrupted qh_qh 0x%llx", 0, 0, 0.0, qh());
    }
    qh()->cpp_user= this;
}//captureOn

}//namespace orgQhull

#//!\name Global functions

/*-<a                             href="qh-user.htm#TOC"
>-------------------------------</a><a name="qh_fprintf">-</a>

  qh_fprintf(qh, fp, msgcode, format, list of args )
    replacement for qh_fprintf in userprintf_r.c, which replaces fprintf for Qhull
    qh.ISqhullQh must be true, indicating that qh a subclass of QhullQh
    qh.cpp_user is an optional QhullUser for trapped MSG_OUTPUT calls
    fp may be NULL
    otherwise behaves the same as qh_fprintf in userprintf_r.c

returns:
    sets qhullQh->qhull_status if msgcode is error 6000..6999
....sets qh.last_errcode if error reported
....if qh.cpp_user defined, records results of 'qhull v Fi Fo'
        See "qvoronoi-fifo" in user_eg3_r.cpp for an example


notes:
    Only called from libqhull_r
    A similar technique is used by RboxPoints and qh_fprintf_rbox
    Do not throw errors from here.  Use qh_errexit;
    fgets() is not trapped like fprintf(), QH11008 FIX: how do users handle input?  A callback?
*/
extern "C"

//! Custom qh_fprintf for transferring Qhull output from io_r.c to QhullUser and QhullQh
//! Identify msgcodes with Qhull option 'Ta'
//! A similar technique is used by RboxPoints with a custom qh_fprintf_rbox
void qh_fprintf(qhT *qh, FILE* fp, int msgcode, const char *fmt, ... ){
    va_list args;
    int last_errcode;

    using namespace orgQhull;
    
    if(qh==NULL || !qh->ISqhullQh){
        qh_fprintf_stderr(10025, "Qhull error: qh_fprintf in QhullUser.cpp called from a Qhull instance without QhullQh defined\n");
        last_errcode= 10025;
        qh_exit(last_errcode);
    }
    va_start(args, fmt);
    if(msgcode>=MSG_OUTPUT && qh->cpp_user){
        QhullUser *out= reinterpret_cast<QhullUser *>(qh->cpp_user);
        bool isOut= false;
        switch (msgcode){
        case 9231:        /* printvdiagram, totcount (ignored) */
            out->setNumResults(va_arg(args, int));
            isOut= true;
            break;
        case 9271:        /* qh_printvnorm, count, pointId, pointId, hyperplane */
            out->appendInt(va_arg(args, int));
            out->appendInt(va_arg(args, int));
            out->appendInt(va_arg(args, int));
            out->appendAndClearInts();
            isOut= true;
            break;
        case 9272:
        case 9273:
            out->appendDouble(va_arg(args, double));
            isOut= true;
            break;
        case 9274:
            out->appendAndClearDoubles();
            isOut= true;
            break;
        default:
            // do nothing
            break;
        }
        if(isOut){
            out->appendCode(msgcode);
            va_end(args);
            return;
        }
    }/*MSG_OUTPUT, cpp_user*/

    QhullQh *qhullQh= static_cast<QhullQh *>(qh);
    char newMessage[MSG_MAXLEN];
    int msgLen= 0;
    if((qh && qh->ANNOTATEoutput) || msgcode < MSG_TRACE4){
        msgLen= snprintf(newMessage, sizeof(newMessage), "[QH%.4d]", msgcode);
    }else if(msgcode>=MSG_ERROR && msgcode < MSG_STDERR){
        msgLen= snprintf(newMessage, sizeof(newMessage), "QH%.4d ", msgcode);
    }
    if(msgLen>=0 && msgLen < (int)sizeof(newMessage)){
        vsnprintf(newMessage + msgLen, sizeof(newMessage) - msgLen, fmt, args);
    }
    if(msgcode < MSG_OUTPUT || fp == qh_FILEstderr){
        if(msgcode>=MSG_ERROR && msgcode < MSG_WARNING){
            qh->last_errcode= msgcode;
            if(qhullQh->qhull_status < MSG_ERROR || qhullQh->qhull_status>=MSG_WARNING){
                qhullQh->qhull_status= msgcode;
            }
        }
        qhullQh->appendQhullMessage(newMessage);
    }else if(qhullQh->output_stream && qhullQh->use_output_stream){
        *qhullQh->output_stream << newMessage;
        if(qh->FLUSHprint){
            qhullQh->output_stream->flush();
        }
    }else{
        qhullQh->appendQhullMessage(newMessage);
    }
    va_end(args);

    /* Place debugging traps here. Use with trace option 'Tn'
         Set qh.tracefacet_id, qh.traceridge_id, and/or qh.tracevertex_id in global_r.c
    */
    if(False){ /* in production skip test for debugging traps */
        facetT *neighbor, **neighborp;
        if(qh->tracefacet && qh->tracefacet->tested){
            if(qh_setsize(qh, qh->tracefacet->neighbors) < qh->hull_dim)
                qh_errexit(qh, qh_ERRdebug, qh->tracefacet, qh->traceridge);
            FOREACHneighbor_(qh->tracefacet){
                if(neighbor != qh_DUPLICATEridge && neighbor != qh_MERGEridge && neighbor->visible)
                    qh_errexit2(qh, qh_ERRdebug, qh->tracefacet, neighbor);
            }
        }
        if(qh->traceridge && qh->traceridge->top->id == 234342223){
            qh_errexit(qh, qh_ERRdebug, qh->tracefacet, qh->traceridge);
        }
        if(qh->tracevertex && qh_setsize(qh, qh->tracevertex->neighbors) > 3434334){
            qh_errexit(qh, qh_ERRdebug, qh->tracefacet, qh->traceridge);
        }
    }
} /* qh_fprintf */



<|start_filename|>3rd-party/qhull/html/qh-quick.htm<|end_filename|>
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>

<head>
<title>Qhull quick reference</title>
</head>

<body>
<!-- Navigation links -->
    <p>
        <a name="TOC"><b>Up:</b></a> <a href="http://www.qhull.org">Home page</a> for
        Qhull (<a href="../index.htm">local</a>)<br>
        <b>Up:</b> <a href="index.htm#TOC">Qhull manual</a> <br>
        <b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
        &#149; <a href="qh-quick.htm#options">Options</a>
        &#149; <a href="qh-opto.htm#output">Output</a>
        &#149; <a href="qh-optf.htm#format">Formats</a>
        &#149; <a href="qh-optg.htm#geomview">Geomview</a>
        &#149; <a href="qh-optp.htm#print">Print</a>
        &#149; <a href="qh-optq.htm#qhull">Qhull</a>
        &#149; <a href="qh-optc.htm#prec">Precision</a>
        &#149; <a href="qh-optt.htm#trace">Trace</a>
        &#149; <a href="http://www.qhull.org/src/libqhull_r/index.htm">Functions</a> (<a href="../src/libqhull_r/index.htm">local</a>)<br>
        <b>To:</b> <a href="qh-code.htm">Qhull code</a><br>
        <b>To:</b> <a href="http://www.qhull.org/src/libqhull_r/index.htm#TOC">Qhull files</a> (<a href="../src/libqhull_r/index.htm#TOC">local</a>)<br>
        <b>To:</b> <a href="../src/libqhull_r/qh-geom_r.htm">Geom</a> &#149; <a href="../src/libqhull_r/qh-globa_r.htm">Global</a>
        &#149; <a href="../src/libqhull_r/qh-io_r.htm">Io</a> &#149; <a href="../src/libqhull_r/qh-mem_r.htm">Mem</a>
        &#149; <a href="../src/libqhull_r/qh-merge_r.htm">Merge</a> &#149; <a href="../src/libqhull_r/qh-poly_r.htm">Poly</a>
        &#149; <a href="../src/libqhull_r/qh-qhull_r.htm">Qhull</a> &#149; <a href="../src/libqhull_r/qh-set_r.htm">Set</a>
        &#149; <a href="../src/libqhull_r/qh-stat_r.htm">Stat</a> &#149; <a href="../src/libqhull_r/qh-user_r.htm">User</a>
    </p>

<hr>
<!-- Main text of document -->
<h1><a
href="http://www.geom.uiuc.edu/graphics/pix/Special_Topics/Computational_Geometry/cone.html"><img
src="qh--cone.gif" alt="[cone]" align="middle" width="100"
height="100"></a> Qhull quick reference</h1>

This section lists all programs and options in Qhull.

<p><b>Copyright &copy; 1995-2020 <NAME></b></p>

<p>
<a name="programs">&nbsp;</a>
<hr>
<b>Qhull programs</b>
<p><a href="#TOC">&#187;</a> <a href="qh-quick.htm#programs">Programs</a>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="http://www.qhull.org/src/libqhull_r/index.htm">Functions</a> (<a href="../src/libqhull_r/index.htm">local</a>)</p>

<dl>
    <dt><a href="qconvex.htm">qconvex</a> -- convex hull</dt>
    <dd><a href="qconvex.htm#synopsis">sy</a>nopsis &#149; <a
            href="qconvex.htm#input">in</a>put &#149; <a
        href="qconvex.htm#outputs">ou</a>tputs &#149; <a
        href="qconvex.htm#controls">co</a>ntrols &#149; <a
        href="qconvex.htm#graphics">gr</a>aphics &#149; <a
        href="qconvex.htm#notes">no</a>tes &#149; <a
        href="qconvex.htm#conventions">co</a>nventions &#149; <a
                href="qconvex.htm#options">op</a>tions</dd>
    <dt>&nbsp;</dt>
    <dt><a href="qdelaun.htm">qdelaunay</a> -- Delaunay triangulation</dt>
    <dd><a href="qdelaun.htm#synopsis">sy</a>nopsis &#149; <a
            href="qdelaun.htm#input">in</a>put &#149; <a
        href="qdelaun.htm#outputs">ou</a>tputs &#149; <a
                 href="qdelaun.htm#controls">co</a>ntrols &#149; <a
        href="qdelaun.htm#graphics">gr</a>aphics &#149; <a
        href="qdelaun.htm#notes">no</a>tes &#149; <a
                href="qdelaun.htm#conventions">co</a>nventions &#149; <a
            href="qdelaun.htm#options">op</a>tions</dd>
    <dt>&nbsp;</dt>
    <dt><a href="qdelau_f.htm">qdelaunay Qu</a> -- furthest-site Delaunay triangulation</dt>
    <dd><a href="qdelau_f.htm#synopsis">sy</a>nopsis &#149; <a
            href="qdelau_f.htm#input">in</a>put &#149; <a
        href="qdelau_f.htm#outputs">ou</a>tputs &#149; <a
                 href="qdelau_f.htm#controls">co</a>ntrols &#149; <a
        href="qdelau_f.htm#graphics">gr</a>aphics &#149; <a
        href="qdelau_f.htm#notes">no</a>tes &#149; <a
                href="qdelau_f.htm#conventions">co</a>nventions &#149; <a
            href="qdelau_f.htm#options">op</a>tions</dd>
    <dt>&nbsp;</dt>
    <dt><a href="qhalf.htm">qhalf</a> -- halfspace intersection about a point</dt>
    <dd><a href="qhalf.htm#synopsis">sy</a>nopsis &#149; <a
            href="qhalf.htm#input">in</a>put &#149; <a
        href="qhalf.htm#outputs">ou</a>tputs &#149; <a
        href="qhalf.htm#controls">co</a>ntrols &#149; <a
        href="qhalf.htm#graphics">gr</a>aphics &#149; <a
        href="qhalf.htm#notes">no</a>tes &#149; <a
        href="qhalf.htm#conventions">co</a>nventions &#149; <a
                href="qhalf.htm#options">op</a>tions</dd>
    <dt>&nbsp;</dt>
    <dt><a href="qvoronoi.htm">qvoronoi</a> -- Voronoi diagram</dt>
    <dd><a href="qvoronoi.htm#synopsis">sy</a>nopsis &#149; <a
            href="qvoronoi.htm#input">in</a>put &#149; <a
            href="qvoronoi.htm#outputs">ou</a>tputs &#149;
        <a href="qvoronoi.htm#controls">co</a>ntrols &#149; <a
        href="qvoronoi.htm#graphics">gr</a>aphics &#149; <a
        href="qvoronoi.htm#notes">no</a>tes  &#149; <a
            href="qvoronoi.htm#conventions">co</a>nventions &#149; <a
            href="qvoronoi.htm#options">op</a>tions</dd>
    <dt>&nbsp;</dt>
    <dt><a href="qvoron_f.htm">qvoronoi Qu</a> -- furthest-site Voronoi diagram</dt>
    <dd><a href="qvoron_f.htm#synopsis">sy</a>nopsis &#149; <a
            href="qvoron_f.htm#input">in</a>put &#149; <a
        href="qvoron_f.htm#outputs">ou</a>tputs &#149; <a
                 href="qvoron_f.htm#controls">co</a>ntrols &#149; <a
        href="qvoron_f.htm#graphics">gr</a>aphics &#149; <a
        href="qvoron_f.htm#notes">no</a>tes &#149; <a
                href="qvoron_f.htm#conventions">co</a>nventions &#149; <a
            href="qvoron_f.htm#options">op</a>tions</dd>
    <dt>&nbsp;</dt>
    <dt><a href="rbox.htm">rbox</a> -- generate point distributions for qhull</dt>
    <dd><a href="rbox.htm#synopsis">sy</a>nopsis &#149; <a
                href="rbox.htm#outputs">ou</a>tputs &#149; <a
        href="rbox.htm#examples">ex</a>amples &#149; <a
        href="rbox.htm#notes">no</a>tes &#149; <a
                href="rbox.htm#options">op</a>tions</dd>
    <dt>&nbsp;</dt>
    <dt><a href="qhull.htm">qhull</a> -- convex hull and related structures</dt>
    <dd><a href="qhull.htm#synopsis">sy</a>nopsis &#149; <a
            href="qhull.htm#input">in</a>put &#149; <a
        href="qhull.htm#outputs">ou</a>tputs &#149; <a
        href="qhull.htm#controls">co</a>ntrols &#149; <a
                href="qhull.htm#options">op</a>tions</dd>
    </dl>
<a name="options">&nbsp;</a>
<hr>
<b>Qhull options</b>

<p><a href="#TOC">&#187;</a> <a href="qh-quick.htm#programs">Programs</a>
&#149; <a href="qh-quick.htm#options">Options</a>
&#149; <a href="qh-opto.htm#output">Output</a>
&#149; <a href="qh-optf.htm#format">Formats</a>
&#149; <a href="qh-optg.htm#geomview">Geomview</a>
&#149; <a href="qh-optp.htm#print">Print</a>
&#149; <a href="qh-optq.htm#qhull">Qhull</a>
&#149; <a href="qh-optc.htm#prec">Precision</a>
&#149; <a href="qh-optt.htm#trace">Trace</a>
&#149; <a href="http://www.qhull.org/src/libqhull_r/index.htm">Functions</a> (<a href="../src/libqhull_r/index.htm">local</a>)</p>

<p><table>
<!-- Note: keep all lines the same for easy reorganization.
     Same list as 'qhull .' -->
<tr>
<td><nobr>'<a href=qhull.htm#outputs>d</a>'
delaunay
</nobr></td><td><nobr>'<a href="qh-opto.htm#f">f</a>'
facet-dump
</nobr></td><td><nobr>'<a href="qh-optg.htm">G</a>'
Geomview
</nobr></td><td><nobr>'<a href=qhull.htm#outputs>H</a>'
Halfspace

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-opto.htm#i">i</a>'
incidences
</nobr></td><td><nobr>'<a href="qh-opto.htm#m">m</a>'
mathematica
</nobr></td><td><nobr>'<a href="qh-opto.htm#n">n</a>'
normals
<td><nobr>'<a href="qh-opto.htm#o">o</a>'
OFF-format

</nobr></td></tr><tr>
</nobr></td><td><nobr>'<a href="qh-opto.htm#p">p</a>'
points
<td><nobr>'<a href="qh-opto.htm#s">s</a>'
summary
</nobr></td><td><nobr>'<a href=qhull.htm#outputs>v</a>'
voronoi

</nobr></td></tr>
<tr><td>&nbsp;</td></tr><tr>
<td><nobr>'<a href="qh-optf.htm#Fa">Fa</a>'
Farea
</nobr></td><td><nobr>'<a href="qh-optf.htm#FA">FA</a>'
FArea-total
</nobr></td><td><nobr>'<a href="qh-optf.htm#Fc">Fc</a>'
Fcoplanars
</nobr></td><td><nobr>'<a href="qh-optf.htm#FC">FC</a>'
FCentrums

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optf.htm#Fd">Fd</a>'
Fd-cdd-in
</nobr></td><td><nobr>'<a href="qh-optf.htm#FD">FD</a>'
FD-cdd-out
</nobr></td><td><nobr>'<a href="qh-optf.htm#FF">FF</a>'
FFacets-xridge
</nobr></td><td><nobr>'<a href="qh-optf.htm#Fi">Fi</a>'
Finner

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optf.htm#Fi2">Fi</a>'
Finner-bounded
</nobr></td><td><nobr>'<a href="qh-optf.htm#FI">FI</a>'
FIDs
</nobr></td><td><nobr>'<a href="qh-optf.htm#Fm">Fm</a>'
Fmerges
</nobr></td><td><nobr>'<a href="qh-optf.htm#FM">FM</a>'
FMaple

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optf.htm#Fn">Fn</a>'
Fneighbors
</nobr></td><td><nobr>'<a href="qh-optf.htm#FN">FN</a>'
FNeigh-vertex
</nobr></td><td><nobr>'<a href="qh-optf.htm#Fo">Fo</a>'
Fouter
</nobr></td><td><nobr>'<a href="qh-optf.htm#Fo2">Fo</a>'
Fouter-unbounded

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optf.htm#FO">FO</a>'
FOptions
</nobr></td><td><nobr>'<a href="qh-optf.htm#Fp">Fp</a>'
Fpoint-intersect
</nobr></td><td><nobr>'<a href="qh-optf.htm#FP">FP</a>'
FPoint-near
</nobr></td><td><nobr>'<a href="qh-optf.htm#FQ">FQ</a>'
FQhull

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optf.htm#Fs">Fs</a>'
Fsummary
</nobr></td><td><nobr>'<a href="qh-optf.htm#FS">FS</a>'
FSize
</nobr></td><td><nobr>'<a href="qh-optf.htm#Ft">Ft</a>'
Ftriangles
</nobr></td><td><nobr>'<a href="qh-optf.htm#Fv">Fv</a>'
Fvertices

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optf.htm#Fv2">Fv</a>'
Fvoronoi
</nobr></td><td><nobr>'<a href="qh-optf.htm#FV">FV</a>'
FVertex-ave
</nobr></td><td><nobr>'<a href="qh-optf.htm#Fx">Fx</a>'
Fxtremes

</nobr></td></tr>
<tr><td>&nbsp;</td></tr><tr>
<td><nobr>'<a href="qh-optg.htm#Ga">Ga</a>'
Gall-points
</nobr></td><td><nobr>'<a href="qh-optg.htm#Gc">Gc</a>'
Gcentrums
</nobr></td><td><nobr>'<a href="qh-optg.htm#GDn">GDn</a>'
GDrop-dim
</nobr></td><td><nobr>'<a href="qh-optg.htm#Gh">Gh</a>'
Ghyperplanes

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optg.htm#Gi">Gi</a>'
Ginner
</nobr></td><td><nobr>'<a href="qh-optg.htm#Gn">Gn</a>'
Gno-planes
</nobr></td><td><nobr>'<a href="qh-optg.htm#Go">Go</a>'
Gouter
</nobr></td><td><nobr>'<a href="qh-optg.htm#Gp">Gp</a>'
Gpoints

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optg.htm#Gr">Gr</a>'
Gridges
</nobr></td><td><nobr>'<a href="qh-optg.htm#Gt">Gt</a>'
Gtransparent
</nobr></td><td><nobr>'<a href="qh-optg.htm#Gv">Gv</a>'
Gvertices

</nobr></td></tr>
<tr><td>&nbsp;</td></tr><tr>
<td><nobr>'<a href="qh-optp.htm#PAn">PAn</a>'
PArea-keep
</nobr></td><td><nobr>'<a href="qh-optp.htm#Pdk">Pdk:n</a>'
Pdrop-low
</nobr></td><td><nobr>'<a href="qh-optp.htm#PDk">PDk:n</a>'
Pdrop-high
</nobr></td><td><nobr>'<a href="qh-optp.htm#PFn">PFn</a>'
PFacet-area-keep

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optp.htm#Pg">Pg</a>'
Pgood
</nobr></td><td><nobr>'<a href="qh-optp.htm#PG">PG</a>'
PGood-neighbors
</nobr></td><td><nobr>'<a href="qh-optp.htm#PMn">PMn</a>'
PMerge-keep
</nobr></td><td><nobr>'<a href="qh-optp.htm#Po">Po</a>'
Poutput-forced

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optp.htm#Po2">Po</a>'
Poutput-error
</nobr></td><td><nobr>'<a href="qh-optp.htm#Pp">Pp</a>'
Pprecision-not

</nobr></td></tr>
<tr><td>&nbsp;</td></tr><tr>
<td><nobr>'<a href="qh-optq.htm#Qa">Qa</a>'
Qallow-short
</nobr></td><td><nobr>'<a href="qh-optq.htm#Qbk">Qbk:n</a>'
Qbound-low
</nobr></td><td><nobr>'<a href="qh-optq.htm#QBk">QBk:n</a>'
QBound-high
</nobr></td><td><nobr>'<a href="qh-optq.htm#QbB">QbB</a>'
QbB-scale-box

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optq.htm#Qbb">Qbb</a>'
Qbb-scale-last
</nobr></td><td><nobr>'<a href="qh-optq.htm#Qb0">Qbk:0Bk:0</a>'
Qbound-drop
</nobr></td><td><nobr>'<a href="qh-optq.htm#Qc">Qc</a>'
Qcoplanar
</nobr></td><td><nobr>'<a href="qh-optq.htm#Qi">Qi</a>'
Qinterior

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optq.htm#QJn">QJn</a>'
QJoggle
</nobr></td><td><nobr>'<a href="qh-optq.htm#QRn">QRn</a>'
QRotate
</nobr></td><td><nobr>'<a href="qh-optq.htm#Qs">Qs</a>'
Qsearch-all
</nobr></td><td><nobr>'<a href="qh-optq.htm#Qt">Qt</a>'
Qtriangulate

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optq.htm#Qu">Qu</a>'
QupperDelaunay
</nobr><td><nobr>'<a href="qh-optq.htm#Qw">Qw</a>'
Qwarn-allow
</nobr></td><td><nobr>'<a href="qh-optq.htm#Qx">Qx</a>'
Qxact-merge
<td><nobr>'<a href="qh-optq.htm#Qz">Qz</a>'
Qzinfinite

</nobr></td></tr>
<tr><td>&nbsp;</td></tr><tr>
<td><nobr>'<a href="qh-optq.htm#Qf">Qf</a>'
Qfurthest
</nobr></td><td><nobr>'<a href="qh-optq.htm#Qg">Qg</a>'
Qgood-only
</nobr></td><td><nobr>'<a href="qh-optq.htm#QGn">QGn</a>'
QGood-point
</nobr></td><td><nobr>'<a href="qh-optq.htm#Qm">Qm</a>'
Qmax-outside

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optq.htm#Qr">Qr</a>'
Qrandom
</nobr></td><td><nobr>'<a href="qh-optq.htm#Qv">Qv</a>'
Qvertex-neighbors
</nobr><td><nobr>'<a href="qh-optq.htm#QVn">QVn</a>'
QVertex-good

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optq.htm#Q0">Q0</a>'
Q0-no-premerge
</nobr></td><td><nobr>'<a href="qh-optq.htm#Q1">Q1</a>'
Q1-angle-merge
</nobr></td><td><nobr>'<a href="qh-optq.htm#Q2">Q2</a>'
Q2-no-independent
</nobr></td><td><nobr>'<a href="qh-optq.htm#Q3">Q3</a>'
Q3-no-redundant

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optq.htm#Q4">Q4</a>'
Q4-no-old
</nobr></td><td><nobr>'<a href="qh-optq.htm#Q5">Q5</a>'
Q5-no-check-out
</nobr></td><td><nobr>'<a href="qh-optq.htm#Q6">Q6</a>'
Q6-no-concave
</nobr></td><td><nobr>'<a href="qh-optq.htm#Q7">Q7</a>'
Q7-depth-first

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optq.htm#Q8">Q8</a>'
Q8-no-near-interior
</nobr></td><td><nobr>'<a href="qh-optq.htm#Q9">Q9</a>'
Q9-pick-furthest
</nobr></td><td><nobr>'<a href="qh-optq.htm#Q10">Q10</a>'
Q10-no-narrow
</nobr></td><td><nobr>'<a href="qh-optq.htm#Q11">Q11</a>'
Q11-trinormals

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optq.htm#Q12">Q12</a>'
Q12-allow-wide
</nobr></td><td><nobr>'<a href="qh-optq.htm#Q14">Q14</a>'
Q14-merge-pinched
</nobr></td><td><nobr>'<a href="qh-optq.htm#Q15">Q15</a>'
Q15-duplicate-ridges

</nobr></td></tr>
<tr><td>&nbsp;</td></tr><tr>
<td><nobr>'<a href="qh-optt.htm#TFn">TFn</a>'
TFacet-log
</nobr></td><td><nobr>'<a href="qh-optt.htm#TI">TI file</a>'
TInput-file
</nobr></td><td><nobr>'<a href="qh-optt.htm#TO">TO file</a>'
TOutput-file
</nobr></td><td><nobr>'<a href="qh-optt.htm#Ts">Ts</a>'
Tstatistics

</nobr></td></tr><tr>
</nobr></td><td><nobr>'<a href="qh-optt.htm#Tv">Tv</a>'
Tverify
<td><nobr>'<a href="qh-optt.htm#Tz">Tz</a>'
Tz-stdout

</nobr></td></tr>
<tr><td>&nbsp;</td></tr><tr>
<td><nobr>'<a href="qh-optt.htm#Tn">T4</a>'
T4-trace
</nobr></td><td><nobr>'<a href="qh-optt.htm#Ta">Ta</a>'
Tannotate
</nobr></td><td><nobr>'<a href="qh-optt.htm#TAn">TAn</a>'
TAdd-stop
</nobr></td><td><nobr>'<a href="qh-optt.htm#Tc">Tc</a>'
Tcheck-often

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optt.htm#TCn">TCn</a>'
TCone-stop
</nobr></td><td><nobr>'<a href="qh-optt.htm#Tf">Tf</a>'
Tflush
</nobr></td><td><nobr>'<a href="qh-optt.htm#TMn">TMn</a>'
TMerge-trace
</nobr></td><td><nobr>'<a href="qh-optt.htm#TPn">TPn</a>'
TPoint-trace

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optt.htm#TRn">TRn</a>'
TRerun
</nobr></td><td><nobr>'<a href="qh-optt.htm#TVn">TV-n</a>'
TVertex-stop-before
</nobr></td><td><nobr>'<a href="qh-optt.htm#TVn2">TVn</a>'
TVertex-stop-after
</nobr></td><td><nobr>'<a href="qh-optt.htm#TWn">TWn</a>'
TWide-trace

</nobr></td></tr>
<tr><td>&nbsp;</td></tr><tr>
<td><nobr>'<a href="qh-optc.htm#An">A-n</a>'
Angle-max-pre
</nobr></td><td><nobr>'<a href="qh-optc.htm#An2">An</a>'
Angle-max-post
</nobr></td><td><nobr>'<a href="qh-optc.htm#C0">C-0</a>'
Centrum-roundoff
</nobr></td><td><nobr>'<a href="qh-optc.htm#Cn">C-n</a>'
Centrum-size-pre

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optc.htm#Cn2">Cn</a>'
Centrum-size-post
</nobr></td><td><nobr>'<a href="qh-optc.htm#En">En</a>'
Error-round
</nobr></td><td><nobr>'<a href="qh-optc.htm#Rn">Rn</a>'
Random-dist
</nobr></td><td><nobr>'<a href="qh-optc.htm#Un">Un</a>'
Ucoplanar-max

</nobr></td></tr><tr>
<td><nobr>'<a href="qh-optc.htm#Vn">Vn</a>'
Visible-min
</nobr></td><td><nobr>'<a href="qh-optc.htm#Wn">Wn</a>'
Wide-outside

</nobr></td></tr>
</table>

<!-- Navigation links -->
<hr>

<p>
    <b>Up:</b> <a href="http://www.qhull.org">Home page</a> for Qhull (<a href="../index.htm">local</a>) <br>
    <b>Up:</b> <a href="index.htm#TOC">Qhull manual</a> <br>
    <b>To:</b> <a href="qh-quick.htm#programs">Programs</a>
    &#149; <a href="qh-quick.htm#options">Options</a>
    &#149; <a href="qh-opto.htm#output">Output</a>
    &#149; <a href="qh-optf.htm#format">Formats</a>
    &#149; <a href="qh-optg.htm#geomview">Geomview</a>
    &#149; <a href="qh-optp.htm#print">Print</a>
    &#149; <a href="qh-optq.htm#qhull">Qhull</a>
    &#149; <a href="qh-optc.htm#prec">Precision</a>
    &#149; <a href="qh-optt.htm#trace">Trace</a>
    &#149; <a href="http://www.qhull.org/src/libqhull_r/index.htm">Functions</a> (<a href="../src/libqhull_r/index.htm">local</a>)<br>
    <b>To:</b> <a href="qh-code.htm">Qhull code</a><br>
    <b>To:</b> <a href="http://www.qhull.org/src/libqhull_r/index.htm#TOC">Qhull files</a> (<a href="../src/libqhull_r/index.htm#TOC">local</a>)<br>
    <b>To:</b> <a href="../src/libqhull_r/qh-geom_r.htm">Geom</a> &#149; <a href="../src/libqhull_r/qh-globa_r.htm">Global</a>
    &#149; <a href="../src/libqhull_r/qh-io_r.htm">Io</a> &#149; <a href="../src/libqhull_r/qh-mem_r.htm">Mem</a>
    &#149; <a href="../src/libqhull_r/qh-merge_r.htm">Merge</a> &#149; <a href="../src/libqhull_r/qh-poly_r.htm">Poly</a>
    &#149; <a href="../src/libqhull_r/qh-qhull_r.htm">Qhull</a> &#149; <a href="../src/libqhull_r/qh-set_r.htm">Set</a>
    &#149; <a href="../src/libqhull_r/qh-stat_r.htm">Stat</a> &#149; <a href="../src/libqhull_r/qh-user_r.htm">User</a><br>
    <!-- GC common information -->
    <hr>

<p><a href="http://www.geom.uiuc.edu/"><img src="qh--geom.gif"
align="middle" width="40" height="40"></a><i>The Geometry Center
Home Page </i></p>

<p>Comments to: <a href=mailto:<EMAIL>><EMAIL></a>
<br>
Created: Sept. 25, 1995 --- <!-- hhmts start --> Last modified: see top <!-- hhmts end --> </p>
</body>
</html>


<|start_filename|>src/mc_control/samples/ExternalForces/src/states/UpdateForces.h<|end_filename|>
/*
 * Copyright 2021 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_control/CompletionCriteria.h>
#include <mc_control/fsm/State.h>
#include <mc_tasks/ImpedanceTask.h>
#include <mc_tasks/lipm_stabilizer/StabilizerTask.h>

/*
 * Update target external forces of hands
 */
struct UpdateForces : mc_control::fsm::State
{

  void configure(const mc_rtc::Configuration & config) override;

  void start(mc_control::fsm::Controller & ctl) override;

  bool run(mc_control::fsm::Controller & ctl) override;

  void teardown(mc_control::fsm::Controller & ctl) override;

private:
  mc_rtc::Configuration config_;

  mc_tasks::lipm_stabilizer::StabilizerTask * stabilizerTask_;

  std::vector<mc_tasks::force::ImpedanceTask *> impedanceTasks_;

  double startTime_;
};


<|start_filename|>include/mc_tasks/EndEffectorTask.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_tasks/OrientationTask.h>
#include <mc_tasks/PositionTask.h>

namespace mc_tasks
{

/*! \brief Controls an end-effector
 *
 * This task is a thin wrapper around the appropriate tasks in Tasks.
 * The task objective is given in the world frame. For relative control
 * see mc_tasks::RelativeEndEffectorTask
 */
struct MC_TASKS_DLLAPI EndEffectorTask : public MetaTask
{
public:
  /*! \brief Constructor
   *
   * \param bodyName Name of the body to control
   *
   * \param robots Robots controlled by this task
   *
   * \param robotIndex Index of the robot controlled by this task
   *
   * \param stiffness Task stiffness
   *
   * \param weight Task weight
   *
   */
  EndEffectorTask(const std::string & bodyName,
                  const mc_rbdyn::Robots & robots,
                  unsigned int robotIndex,
                  double stiffness = 2.0,
                  double weight = 1000.0);

  /*! \brief Constructor with bodyPoint
   *
   * \param bodyPoint Point to be controlled in body coordinates
   *
   * @see EndEffectorTask
   *
   */
  EndEffectorTask(const std::string & bodyName,
                  const Eigen::Vector3d & bodyPoint,
                  const mc_rbdyn::Robots & robots,
                  unsigned int robotIndex,
                  double stiffness = 2.0,
                  double weight = 1000.0);

  /*! \brief Reset the task
   *
   * Set the task objective to the current end-effector position
   */
  void reset() override;

  /*! \brief Increment the target position
   *
   * \param dtr Change in target position
   *
   */
  virtual void add_ef_pose(const sva::PTransformd & dtr);

  /*! \brief Change the target position
   *
   * \param tf New target position
   *
   */
  virtual void set_ef_pose(const sva::PTransformd & tf);

  /*! \brief Returns the current target positions
   *
   * \returns Current target position
   *
   */
  virtual sva::PTransformd get_ef_pose();

  void dimWeight(const Eigen::VectorXd & dimW) override;

  Eigen::VectorXd dimWeight() const override;

  void selectActiveJoints(mc_solver::QPSolver & solver,
                          const std::vector<std::string> & activeJointsName,
                          const std::map<std::string, std::vector<std::array<int, 2>>> & activeDofs = {}) override;

  void selectUnactiveJoints(mc_solver::QPSolver & solver,
                            const std::vector<std::string> & unactiveJointsName,
                            const std::map<std::string, std::vector<std::array<int, 2>>> & unactiveDofs = {}) override;

  void resetJointsSelector(mc_solver::QPSolver & solver) override;

  Eigen::VectorXd eval() const override;

  Eigen::VectorXd speed() const override;

  void load(mc_solver::QPSolver & solver, const mc_rtc::Configuration & config) override;

  using MetaTask::name;

  void name(const std::string & name) override;

public:
  const mc_rbdyn::Robots & robots;
  unsigned int robotIndex;
  unsigned int bodyIndex;

  std::shared_ptr<mc_tasks::PositionTask> positionTask;
  std::shared_ptr<mc_tasks::OrientationTask> orientationTask;

  std::string bodyName;
  sva::PTransformd curTransform;

protected:
  void removeFromSolver(mc_solver::QPSolver & solver) override;

  void addToSolver(mc_solver::QPSolver & solver) override;

  void update(mc_solver::QPSolver &) override;

  void addToLogger(mc_rtc::Logger & logger) override;

  void removeFromLogger(mc_rtc::Logger & logger) override;

  void addToGUI(mc_rtc::gui::StateBuilder & gui) override;
};

} // namespace mc_tasks


<|start_filename|>doc/_data/schemas/ConstraintSet/CompoundJointConstraint.json<|end_filename|>
{
  "title": "mc_solver::CompoundJointConstraint",
  "description": "Handle compound joint limits (joints whole limits depend on the position of another joint). By default this adds all the compound joints defined in the robot's module.",
  "type": "object",
  "properties":
  {
    "type": { "enum": ["compoundJoint"] },
    "robot": { "$ref": "/../../common/ConstraintSet_robot.json" }
  },
 "required": ["type"]
}


<|start_filename|>include/mc_control/GlobalPluginMacros.h<|end_filename|>
/*
 * Copyright 2015-2020 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_control/GlobalPlugin.h>

#include <mc_rtc/version.h>

/* Set of macros to assist with the writing of a GlobalPlugin */

/** A simple compile-time versus run-time version checking macro
 *
 * If you are not relying on EXPORT_MC_RTC_PLUGIN you should use this in your
 * MC_RTC_GLOBAL_PLUGIN implementation
 *
 */
#define MC_RTC_GLOBAL_PLUGIN_CHECK_VERSION(NAME)                                                                     \
  if(mc_rtc::MC_RTC_VERSION != mc_rtc::version())                                                                    \
  {                                                                                                                  \
    mc_rtc::log::error("{} was compiled with {} but mc_rtc is currently at version {}, you might experience subtle " \
                       "issues and should recompile your code",                                                      \
                       NAME, mc_rtc::MC_RTC_VERSION, mc_rtc::version());                                             \
  }

#ifndef MC_RTC_BUILD_STATIC

#  define EXPORT_MC_RTC_PLUGIN(NAME, TYPE)                                          \
    extern "C"                                                                      \
    {                                                                               \
      GLOBAL_PLUGIN_API void MC_RTC_GLOBAL_PLUGIN(std::vector<std::string> & names) \
      {                                                                             \
        MC_RTC_GLOBAL_PLUGIN_CHECK_VERSION(NAME)                                    \
        names = {NAME};                                                             \
      }                                                                             \
                                                                                    \
      GLOBAL_PLUGIN_API void destroy(mc_control::GlobalPlugin * ptr)                \
      {                                                                             \
        delete ptr;                                                                 \
      }                                                                             \
                                                                                    \
      GLOBAL_PLUGIN_API mc_control::GlobalPlugin * create(const std::string &)      \
      {                                                                             \
        return new TYPE();                                                          \
      }                                                                             \
    }

#else

#  include <mc_control/GlobalPluginLoader.h>

#  define EXPORT_MC_RTC_PLUGIN(NAME, TYPE)                                                               \
    namespace                                                                                            \
    {                                                                                                    \
    static auto registered = []() {                                                                      \
      using fn_t = std::function<TYPE *()>;                                                              \
      mc_control::GlobalPluginLoader::loader().register_object(NAME, fn_t([]() { return new TYPE(); })); \
      return true;                                                                                       \
    }();                                                                                                 \
    }

#endif


<|start_filename|>doc/_includes/schema_categories/MetaTask.html<|end_filename|>
<p>These objects represent tasks that can be added to the solver.</p>

<p>They can be loaded from C++ with the following code:</p>

<div class="card bg-light card-source">
  <div class="card-body">
{% highlight cpp %}
// Access the meta task loader
#include <mc_tasks/MetaTaskLoader.h>

// Get the task from a JSON file
auto task = mc_tasks::MetaTaskLoader::load(solver(), "/my/path/task.json");

// Get the task from a YAML file
auto task = mc_tasks::MetaTaskLoader::load(solver(), "/my/path/task.yaml");

// Actually you can get the task from any mc_rtc::Configuration entry
auto task = mc_tasks::MetaTaskLoader::load(solver(), config("task"));

// In all the cases above, task is an std::shared_ptr<mc_tasks::MetaTask>
// but you can retrieve a more precise type, mc_rtc will check that the
// task that was retrieved from disk is compatible with the task you requested
auto task = mc_tasks::MetaTaskLoader::load<mc_tasks::EndEffectorTask>(solver(), config("task"));
{% endhighlight %}
  </div>
</div>

<p>This page shows what data is expected for each task available in mc_rtc.</p>


<|start_filename|>doc/_data/schemas/mc_rbdyn/Collision.json<|end_filename|>
{
  "title": "mc_rbdyn::Collision",
  "type": "object",
  "properties":
  {
    "body1": { "type": "string", "description": "First convex body used, wildcards can be used" },
    "body2": { "type": "string", "description": "Second convex body used, wildcards can be used" },
    "iDist": { "type": "number", "default": 0.05, "description": "Interaction distance" },
    "sDist": { "type": "number", "default": 0.01, "description": "Safety distance" },
    "damping": { "type": "number", "default": 0.0, "description": "Damping, 0 enables automatic computation" }
  },
  "required": ["body1", "body2", "iDist", "sDist", "damping"],
  "additionalProperties": false
}


<|start_filename|>include/mc_trajectory/LinearInterpolation.h<|end_filename|>
/*
 * Copyright 2015-2020 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

namespace mc_trajectory
{

template<typename T>
struct LinearInterpolation
{
  /**
   * @brief Linear interpolation between two values
   *
   * \f[
   *  f(t) = (1 - t) * v_1 + t * v_2;
   * \f]
   *
   * @tparam T value type, must support multiplication with scalar and addition with T
   * @param v1 First value
   * @param v2 Second value
   * @param t Interpolation ratio between 0 and 1
   *
   * @return
   * - v1 for t = 0
   * - v2 for t = 1
   * - value linearly interpolated between v1 and v2 otherwise
   */
  T operator()(const T & v1, const T & v2, double t) const
  {
    return (1 - t) * v1 + t * v2;
  }
};

} // namespace mc_trajectory


<|start_filename|>doc/_includes/json_schema_object_property.html<|end_filename|>
<tr>
  {% assign required = false %}
  {% for req in include.required %}
    {% if prop[0] == req %}
      {% assign required = true %}
    {% endif %}
  {% endfor %}
  {% capture PROP_NAME %}{{include.name}}{% endcapture %}
  {% if required %}
    {% capture PROP_NAME %}<strong>{{PROP_NAME}}</strong>{% endcapture %}
  {% endif %}
  {% if include.pattern %}
    {% capture PROP_NAME %}<i>{{PROP_NAME}}</i>&nbsp;&nbsp;<small class="text-secondary">(pattern)</small>{% endcapture %}
  {% endif %}
  <td>{{PROP_NAME}}</td>
  {% if include.data contains "oneOf" %}
    {% assign oneOf = include.data.oneOf %}
    {% for data in oneOf %}
      {% capture id %}{{include.id}}{{forloop.index0}}{% endcapture %}
      {% if data contains "description" %}
        <td>
          {% include json_schema_item.html data=data id=id %}
        </td>
        <td>
          <span class="text-secondary"><small>{{data.description | liquefy}}</small></span>
        </td>
      {% else %}
        <td colspan="2">
          {% include json_schema_item.html data=data id=id %}
        </td>
      {% endif %}
      {% if forloop.last == false %}
        </tr>
        <tr>
          <td></td>
          <td colspan="2">
            <strong>OR</strong>
          </td>
        </tr>
        <tr>
          <td></td>
      {% endif %}
    {% endfor %}
  {% else %}
    {% if include.has_description %}
    <td>
      {% include json_schema_item.html data=include.data id=include.id %}
    </td>
    <td>
      <span class="text-secondary"><small>{{include.description | liquefy}}</small></span>
    </td>
    {% else %}
    <td colspan="2">
      {% include json_schema_item.html data=include.data id=include.id %}
    </td>
    {% endif %}
  {% endif %}
</tr>
{% if INCLUDE_AFTER != "" %}
  {{ INCLUDE_AFTER }}
{% endif %}
{% capture INCLUDE_AFTER %}{% endcapture %}


<|start_filename|>src/mc_robots/json.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_rbdyn/RobotModule.h>

#include "api.h"


<|start_filename|>3rd-party/qhull/src/qhulltest/QhullFacet_test.cpp<|end_filename|>
/****************************************************************************
**
** Copyright (c) 2008-2020 <NAME>. All rights reserved.
** $Id: //main/2019/qhull/src/qhulltest/QhullFacet_test.cpp#6 $$Change: 3006 $
** $DateTime: 2020/07/29 18:28:16 $$Author: bbarber $
**
****************************************************************************/

//pre-compiled headers
#include <iostream>
#include "qhulltest/RoadTest.h" // QT_VERSION

#include "libqhullcpp/QhullFacet.h"
#include "libqhullcpp/QhullError.h"
#include "libqhullcpp/Coordinates.h"
#include "libqhullcpp/RboxPoints.h"
#include "libqhullcpp/QhullFacetList.h"
#include "libqhullcpp/QhullFacetSet.h"
#include "libqhullcpp/QhullPointSet.h"
#include "libqhullcpp/QhullRidge.h"
#include "libqhullcpp/Qhull.h"

using std::cout;
using std::endl;
using std::ostringstream;
using std::ostream;
using std::string;

namespace orgQhull {

class QhullFacet_test : public RoadTest
{
    Q_OBJECT

#//!\name Test slots
private slots:
    void cleanup();
    void t_construct_qh();
    void t_constructConvert();
    void t_getSet();
    void t_getSet2d();
    void t_value();
    void t_foreach();
    void t_io();
};//QhullFacet_test

void
add_QhullFacet_test()
{
    new QhullFacet_test();  // RoadTest::s_testcases
}

//Executed after each testcase
void QhullFacet_test::
cleanup()
{
    RoadTest::cleanup();
}

void QhullFacet_test::
t_construct_qh()
{
    // Qhull.runQhull() constructs QhullFacets as facetT
    QhullQh qh;
    QhullFacet f(&qh);
    QVERIFY(!f.isValid());
    QCOMPARE(f.dimension(),0);
}//t_construct_qh

void QhullFacet_test::
t_constructConvert()
{
    // Qhull.runQhull() constructs QhullFacets as facetT
    Qhull q2;
    QhullFacet f(q2);
    QVERIFY(!f.isValid());
    QCOMPARE(f.dimension(),0);
    RboxPoints rcube("c");
    Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
    QhullFacet f2(q.beginFacet());
    QCOMPARE(f2.dimension(),3);
    f= f2; // copy assignment
    QVERIFY(f.isValid());
    QCOMPARE(f.dimension(),3);
    QhullFacet f5= f2;
    QVERIFY(f5==f2);
    QVERIFY(f5==f);
    QhullFacet f3(q, f2.getFacetT());
    QCOMPARE(f,f3);
    QhullFacet f4(q, f2.getBaseT());
    QCOMPARE(f,f4);
}//t_constructConvert

void QhullFacet_test::
t_getSet()
{
    RboxPoints rcube("c");
    {
        Qhull q(rcube,"Qt QR0");  // triangulation of rotated unit cube
        cout << " rbox c | qhull Qt QR0 QR" << q.rotateRandom() << "   distanceEpsilon " << q.distanceEpsilon() << endl;
        QCOMPARE(q.facetCount(), 12);
        QCOMPARE(q.vertexCount(), 8);
        QhullFacetListIterator i(q.facetList());
        while(i.hasNext()){
            const QhullFacet f= i.next();
            cout << f.id() << endl;
            QhullFacet f2;
            f2.setFacetT(f.qh(), f.getFacetT());
            QCOMPARE(f, f2);
            QCOMPARE(f.dimension(),3);
            QVERIFY(f.id()>0 && f.id()<=39);
            QVERIFY(f.isValid());
            if(i.hasNext()){
                QCOMPARE(f.next(), i.peekNext());
                QVERIFY(f.next()!=f);
                QCOMPARE(f.next().previous(), f);
                QVERIFY(f.hasNext());
                QVERIFY(f.next().hasPrevious());
            }else
              QVERIFY(!f.hasNext());
            QVERIFY(i.hasPrevious());
            QCOMPARE(f, i.peekPrevious());
        }
        while(i.hasPrevious()){
          const QhullFacet f= i.previous();
          cout << f.id() << endl;
          QVERIFY(f.isValid());
          if(i.hasPrevious()){
            QVERIFY(f.hasPrevious());
            QCOMPARE(f.previous(), i.peekPrevious());
            QVERIFY(f.previous()!=f);
            QVERIFY(f.previous().hasNext());
            QCOMPARE(f.previous().next(), f);
          }else
            QVERIFY(!f.hasPrevious());
          QVERIFY(i.hasNext());
          QCOMPARE(f, i.peekNext());
        }
        // test tricoplanarOwner
        QhullFacet facet= q.beginFacet();
        QhullFacet tricoplanarOwner= facet.tricoplanarOwner();
        int tricoplanarCount= 0;
        i.toFront();
        while(i.hasNext()){
            const QhullFacet f= i.next();
            if(f.tricoplanarOwner()==tricoplanarOwner){
                tricoplanarCount++;
            }
        }
        QCOMPARE(tricoplanarCount, 2);
        int tricoplanarCount2= 0;
        foreach (QhullFacet f, q.facetList()){  // Qt only
            QhullHyperplane hi= f.innerplane();
            QCOMPARE(hi.count(), 3);
            double innerOffset= hi.offset() + 0.5;
            cout << "InnerPlane: " << hi << "   innerOffset+0.5 " << innerOffset << endl;
            QhullHyperplane h= f.hyperplane();
            double offset= h.offset() + 0.5;
            cout << "Hyperplane: " << h << "   offset+0.5 " << offset << endl;
            QCOMPARE(h.count(), 3);
            QCOMPARE(h.offset(), -0.5);
            double n= h.norm();
            QCOMPARE(n, 1.0);
            QhullHyperplane ho= f.outerplane();
            QCOMPARE(ho.count(), 3);
            double outerOffset= ho.offset()+0.5;
            cout << "OuterPlane: " << ho << "   outerOffset+0.5 " << outerOffset << endl;
            QVERIFY(offset < innerOffset); // the outerOffset is more negative than the innerOffset.  The expected values are -0.5 for the unit cube centered at the origin
            QVERIFY(outerOffset < offset); // QR1595623290 was 8.4x,   innerOffset+0.5 9.71445e-15, 2.5e-16 max. distance of a new vertex to a facet
            QVERIFY2(fabs(innerOffset) < (10 * q.distanceEpsilon()), "Guessed epsilon from -0.5 due to rotation");   
            QVERIFY2(fabs(outerOffset) < (10 * q.distanceEpsilon()), "Guessed epsilon from -0.5 due to rotation");

            for(int k= 0; k<3; k++){
                QVERIFY(ho[k]==hi[k]);
                QVERIFY(ho[k]==h[k]);
            }
            QhullPoint center= f.getCenter();
            cout << "Center: " << center;
            double d= f.distance(center);
            QVERIFY(d < innerOffset-outerOffset);
            QhullPoint center2= f.getCenter(qh_PRINTcentrums);
            QCOMPARE(center, center2);
            if(f.tricoplanarOwner()==tricoplanarOwner){
                tricoplanarCount2++;
            }
            cout << endl;
        }
        QCOMPARE(tricoplanarCount2, 2);
        Qhull q2(rcube,"d Qz Qt QR0");  // 3-d triangulation of Delaunay triangulation (the cube)
        cout << " rbox c | qhull d Qz Qt QR0 QR" << q2.rotateRandom() << "   distanceEpsilon " << q2.distanceEpsilon() << endl;
        QhullFacet f2= q2.firstFacet();
        QhullPoint center3= f2.getCenter(qh_PRINTtriangles);
        QCOMPARE(center3.dimension(), 3);
        QhullPoint center4= f2.getCenter();
        QCOMPARE(center4.dimension(), 4);
        for(int k= 0; k<3; k++){
            QVERIFY(center4[k]==center3[k]);
        }
        Qhull q3(rcube,"v Qz QR0");  // Voronoi diagram of a cube (one vertex)
        cout << " rbox c | qhull v Qz QR0 QR" << q3.rotateRandom() << "   distanceEpsilon " << q3.distanceEpsilon() << endl;

        q3.setFactorEpsilon(400); // Voronoi vertices are not necessarily within distance episilon
        QhullPoint origin= q3.inputOrigin();
        int voronoiCount= 0;
        foreach(QhullFacet f, q3.facetList()){ //Qt only
            if(f.isGood()){
                ++voronoiCount;
                QhullPoint p= f.voronoiVertex();
                cout << p.print("Voronoi vertex: ")
                    << " Is it within " << q3.factorEpsilon() << " * distanceEpsilon (" << q3.distanceEpsilon() << ") of the origin?" << endl;
                QCOMPARE(p, origin);
            }
        }
        QCOMPARE(voronoiCount, 1);
    }
}//t_getSet

void QhullFacet_test::
t_getSet2d()
{
    RboxPoints rsquare("c D2");
    Qhull q(rsquare, "o");  // convex hull of square
    q.setOutputStream(&cout);
    cout << "Points and facets.  Facet vertices in counter-clockwise order (option 'o')\n";
    q.outputQhull();
    int n= q.facetCount();
    QhullFacet f= q.firstFacet();
    QhullVertex v;
    cout << "Facets and vertices in counter-clockwise order (f.nextFacet2d)\n";
    for(int i= 0; i<n; ++i){
        f= f.nextFacet2d(&v);
        cout << "f" << f.id() << " v" << v.id() << " p" << v.point().id() << "\n";
    }
    cout << "Extreme points in counter-clockwise order (option 'Fx')\n";
    q.outputQhull("Fx");
}//t_getSet2d

void QhullFacet_test::
t_value()
{
    RboxPoints rcube("c");
    {
        Qhull q(rcube, "");
        coordT c[]= {0.0, 0.0, 0.0};
        foreach (QhullFacet f, q.facetList()){  // Qt only
            double d= f.distance(q.origin());
            QCOMPARE(d, -0.5);
            double d0= f.distance(c);
            QCOMPARE(d0, -0.5);
            double facetArea= f.facetArea();
            QCOMPARE(facetArea, 1.0);
            #if qh_MAXoutside
                double maxoutside= f.getFacetT()->maxoutside;
                QVERIFY(maxoutside<1e-7);
            #endif
        }
    }
}//t_value

void QhullFacet_test::
t_foreach()
{
    RboxPoints rcube("c W0 300");  // cube plus 300 points on its surface
    {
        Qhull q(rcube, "QR0 Qc"); // keep coplanars, thick facet, and rotate the cube
        int coplanarCount= 0;
        foreach(const QhullFacet f, q.facetList()){
            QhullPointSet coplanars= f.coplanarPoints();
            coplanarCount += coplanars.count();
            QhullFacetSet neighbors= f.neighborFacets();
            QCOMPARE(neighbors.count(), 4);
            QhullPointSet outsides= f.outsidePoints();
            QCOMPARE(outsides.count(), 0);
            QhullRidgeSet ridges= f.ridges();
            QCOMPARE(ridges.count(), 4);
            QhullVertexSet vertices= f.vertices();
            QCOMPARE(vertices.count(), 4);
            int ridgeCount= 0;
            QhullRidge r= ridges.first();
            for(int r0= r.id(); ridgeCount==0 || r.id()!=r0; r= r.nextRidge3d(f)){
                ++ridgeCount;
                if(!r.hasNextRidge3d(f)){
                    QFAIL("Unexpected simplicial facet.  They only have ridges to non-simplicial neighbors.");
                }
            }
            QCOMPARE(ridgeCount, 4);
        }
        QCOMPARE(coplanarCount, 300);
    }
}//t_foreach

void QhullFacet_test::
t_io()
{
    RboxPoints rcube("c");
    {
        Qhull q(rcube, "");
        QhullFacet f= q.beginFacet();
        cout << f;
        ostringstream os;
        os << f.print("\nWith a message\n");
        os << "\nPrint header for the same facet\n";
        os << f.printHeader();
        os << "\nPrint each component\n";
        os << f.printFlags("    - flags:");
        os << f.printCenter(qh_PRINTfacets, "    - center: ");
        os << f.printRidges();
        cout << os.str();
        ostringstream os2;
        os2 << f;
        QString facetString2= QString::fromStdString(os2.str());
        facetString2.replace(QRegExp("\\s\\s+"), " ");
        facetString2.replace(QRegExp("seen "), "");
        ostringstream os3;
        q.qh()->setOutputStream(&os3);
        q.outputQhull("f");
        QString facetsString= QString::fromStdString(os3.str());
        QString facetString3= facetsString.mid(facetsString.indexOf("- f1\n"));
        facetString3= facetString3.left(facetString3.indexOf("\n- f")+1);
        facetString3.replace(QRegExp("\\s\\s+"), " ");
        QCOMPARE(facetString2, facetString3);
    }
}//t_io

// toQhullFacet is static_cast only

}//orgQhull

#include "moc/QhullFacet_test.moc"


<|start_filename|>binding/python/include/mc_rtc_gui_wrapper.hpp<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_rbdyn/configuration_io.h>
#include <mc_rtc/Configuration.h>
#include <mc_rtc/gui.h>

#include <SpaceVecAlg/SpaceVecAlg>

#include <Eigen/Core>
#include <functional>

using get_fn = std::function<mc_rtc::Configuration()>;
using set_fn = std::function<void(const mc_rtc::Configuration &)>;
using void_cb = std::function<void()>;

template<typename T, typename U>
get_fn make_getter(T fn, U arg)
{
  return [fn, arg] { return fn(arg); };
}

template<typename T, typename Cb, typename t>
set_fn make_setter(T fn, Cb cb, t type)
{
  return [fn, cb, type](const mc_rtc::Configuration & data) mutable {
    return fn(cb, type, const_cast<mc_rtc::Configuration &>(data));
  };
}

template<typename T, typename Cb>
void_cb make_void_cb(T fn, Cb cb)
{
  return [fn, cb]() mutable { return fn(cb); };
}

template<typename Callback>
using Form = mc_rtc::gui::FormImpl<Callback>;

template<typename Callback>
void add_form_checkbox(Form<Callback> & form, const std::string & name, bool required)
{
  form.addElement(mc_rtc::gui::FormCheckbox(name, required));
}

template<typename Callback>
void add_form_checkbox(Form<Callback> & form, const std::string & name, bool required, bool value)
{
  form.addElement(mc_rtc::gui::FormCheckbox(name, required, value));
}

template<typename Callback>
void add_form_integer(Form<Callback> & form, const std::string & name, bool required)
{
  form.addElement(mc_rtc::gui::FormIntegerInput(name, required));
}

template<typename Callback>
void add_form_integer(Form<Callback> & form, const std::string & name, bool required, int value)
{
  form.addElement(mc_rtc::gui::FormIntegerInput(name, required, value));
}

template<typename Callback>
void add_form_number(Form<Callback> & form, const std::string & name, bool required)
{
  form.addElement(mc_rtc::gui::FormNumberInput(name, required));
}

template<typename Callback>
void add_form_number(Form<Callback> & form, const std::string & name, bool required, double value)
{
  form.addElement(mc_rtc::gui::FormNumberInput(name, required, value));
}

template<typename Callback>
void add_form_string(Form<Callback> & form, const std::string & name, bool required)
{
  form.addElement(mc_rtc::gui::FormStringInput(name, required));
}

template<typename Callback>
void add_form_string(Form<Callback> & form, const std::string & name, bool required, const std::string & value)
{
  form.addElement(mc_rtc::gui::FormStringInput(name, required, value));
}

template<typename Callback>
void add_form_number_array(Form<Callback> & form, const std::string & name, bool required, bool fixed_size)
{
  form.addElement(mc_rtc::gui::FormArrayInput<std::vector<double>>(name, required, fixed_size));
}

template<typename Callback>
void add_form_number_array(Form<Callback> & form,
                           const std::string & name,
                           bool required,
                           const std::vector<double> & value,
                           bool fixed_size)
{
  form.addElement(mc_rtc::gui::FormArrayInput(name, required, value, fixed_size));
}

template<typename Callback>
void add_form_string_array(Form<Callback> & form, const std::string & name, bool required, bool fixed_size)
{
  form.addElement(mc_rtc::gui::FormArrayInput<std::vector<std::string>>(name, required, fixed_size));
}

template<typename Callback>
void add_form_string_array(Form<Callback> & form,
                           const std::string & name,
                           bool required,
                           const std::vector<std::string> & value,
                           bool fixed_size)
{
  form.addElement(mc_rtc::gui::FormArrayInput(name, required, value, fixed_size));
}


<|start_filename|>3rd-party/qhull/src/libqhull/merge.c<|end_filename|>
/*<html><pre>  -<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="TOP">-</a>

   merge.c
   merges non-convex facets

   see qh-merge.htm and merge.h

   other modules call qh_premerge() and qh_postmerge()

   the user may call qh_postmerge() to perform additional merges.

   To remove deleted facets and vertices (qhull() in libqhull.c):
     qh_partitionvisible(!qh_ALL, &numoutside);  // visible_list, newfacet_list
     qh_deletevisible();         // qh.visible_list
     qh_resetlists(False, qh_RESETvisible);       // qh.visible_list newvertex_list newfacet_list

   assumes qh.CENTERtype= centrum

   merges occur in qh_mergefacet and in qh_mergecycle
   vertex->neighbors not set until the first merge occurs

   Copyright (c) 1993-2020 <NAME>.
   $Id: //main/2019/qhull/src/libqhull/merge.c#12 $$Change: 2958 $
   $DateTime: 2020/05/26 16:17:49 $$Author: bbarber $
*/

#include "qhull_a.h"

#ifndef qh_NOmerge

/* MRGnone, etc. */
const char *mergetypes[]= {
  "none",
  "coplanar",
  "anglecoplanar",
  "concave",
  "concavecoplanar",
  "twisted",
  "flip",
  "dupridge",
  "subridge",
  "vertices",
  "degen",
  "redundant",
  "mirror",
  "coplanarhorizon",
};

/*===== functions(alphabetical after premerge and postmerge) ======*/

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="premerge">-</a>

  qh_premerge( apexpointid, maxcentrum )
    pre-merge nonconvex facets in qh.newfacet_list for apexpointid
    maxcentrum defines coplanar and concave (qh_test_appendmerge)

  returns:
    deleted facets added to qh.visible_list with facet->visible set

  notes:
    only called by qh_addpoint
    uses globals, qh.MERGEexact, qh.PREmerge

  design:
    mark dupridges in qh.newfacet_list
    merge facet cycles in qh.newfacet_list
    merge dupridges and concave facets in qh.newfacet_list
    check merged facet cycles for degenerate and redundant facets
    merge degenerate and redundant facets
    collect coplanar and concave facets
    merge concave, coplanar, degenerate, and redundant facets
*/
void qh_premerge(int apexpointid, realT maxcentrum, realT maxangle /* qh.newfacet_list */) {
  boolT othermerge= False;

  if (qh ZEROcentrum && qh_checkzero(!qh_ALL))
    return;
  trace2((qh ferr, 2008, "qh_premerge: premerge centrum %2.2g angle %4.4g for apex p%d newfacet_list f%d\n",
            maxcentrum, maxangle, apexpointid, getid_(qh newfacet_list)));
  if (qh IStracing >= 4 && qh num_facets < 100)
    qh_printlists();
  qh centrum_radius= maxcentrum;
  qh cos_max= maxangle;
  if (qh hull_dim >=3) {
    qh_mark_dupridges(qh newfacet_list, qh_ALL); /* facet_mergeset */
    qh_mergecycle_all(qh newfacet_list, &othermerge);
    qh_forcedmerges(&othermerge /* qh.facet_mergeset */);
  }else /* qh.hull_dim == 2 */
    qh_mergecycle_all(qh newfacet_list, &othermerge);
  qh_flippedmerges(qh newfacet_list, &othermerge);
  if (!qh MERGEexact || zzval_(Ztotmerge)) {
    zinc_(Zpremergetot);
    qh POSTmerging= False;
    qh_getmergeset_initial(qh newfacet_list);
    qh_all_merges(othermerge, False);
  }
} /* premerge */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="postmerge">-</a>

  qh_postmerge( reason, maxcentrum, maxangle, vneighbors )
    post-merge nonconvex facets as defined by maxcentrum and maxangle
    'reason' is for reporting progress
    if vneighbors ('Qv'),
      calls qh_test_vneighbors at end of qh_all_merge from qh_postmerge

  returns:
    if first call (qh.visible_list != qh.facet_list),
      builds qh.facet_newlist, qh.newvertex_list
    deleted facets added to qh.visible_list with facet->visible
    qh.visible_list == qh.facet_list

  notes:
    called by qh_qhull after qh_buildhull
    called if a merge may be needed due to
      qh.MERGEexact ('Qx'), qh_DIMreduceBuild, POSTmerge (e.g., 'Cn'), or TESTvneighbors ('Qv')
    if firstmerge,
      calls qh_reducevertices before qh_getmergeset

  design:
    if first call
      set qh.visible_list and qh.newfacet_list to qh.facet_list
      add all facets to qh.newfacet_list
      mark non-simplicial facets, facet->newmerge
      set qh.newvertext_list to qh.vertex_list
      add all vertices to qh.newvertex_list
      if a pre-merge occurred
        set vertex->delridge {will retest the ridge}
        if qh.MERGEexact
          call qh_reducevertices()
      if no pre-merging
        merge flipped facets
    determine non-convex facets
    merge all non-convex facets
*/
void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle,
                      boolT vneighbors) {
  facetT *newfacet;
  boolT othermerges= False;
  vertexT *vertex;

  if (qh REPORTfreq || qh IStracing) {
    qh_buildtracing(NULL, NULL);
    qh_printsummary(qh ferr);
    if (qh PRINTstatistics)
      qh_printallstatistics(qh ferr, "reason");
    qh_fprintf(qh ferr, 8062, "\n%s with 'C%.2g' and 'A%.2g'\n",
        reason, maxcentrum, maxangle);
  }
  trace2((qh ferr, 2009, "qh_postmerge: postmerge.  test vneighbors? %d\n",
            vneighbors));
  qh centrum_radius= maxcentrum;
  qh cos_max= maxangle;
  qh POSTmerging= True;
  if (qh visible_list != qh facet_list) {  /* first call due to qh_buildhull, multiple calls if qh.POSTmerge */
    qh NEWfacets= True;
    qh visible_list= qh newfacet_list= qh facet_list;
    FORALLnew_facets {              /* all facets are new facets for qh_postmerge */
      newfacet->newfacet= True;
       if (!newfacet->simplicial)
        newfacet->newmerge= True;   /* test f.vertices for 'delridge'.  'newmerge' was cleared at end of qh_all_merges */
     zinc_(Zpostfacets);
    }
    qh newvertex_list= qh vertex_list;
    FORALLvertices
      vertex->newfacet= True;
    if (qh VERTEXneighbors) {  /* a merge has occurred */
      if (qh MERGEexact && qh hull_dim <= qh_DIMreduceBuild)
        qh_reducevertices();  /* qh_all_merges did not call qh_reducevertices for v.delridge */
    }
    if (!qh PREmerge && !qh MERGEexact)
      qh_flippedmerges(qh newfacet_list, &othermerges);
  }
  qh_getmergeset_initial(qh newfacet_list);
  qh_all_merges(False, vneighbors); /* calls qh_reducevertices before exiting */
  FORALLnew_facets
    newfacet->newmerge= False;   /* Was True if no vertex in f.vertices was 'delridge' */
} /* post_merge */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="all_merges">-</a>

  qh_all_merges( othermerge, vneighbors )
    merge all non-convex facets

    set othermerge if already merged facets (calls qh_reducevertices)
    if vneighbors ('Qv' at qh.POSTmerge)
      tests vertex neighbors for convexity at end (qh_test_vneighbors)
    qh.facet_mergeset lists the non-convex ridges in qh_newfacet_list
    qh.degen_mergeset is defined
    if qh.MERGEexact && !qh.POSTmerging,
      does not merge coplanar facets

  returns:
    deleted facets added to qh.visible_list with facet->visible
    deleted vertices added qh.delvertex_list with vertex->delvertex

  notes:
    unless !qh.MERGEindependent,
      merges facets in independent sets
    uses qh.newfacet_list as implicit argument since merges call qh_removefacet()
    [apr'19] restored qh_setdellast in place of qh_next_facetmerge.  Much faster for post-merge

  design:
    while merges occur
      for each merge in qh.facet_mergeset
        unless one of the facets was already merged in this pass
          merge the facets
        test merged facets for additional merges
        add merges to qh.facet_mergeset
        if qh.POSTmerging
          periodically call qh_reducevertices to reduce extra vertices and redundant vertices
      after each pass, if qh.VERTEXneighbors
        if qh.POSTmerging or was a merge with qh.hull_dim<=5
          call qh_reducevertices
          update qh.facet_mergeset if degenredundant merges
      if 'Qv' and qh.POSTmerging
        test vertex neighbors for convexity
*/
void qh_all_merges(boolT othermerge, boolT vneighbors) {
  facetT *facet1, *facet2, *newfacet;
  mergeT *merge;
  boolT wasmerge= False, isreduce;
  void **freelistp;  /* used if !qh_NOmem by qh_memfree_() */
  vertexT *vertex;
  realT angle, distance;
  mergeType mergetype;
  int numcoplanar=0, numconcave=0, numconcavecoplanar= 0, numdegenredun= 0, numnewmerges= 0, numtwisted= 0;

  trace2((qh ferr, 2010, "qh_all_merges: starting to merge %d facet and %d degenerate merges for new facets f%d, othermerge? %d\n",
            qh_setsize(qh facet_mergeset), qh_setsize(qh degen_mergeset), getid_(qh newfacet_list), othermerge));

  while (True) {
    wasmerge= False;
    while (qh_setsize(qh facet_mergeset) > 0 || qh_setsize(qh degen_mergeset) > 0) {
      if (qh_setsize(qh degen_mergeset) > 0) {
        numdegenredun += qh_merge_degenredundant();
        wasmerge= True;
      }
      while ((merge= (mergeT *)qh_setdellast(qh facet_mergeset))) {
        facet1= merge->facet1;
        facet2= merge->facet2;
        vertex= merge->vertex1;  /* not used for qh.facet_mergeset*/
        mergetype= merge->mergetype;
        angle= merge->angle;
        distance= merge->distance;
        qh_memfree_(merge, (int)sizeof(mergeT), freelistp);   /* 'merge' is invalid */
        if (facet1->visible || facet2->visible) {
          trace3((qh ferr, 3045, "qh_all_merges: drop merge of f%d (del? %d) into f%d (del? %d) mergetype %d, dist %4.4g, angle %4.4g.  One or both facets is deleted\n",
            facet1->id, facet1->visible, facet2->id, facet2->visible, mergetype, distance, angle));
          continue;
        }else if (mergetype == MRGcoplanar || mergetype == MRGanglecoplanar) {
          if (qh MERGEindependent) {
            if ((!facet1->tested && facet1->newfacet)
            || (!facet2->tested && facet2->newfacet)) {
              trace3((qh ferr, 3064, "qh_all_merges: drop merge of f%d (tested? %d) into f%d (tested? %d) mergetype %d, dist %2.2g, angle %4.4g.  Merge independent sets of coplanar merges\n",
                facet1->id, facet1->visible, facet2->id, facet2->visible, mergetype, distance, angle));
              continue;
            }
          }
        }
        trace3((qh ferr, 3047, "qh_all_merges: merge f%d and f%d type %d dist %2.2g angle %4.4g\n",
          facet1->id, facet2->id, mergetype, distance, angle));
        if (mergetype == MRGtwisted)
          qh_merge_twisted(facet1, facet2);
        else
          qh_merge_nonconvex(facet1, facet2, mergetype);
        numnewmerges++;
        numdegenredun += qh_merge_degenredundant();
        wasmerge= True;
        if (mergetype == MRGconcave)
          numconcave++;
        else if (mergetype == MRGconcavecoplanar)
          numconcavecoplanar++;
        else if (mergetype == MRGtwisted)
          numtwisted++;
        else if (mergetype == MRGcoplanar || mergetype == MRGanglecoplanar)
          numcoplanar++;
        else {
          qh_fprintf(qh ferr, 6394, "qhull internal error (qh_all_merges): expecting concave, coplanar, or twisted merge.  Got merge f%d f%d v%d mergetype %d\n",
            getid_(facet1), getid_(facet2), getid_(vertex), mergetype);
          qh_errexit2(qh_ERRqhull, facet1, facet2);
        }
      } /* while qh_setdellast */
      if (qh POSTmerging && qh hull_dim <= qh_DIMreduceBuild
      && numnewmerges > qh_MAXnewmerges) {
        numnewmerges= 0;
        wasmerge= othermerge= False;
        qh_reducevertices();  /* otherwise large post merges too slow */
      }
      qh_getmergeset(qh newfacet_list); /* qh.facet_mergeset */
    } /* while facet_mergeset or degen_mergeset */
    if (qh VERTEXneighbors) {  /* at least one merge */
      isreduce= False;
      if (qh POSTmerging && qh hull_dim >= 4) {
        isreduce= True;
      }else if (qh POSTmerging || !qh MERGEexact) {
        if ((wasmerge || othermerge) && qh hull_dim > 2 && qh hull_dim <= qh_DIMreduceBuild)
          isreduce= True;
      }
      if (isreduce) {
        wasmerge= othermerge= False;
        if (qh_reducevertices()) {
          qh_getmergeset(qh newfacet_list); /* facet_mergeset */
          continue;
        }
      }
    }
    if (vneighbors && qh_test_vneighbors(/* qh.newfacet_list */))
      continue;
    break;
  } /* while (True) */
  if (wasmerge || othermerge) {
    trace3((qh ferr, 3033, "qh_all_merges: skip qh_reducevertices due to post-merging, no qh.VERTEXneighbors (%d), or hull_dim %d ==2 or >%d\n", qh VERTEXneighbors, qh hull_dim, qh_DIMreduceBuild))
    FORALLnew_facets {
      newfacet->newmerge= False;
    }
  }
  if (qh CHECKfrequently && !qh MERGEexact) {
    qh old_randomdist= qh RANDOMdist;
    qh RANDOMdist= False;
    qh_checkconvex(qh newfacet_list, qh_ALGORITHMfault);
    /* qh_checkconnect(); [this is slow and it changes the facet order] */
    qh RANDOMdist= qh old_randomdist;
  }
  trace1((qh ferr, 1009, "qh_all_merges: merged %d coplanar %d concave %d concavecoplanar %d twisted facets and %d degen or redundant facets.\n",
    numcoplanar, numconcave, numconcavecoplanar, numtwisted, numdegenredun));
  if (qh IStracing >= 4 && qh num_facets < 500)
    qh_printlists();
} /* all_merges */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="all_vertexmerges">-</a>

  qh_all_vertexmerges( apexpointid, facet, &retryfacet )
    merge vertices in qh.vertex_mergeset and subsequent merges

  returns:
    returns retryfacet for facet (if defined)
    updates qh.facet_list, qh.num_facets, qh.vertex_list, qh.num_vertices
    mergesets are empty
    if merges, resets facet lists

  notes:
    called from qh_qhull, qh_addpoint, and qh_buildcone_mergepinched
    vertex merges occur after facet merges and qh_resetlists

  design:
    while merges in vertex_mergeset (MRGvertices)
      merge a pair of pinched vertices
      update vertex neighbors
      merge non-convex and degenerate facets and check for ridges with duplicate vertices
      partition outside points of deleted, "visible" facets
*/
void qh_all_vertexmerges(int apexpointid, facetT *facet, facetT **retryfacet) {
  int numpoints; /* ignore count of partitioned points.  Used by qh_addpoint for Zpbalance */

  if (retryfacet)
    *retryfacet= facet;
  while (qh_setsize(qh vertex_mergeset) > 0) {
    trace1((qh ferr, 1057, "qh_all_vertexmerges: starting to merge %d vertex merges for apex p%d facet f%d\n",
            qh_setsize(qh vertex_mergeset), apexpointid, getid_(facet)));
    if (qh IStracing >= 4  && qh num_facets < 1000)
      qh_printlists();
    qh_merge_pinchedvertices(apexpointid /* qh.vertex_mergeset, visible_list, newvertex_list, newfacet_list */);
    qh_update_vertexneighbors(); /* update neighbors of qh.newvertex_list from qh_newvertices for deleted facets on qh.visible_list */
                           /* test ridges and merge non-convex facets */
    qh_getmergeset(qh newfacet_list);
    qh_all_merges(True, False); /* calls qh_reducevertices */
    if (qh CHECKfrequently)
      qh_checkpolygon(qh facet_list);
    qh_partitionvisible(!qh_ALL, &numpoints /* qh.visible_list qh.del_vertices*/);
    if (retryfacet)
      *retryfacet= qh_getreplacement(*retryfacet);
    qh_deletevisible(/* qh.visible_list  qh.del_vertices*/);
    qh_resetlists(False, qh_RESETvisible /* qh.visible_list newvertex_list qh.newfacet_list */);
    if (qh IStracing >= 4  && qh num_facets < 1000) {
      qh_printlists();
      qh_checkpolygon(qh facet_list);
    }
  }
} /* all_vertexmerges */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="appendmergeset">-</a>

  qh_appendmergeset( facet, vertex, neighbor, mergetype, dist, angle )
    appends an entry to qh.facet_mergeset or qh.degen_mergeset
    if 'dist' is unknown, set it to 0.0
        if 'angle' is unknown, set it to 1.0 (coplanar)

  returns:
    merge appended to facet_mergeset or degen_mergeset
      sets ->degenerate or ->redundant if degen_mergeset

  notes:
    caller collects statistics and/or caller of qh_mergefacet
    see: qh_test_appendmerge()

  design:
    allocate merge entry
    if regular merge
      append to qh.facet_mergeset
    else if degenerate merge and qh.facet_mergeset is all degenerate
      append to qh.degen_mergeset
    else if degenerate merge
      prepend to qh.degen_mergeset (merged last)
    else if redundant merge
      append to qh.degen_mergeset
*/
void qh_appendmergeset(facetT *facet, facetT *neighbor, mergeType mergetype, coordT dist, realT angle) {
  mergeT *merge, *lastmerge;
  void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
  const char *mergename;

  if ((facet->redundant && mergetype != MRGmirror) || neighbor->redundant) {
    trace3((qh ferr, 3051, "qh_appendmergeset: f%d is already redundant (%d) or f%d is already redundant (%d).  Ignore merge f%d and f%d type %d\n",
      facet->id, facet->redundant, neighbor->id, neighbor->redundant, facet->id, neighbor->id, mergetype));
    return;
  }
  if (facet->degenerate && mergetype == MRGdegen) {
    trace3((qh ferr, 3077, "qh_appendmergeset: f%d is already degenerate.  Ignore merge f%d type %d (MRGdegen)\n",
      facet->id, facet->id, mergetype));
    return;
  }
  if (!qh facet_mergeset || !qh degen_mergeset) {
    qh_fprintf(qh ferr, 6403, "qhull internal error (qh_appendmergeset): expecting temp set defined for qh.facet_mergeset (0x%x) and qh.degen_mergeset (0x%x).  Got NULL\n",
      qh facet_mergeset, qh degen_mergeset);
    /* otherwise qh_setappend creates a new set that is not freed by qh_freebuild() */
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  if (neighbor->flipped && !facet->flipped) {
    if (mergetype != MRGdupridge) {
      qh_fprintf(qh ferr, 6355, "qhull internal error (qh_appendmergeset): except for MRGdupridge, cannot merge a non-flipped facet f%d into flipped f%d, mergetype %d, dist %4.4g\n",
        facet->id, neighbor->id, mergetype, dist);
      qh_errexit(qh_ERRqhull, NULL, NULL);
    }else {
      trace2((qh ferr, 2106, "qh_appendmergeset: dupridge will merge a non-flipped facet f%d into flipped f%d, dist %4.4g\n",
        facet->id, neighbor->id, dist));
    }
  }
  qh_memalloc_((int)sizeof(mergeT), freelistp, merge, mergeT);
  merge->angle= angle;
  merge->distance= dist;
  merge->facet1= facet;
  merge->facet2= neighbor;
  merge->vertex1= NULL;
  merge->vertex2= NULL;
  merge->ridge1= NULL;
  merge->ridge2= NULL;
  merge->mergetype= mergetype;
  if(mergetype > 0 && mergetype < sizeof(mergetypes)/sizeof(char *))
    mergename= mergetypes[mergetype];
  else
    mergename= mergetypes[MRGnone];
  if (mergetype < MRGdegen)
    qh_setappend(&(qh facet_mergeset), merge);
  else if (mergetype == MRGdegen) {
    facet->degenerate= True;
    if (!(lastmerge= (mergeT *)qh_setlast(qh degen_mergeset))
    || lastmerge->mergetype == MRGdegen)
      qh_setappend(&(qh degen_mergeset), merge);
    else
      qh_setaddnth(&(qh degen_mergeset), 0, merge);    /* merged last */
  }else if (mergetype == MRGredundant) {
    facet->redundant= True;
    qh_setappend(&(qh degen_mergeset), merge);
  }else /* mergetype == MRGmirror */ {
    if (facet->redundant || neighbor->redundant) {
      qh_fprintf(qh ferr, 6092, "qhull internal error (qh_appendmergeset): facet f%d or f%d is already a mirrored facet (i.e., 'redundant')\n",
           facet->id, neighbor->id);
      qh_errexit2(qh_ERRqhull, facet, neighbor);
    }
    if (!qh_setequal(facet->vertices, neighbor->vertices)) {
      qh_fprintf(qh ferr, 6093, "qhull internal error (qh_appendmergeset): mirrored facets f%d and f%d do not have the same vertices\n",
           facet->id, neighbor->id);
      qh_errexit2(qh_ERRqhull, facet, neighbor);
    }
    facet->redundant= True;
    neighbor->redundant= True;
    qh_setappend(&(qh degen_mergeset), merge);
  }
  if (merge->mergetype >= MRGdegen) {
    trace3((qh ferr, 3044, "qh_appendmergeset: append merge f%d and f%d type %d (%s) to qh.degen_mergeset (size %d)\n",
      merge->facet1->id, merge->facet2->id, merge->mergetype, mergename, qh_setsize(qh degen_mergeset)));
  }else {
    trace3((qh ferr, 3027, "qh_appendmergeset: append merge f%d and f%d type %d (%s) dist %2.2g angle %4.4g to qh.facet_mergeset (size %d)\n",
      merge->facet1->id, merge->facet2->id, merge->mergetype, mergename, merge->distance, merge->angle, qh_setsize(qh facet_mergeset)));
  }
} /* appendmergeset */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="appendvertexmerge">-</a>

  qh_appendvertexmerge( vertex, vertex2, mergetype, distance, ridge1, ridge2 )
    appends a vertex merge to qh.vertex_mergeset
    MRGsubridge includes two ridges (from MRGdupridge)
    MRGvertices includes two ridges

  notes:
    called by qh_getpinchedmerges for MRGsubridge
    called by qh_maybe_duplicateridge and qh_maybe_duplicateridges for MRGvertices
    only way to add a vertex merge to qh.vertex_mergeset
    checked by qh_next_vertexmerge
*/
void qh_appendvertexmerge(vertexT *vertex, vertexT *destination, mergeType mergetype, realT distance, ridgeT *ridge1, ridgeT *ridge2) {
  mergeT *merge;
  void **freelistp; /* used if !qh_NOmem by qh_memalloc_() */
  const char *mergename;

  if (!qh vertex_mergeset) {
    qh_fprintf(qh ferr, 6387, "qhull internal error (qh_appendvertexmerge): expecting temp set defined for qh.vertex_mergeset (0x%x).  Got NULL\n",
      qh vertex_mergeset);
    /* otherwise qh_setappend creates a new set that is not freed by qh_freebuild() */
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  qh_memalloc_((int)sizeof(mergeT), freelistp, merge, mergeT);
  merge->angle= qh_ANGLEnone;
  merge->distance= distance;
  merge->facet1= NULL;
  merge->facet2= NULL;
  merge->vertex1= vertex;
  merge->vertex2= destination;
  merge->ridge1= ridge1;
  merge->ridge2= ridge2;
  merge->mergetype= mergetype;
  if(mergetype > 0 && mergetype < sizeof(mergetypes)/sizeof(char *))
    mergename= mergetypes[mergetype];
  else
    mergename= mergetypes[MRGnone];
  if (mergetype == MRGvertices) {
    if (!ridge1 || !ridge2 || ridge1 == ridge2) {
      qh_fprintf(qh ferr, 6106, "qhull internal error (qh_appendvertexmerge): expecting two distinct ridges for MRGvertices.  Got r%d r%d\n",
        getid_(ridge1), getid_(ridge2));
      qh_errexit(qh_ERRqhull, NULL, ridge1);
    }
  }
  qh_setappend(&(qh vertex_mergeset), merge);
  trace3((qh ferr, 3034, "qh_appendvertexmerge: append merge v%d into v%d r%d r%d dist %2.2g type %d (%s)\n",
    vertex->id, destination->id, getid_(ridge1), getid_(ridge2), distance, merge->mergetype, mergename));
} /* appendvertexmerge */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="basevertices">-</a>

  qh_basevertices( samecycle )
    return temporary set of base vertices for samecycle
    samecycle is first facet in the cycle
    assumes apex is SETfirst_( samecycle->vertices )

  returns:
    vertices(settemp)
    all ->seen are cleared

  notes:
    uses qh_vertex_visit;

  design:
    for each facet in samecycle
      for each unseen vertex in facet->vertices
        append to result
*/
setT *qh_basevertices(facetT *samecycle) {
  facetT *same;
  vertexT *apex, *vertex, **vertexp;
  setT *vertices= qh_settemp(qh TEMPsize);

  apex= SETfirstt_(samecycle->vertices, vertexT);
  apex->visitid= ++qh vertex_visit;
  FORALLsame_cycle_(samecycle) {
    if (same->mergeridge)
      continue;
    FOREACHvertex_(same->vertices) {
      if (vertex->visitid != qh vertex_visit) {
        qh_setappend(&vertices, vertex);
        vertex->visitid= qh vertex_visit;
        vertex->seen= False;
      }
    }
  }
  trace4((qh ferr, 4019, "qh_basevertices: found %d vertices\n",
         qh_setsize(vertices)));
  return vertices;
} /* basevertices */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="check_dupridge">-</a>

  qh_check_dupridge( facet1, dist1, facet2, dist2 )
    Check dupridge between facet1 and facet2 for wide merge
    dist1 is the maximum distance of facet1's vertices to facet2
    dist2 is the maximum distance of facet2's vertices to facet1

  returns
    Level 1 log of the dupridge with the minimum distance between vertices
    Throws error if the merge will increase the maximum facet width by qh_WIDEduplicate (100x)

  notes:
    only called from qh_forcedmerges
*/
void qh_check_dupridge(facetT *facet1, realT dist1, facetT *facet2, realT dist2) {
  vertexT *vertex, **vertexp, *vertexA, **vertexAp;
  realT dist, innerplane, mergedist, outerplane, prevdist, ratio, vertexratio;
  realT minvertex= REALmax;

  mergedist= fmin_(dist1, dist2);
  qh_outerinner(NULL, &outerplane, &innerplane);  /* ratio from qh_printsummary */
  FOREACHvertex_(facet1->vertices) {     /* The dupridge is between facet1 and facet2, so either facet can be tested */
    FOREACHvertexA_(facet1->vertices) {
      if (vertex > vertexA){   /* Test each pair once */
        dist= qh_pointdist(vertex->point, vertexA->point, qh hull_dim);
        minimize_(minvertex, dist);
        /* Not quite correct.  A facet may have a dupridge and another pair of nearly adjacent vertices. */
      }
    }
  }
  prevdist= fmax_(outerplane, innerplane);
  maximize_(prevdist, qh ONEmerge + qh DISTround);
  maximize_(prevdist, qh MINoutside + qh DISTround);
  ratio= mergedist/prevdist;
  vertexratio= minvertex/prevdist;
  trace0((qh ferr, 16, "qh_check_dupridge: dupridge between f%d and f%d (vertex dist %2.2g), dist %2.2g, reverse dist %2.2g, ratio %2.2g while processing p%d\n",
        facet1->id, facet2->id, minvertex, dist1, dist2, ratio, qh furthest_id));
  if (ratio > qh_WIDEduplicate) {
    qh_fprintf(qh ferr, 6271, "qhull topology error (qh_check_dupridge): wide merge (%.1fx wider) due to dupridge between f%d and f%d (vertex dist %2.2g), merge dist %2.2g, while processing p%d\n- Allow error with option 'Q12'\n",
      ratio, facet1->id, facet2->id, minvertex, mergedist, qh furthest_id);
    if (vertexratio < qh_WIDEpinched)
      qh_fprintf(qh ferr, 8145, "- Experimental option merge-pinched-vertices ('Q14') may avoid this error.  It merges nearly adjacent vertices.\n");
    if (qh DELAUNAY)
      qh_fprintf(qh ferr, 8145, "- A bounding box for the input sites may alleviate this error.\n");
    if (!qh ALLOWwide)
      qh_errexit2(qh_ERRwide, facet1, facet2);
  }
} /* check_dupridge */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="checkconnect">-</a>

  qh_checkconnect( )
    check that new facets are connected
    new facets are on qh.newfacet_list

  notes:
    this is slow and it changes the order of the facets
    uses qh.visit_id

  design:
    move first new facet to end of qh.facet_list
    for all newly appended facets
      append unvisited neighbors to end of qh.facet_list
    for all new facets
      report error if unvisited
*/
void qh_checkconnect(void /* qh.newfacet_list */) {
  facetT *facet, *newfacet, *errfacet= NULL, *neighbor, **neighborp;

  facet= qh newfacet_list;
  qh_removefacet(facet);
  qh_appendfacet(facet);
  facet->visitid= ++qh visit_id;
  FORALLfacet_(facet) {
    FOREACHneighbor_(facet) {
      if (neighbor->visitid != qh visit_id) {
        qh_removefacet(neighbor);
        qh_appendfacet(neighbor);
        neighbor->visitid= qh visit_id;
      }
    }
  }
  FORALLnew_facets {
    if (newfacet->visitid == qh visit_id)
      break;
    qh_fprintf(qh ferr, 6094, "qhull internal error (qh_checkconnect): f%d is not attached to the new facets\n",
         newfacet->id);
    errfacet= newfacet;
  }
  if (errfacet)
    qh_errexit(qh_ERRqhull, errfacet, NULL);
} /* checkconnect */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="checkdelfacet">-</a>

  qh_checkdelfacet( facet, mergeset )
    check that mergeset does not reference facet

*/
void qh_checkdelfacet(facetT *facet, setT *mergeset) {
  mergeT *merge, **mergep;

  FOREACHmerge_(mergeset) {
    if (merge->facet1 == facet || merge->facet2 == facet) {
      qh_fprintf(qh ferr, 6390, "qhull internal error (qh_checkdelfacet): cannot delete f%d.  It is referenced by merge f%d f%d mergetype %d\n",
        facet->id, merge->facet1->id, getid_(merge->facet2), merge->mergetype);
      qh_errexit2(qh_ERRqhull, merge->facet1, merge->facet2);
    }
  }
} /* checkdelfacet */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="checkdelridge">-</a>

  qh_checkdelridge( )
    check that qh_delridge_merge is not needed for deleted ridges

    notes:
      called from qh_mergecycle, qh_makenewfacets, qh_attachnewfacets
      errors if qh.vertex_mergeset is non-empty
      errors if any visible or new facet has a ridge with r.nonconvex set
      assumes that vertex.delfacet is not needed
*/
void qh_checkdelridge(void /* qh.visible_facets, vertex_mergeset */) {
  facetT *newfacet, *visible;
  ridgeT *ridge, **ridgep;

  if (!SETempty_(qh vertex_mergeset)) {
    qh_fprintf(qh ferr, 6382, "qhull internal error (qh_checkdelridge): expecting empty qh.vertex_mergeset in order to avoid calling qh_delridge_merge.  Got %d merges\n", qh_setsize(qh vertex_mergeset));
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }

  FORALLnew_facets {
    FOREACHridge_(newfacet->ridges) {
      if (ridge->nonconvex) {
        qh_fprintf(qh ferr, 6313, "qhull internal error (qh_checkdelridge): unexpected 'nonconvex' flag for ridge r%d in newfacet f%d.  Otherwise need to call qh_delridge_merge\n",
           ridge->id, newfacet->id);
        qh_errexit(qh_ERRqhull, newfacet, ridge);
      }
    }
  }

  FORALLvisible_facets {
    FOREACHridge_(visible->ridges) {
      if (ridge->nonconvex) {
        qh_fprintf(qh ferr, 6385, "qhull internal error (qh_checkdelridge): unexpected 'nonconvex' flag for ridge r%d in visible facet f%d.  Otherwise need to call qh_delridge_merge\n",
          ridge->id, visible->id);
        qh_errexit(qh_ERRqhull, visible, ridge);
      }
    }
  }
} /* checkdelridge */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="checkzero">-</a>

  qh_checkzero( testall )
    check that facets are clearly convex for qh.DISTround with qh.MERGEexact

    if testall,
      test all facets for qh.MERGEexact post-merging
    else
      test qh.newfacet_list

    if qh.MERGEexact,
      allows coplanar ridges
      skips convexity test while qh.ZEROall_ok

  returns:
    True if all facets !flipped, !dupridge, normal
         if all horizon facets are simplicial
         if all vertices are clearly below neighbor
         if all opposite vertices of horizon are below
    clears qh.ZEROall_ok if any problems or coplanar facets

  notes:
    called by qh_premerge (qh.CHECKzero, 'C-0') and qh_qhull ('Qx')
    uses qh.vertex_visit
    horizon facets may define multiple new facets

  design:
    for all facets in qh.newfacet_list or qh.facet_list
      check for flagged faults (flipped, etc.)
    for all facets in qh.newfacet_list or qh.facet_list
      for each neighbor of facet
        skip horizon facets for qh.newfacet_list
        test the opposite vertex
      if qh.newfacet_list
        test the other vertices in the facet's horizon facet
*/
boolT qh_checkzero(boolT testall) {
  facetT *facet, *neighbor;
  facetT *horizon, *facetlist;
  int neighbor_i, neighbor_n;
  vertexT *vertex, **vertexp;
  realT dist;

  if (testall)
    facetlist= qh facet_list;
  else {
    facetlist= qh newfacet_list;
    FORALLfacet_(facetlist) {
      horizon= SETfirstt_(facet->neighbors, facetT);
      if (!horizon->simplicial)
        goto LABELproblem;
      if (facet->flipped || facet->dupridge || !facet->normal)
        goto LABELproblem;
    }
    if (qh MERGEexact && qh ZEROall_ok) {
      trace2((qh ferr, 2011, "qh_checkzero: skip convexity check until first pre-merge\n"));
      return True;
    }
  }
  FORALLfacet_(facetlist) {
    qh vertex_visit++;
    horizon= NULL;
    FOREACHneighbor_i_(facet) {
      if (!neighbor_i && !testall) {
        horizon= neighbor;
        continue; /* horizon facet tested in qh_findhorizon */
      }
      vertex= SETelemt_(facet->vertices, neighbor_i, vertexT);
      vertex->visitid= qh vertex_visit;
      zzinc_(Zdistzero);
      qh_distplane(vertex->point, neighbor, &dist);
      if (dist >= -2 * qh DISTround) {  /* need 2x for qh_distround and 'Rn' for qh_checkconvex, same as qh.premerge_centrum */
        qh ZEROall_ok= False;
        if (!qh MERGEexact || testall || dist > qh DISTround)
          goto LABELnonconvex;
      }
    }
    if (!testall && horizon) {
      FOREACHvertex_(horizon->vertices) {
        if (vertex->visitid != qh vertex_visit) {
          zzinc_(Zdistzero);
          qh_distplane(vertex->point, facet, &dist);
          if (dist >= -2 * qh DISTround) {
            qh ZEROall_ok= False;
            if (!qh MERGEexact || dist > qh DISTround)
              goto LABELnonconvexhorizon;
          }
          break;
        }
      }
    }
  }
  trace2((qh ferr, 2012, "qh_checkzero: testall %d, facets are %s\n", testall,
        (qh MERGEexact && !testall) ?
           "not concave, flipped, or dupridge" : "clearly convex"));
  return True;

 LABELproblem:
  qh ZEROall_ok= False;
  trace2((qh ferr, 2013, "qh_checkzero: qh_premerge is needed.  New facet f%d or its horizon f%d is non-simplicial, flipped, dupridge, or mergehorizon\n",
       facet->id, horizon->id));
  return False;

 LABELnonconvex:
  trace2((qh ferr, 2014, "qh_checkzero: facet f%d and f%d are not clearly convex.  v%d dist %.2g\n",
         facet->id, neighbor->id, vertex->id, dist));
  return False;

 LABELnonconvexhorizon:
  trace2((qh ferr, 2060, "qh_checkzero: facet f%d and horizon f%d are not clearly convex.  v%d dist %.2g\n",
      facet->id, horizon->id, vertex->id, dist));
  return False;
} /* checkzero */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="compare_anglemerge">-</a>

  qh_compare_anglemerge( mergeA, mergeB )
    used by qsort() to order qh.facet_mergeset by mergetype and angle (qh.ANGLEmerge, 'Q1')
    lower numbered mergetypes done first (MRGcoplanar before MRGconcave)

  notes:
    qh_all_merges processes qh.facet_mergeset by qh_setdellast
    [mar'19] evaluated various options with eg/q_benchmark and merging of pinched vertices (Q14)
*/
int qh_compare_anglemerge(const void *p1, const void *p2) {
  const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);

  if (a->mergetype != b->mergetype)
    return (a->mergetype < b->mergetype ? 1 : -1); /* select MRGcoplanar (1) before MRGconcave (3) */
  else
    return (a->angle > b->angle ? 1 : -1);         /* select coplanar merge (1.0) before sharp merge (-0.5) */
} /* compare_anglemerge */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="compare_facetmerge">-</a>

  qh_compare_facetmerge( mergeA, mergeB )
    used by qsort() to order merges by mergetype, first merge, first
    lower numbered mergetypes done first (MRGcoplanar before MRGconcave)
    if same merge type, flat merges are first

  notes:
    qh_all_merges processes qh.facet_mergeset by qh_setdellast
    [mar'19] evaluated various options with eg/q_benchmark and merging of pinched vertices (Q14)
*/
int qh_compare_facetmerge(const void *p1, const void *p2) {
  const mergeT *a= *((mergeT *const*)p1), *b= *((mergeT *const*)p2);

  if (a->mergetype != b->mergetype)
    return (a->mergetype < b->mergetype ? 1 : -1); /* select MRGcoplanar (1) before MRGconcave (3) */
  else if (a->mergetype == MRGanglecoplanar)
    return (a->angle > b->angle ? 1 : -1);         /* if MRGanglecoplanar, select coplanar merge (1.0) before sharp merge (-0.5) */
  else
    return (a->distance < b->distance ? 1 : -1);   /* select flat (0.0) merge before wide (1e-10) merge */
} /* compare_facetmerge */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="comparevisit">-</a>

  qh_comparevisit( vertexA, vertexB )
    used by qsort() to order vertices by their visitid

  notes:
    only called by qh_find_newvertex
*/
int qh_comparevisit(const void *p1, const void *p2) {
  const vertexT *a= *((vertexT *const*)p1), *b= *((vertexT *const*)p2);

  if (a->visitid > b->visitid)
    return 1;
  return -1;
} /* comparevisit */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="copynonconvex">-</a>

  qh_copynonconvex( atridge )
    set non-convex flag on other ridges (if any) between same neighbors

  notes:
    may be faster if use smaller ridge set

  design:
    for each ridge of atridge's top facet
      if ridge shares the same neighbor
        set nonconvex flag
*/
void qh_copynonconvex(ridgeT *atridge) {
  facetT *facet, *otherfacet;
  ridgeT *ridge, **ridgep;

  facet= atridge->top;
  otherfacet= atridge->bottom;
  atridge->nonconvex= False;
  FOREACHridge_(facet->ridges) {
    if (otherfacet == ridge->top || otherfacet == ridge->bottom) {
      if (ridge != atridge) {
        ridge->nonconvex= True;
        trace4((qh ferr, 4020, "qh_copynonconvex: moved nonconvex flag from r%d to r%d between f%d and f%d\n",
                atridge->id, ridge->id, facet->id, otherfacet->id));
        break;
      }
    }
  }
} /* copynonconvex */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="degen_redundant_facet">-</a>

  qh_degen_redundant_facet( facet )
    check for a degenerate (too few neighbors) or redundant (subset of vertices) facet

  notes:
    called at end of qh_mergefacet, qh_renamevertex, and qh_reducevertices
    bumps vertex_visit
    called if a facet was redundant but no longer is (qh_merge_degenredundant)
    qh_appendmergeset() only appends first reference to facet (i.e., redundant)
    see: qh_test_redundant_neighbors, qh_maydropneighbor

  design:
    test for redundant neighbor
    test for degenerate facet
*/
void qh_degen_redundant_facet(facetT *facet) {
  vertexT *vertex, **vertexp;
  facetT *neighbor, **neighborp;

  trace3((qh ferr, 3028, "qh_degen_redundant_facet: test facet f%d for degen/redundant\n",
          facet->id));
  if (facet->flipped) {
    trace2((qh ferr, 3074, "qh_degen_redundant_facet: f%d is flipped, will merge later\n", facet->id));
    return;
  }
  FOREACHneighbor_(facet) {
    if (neighbor->flipped) /* disallow merge of non-flipped into flipped, neighbor will be merged later */
      continue;
    if (neighbor->visible) {
      qh_fprintf(qh ferr, 6357, "qhull internal error (qh_degen_redundant_facet): facet f%d has deleted neighbor f%d (qh.visible_list)\n",
        facet->id, neighbor->id);
      qh_errexit2(qh_ERRqhull, facet, neighbor);
    }
    qh vertex_visit++;
    FOREACHvertex_(neighbor->vertices)
      vertex->visitid= qh vertex_visit;
    FOREACHvertex_(facet->vertices) {
      if (vertex->visitid != qh vertex_visit)
        break;
    }
    if (!vertex) {
      trace2((qh ferr, 2015, "qh_degen_redundant_facet: f%d is contained in f%d.  merge\n", facet->id, neighbor->id));
      qh_appendmergeset(facet, neighbor, MRGredundant, 0.0, 1.0);
      return;
    }
  }
  if (qh_setsize(facet->neighbors) < qh hull_dim) {
    qh_appendmergeset(facet, facet, MRGdegen, 0.0, 1.0);
    trace2((qh ferr, 2016, "qh_degen_redundant_facet: f%d is degenerate.\n", facet->id));
  }
} /* degen_redundant_facet */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="delridge_merge">-</a>

  qh_delridge_merge( ridge )
    delete ridge due to a merge

  notes:
    only called by merge.c (qh_mergeridges, qh_renameridgevertex)
    ridges also freed in qh_freeqhull and qh_mergecycle_ridges

  design:
    if needed, moves ridge.nonconvex to another ridge
    sets vertex.delridge for qh_reducevertices
    deletes ridge from qh.vertex_mergeset
    deletes ridge from its neighboring facets
    frees up its memory
*/
void qh_delridge_merge(ridgeT *ridge) {
  vertexT *vertex, **vertexp;
  mergeT *merge;
  int merge_i, merge_n;

  trace3((qh ferr, 3036, "qh_delridge_merge: delete ridge r%d between f%d and f%d\n",
    ridge->id, ridge->top->id, ridge->bottom->id));
  if (ridge->nonconvex)
    qh_copynonconvex(ridge);
  FOREACHvertex_(ridge->vertices)
    vertex->delridge= True;
  FOREACHmerge_i_(qh vertex_mergeset) {
    if (merge->ridge1 == ridge || merge->ridge2 == ridge) {
      trace3((qh ferr, 3029, "qh_delridge_merge: drop merge of v%d into v%d (dist %2.2g r%d r%d) due to deleted, duplicated ridge r%d\n",
        merge->vertex1->id, merge->vertex2->id, merge->distance, merge->ridge1->id, merge->ridge2->id, ridge->id));
      if (merge->ridge1 == ridge)
        merge->ridge2->mergevertex= False;
      else
        merge->ridge1->mergevertex= False;
      qh_setdelnth(qh vertex_mergeset, merge_i);
      merge_i--; merge_n--; /* next merge after deleted */
    }
  }
  qh_setdel(ridge->top->ridges, ridge);
  qh_setdel(ridge->bottom->ridges, ridge);
  qh_delridge(ridge);
} /* delridge_merge */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="drop_mergevertex">-</a>

  qh_drop_mergevertex( merge )

  clear mergevertex flags for ridges of a vertex merge
*/
void qh_drop_mergevertex(mergeT *merge)
{
  if (merge->mergetype == MRGvertices) {
    merge->ridge1->mergevertex= False;
    merge->ridge1->mergevertex2= True;
    merge->ridge2->mergevertex= False;
    merge->ridge2->mergevertex2= True;
    trace3((qh ferr, 3032, "qh_drop_mergevertex: unset mergevertex for r%d and r%d due to dropped vertex merge v%d to v%d.  Sets mergevertex2\n",
      merge->ridge1->id, merge->ridge2->id, merge->vertex1->id, merge->vertex2->id));
  }
} /* drop_mergevertex */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="find_newvertex">-</a>

  qh_find_newvertex( oldvertex, vertices, ridges )
    locate new vertex for renaming old vertex
    vertices is a set of possible new vertices
      vertices sorted by number of deleted ridges

  returns:
    newvertex or NULL
      each ridge includes both newvertex and oldvertex
    vertices without oldvertex sorted by number of deleted ridges
    qh.vertex_visit updated
    sets v.seen

  notes:
    called by qh_redundant_vertex due to vertex->delridge and qh_rename_sharedvertex
    sets vertex->visitid to 0..setsize() for vertices
    new vertex is in one of the ridges
    renaming will not cause a duplicate ridge
    renaming will minimize the number of deleted ridges
    newvertex may not be adjacent in the dual (though unlikely)

  design:
    for each vertex in vertices
      set vertex->visitid to number of ridges
    remove unvisited vertices
    set qh.vertex_visit above all possible values
    sort vertices by number of ridges (minimize ridges that need renaming
    add each ridge to qh.hash_table
    for each vertex in vertices
      find the first vertex that would not cause a duplicate ridge after a rename
*/
vertexT *qh_find_newvertex(vertexT *oldvertex, setT *vertices, setT *ridges) {
  vertexT *vertex, **vertexp;
  setT *newridges;
  ridgeT *ridge, **ridgep;
  int size, hashsize;
  int hash;
  unsigned int maxvisit;

#ifndef qh_NOtrace
  if (qh IStracing >= 4) {
    qh_fprintf(qh ferr, 8063, "qh_find_newvertex: find new vertex for v%d from ",
             oldvertex->id);
    FOREACHvertex_(vertices)
      qh_fprintf(qh ferr, 8064, "v%d ", vertex->id);
    FOREACHridge_(ridges)
      qh_fprintf(qh ferr, 8065, "r%d ", ridge->id);
    qh_fprintf(qh ferr, 8066, "\n");
  }
#endif
  FOREACHridge_(ridges) {
    FOREACHvertex_(ridge->vertices)
      vertex->seen= False;
  }
  FOREACHvertex_(vertices) {
    vertex->visitid= 0;  /* v.visitid will be number of ridges */
    vertex->seen= True;
  }
  FOREACHridge_(ridges) {
    FOREACHvertex_(ridge->vertices) {
      if (vertex->seen)
        vertex->visitid++;
    }
  }
  FOREACHvertex_(vertices) {
    if (!vertex->visitid) {
      qh_setdelnth(vertices, SETindex_(vertices,vertex));
      vertexp--; /* repeat since deleted this vertex */
    }
  }
  maxvisit= (unsigned int)qh_setsize(ridges);
  maximize_(qh vertex_visit, maxvisit);
  if (!qh_setsize(vertices)) {
    trace4((qh ferr, 4023, "qh_find_newvertex: vertices not in ridges for v%d\n",
            oldvertex->id));
    return NULL;
  }
  qsort(SETaddr_(vertices, vertexT), (size_t)qh_setsize(vertices),
                sizeof(vertexT *), qh_comparevisit);
  /* can now use qh vertex_visit */
  if (qh PRINTstatistics) {
    size= qh_setsize(vertices);
    zinc_(Zintersect);
    zadd_(Zintersecttot, size);
    zmax_(Zintersectmax, size);
  }
  hashsize= qh_newhashtable(qh_setsize(ridges));
  FOREACHridge_(ridges)
    qh_hashridge(qh hash_table, hashsize, ridge, oldvertex);
  FOREACHvertex_(vertices) {
    newridges= qh_vertexridges(vertex, !qh_ALL);
    FOREACHridge_(newridges) {
      if (qh_hashridge_find(qh hash_table, hashsize, ridge, vertex, oldvertex, &hash)) {
        zinc_(Zvertexridge);
        break;
      }
    }
    qh_settempfree(&newridges);
    if (!ridge)
      break;  /* found a rename */
  }
  if (vertex) {
    /* counted in qh_renamevertex */
    trace2((qh ferr, 2020, "qh_find_newvertex: found v%d for old v%d from %d vertices and %d ridges.\n",
      vertex->id, oldvertex->id, qh_setsize(vertices), qh_setsize(ridges)));
  }else {
    zinc_(Zfindfail);
    trace0((qh ferr, 14, "qh_find_newvertex: no vertex for renaming v%d (all duplicated ridges) during p%d\n",
      oldvertex->id, qh furthest_id));
  }
  qh_setfree(&qh hash_table);
  return vertex;
} /* find_newvertex */

/*-<a                             href="qh-geom2.htm#TOC"
  >-------------------------------</a><a name="findbest_pinchedvertex">-</a>

  qh_findbest_pinchedvertex( merge, apex, nearestp, distp )
    Determine the best pinched vertex to rename as its nearest neighboring vertex
    Renaming will remove a duplicate MRGdupridge in newfacet_list

  returns:
    pinched vertex (either apex or subridge), nearest vertex (subridge or neighbor vertex), and the distance between them

  notes:
    only called by qh_getpinchedmerges
    assumes qh.VERTEXneighbors
    see qh_findbest_ridgevertex

  design:
    if the facets have the same vertices
      return the nearest vertex pair
    else
      the subridge is the intersection of the two new facets minus the apex
      the subridge consists of qh.hull_dim-2 horizon vertices
      the subridge is also a matched ridge for the new facets (its duplicate)
      determine the nearest vertex to the apex
      determine the nearest pair of subridge vertices
      for each vertex in the subridge
        determine the nearest neighbor vertex (not in the subridge)
*/
vertexT *qh_findbest_pinchedvertex(mergeT *merge, vertexT *apex, vertexT **nearestp, coordT *distp /* qh.newfacet_list */) {
  vertexT *vertex, **vertexp, *vertexA, **vertexAp;
  vertexT *bestvertex= NULL, *bestpinched= NULL;
  setT *subridge, *maybepinched;
  coordT dist, bestdist= REALmax;
  coordT pincheddist= (qh ONEmerge+qh DISTround)*qh_RATIOpinchedsubridge;

  if (!merge->facet1->simplicial || !merge->facet2->simplicial) {
    qh_fprintf(qh ferr, 6351, "qhull internal error (qh_findbest_pinchedvertex): expecting merge of adjacent, simplicial new facets.  f%d or f%d is not simplicial\n",
      merge->facet1->id, merge->facet2->id);
    qh_errexit2(qh_ERRqhull, merge->facet1, merge->facet2);
  }
  subridge= qh_vertexintersect_new(merge->facet1->vertices, merge->facet2->vertices); /* new setT.  No error_exit() */
  if (qh_setsize(subridge) == qh hull_dim) { /* duplicate vertices */
    bestdist= qh_vertex_bestdist2(subridge, &bestvertex, &bestpinched);
    if(bestvertex == apex) {
      bestvertex= bestpinched;
      bestpinched= apex;
    }
  }else {
    qh_setdel(subridge, apex);
    if (qh_setsize(subridge) != qh hull_dim - 2) {
      qh_fprintf(qh ferr, 6409, "qhull internal error (qh_findbest_pinchedvertex): expecting subridge of qh.hull_dim-2 vertices for the intersection of new facets f%d and f%d minus their apex.  Got %d vertices\n",
          merge->facet1->id, merge->facet2->id, qh_setsize(subridge));
      qh_errexit2(qh_ERRqhull, merge->facet1, merge->facet2);
    }
    FOREACHvertex_(subridge) {
      dist= qh_pointdist(vertex->point, apex->point, qh hull_dim);
      if (dist < bestdist) {
        bestpinched= apex;
        bestvertex= vertex;
        bestdist= dist;
      }
    }
    if (bestdist > pincheddist) {
      FOREACHvertex_(subridge) {
        FOREACHvertexA_(subridge) {
          if (vertexA->id > vertex->id) { /* once per vertex pair, do not compare addresses */
            dist= qh_pointdist(vertexA->point, vertex->point, qh hull_dim);
            if (dist < bestdist) {
              bestpinched= vertexA;
              bestvertex= vertex;
              bestdist= dist;
            }
          }
        }
      }
    }
    if (bestdist > pincheddist) {
      FOREACHvertexA_(subridge) {
        maybepinched= qh_neighbor_vertices(vertexA, subridge); /* subridge and apex tested above */
        FOREACHvertex_(maybepinched) {
          dist= qh_pointdist(vertex->point, vertexA->point, qh hull_dim);
          if (dist < bestdist) {
            bestvertex= vertex;
            bestpinched= vertexA;
            bestdist= dist;
          }
        }
        qh_settempfree(&maybepinched);
      }
    }
  }
  *distp= bestdist;
  qh_setfree(&subridge); /* qh_err_exit not called since allocated */
  if (!bestvertex) {  /* should never happen if qh.hull_dim > 2 */
    qh_fprintf(qh ferr, 6274, "qhull internal error (qh_findbest_pinchedvertex): did not find best vertex for subridge of dupridge between f%d and f%d, while processing p%d\n", merge->facet1->id, merge->facet2->id, qh furthest_id);
    qh_errexit2(qh_ERRqhull, merge->facet1, merge->facet2);
  }
  *nearestp= bestvertex;
  trace2((qh ferr, 2061, "qh_findbest_pinchedvertex: best pinched p%d(v%d) and vertex p%d(v%d) are closest (%2.2g) for duplicate subridge between f%d and f%d\n",
      qh_pointid(bestpinched->point), bestpinched->id, qh_pointid(bestvertex->point), bestvertex->id, bestdist, merge->facet1->id, merge->facet2->id));
  return bestpinched;
} /* findbest_pinchedvertex */

/*-<a                             href="qh-geom2.htm#TOC"
  >-------------------------------</a><a name="findbest_ridgevertex">-</a>

  qh_findbest_ridgevertex( ridge, pinchedp, distp )
    Determine the best vertex/pinched-vertex to merge for ridges with the same vertices

  returns:
    vertex, pinched vertex, and the distance between them

  notes:
    assumes qh.hull_dim>=3
    see qh_findbest_pinchedvertex

*/
vertexT *qh_findbest_ridgevertex(ridgeT *ridge, vertexT **pinchedp, coordT *distp) {
  vertexT *bestvertex;

  *distp= qh_vertex_bestdist2(ridge->vertices, &bestvertex, pinchedp);
  trace4((qh ferr, 4069, "qh_findbest_ridgevertex: best pinched p%d(v%d) and vertex p%d(v%d) are closest (%2.2g) for duplicated ridge r%d (same vertices) between f%d and f%d\n",
      qh_pointid((*pinchedp)->point), (*pinchedp)->id, qh_pointid(bestvertex->point), bestvertex->id, *distp, ridge->id, ridge->top->id, ridge->bottom->id));
  return bestvertex;
} /* findbest_ridgevertex */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="findbest_test">-</a>

  qh_findbest_test( testcentrum, facet, neighbor, &bestfacet, &dist, &mindist, &maxdist )
    test neighbor of facet for qh_findbestneighbor()
    if testcentrum,
      tests centrum (assumes it is defined)
    else
      tests vertices
    initially *bestfacet==NULL and *dist==REALmax

  returns:
    if a better facet (i.e., vertices/centrum of facet closer to neighbor)
      updates bestfacet, dist, mindist, and maxdist

  notes:
    called by qh_findbestneighbor
    ignores pairs of flipped facets, unless that's all there is
*/
void qh_findbest_test(boolT testcentrum, facetT *facet, facetT *neighbor,
      facetT **bestfacet, realT *distp, realT *mindistp, realT *maxdistp) {
  realT dist, mindist, maxdist;

  if (facet->flipped && neighbor->flipped && *bestfacet && !(*bestfacet)->flipped)
    return; /* do not merge flipped into flipped facets */
  if (testcentrum) {
    zzinc_(Zbestdist);
    qh_distplane(facet->center, neighbor, &dist);
    dist *= qh hull_dim; /* estimate furthest vertex */
    if (dist < 0) {
      maxdist= 0;
      mindist= dist;
      dist= -dist;
    }else {
      mindist= 0;
      maxdist= dist;
    }
  }else
    dist= qh_getdistance(facet, neighbor, &mindist, &maxdist);
  if (dist < *distp) {
    *bestfacet= neighbor;
    *mindistp= mindist;
    *maxdistp= maxdist;
    *distp= dist;
  }
} /* findbest_test */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="findbestneighbor">-</a>

  qh_findbestneighbor( facet, dist, mindist, maxdist )
    finds best neighbor (least dist) of a facet for merging

  returns:
    returns min and max distances and their max absolute value

  notes:
    error if qh_ASvoronoi
    avoids merging old into new
    assumes ridge->nonconvex only set on one ridge between a pair of facets
    could use an early out predicate but not worth it

  design:
    if a large facet
      will test centrum
    else
      will test vertices
    if a large facet
      test nonconvex neighbors for best merge
    else
      test all neighbors for the best merge
    if testing centrum
      get distance information
*/
facetT *qh_findbestneighbor(facetT *facet, realT *distp, realT *mindistp, realT *maxdistp) {
  facetT *neighbor, **neighborp, *bestfacet= NULL;
  ridgeT *ridge, **ridgep;
  boolT nonconvex= True, testcentrum= False;
  int size= qh_setsize(facet->vertices);

  if(qh CENTERtype==qh_ASvoronoi){
    qh_fprintf(qh ferr, 6272, "qhull internal error: cannot call qh_findbestneighor for f%d while qh.CENTERtype is qh_ASvoronoi\n", facet->id);
    qh_errexit(qh_ERRqhull, facet, NULL);
  }
  *distp= REALmax;
  if (size > qh_BESTcentrum2 * qh hull_dim + qh_BESTcentrum) {
    testcentrum= True;
    zinc_(Zbestcentrum);
    if (!facet->center)
       facet->center= qh_getcentrum(facet);
  }
  if (size > qh hull_dim + qh_BESTnonconvex) {
    FOREACHridge_(facet->ridges) {
      if (ridge->nonconvex) {
        neighbor= otherfacet_(ridge, facet);
        qh_findbest_test(testcentrum, facet, neighbor,
                          &bestfacet, distp, mindistp, maxdistp);
      }
    }
  }
  if (!bestfacet) {
    nonconvex= False;
    FOREACHneighbor_(facet)
      qh_findbest_test(testcentrum, facet, neighbor,
                        &bestfacet, distp, mindistp, maxdistp);
  }
  if (!bestfacet) {
    qh_fprintf(qh ferr, 6095, "qhull internal error (qh_findbestneighbor): no neighbors for f%d\n", facet->id);
    qh_errexit(qh_ERRqhull, facet, NULL);
  }
  if (testcentrum)
    qh_getdistance(facet, bestfacet, mindistp, maxdistp);
  trace3((qh ferr, 3002, "qh_findbestneighbor: f%d is best neighbor for f%d testcentrum? %d nonconvex? %d dist %2.2g min %2.2g max %2.2g\n",
     bestfacet->id, facet->id, testcentrum, nonconvex, *distp, *mindistp, *maxdistp));
  return(bestfacet);
} /* findbestneighbor */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="flippedmerges">-</a>

  qh_flippedmerges( facetlist, wasmerge )
    merge flipped facets into best neighbor
    assumes qh.facet_mergeset at top of temporary stack

  returns:
    no flipped facets on facetlist
    sets wasmerge if merge occurred
    degen/redundant merges passed through

  notes:
    othermerges not needed since qh.facet_mergeset is empty before & after
      keep it in case of change

  design:
    append flipped facets to qh.facetmergeset
    for each flipped merge
      find best neighbor
      merge facet into neighbor
      merge degenerate and redundant facets
    remove flipped merges from qh.facet_mergeset
*/
void qh_flippedmerges(facetT *facetlist, boolT *wasmerge) {
  facetT *facet, *neighbor, *facet1;
  realT dist, mindist, maxdist;
  mergeT *merge, **mergep;
  setT *othermerges;
  int nummerge= 0, numdegen= 0;

  trace4((qh ferr, 4024, "qh_flippedmerges: begin\n"));
  FORALLfacet_(facetlist) {
    if (facet->flipped && !facet->visible)
      qh_appendmergeset(facet, facet, MRGflip, 0.0, 1.0);
  }
  othermerges= qh_settemppop();
  if(othermerges != qh facet_mergeset) {
    qh_fprintf(qh ferr, 6392, "qhull internal error (qh_flippedmerges): facet_mergeset (%d merges) not at top of tempstack (%d merges)\n",
        qh_setsize(qh facet_mergeset), qh_setsize(othermerges));
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  qh facet_mergeset= qh_settemp(qh TEMPsize);
  qh_settemppush(othermerges);
  FOREACHmerge_(othermerges) {
    facet1= merge->facet1;
    if (merge->mergetype != MRGflip || facet1->visible)
      continue;
    if (qh TRACEmerge-1 == zzval_(Ztotmerge))
      qhmem.IStracing= qh IStracing= qh TRACElevel;
    neighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist);
    trace0((qh ferr, 15, "qh_flippedmerges: merge flipped f%d into f%d dist %2.2g during p%d\n",
      facet1->id, neighbor->id, dist, qh furthest_id));
    qh_mergefacet(facet1, neighbor, merge->mergetype, &mindist, &maxdist, !qh_MERGEapex);
    nummerge++;
    if (qh PRINTstatistics) {
      zinc_(Zflipped);
      wadd_(Wflippedtot, dist);
      wmax_(Wflippedmax, dist);
    }
  }
  FOREACHmerge_(othermerges) {
    if (merge->facet1->visible || merge->facet2->visible)
      qh_memfree(merge, (int)sizeof(mergeT)); /* invalidates merge and othermerges */
    else
      qh_setappend(&qh facet_mergeset, merge);
  }
  qh_settempfree(&othermerges);
  numdegen += qh_merge_degenredundant(); /* somewhat better here than after each flipped merge -- qtest.sh 10 '500 C1,2e-13 D4' 'd Qbb' */
  if (nummerge)
    *wasmerge= True;
  trace1((qh ferr, 1010, "qh_flippedmerges: merged %d flipped and %d degenredundant facets into a good neighbor\n",
    nummerge, numdegen));
} /* flippedmerges */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="forcedmerges">-</a>

  qh_forcedmerges( wasmerge )
    merge dupridges
    calls qh_check_dupridge to report an error on wide merges
    assumes qh_settemppop is qh.facet_mergeset

  returns:
    removes all dupridges on facet_mergeset
    wasmerge set if merge
    qh.facet_mergeset may include non-forced merges(none for now)
    qh.degen_mergeset includes degen/redun merges

  notes:
    called by qh_premerge
    dupridges occur when the horizon is pinched,
        i.e. a subridge occurs in more than two horizon ridges.
     could rename vertices that pinch the horizon
    assumes qh_merge_degenredundant() has not be called
    othermerges isn't needed since facet_mergeset is empty afterwards
      keep it in case of change

  design:
    for each dupridge
      find current facets by chasing f.replace links
      check for wide merge due to dupridge
      determine best direction for facet
      merge one facet into the other
      remove dupridges from qh.facet_mergeset
*/
void qh_forcedmerges(boolT *wasmerge) {
  facetT *facet1, *facet2, *merging, *merged, *newfacet;
  mergeT *merge, **mergep;
  realT dist, mindist, maxdist, dist2, mindist2, maxdist2;
  setT *othermerges;
  int nummerge=0, numflip=0, numdegen= 0;
  boolT wasdupridge= False;

  if (qh TRACEmerge-1 == zzval_(Ztotmerge))
    qhmem.IStracing= qh IStracing= qh TRACElevel;
  trace3((qh ferr, 3054, "qh_forcedmerges: merge dupridges\n"));
  othermerges= qh_settemppop(); /* was facet_mergeset */
  if (qh facet_mergeset != othermerges ) {
      qh_fprintf(qh ferr, 6279, "qhull internal error (qh_forcedmerges): qh_settemppop (size %d) is not qh facet_mergeset (size %d)\n",
          qh_setsize(othermerges), qh_setsize(qh facet_mergeset));
      qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  qh facet_mergeset= qh_settemp(qh TEMPsize);
  qh_settemppush(othermerges);
  FOREACHmerge_(othermerges) {
    if (merge->mergetype != MRGdupridge)
        continue;
    wasdupridge= True;
    if (qh TRACEmerge-1 == zzval_(Ztotmerge))
        qhmem.IStracing= qh IStracing= qh TRACElevel;
    facet1= qh_getreplacement(merge->facet1);  /* must exist, no qh_merge_degenredunant */
    facet2= qh_getreplacement(merge->facet2);  /* previously merged facet, if any */
    if (facet1 == facet2)
      continue;
    if (!qh_setin(facet2->neighbors, facet1)) {
      qh_fprintf(qh ferr, 6096, "qhull internal error (qh_forcedmerges): f%d and f%d had a dupridge but as f%d and f%d they are no longer neighbors\n",
               merge->facet1->id, merge->facet2->id, facet1->id, facet2->id);
      qh_errexit2(qh_ERRqhull, facet1, facet2);
    }
    dist= qh_getdistance(facet1, facet2, &mindist, &maxdist);
    dist2= qh_getdistance(facet2, facet1, &mindist2, &maxdist2);
    qh_check_dupridge(facet1, dist, facet2, dist2);
    if (dist < dist2) {
      if (facet2->flipped && !facet1->flipped && dist2 < qh_WIDEdupridge*(qh ONEmerge+qh DISTround)) { /* prefer merge of flipped facet */
        merging= facet2;
        merged= facet1;
        dist= dist2;
        mindist= mindist2;
        maxdist= maxdist2;
      }else {
        merging= facet1;
        merged= facet2;
      }
    }else {
      if (facet1->flipped && !facet2->flipped && dist < qh_WIDEdupridge*(qh ONEmerge+qh DISTround)) { /* prefer merge of flipped facet */
        merging= facet1;
        merged= facet2;
      }else {
        merging= facet2;
        merged= facet1;
        dist= dist2;
        mindist= mindist2;
        maxdist= maxdist2;
      }
    }
    qh_mergefacet(merging, merged, merge->mergetype, &mindist, &maxdist, !qh_MERGEapex);
    numdegen += qh_merge_degenredundant(); /* better here than at end -- qtest.sh 10 '500 C1,2e-13 D4' 'd Qbb' */
    if (facet1->flipped) {
      zinc_(Zmergeflipdup);
      numflip++;
    }else
      nummerge++;
    if (qh PRINTstatistics) {
      zinc_(Zduplicate);
      wadd_(Wduplicatetot, dist);
      wmax_(Wduplicatemax, dist);
    }
  }
  FOREACHmerge_(othermerges) {
    if (merge->mergetype == MRGdupridge)
      qh_memfree(merge, (int)sizeof(mergeT)); /* invalidates merge and othermerges */
    else
      qh_setappend(&qh facet_mergeset, merge);
  }
  qh_settempfree(&othermerges);
  if (wasdupridge) {
    FORALLnew_facets {
      if (newfacet->dupridge) {
        newfacet->dupridge= False;
        newfacet->mergeridge= False;
        newfacet->mergeridge2= False;
        if (qh_setsize(newfacet->neighbors) < qh hull_dim) { /* not tested for MRGdupridge */
          qh_appendmergeset(newfacet, newfacet, MRGdegen, 0.0, 1.0);
          trace2((qh ferr, 2107, "qh_forcedmerges: dupridge f%d is degenerate with fewer than %d neighbors\n",
                      newfacet->id, qh hull_dim));
        }
      }
    }
    numdegen += qh_merge_degenredundant();
  }
  if (nummerge || numflip) {
    *wasmerge= True;
    trace1((qh ferr, 1011, "qh_forcedmerges: merged %d facets, %d flipped facets, and %d degenredundant facets across dupridges\n",
                  nummerge, numflip, numdegen));
  }
} /* forcedmerges */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="freemergesets">-</a>

  qh_freemergesets( )
    free the merge sets

  notes:
    matches qh_initmergesets
*/
void qh_freemergesets(void) {

  if (!qh facet_mergeset || !qh degen_mergeset || !qh vertex_mergeset) {
    qh_fprintf(qh ferr, 6388, "qhull internal error (qh_freemergesets): expecting mergesets.  Got a NULL mergeset, qh.facet_mergeset (0x%x), qh.degen_mergeset (0x%x), qh.vertex_mergeset (0x%x)\n",
      qh facet_mergeset, qh degen_mergeset, qh vertex_mergeset);
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  if (!SETempty_(qh facet_mergeset) || !SETempty_(qh degen_mergeset) || !SETempty_(qh vertex_mergeset)) {
    qh_fprintf(qh ferr, 6389, "qhull internal error (qh_freemergesets): expecting empty mergesets.  Got qh.facet_mergeset (%d merges), qh.degen_mergeset (%d merges), qh.vertex_mergeset (%d merges)\n",
      qh_setsize(qh facet_mergeset), qh_setsize(qh degen_mergeset), qh_setsize(qh vertex_mergeset));
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  qh_settempfree(&qh facet_mergeset);
  qh_settempfree(&qh vertex_mergeset);
  qh_settempfree(&qh degen_mergeset);
} /* freemergesets */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="getmergeset">-</a>

  qh_getmergeset( facetlist )
    determines nonconvex facets on facetlist
    tests !tested ridges and nonconvex ridges of !tested facets

  returns:
    returns sorted qh.facet_mergeset of facet-neighbor pairs to be merged
    all ridges tested

  notes:
    facetlist is qh.facet_newlist, use qh_getmergeset_initial for all facets
    assumes no nonconvex ridges with both facets tested
    uses facet->tested/ridge->tested to prevent duplicate tests
    can not limit tests to modified ridges since the centrum changed
    uses qh.visit_id

  design:
    for each facet on facetlist
      for each ridge of facet
        if untested ridge
          test ridge for convexity
          if non-convex
            append ridge to qh.facet_mergeset
    sort qh.facet_mergeset by mergetype and angle or distance
*/
void qh_getmergeset(facetT *facetlist) {
  facetT *facet, *neighbor, **neighborp;
  ridgeT *ridge, **ridgep;
  int nummerges;
  boolT simplicial;

  nummerges= qh_setsize(qh facet_mergeset);
  trace4((qh ferr, 4026, "qh_getmergeset: started.\n"));
  qh visit_id++;
  FORALLfacet_(facetlist) {
    if (facet->tested)
      continue;
    facet->visitid= qh visit_id;
    FOREACHneighbor_(facet)
      neighbor->seen= False;
    /* facet must be non-simplicial due to merge to qh.facet_newlist */
    FOREACHridge_(facet->ridges) {
      if (ridge->tested && !ridge->nonconvex)
        continue;
      /* if r.tested & r.nonconvex, need to retest and append merge */
      neighbor= otherfacet_(ridge, facet);
      if (neighbor->seen) { /* another ridge for this facet-neighbor pair was already tested in this loop */
        ridge->tested= True;
        ridge->nonconvex= False;   /* only one ridge is marked nonconvex per facet-neighbor pair */
      }else if (neighbor->visitid != qh visit_id) {
        neighbor->seen= True;
        ridge->nonconvex= False;
        simplicial= False;
        if (ridge->simplicialbot && ridge->simplicialtop)
          simplicial= True;
        if (qh_test_appendmerge(facet, neighbor, simplicial))
          ridge->nonconvex= True;
        ridge->tested= True;
      }
    }
    facet->tested= True;
  }
  nummerges= qh_setsize(qh facet_mergeset);
  if (qh ANGLEmerge)
    qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compare_anglemerge);
  else
    qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compare_facetmerge);
  nummerges += qh_setsize(qh degen_mergeset);
  if (qh POSTmerging) {
    zadd_(Zmergesettot2, nummerges);
  }else {
    zadd_(Zmergesettot, nummerges);
    zmax_(Zmergesetmax, nummerges);
  }
  trace2((qh ferr, 2021, "qh_getmergeset: %d merges found\n", nummerges));
} /* getmergeset */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="getmergeset_initial">-</a>

  qh_getmergeset_initial( facetlist )
    determine initial qh.facet_mergeset for facets
    tests all facet/neighbor pairs on facetlist

  returns:
    sorted qh.facet_mergeset with nonconvex ridges
    sets facet->tested, ridge->tested, and ridge->nonconvex

  notes:
    uses visit_id, assumes ridge->nonconvex is False
    see qh_getmergeset

  design:
    for each facet on facetlist
      for each untested neighbor of facet
        test facet and neighbor for convexity
        if non-convex
          append merge to qh.facet_mergeset
          mark one of the ridges as nonconvex
    sort qh.facet_mergeset by mergetype and angle or distance
*/
void qh_getmergeset_initial(facetT *facetlist) {
  facetT *facet, *neighbor, **neighborp;
  ridgeT *ridge, **ridgep;
  int nummerges;
  boolT simplicial;

  qh visit_id++;
  FORALLfacet_(facetlist) {
    facet->visitid= qh visit_id;
    FOREACHneighbor_(facet) {
      if (neighbor->visitid != qh visit_id) {
        simplicial= False; /* ignores r.simplicialtop/simplicialbot.  Need to test horizon facets */
        if (facet->simplicial && neighbor->simplicial)
          simplicial= True;
        if (qh_test_appendmerge(facet, neighbor, simplicial)) {
          FOREACHridge_(neighbor->ridges) {
            if (facet == otherfacet_(ridge, neighbor)) {
              ridge->nonconvex= True;
              break;    /* only one ridge is marked nonconvex */
            }
          }
        }
      }
    }
    facet->tested= True;
    FOREACHridge_(facet->ridges)
      ridge->tested= True;
  }
  nummerges= qh_setsize(qh facet_mergeset);
  if (qh ANGLEmerge)
    qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compare_anglemerge);
  else
    qsort(SETaddr_(qh facet_mergeset, mergeT), (size_t)nummerges, sizeof(mergeT *), qh_compare_facetmerge);
  nummerges += qh_setsize(qh degen_mergeset);
  if (qh POSTmerging) {
    zadd_(Zmergeinittot2, nummerges);
  }else {
    zadd_(Zmergeinittot, nummerges);
    zmax_(Zmergeinitmax, nummerges);
  }
  trace2((qh ferr, 2022, "qh_getmergeset_initial: %d merges found\n", nummerges));
} /* getmergeset_initial */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="getpinchedmerges">-</a>

  qh_getpinchedmerges( apex, maxdist, iscoplanar )
    get pinched merges for dupridges in qh.facet_mergeset
    qh.NEWtentative==True
      qh.newfacet_list with apex
      qh.horizon_list is attached to qh.visible_list instead of qh.newfacet_list
      maxdist for vertex-facet of a dupridge
    qh.facet_mergeset is empty
    qh.vertex_mergeset is a temporary set

  returns:
    False if nearest vertex would increase facet width by more than maxdist or qh_WIDEpinched
    True and iscoplanar, if the pinched vertex is the apex (i.e., make the apex a coplanar point)
    True and !iscoplanar, if should merge a pinched vertex of a dupridge
      qh.vertex_mergeset contains one or more MRGsubridge with a pinched vertex and a nearby, neighboring vertex
    qh.facet_mergeset is empty

  notes:
    called by qh_buildcone_mergepinched
    hull_dim >= 3
    a pinched vertex is in a dupridge and the horizon
    selects the pinched vertex that is closest to its neighbor

  design:
    for each dupridge
        determine the best pinched vertex to be merged into a neighboring vertex
        if merging the pinched vertex would produce a wide merge (qh_WIDEpinched)
           ignore pinched vertex with a warning, and use qh_merge_degenredundant instead
        else
           append the pinched vertex to vertex_mergeset for merging
*/
boolT qh_getpinchedmerges(vertexT *apex, coordT maxdupdist, boolT *iscoplanar /* qh.newfacet_list, qh.vertex_mergeset */) {
  mergeT *merge, **mergep, *bestmerge= NULL;
  vertexT *nearest, *pinched, *bestvertex= NULL, *bestpinched= NULL;
  boolT result;
  coordT dist, prevdist, bestdist= REALmax/(qh_RATIOcoplanarapex+1.0); /* allow *3.0 */
  realT ratio;

  trace2((qh ferr, 2062, "qh_getpinchedmerges: try to merge pinched vertices for dupridges in new facets with apex p%d(v%d) max dupdist %2.2g\n",
      qh_pointid(apex->point), apex->id, maxdupdist));
  *iscoplanar= False;
  prevdist= fmax_(qh ONEmerge + qh DISTround, qh MINoutside + qh DISTround);
  maximize_(prevdist, qh max_outside);
  maximize_(prevdist, -qh min_vertex);
  qh_mark_dupridges(qh newfacet_list, !qh_ALL); /* qh.facet_mergeset, creates ridges */
  /* qh_mark_dupridges is called a second time in qh_premerge */
  FOREACHmerge_(qh facet_mergeset) {  /* read-only */
    if (merge->mergetype != MRGdupridge) {
      qh_fprintf(qh ferr, 6393, "qhull internal error (qh_getpinchedmerges): expecting MRGdupridge from qh_mark_dupridges.  Got merge f%d f%d type %d\n",
        getid_(merge->facet1), getid_(merge->facet2), merge->mergetype);
      qh_errexit(qh_ERRqhull, NULL, NULL);
    }
    /* dist is distance between vertices */
    pinched= qh_findbest_pinchedvertex(merge, apex, &nearest, &dist /* qh.newfacet_list */);
    if (pinched == apex && dist < qh_RATIOcoplanarapex*bestdist) { /* prefer coplanar apex since it always works */
      bestdist= dist/qh_RATIOcoplanarapex;
      bestmerge= merge;
      bestpinched= pinched;
      bestvertex= nearest;
    }else if (dist < bestdist) {
      bestdist= dist;
      bestmerge= merge;
      bestpinched= pinched;
      bestvertex= nearest;
    }
  }
  result= False;
  if (bestmerge && bestdist < maxdupdist) {
    ratio= bestdist / prevdist;
    if (ratio > qh_WIDEpinched) {
      if (bestmerge->facet1->mergehorizon || bestmerge->facet2->mergehorizon) { /* e.g., rbox 175 C3,2e-13 t1539182828 | qhull d */
        trace1((qh ferr, 1051, "qh_getpinchedmerges: dupridge (MRGdupridge) of coplanar horizon would produce a wide merge (%.0fx) due to pinched vertices v%d and v%d (dist %2.2g) for f%d and f%d.  qh_mergecycle_all will merge one or both facets\n",
          ratio, bestpinched->id, bestvertex->id, bestdist, bestmerge->facet1->id, bestmerge->facet2->id));
      }else {
        qh_fprintf(qh ferr, 7081, "qhull precision warning (qh_getpinchedmerges): pinched vertices v%d and v%d (dist %2.2g, %.0fx) would produce a wide merge for f%d and f%d.  Will merge dupridge instead\n",
          bestpinched->id, bestvertex->id, bestdist, ratio, bestmerge->facet1->id, bestmerge->facet2->id);
      }
    }else {
      if (bestpinched == apex) {
        trace2((qh ferr, 2063, "qh_getpinchedmerges: will make the apex a coplanar point.  apex p%d(v%d) is the nearest vertex to v%d on dupridge.  Dist %2.2g\n",
          qh_pointid(apex->point), apex->id, bestvertex->id, bestdist*qh_RATIOcoplanarapex));
        qh coplanar_apex= apex->point;
        *iscoplanar= True;
        result= True;
      }else if (qh_setin(bestmerge->facet1->vertices, bestpinched) != qh_setin(bestmerge->facet2->vertices, bestpinched)) { /* pinched in one facet but not the other facet */
        trace2((qh ferr, 2064, "qh_getpinchedmerges: will merge new facets to resolve dupridge between f%d and f%d with pinched v%d and v%d\n",
          bestmerge->facet1->id, bestmerge->facet2->id, bestpinched->id, bestvertex->id));
        qh_appendvertexmerge(bestpinched, bestvertex, MRGsubridge, bestdist, NULL, NULL);
        result= True;
      }else {
        trace2((qh ferr, 2065, "qh_getpinchedmerges: will merge pinched v%d into v%d to resolve dupridge between f%d and f%d\n",
          bestpinched->id, bestvertex->id, bestmerge->facet1->id, bestmerge->facet2->id));
        qh_appendvertexmerge(bestpinched, bestvertex, MRGsubridge, bestdist, NULL, NULL);
        result= True;
      }
    }
  }
  /* delete MRGdupridge, qh_mark_dupridges is called a second time in qh_premerge */
  while ((merge= (mergeT *)qh_setdellast(qh facet_mergeset)))
    qh_memfree(merge, (int)sizeof(mergeT));
  return result;
}/* getpinchedmerges */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="hasmerge">-</a>

  qh_hasmerge( mergeset, mergetype, facetA, facetB )
    True if mergeset has mergetype for facetA and facetB
*/
boolT   qh_hasmerge(setT *mergeset, mergeType type, facetT *facetA, facetT *facetB) {
  mergeT *merge, **mergep;

  FOREACHmerge_(mergeset) {
    if (merge->mergetype == type) {
      if (merge->facet1 == facetA && merge->facet2 == facetB)
        return True;
      if (merge->facet1 == facetB && merge->facet2 == facetA)
        return True;
    }
  }
  return False;
}/* hasmerge */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="hashridge">-</a>

  qh_hashridge( hashtable, hashsize, ridge, oldvertex )
    add ridge to hashtable without oldvertex

  notes:
    assumes hashtable is large enough

  design:
    determine hash value for ridge without oldvertex
    find next empty slot for ridge
*/
void qh_hashridge(setT *hashtable, int hashsize, ridgeT *ridge, vertexT *oldvertex) {
  int hash;
  ridgeT *ridgeA;

  hash= qh_gethash(hashsize, ridge->vertices, qh hull_dim-1, 0, oldvertex);
  while (True) {
    if (!(ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
      SETelem_(hashtable, hash)= ridge;
      break;
    }else if (ridgeA == ridge)
      break;
    if (++hash == hashsize)
      hash= 0;
  }
} /* hashridge */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="hashridge_find">-</a>

  qh_hashridge_find( hashtable, hashsize, ridge, vertex, oldvertex, hashslot )
    returns matching ridge without oldvertex in hashtable
      for ridge without vertex
    if oldvertex is NULL
      matches with any one skip

  returns:
    matching ridge or NULL
    if no match,
      if ridge already in   table
        hashslot= -1
      else
        hashslot= next NULL index

  notes:
    assumes hashtable is large enough
    can't match ridge to itself

  design:
    get hash value for ridge without vertex
    for each hashslot
      return match if ridge matches ridgeA without oldvertex
*/
ridgeT *qh_hashridge_find(setT *hashtable, int hashsize, ridgeT *ridge,
              vertexT *vertex, vertexT *oldvertex, int *hashslot) {
  int hash;
  ridgeT *ridgeA;

  *hashslot= 0;
  zinc_(Zhashridge);
  hash= qh_gethash(hashsize, ridge->vertices, qh hull_dim-1, 0, vertex);
  while ((ridgeA= SETelemt_(hashtable, hash, ridgeT))) {
    if (ridgeA == ridge)
      *hashslot= -1;
    else {
      zinc_(Zhashridgetest);
      if (qh_setequal_except(ridge->vertices, vertex, ridgeA->vertices, oldvertex))
        return ridgeA;
    }
    if (++hash == hashsize)
      hash= 0;
  }
  if (!*hashslot)
    *hashslot= hash;
  return NULL;
} /* hashridge_find */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="initmergesets">-</a>

  qh_initmergesets( )
    initialize the merge sets
    if 'all', include qh.degen_mergeset

  notes:
    matches qh_freemergesets
*/
void qh_initmergesets(void /* qh.facet_mergeset,degen_mergeset,vertex_mergeset */) {

  if (qh facet_mergeset || qh degen_mergeset || qh vertex_mergeset) {
    qh_fprintf(qh ferr, 6386, "qhull internal error (qh_initmergesets): expecting NULL mergesets.  Got qh.facet_mergeset (0x%x), qh.degen_mergeset (0x%x), qh.vertex_mergeset (0x%x)\n",
      qh facet_mergeset, qh degen_mergeset, qh vertex_mergeset);
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  qh degen_mergeset= qh_settemp(qh TEMPsize);
  qh vertex_mergeset= qh_settemp(qh TEMPsize);
  qh facet_mergeset= qh_settemp(qh TEMPsize); /* last temporary set for qh_forcedmerges */
} /* initmergesets */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="makeridges">-</a>

  qh_makeridges( facet )
    creates explicit ridges between simplicial facets

  returns:
    facet with ridges and without qh_MERGEridge
    ->simplicial is False
    if facet was tested, new ridges are tested

  notes:
    allows qh_MERGEridge flag
    uses existing ridges
    duplicate neighbors ok if ridges already exist (qh_mergecycle_ridges)

  see:
    qh_mergecycle_ridges()
    qh_rename_adjacentvertex for qh_merge_pinchedvertices

  design:
    look for qh_MERGEridge neighbors
    mark neighbors that already have ridges
    for each unprocessed neighbor of facet
      create a ridge for neighbor and facet
    if any qh_MERGEridge neighbors
      delete qh_MERGEridge flags (previously processed by qh_mark_dupridges)
*/
void qh_makeridges(facetT *facet) {
  facetT *neighbor, **neighborp;
  ridgeT *ridge, **ridgep;
  int neighbor_i, neighbor_n;
  boolT toporient, mergeridge= False;

  if (!facet->simplicial)
    return;
  trace4((qh ferr, 4027, "qh_makeridges: make ridges for f%d\n", facet->id));
  facet->simplicial= False;
  FOREACHneighbor_(facet) {
    if (neighbor == qh_MERGEridge)
      mergeridge= True;
    else
      neighbor->seen= False;
  }
  FOREACHridge_(facet->ridges)
    otherfacet_(ridge, facet)->seen= True;
  FOREACHneighbor_i_(facet) {
    if (neighbor == qh_MERGEridge)
      continue;  /* fixed by qh_mark_dupridges */
    else if (!neighbor->seen) {  /* no current ridges */
      ridge= qh_newridge();
      ridge->vertices= qh_setnew_delnthsorted(facet->vertices, qh hull_dim,
                                                          neighbor_i, 0);
      toporient= (boolT)(facet->toporient ^ (neighbor_i & 0x1));
      if (toporient) {
        ridge->top= facet;
        ridge->bottom= neighbor;
        ridge->simplicialtop= True;
        ridge->simplicialbot= neighbor->simplicial;
      }else {
        ridge->top= neighbor;
        ridge->bottom= facet;
        ridge->simplicialtop= neighbor->simplicial;
        ridge->simplicialbot= True;
      }
      if (facet->tested && !mergeridge)
        ridge->tested= True;
#if 0 /* this also works */
      flip= (facet->toporient ^ neighbor->toporient)^(skip1 & 0x1) ^ (skip2 & 0x1);
      if (facet->toporient ^ (skip1 & 0x1) ^ flip) {
        ridge->top= neighbor;
        ridge->bottom= facet;
        ridge->simplicialtop= True;
        ridge->simplicialbot= neighbor->simplicial;
      }else {
        ridge->top= facet;
        ridge->bottom= neighbor;
        ridge->simplicialtop= neighbor->simplicial;
        ridge->simplicialbot= True;
      }
#endif
      qh_setappend(&(facet->ridges), ridge);
      trace5((qh ferr, 5005, "makeridges: appended r%d to ridges for f%d.  Next is ridges for neighbor f%d\n",
            ridge->id, facet->id, neighbor->id));
      qh_setappend(&(neighbor->ridges), ridge);
      if (qh ridge_id == qh traceridge_id)
        qh traceridge= ridge;
    }
  }
  if (mergeridge) {
    while (qh_setdel(facet->neighbors, qh_MERGEridge))
      ; /* delete each one */
  }
} /* makeridges */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="mark_dupridges">-</a>

  qh_mark_dupridges( facetlist, allmerges )
    add duplicated ridges to qh.facet_mergeset
    facet-dupridge is true if it contains a subridge shared by more than one new facet
    for each such facet, one has a neighbor marked qh_MERGEridge
    allmerges is true if merging dupridges
    allmerges is false if merging pinched vertices followed by retry addpoint
      qh_mark_dupridges will be called again if pinched vertices not found

  returns:
    dupridges on qh.facet_mergeset (MRGdupridge)
    f.mergeridge and f.mergeridge2 set for facet
    f.mergeridge set for neighbor
    if allmerges is true
      make ridges for facets with dupridges as marked by qh_MERGEridge and both sides facet->dupridge
      removes qh_MERGEridge from neighbor sets

  notes:
    called by qh_premerge and qh_getpinchedmerges
    dupridges are due to duplicate subridges
        i.e. a subridge occurs in more than two horizon ridges.
        i.e., a ridge has more than two neighboring facets
    dupridges occur in at least two cases
    1) a pinched horizon with nearly adjacent vertices -> merge the vertices (qh_getpinchedmerges)
    2) more than one newfacet for a horizon face -> merge coplanar facets (qh_premerge)
    qh_matchdupridge previously identified the furthest apart pair of facets to retain
       they must have a matching subridge and the same orientation
    only way to set facet->mergeridge and mergeridge2
    uses qh.visit_id

  design:
    for all facets on facetlist
      if facet contains a dupridge
        for each neighbor of facet
          if neighbor marked qh_MERGEridge (one side of the merge)
            set facet->mergeridge
          else
            if neighbor contains a dupridge
            and the back link is qh_MERGEridge
              append dupridge to qh.facet_mergeset
   exit if !allmerges for repeating qh_mark_dupridges later
   for each dupridge
     make ridge sets in preparation for merging
     remove qh_MERGEridge from neighbor set
   for each dupridge
     restore the missing neighbor from the neighbor set that was qh_MERGEridge
     add the missing ridge for this neighbor
*/
void qh_mark_dupridges(facetT *facetlist, boolT allmerges) {
  facetT *facet, *neighbor, **neighborp;
  int nummerge=0;
  mergeT *merge, **mergep;

  trace4((qh ferr, 4028, "qh_mark_dupridges: identify dupridges in facetlist f%d, allmerges? %d\n",
    facetlist->id, allmerges));
  FORALLfacet_(facetlist) {  /* not necessary for first call */
    facet->mergeridge2= False;
    facet->mergeridge= False;
  }
  FORALLfacet_(facetlist) {
    if (facet->dupridge) {
      FOREACHneighbor_(facet) {
        if (neighbor == qh_MERGEridge) {
          facet->mergeridge= True;
          continue;
        }
        if (neighbor->dupridge) {
          if (!qh_setin(neighbor->neighbors, facet)) { /* i.e., it is qh_MERGEridge, neighbors are distinct */
            qh_appendmergeset(facet, neighbor, MRGdupridge, 0.0, 1.0);
            facet->mergeridge2= True;
            facet->mergeridge= True;
            nummerge++;
          }else if (qh_setequal(facet->vertices, neighbor->vertices)) { /* neighbors are the same except for horizon and qh_MERGEridge, see QH7085 */
            trace3((qh ferr, 3043, "qh_mark_dupridges): dupridge due to duplicate vertices for subridges f%d and f%d\n",
                 facet->id, neighbor->id));
            qh_appendmergeset(facet, neighbor, MRGdupridge, 0.0, 1.0);
            facet->mergeridge2= True;
            facet->mergeridge= True;
            nummerge++;
            break; /* same for all neighbors */
          }
        }
      }
    }
  }
  if (!nummerge)
    return;
  if (!allmerges) {
    trace1((qh ferr, 1012, "qh_mark_dupridges: found %d duplicated ridges (MRGdupridge) for qh_getpinchedmerges\n", nummerge));
    return;
  }
  trace1((qh ferr, 1048, "qh_mark_dupridges: found %d duplicated ridges (MRGdupridge) for qh_premerge.  Prepare facets for merging\n", nummerge));
  /* make ridges in preparation for merging */
  FORALLfacet_(facetlist) {
    if (facet->mergeridge && !facet->mergeridge2)
      qh_makeridges(facet);
  }
  trace3((qh ferr, 3075, "qh_mark_dupridges: restore missing neighbors and ridges due to qh_MERGEridge\n"));
  FOREACHmerge_(qh facet_mergeset) {   /* restore the missing neighbors */
    if (merge->mergetype == MRGdupridge) { /* only between simplicial facets */
      if (merge->facet2->mergeridge2 && qh_setin(merge->facet2->neighbors, merge->facet1)) {
        /* Due to duplicate or multiple subridges, e.g., ../eg/qtest.sh t712682 '200 s W1e-13  C1,1e-13 D5' 'd'
            merge->facet1:    - neighboring facets: f27779 f59186 f59186 f59186 MERGEridge f59186
            merge->facet2:    - neighboring facets: f27779 f59100 f59100 f59100 f59100 f59100
           or, ../eg/qtest.sh 100 '500 s W1e-13 C1,1e-13 D4' 'd'
           both facets will be degenerate after merge, consider for special case handling
        */
        qh_fprintf(qh ferr, 6361, "qhull topological error (qh_mark_dupridges): multiple dupridges for f%d and f%d, including reverse\n",
          merge->facet1->id, merge->facet2->id);
        qh_errexit2(qh_ERRtopology, merge->facet1, merge->facet2);
      }else
        qh_setappend(&merge->facet2->neighbors, merge->facet1);
      qh_makeridges(merge->facet1);   /* and the missing ridges */
    }
  }
} /* mark_dupridges */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="maybe_duplicateridge">-</a>

  qh_maybe_duplicateridge( ridge )
    add MRGvertices if neighboring facet has another ridge with the same vertices

  returns:
    adds rename requests to qh.vertex_mergeset

  notes:
    called by qh_renamevertex
    nop if 2-D
    expensive test
    Duplicate ridges may lead to new facets with same vertex set (QH7084), will try merging vertices
    same as qh_maybe_duplicateridges

  design:
    for the two neighbors
      if non-simplicial
        for each ridge with the same first and last vertices (max id and min id)
          if the remaining vertices are the same
            get the closest pair of vertices
            add to vertex_mergeset for merging
*/
void qh_maybe_duplicateridge(ridgeT *ridgeA) {
  ridgeT *ridge, **ridgep;
  vertexT *vertex, *pinched;
  facetT *neighbor;
  coordT dist;
  int i, k, last= qh hull_dim-2;

  if (qh hull_dim < 3 )
    return;

  for (neighbor= ridgeA->top, i=0; i<2; neighbor= ridgeA->bottom, i++) {
    if (!neighbor->simplicial && neighbor->nummerge > 0) { /* skip degenerate neighbors with both new and old vertices that will be merged */
      FOREACHridge_(neighbor->ridges) {
        if (ridge != ridgeA && SETfirst_(ridge->vertices) == SETfirst_(ridgeA->vertices)) {
          if (SETelem_(ridge->vertices, last) == SETelem_(ridgeA->vertices, last)) {
            for (k=1; k<last; k++) {
              if (SETelem_(ridge->vertices, k) != SETelem_(ridgeA->vertices, k))
                break;
            }
            if (k == last) {
              vertex= qh_findbest_ridgevertex(ridge, &pinched, &dist);
              trace2((qh ferr, 2069, "qh_maybe_duplicateridge: will merge v%d into v%d (dist %2.2g) due to duplicate ridges r%d/r%d with the same vertices.  mergevertex set\n",
                pinched->id, vertex->id, dist, ridgeA->id, ridge->id, ridgeA->top->id, ridgeA->bottom->id, ridge->top->id, ridge->bottom->id));
              qh_appendvertexmerge(pinched, vertex, MRGvertices, dist, ridgeA, ridge);
              ridge->mergevertex= True; /* disables check for duplicate vertices in qh_checkfacet */
              ridgeA->mergevertex= True;
            }
          }
        }
      }
    }
  }
} /* maybe_duplicateridge */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="maybe_duplicateridges">-</a>

  qh_maybe_duplicateridges( facet )
    if Q15, add MRGvertices if facet has ridges with the same vertices

  returns:
    adds rename requests to qh.vertex_mergeset

  notes:
    called at end of qh_mergefacet and qh_mergecycle_all
    only enabled if qh.CHECKduplicates ('Q15') and 3-D or more
    expensive test, not worth it
    same as qh_maybe_duplicateridge

  design:
    for all ridge pairs in facet
        if the same first and last vertices (max id and min id)
          if the remaining vertices are the same
            get the closest pair of vertices
            add to vertex_mergeset for merging
*/
void qh_maybe_duplicateridges(facetT *facet) {
  facetT *otherfacet;
  ridgeT *ridge, *ridge2;
  vertexT *vertex, *pinched;
  coordT dist;
  int ridge_i, ridge_n, i, k, last_v= qh hull_dim-2;

  if (qh hull_dim < 3 || !qh CHECKduplicates)
    return;

  FOREACHridge_i_(facet->ridges) {
    otherfacet= otherfacet_(ridge, facet);
    if (otherfacet->degenerate || otherfacet->redundant || otherfacet->dupridge || otherfacet->flipped) /* will merge */
      continue;
    for (i=ridge_i+1; i < ridge_n; i++) {
      ridge2= SETelemt_(facet->ridges, i, ridgeT);
      otherfacet= otherfacet_(ridge2, facet);
      if (otherfacet->degenerate || otherfacet->redundant || otherfacet->dupridge || otherfacet->flipped) /* will merge */
        continue;
      /* optimize qh_setequal(ridge->vertices, ridge2->vertices) */
      if (SETelem_(ridge->vertices, last_v) == SETelem_(ridge2->vertices, last_v)) { /* SETfirst is likely to be the same */
        if (SETfirst_(ridge->vertices) == SETfirst_(ridge2->vertices)) {
          for (k=1; k<last_v; k++) {
            if (SETelem_(ridge->vertices, k) != SETelem_(ridge2->vertices, k))
              break;
          }
          if (k == last_v) {
            vertex= qh_findbest_ridgevertex(ridge, &pinched, &dist);
            if (ridge->top == ridge2->bottom && ridge->bottom == ridge2->top) {
              /* proof that ridges may have opposite orientation */
              trace2((qh ferr, 2088, "qh_maybe_duplicateridges: will merge v%d into v%d (dist %2.2g) due to opposite oriented ridges r%d/r%d for f%d and f%d\n",
                pinched->id, vertex->id, dist, ridge->id, ridge2->id, ridge->top->id, ridge->bottom->id));
            }else {
              trace2((qh ferr, 2083, "qh_maybe_duplicateridges: will merge v%d into v%d (dist %2.2g) due to duplicate ridges with the same vertices r%d/r%d in merged facet f%d\n",
                pinched->id, vertex->id, dist, ridge->id, ridge2->id, facet->id));
            }
            qh_appendvertexmerge(pinched, vertex, MRGvertices, dist, ridge, ridge2);
            ridge->mergevertex= True; /* disables check for duplicate vertices in qh_checkfacet */
            ridge2->mergevertex= True;
          }
        }
      }
    }
  }
} /* maybe_duplicateridges */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="maydropneighbor">-</a>

  qh_maydropneighbor( facet )
    drop neighbor relationship if ridge was deleted between a non-simplicial facet and its neighbors

  returns:
    for deleted ridges
      ridges made for simplicial neighbors
      neighbor sets updated
      appends degenerate facets to qh.facet_mergeset

  notes:
    called by qh_renamevertex
    assumes neighbors do not include qh_MERGEridge (qh_makeridges)
    won't cause redundant facets since vertex inclusion is the same
    may drop vertex and neighbor if no ridge
    uses qh.visit_id

  design:
    visit all neighbors with ridges
    for each unvisited neighbor of facet
      delete neighbor and facet from the non-simplicial neighbor sets
      if neighbor becomes degenerate
        append neighbor to qh.degen_mergeset
    if facet is degenerate
      append facet to qh.degen_mergeset
*/
void qh_maydropneighbor(facetT *facet) {
  ridgeT *ridge, **ridgep;
  facetT *neighbor, **neighborp;

  qh visit_id++;
  trace4((qh ferr, 4029, "qh_maydropneighbor: test f%d for no ridges to a neighbor\n",
          facet->id));
  if (facet->simplicial) {
    qh_fprintf(qh ferr, 6278, "qhull internal error (qh_maydropneighbor): not valid for simplicial f%d while adding furthest p%d\n",
      facet->id, qh furthest_id);
    qh_errexit(qh_ERRqhull, facet, NULL);
  }
  FOREACHridge_(facet->ridges) {
    ridge->top->visitid= qh visit_id;
    ridge->bottom->visitid= qh visit_id;
  }
  FOREACHneighbor_(facet) {
    if (neighbor->visible) {
      qh_fprintf(qh ferr, 6358, "qhull internal error (qh_maydropneighbor): facet f%d has deleted neighbor f%d (qh.visible_list)\n",
            facet->id, neighbor->id);
      qh_errexit2(qh_ERRqhull, facet, neighbor);
    }
    if (neighbor->visitid != qh visit_id) {
      trace2((qh ferr, 2104, "qh_maydropneighbor: facets f%d and f%d are no longer neighbors while adding furthest p%d\n",
            facet->id, neighbor->id, qh furthest_id));
      if (neighbor->simplicial) {
        qh_fprintf(qh ferr, 6280, "qhull internal error (qh_maydropneighbor): not valid for simplicial neighbor f%d of f%d while adding furthest p%d\n",
            neighbor->id, facet->id, qh furthest_id);
        qh_errexit2(qh_ERRqhull, neighbor, facet);
      }
      zinc_(Zdropneighbor);
      qh_setdel(neighbor->neighbors, facet);
      if (qh_setsize(neighbor->neighbors) < qh hull_dim) {
        zinc_(Zdropdegen);
        qh_appendmergeset(neighbor, neighbor, MRGdegen, 0.0, qh_ANGLEnone);
        trace2((qh ferr, 2023, "qh_maydropneighbors: f%d is degenerate.\n", neighbor->id));
      }
      qh_setdel(facet->neighbors, neighbor);
      neighborp--;  /* repeat, deleted a neighbor */
    }
  }
  if (qh_setsize(facet->neighbors) < qh hull_dim) {
    zinc_(Zdropdegen);
    qh_appendmergeset(facet, facet, MRGdegen, 0.0, qh_ANGLEnone);
    trace2((qh ferr, 2024, "qh_maydropneighbors: f%d is degenerate.\n", facet->id));
  }
} /* maydropneighbor */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="merge_degenredundant">-</a>

  qh_merge_degenredundant( )
    merge all degenerate and redundant facets
    qh.degen_mergeset contains merges from  qh_test_degen_neighbors, qh_test_redundant_neighbors, and qh_degen_redundant_facet

  returns:
    number of merges performed
    resets facet->degenerate/redundant
    if deleted (visible) facet has no neighbors
      sets ->f.replace to NULL

  notes:
    redundant merges happen before degenerate ones
    merging and renaming vertices can result in degen/redundant facets
    check for coplanar and convex neighbors afterwards

  design:
    for each merge on qh.degen_mergeset
      if redundant merge
        if non-redundant facet merged into redundant facet
          recheck facet for redundancy
        else
          merge redundant facet into other facet
*/
int qh_merge_degenredundant(void) {
  int size;
  mergeT *merge;
  facetT *bestneighbor, *facet1, *facet2, *facet3;
  realT dist, mindist, maxdist;
  vertexT *vertex, **vertexp;
  int nummerges= 0;
  mergeType mergetype;
  setT *mergedfacets;

  trace2((qh ferr, 2095, "qh_merge_degenredundant: merge %d degenerate, redundant, and mirror facets\n",
    qh_setsize(qh degen_mergeset)));
  mergedfacets= qh_settemp(qh TEMPsize);
  while ((merge= (mergeT *)qh_setdellast(qh degen_mergeset))) {
    facet1= merge->facet1;
    facet2= merge->facet2;
    mergetype= merge->mergetype;
    qh_memfree(merge, (int)sizeof(mergeT)); /* 'merge' is invalidated */
    if (facet1->visible)
      continue;
    facet1->degenerate= False;
    facet1->redundant= False;
    if (qh TRACEmerge-1 == zzval_(Ztotmerge))
      qhmem.IStracing= qh IStracing= qh TRACElevel;
    if (mergetype == MRGredundant) {
      zinc_(Zredundant);
      facet3= qh_getreplacement(facet2); /* the same facet if !facet2.visible */
      if (!facet3) {
          qh_fprintf(qh ferr, 6097, "qhull internal error (qh_merge_degenredunant): f%d is redundant but visible f%d has no replacement\n",
               facet1->id, getid_(facet2));
          qh_errexit2(qh_ERRqhull, facet1, facet2);
      }
      qh_setunique(&mergedfacets, facet3);
      if (facet1 == facet3) {
        continue;
      }
      trace2((qh ferr, 2025, "qh_merge_degenredundant: merge redundant f%d into f%d (arg f%d)\n",
            facet1->id, facet3->id, facet2->id));
      qh_mergefacet(facet1, facet3, mergetype, NULL, NULL, !qh_MERGEapex);
      /* merge distance is already accounted for */
      nummerges++;
    }else {  /* mergetype == MRGdegen or MRGmirror, other merges may have fixed */
      if (!(size= qh_setsize(facet1->neighbors))) {
        zinc_(Zdelfacetdup);
        trace2((qh ferr, 2026, "qh_merge_degenredundant: facet f%d has no neighbors.  Deleted\n", facet1->id));
        qh_willdelete(facet1, NULL);
        FOREACHvertex_(facet1->vertices) {
          qh_setdel(vertex->neighbors, facet1);
          if (!SETfirst_(vertex->neighbors)) {
            zinc_(Zdegenvertex);
            trace2((qh ferr, 2027, "qh_merge_degenredundant: deleted v%d because f%d has no neighbors\n",
                 vertex->id, facet1->id));
            vertex->deleted= True;
            qh_setappend(&qh del_vertices, vertex);
          }
        }
        nummerges++;
      }else if (size < qh hull_dim) {
        bestneighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist);
        trace2((qh ferr, 2028, "qh_merge_degenredundant: facet f%d has %d neighbors, merge into f%d dist %2.2g\n",
              facet1->id, size, bestneighbor->id, dist));
        qh_mergefacet(facet1, bestneighbor, mergetype, &mindist, &maxdist, !qh_MERGEapex);
        nummerges++;
        if (qh PRINTstatistics) {
          zinc_(Zdegen);
          wadd_(Wdegentot, dist);
          wmax_(Wdegenmax, dist);
        }
      } /* else, another merge fixed the degeneracy and redundancy tested */
    }
  }
  qh_settempfree(&mergedfacets);
  return nummerges;
} /* merge_degenredundant */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="merge_nonconvex">-</a>

  qh_merge_nonconvex( facet1, facet2, mergetype )
    remove non-convex ridge between facet1 into facet2
    mergetype gives why the facet's are non-convex

  returns:
    merges one of the facets into the best neighbor

  notes:
    mergetype is MRGcoplanar..MRGconvex

  design:
    if one of the facets is a new facet
      prefer merging new facet into old facet
    find best neighbors for both facets
    merge the nearest facet into its best neighbor
    update the statistics
*/
void qh_merge_nonconvex(facetT *facet1, facetT *facet2, mergeType mergetype) {
  facetT *bestfacet, *bestneighbor, *neighbor, *merging, *merged;
  realT dist, dist2, mindist, mindist2, maxdist, maxdist2;

  if (mergetype < MRGcoplanar || mergetype > MRGconcavecoplanar) {
    qh_fprintf(qh ferr, 6398, "qhull internal error (qh_merge_nonconvex): expecting mergetype MRGcoplanar..MRGconcavecoplanar.  Got merge f%d and f%d type %d\n",
      facet1->id, facet2->id, mergetype);
    qh_errexit2(qh_ERRqhull, facet1, facet2);
  }
  if (qh TRACEmerge-1 == zzval_(Ztotmerge))
    qhmem.IStracing= qh IStracing= qh TRACElevel;
  trace3((qh ferr, 3003, "qh_merge_nonconvex: merge #%d for f%d and f%d type %d\n",
      zzval_(Ztotmerge) + 1, facet1->id, facet2->id, mergetype));
  /* concave or coplanar */
  if (!facet1->newfacet) {
    bestfacet= facet2;   /* avoid merging old facet if new is ok */
    facet2= facet1;
    facet1= bestfacet;
  }else
    bestfacet= facet1;
  bestneighbor= qh_findbestneighbor(bestfacet, &dist, &mindist, &maxdist);
  neighbor= qh_findbestneighbor(facet2, &dist2, &mindist2, &maxdist2);
  if (dist < dist2) {
    merging= bestfacet;
    merged= bestneighbor;
  }else if (qh AVOIDold && !facet2->newfacet
  && ((mindist >= -qh MAXcoplanar && maxdist <= qh max_outside)
       || dist * 1.5 < dist2)) {
    zinc_(Zavoidold);
    wadd_(Wavoidoldtot, dist);
    wmax_(Wavoidoldmax, dist);
    trace2((qh ferr, 2029, "qh_merge_nonconvex: avoid merging old facet f%d dist %2.2g.  Use f%d dist %2.2g instead\n",
           facet2->id, dist2, facet1->id, dist2));
    merging= bestfacet;
    merged= bestneighbor;
  }else {
    merging= facet2;
    merged= neighbor;
    dist= dist2;
    mindist= mindist2;
    maxdist= maxdist2;
  }
  qh_mergefacet(merging, merged, mergetype, &mindist, &maxdist, !qh_MERGEapex);
  /* caller merges qh_degenredundant */
  if (qh PRINTstatistics) {
    if (mergetype == MRGanglecoplanar) {
      zinc_(Zacoplanar);
      wadd_(Wacoplanartot, dist);
      wmax_(Wacoplanarmax, dist);
    }else if (mergetype == MRGconcave) {
      zinc_(Zconcave);
      wadd_(Wconcavetot, dist);
      wmax_(Wconcavemax, dist);
    }else if (mergetype == MRGconcavecoplanar) {
      zinc_(Zconcavecoplanar);
      wadd_(Wconcavecoplanartot, dist);
      wmax_(Wconcavecoplanarmax, dist);
    }else { /* MRGcoplanar */
      zinc_(Zcoplanar);
      wadd_(Wcoplanartot, dist);
      wmax_(Wcoplanarmax, dist);
    }
  }
} /* merge_nonconvex */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="merge_pinchedvertices">-</a>

  qh_merge_pinchedvertices( apex )
    merge pinched vertices in qh.vertex_mergeset to avoid qh_forcedmerges of dupridges

  notes:
    only called by qh_all_vertexmerges
    hull_dim >= 3

  design:
    make vertex neighbors if necessary
    for each pinched vertex
      determine the ridges for the pinched vertex (make ridges as needed)
      merge the pinched vertex into the horizon vertex
      merge the degenerate and redundant facets that result
    check and resolve new dupridges
*/
void qh_merge_pinchedvertices(int apexpointid /* qh.newfacet_list */) {
  mergeT *merge, *mergeA, **mergeAp;
  vertexT *vertex, *vertex2;
  realT dist;
  boolT firstmerge= True;

  qh_vertexneighbors();
  if (qh visible_list || qh newfacet_list || qh newvertex_list) {
    qh_fprintf(qh ferr, 6402, "qhull internal error (qh_merge_pinchedvertices): qh.visible_list (f%d), newfacet_list (f%d), or newvertex_list (v%d) not empty\n",
      getid_(qh visible_list), getid_(qh newfacet_list), getid_(qh newvertex_list));
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  qh visible_list= qh newfacet_list= qh facet_tail;
  qh newvertex_list= qh vertex_tail;
  qh isRenameVertex= True; /* disable duplicate ridge vertices check in qh_checkfacet */
  while ((merge= qh_next_vertexmerge(/* qh.vertex_mergeset */))) { /* only one at a time from qh_getpinchedmerges */
    if (qh TRACEmerge-1 == zzval_(Ztotmerge))
      qhmem.IStracing= qh IStracing= qh TRACElevel;
    if (merge->mergetype == MRGsubridge) {
      zzinc_(Zpinchedvertex);
      trace1((qh ferr, 1050, "qh_merge_pinchedvertices: merge one of %d pinched vertices before adding apex p%d.  Try to resolve duplicate ridges in newfacets\n",
        qh_setsize(qh vertex_mergeset)+1, apexpointid));
      qh_remove_mergetype(qh vertex_mergeset, MRGsubridge);
    }else {
      zzinc_(Zpinchduplicate);
      if (firstmerge)
        trace1((qh ferr, 1056, "qh_merge_pinchedvertices: merge %d pinched vertices from dupridges in merged facets, apex p%d\n",
           qh_setsize(qh vertex_mergeset)+1, apexpointid));
      firstmerge= False;
    }
    vertex= merge->vertex1;
    vertex2= merge->vertex2;
    dist= merge->distance;
    qh_memfree(merge, (int)sizeof(mergeT)); /* merge is invalidated */
    qh_rename_adjacentvertex(vertex, vertex2, dist);
#ifndef qh_NOtrace
    if (qh IStracing >= 2) {
      FOREACHmergeA_(qh degen_mergeset) {
        if (mergeA->mergetype== MRGdegen) {
          qh_fprintf(qh ferr, 2072, "qh_merge_pinchedvertices: merge degenerate f%d into an adjacent facet\n", mergeA->facet1->id);
        }else {
          qh_fprintf(qh ferr, 2084, "qh_merge_pinchedvertices: merge f%d into f%d mergeType %d\n", mergeA->facet1->id, mergeA->facet2->id, mergeA->mergetype);
        }
      }
    }
#endif
    qh_merge_degenredundant(); /* simplicial facets with both old and new vertices */
  }
  qh isRenameVertex= False;
}/* merge_pinchedvertices */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="merge_twisted">-</a>

  qh_merge_twisted( facet1, facet2 )
    remove twisted ridge between facet1 into facet2 or report error

  returns:
    merges one of the facets into the best neighbor

  notes:
    a twisted ridge has opposite vertices that are convex and concave

  design:
    find best neighbors for both facets
    error if wide merge
    merge the nearest facet into its best neighbor
    update statistics
*/
void qh_merge_twisted(facetT *facet1, facetT *facet2) {
  facetT *neighbor2, *neighbor, *merging, *merged;
  vertexT *bestvertex, *bestpinched;
  realT dist, dist2, mindist, mindist2, maxdist, maxdist2, mintwisted, bestdist;

  if (qh TRACEmerge-1 == zzval_(Ztotmerge))
    qhmem.IStracing= qh IStracing= qh TRACElevel;
  trace3((qh ferr, 3050, "qh_merge_twisted: merge #%d for twisted f%d and f%d\n",
      zzval_(Ztotmerge) + 1, facet1->id, facet2->id));
  /* twisted */
  neighbor= qh_findbestneighbor(facet1, &dist, &mindist, &maxdist);
  neighbor2= qh_findbestneighbor(facet2, &dist2, &mindist2, &maxdist2);
  mintwisted= qh_RATIOtwisted * qh ONEmerge;
  maximize_(mintwisted, facet1->maxoutside);
  maximize_(mintwisted, facet2->maxoutside);
  if (dist > mintwisted && dist2 > mintwisted) {
    bestdist= qh_vertex_bestdist2(facet1->vertices, &bestvertex, &bestpinched);
    if (bestdist > mintwisted) {
      qh_fprintf(qh ferr, 6417, "qhull precision error (qh_merge_twisted): twisted facet f%d does not contain pinched vertices.  Too wide to merge into neighbor.  mindist %2.2g maxdist %2.2g vertexdist %2.2g maxpinched %2.2g neighbor f%d mindist %2.2g maxdist %2.2g\n",
        facet1->id, mindist, maxdist, bestdist, mintwisted, facet2->id, mindist2, maxdist2);
    }else {
      qh_fprintf(qh ferr, 6418, "qhull precision error (qh_merge_twisted): twisted facet f%d with pinched vertices.  Could merge vertices, but too wide to merge into neighbor.   mindist %2.2g maxdist %2.2g vertexdist %2.2g neighbor f%d mindist %2.2g maxdist %2.2g\n",
        facet1->id, mindist, maxdist, bestdist, facet2->id, mindist2, maxdist2);
    }
    qh_errexit2(qh_ERRwide, facet1, facet2);
  }
  if (dist < dist2) {
    merging= facet1;
    merged= neighbor;
  }else {
    /* ignores qh.AVOIDold ('Q4') */
    merging= facet2;
    merged= neighbor2;
    dist= dist2;
    mindist= mindist2;
    maxdist= maxdist2;
  }
  qh_mergefacet(merging, merged, MRGtwisted, &mindist, &maxdist, !qh_MERGEapex);
  /* caller merges qh_degenredundant */
  zinc_(Ztwisted);
  wadd_(Wtwistedtot, dist);
  wmax_(Wtwistedmax, dist);
} /* merge_twisted */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="mergecycle">-</a>

  qh_mergecycle( samecycle, newfacet )
    merge a cycle of facets starting at samecycle into a newfacet
    newfacet is a horizon facet with ->normal
    samecycle facets are simplicial from an apex

  returns:
    initializes vertex neighbors on first merge
    samecycle deleted (placed on qh.visible_list)
    newfacet at end of qh.facet_list
    deleted vertices on qh.del_vertices

  notes:
    only called by qh_mergecycle_all for multiple, same cycle facets
    see qh_mergefacet

  design:
    make vertex neighbors if necessary
    make ridges for newfacet
    merge neighbor sets of samecycle into newfacet
    merge ridges of samecycle into newfacet
    merge vertex neighbors of samecycle into newfacet
    make apex of samecycle the apex of newfacet
    if newfacet wasn't a new facet
      add its vertices to qh.newvertex_list
    delete samecycle facets a make newfacet a newfacet
*/
void qh_mergecycle(facetT *samecycle, facetT *newfacet) {
  int traceonce= False, tracerestore= 0;
  vertexT *apex;
#ifndef qh_NOtrace
  facetT *same;
#endif

  zzinc_(Ztotmerge);
  if (qh REPORTfreq2 && qh POSTmerging) {
    if (zzval_(Ztotmerge) > qh mergereport + qh REPORTfreq2)
      qh_tracemerging();
  }
#ifndef qh_NOtrace
  if (qh TRACEmerge == zzval_(Ztotmerge))
    qhmem.IStracing= qh IStracing= qh TRACElevel;
  trace2((qh ferr, 2030, "qh_mergecycle: merge #%d for facets from cycle f%d into coplanar horizon f%d\n",
        zzval_(Ztotmerge), samecycle->id, newfacet->id));
  if (newfacet == qh tracefacet) {
    tracerestore= qh IStracing;
    qh IStracing= 4;
    qh_fprintf(qh ferr, 8068, "qh_mergecycle: ========= trace merge %d of samecycle %d into trace f%d, furthest is p%d\n",
               zzval_(Ztotmerge), samecycle->id, newfacet->id,  qh furthest_id);
    traceonce= True;
  }
  if (qh IStracing >=4) {
    qh_fprintf(qh ferr, 8069, "  same cycle:");
    FORALLsame_cycle_(samecycle)
      qh_fprintf(qh ferr, 8070, " f%d", same->id);
    qh_fprintf(qh ferr, 8071, "\n");
  }
  if (qh IStracing >=4)
    qh_errprint("MERGING CYCLE", samecycle, newfacet, NULL, NULL);
#endif /* !qh_NOtrace */
  if (newfacet->tricoplanar) {
    if (!qh TRInormals) {
      qh_fprintf(qh ferr, 6224, "qhull internal error (qh_mergecycle): does not work for tricoplanar facets.  Use option 'Q11'\n");
      qh_errexit(qh_ERRqhull, newfacet, NULL);
    }
    newfacet->tricoplanar= False;
    newfacet->keepcentrum= False;
  }
  if (qh CHECKfrequently)
    qh_checkdelridge();
  if (!qh VERTEXneighbors)
    qh_vertexneighbors();
  apex= SETfirstt_(samecycle->vertices, vertexT);
  qh_makeridges(newfacet);
  qh_mergecycle_neighbors(samecycle, newfacet);
  qh_mergecycle_ridges(samecycle, newfacet);
  qh_mergecycle_vneighbors(samecycle, newfacet);
  if (SETfirstt_(newfacet->vertices, vertexT) != apex)
    qh_setaddnth(&newfacet->vertices, 0, apex);  /* apex has last id */
  if (!newfacet->newfacet)
    qh_newvertices(newfacet->vertices);
  qh_mergecycle_facets(samecycle, newfacet);
  qh_tracemerge(samecycle, newfacet, MRGcoplanarhorizon);
  /* check for degen_redundant_neighbors after qh_forcedmerges() */
  if (traceonce) {
    qh_fprintf(qh ferr, 8072, "qh_mergecycle: end of trace facet\n");
    qh IStracing= tracerestore;
  }
} /* mergecycle */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="mergecycle_all">-</a>

  qh_mergecycle_all( facetlist, wasmerge )
    merge all samecycles of coplanar facets into horizon
    don't merge facets with ->mergeridge (these already have ->normal)
    all facets are simplicial from apex
    all facet->cycledone == False

  returns:
    all newfacets merged into coplanar horizon facets
    deleted vertices on  qh.del_vertices
    sets wasmerge if any merge

  notes:
    called by qh_premerge
    calls qh_mergecycle for multiple, same cycle facets

  design:
    for each facet on facetlist
      skip facets with dupridges and normals
      check that facet is in a samecycle (->mergehorizon)
      if facet only member of samecycle
        sets vertex->delridge for all vertices except apex
        merge facet into horizon
      else
        mark all facets in samecycle
        remove facets with dupridges from samecycle
        merge samecycle into horizon (deletes facets from facetlist)
*/
void qh_mergecycle_all(facetT *facetlist, boolT *wasmerge) {
  facetT *facet, *same, *prev, *horizon, *newfacet;
  facetT *samecycle= NULL, *nextfacet, *nextsame;
  vertexT *apex, *vertex, **vertexp;
  int cycles=0, total=0, facets, nummerge, numdegen= 0;

  trace2((qh ferr, 2031, "qh_mergecycle_all: merge new facets into coplanar horizon facets.  Bulk merge a cycle of facets with the same horizon facet\n"));
  for (facet=facetlist; facet && (nextfacet= facet->next); facet= nextfacet) {
    if (facet->normal)
      continue;
    if (!facet->mergehorizon) {
      qh_fprintf(qh ferr, 6225, "qhull internal error (qh_mergecycle_all): f%d without normal\n", facet->id);
      qh_errexit(qh_ERRqhull, facet, NULL);
    }
    horizon= SETfirstt_(facet->neighbors, facetT);
    if (facet->f.samecycle == facet) {
      if (qh TRACEmerge-1 == zzval_(Ztotmerge))
        qhmem.IStracing= qh IStracing= qh TRACElevel;
      zinc_(Zonehorizon);
      /* merge distance done in qh_findhorizon */
      apex= SETfirstt_(facet->vertices, vertexT);
      FOREACHvertex_(facet->vertices) {
        if (vertex != apex)
          vertex->delridge= True;
      }
      horizon->f.newcycle= NULL;
      qh_mergefacet(facet, horizon, MRGcoplanarhorizon, NULL, NULL, qh_MERGEapex);
    }else {
      samecycle= facet;
      facets= 0;
      prev= facet;
      for (same= facet->f.samecycle; same;  /* FORALLsame_cycle_(facet) */
           same= (same == facet ? NULL :nextsame)) { /* ends at facet */
        nextsame= same->f.samecycle;
        if (same->cycledone || same->visible)
          qh_infiniteloop(same);
        same->cycledone= True;
        if (same->normal) {
          prev->f.samecycle= same->f.samecycle; /* unlink ->mergeridge */
          same->f.samecycle= NULL;
        }else {
          prev= same;
          facets++;
        }
      }
      while (nextfacet && nextfacet->cycledone)  /* will delete samecycle */
        nextfacet= nextfacet->next;
      horizon->f.newcycle= NULL;
      qh_mergecycle(samecycle, horizon);
      nummerge= horizon->nummerge + facets;
      if (nummerge > qh_MAXnummerge)
        horizon->nummerge= qh_MAXnummerge;
      else
        horizon->nummerge= (short unsigned int)nummerge; /* limited to 9 bits by qh_MAXnummerge, -Wconversion */
      zzinc_(Zcyclehorizon);
      total += facets;
      zzadd_(Zcyclefacettot, facets);
      zmax_(Zcyclefacetmax, facets);
    }
    cycles++;
  }
  if (cycles) {
    FORALLnew_facets {
      /* qh_maybe_duplicateridges postponed since qh_mergecycle_ridges deletes ridges without calling qh_delridge_merge */
      if (newfacet->coplanarhorizon) {
        qh_test_redundant_neighbors(newfacet);
        qh_maybe_duplicateridges(newfacet);
        newfacet->coplanarhorizon= False;
      }
    }
    numdegen += qh_merge_degenredundant();
    *wasmerge= True;
    trace1((qh ferr, 1013, "qh_mergecycle_all: merged %d same cycles or facets into coplanar horizons and %d degenredundant facets\n",
      cycles, numdegen));
  }
} /* mergecycle_all */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="mergecycle_facets">-</a>

  qh_mergecycle_facets( samecycle, newfacet )
    finish merge of samecycle into newfacet

  returns:
    samecycle prepended to visible_list for later deletion and partitioning
      each facet->f.replace == newfacet

    newfacet moved to end of qh.facet_list
      makes newfacet a newfacet (get's facet1->id if it was old)
      sets newfacet->newmerge
      clears newfacet->center (unless merging into a large facet)
      clears newfacet->tested and ridge->tested for facet1

    adds neighboring facets to facet_mergeset if redundant or degenerate

  design:
    make newfacet a new facet and set its flags
    move samecycle facets to qh.visible_list for later deletion
    unless newfacet is large
      remove its centrum
*/
void qh_mergecycle_facets(facetT *samecycle, facetT *newfacet) {
  facetT *same, *next;

  trace4((qh ferr, 4030, "qh_mergecycle_facets: make newfacet new and samecycle deleted\n"));
  qh_removefacet(newfacet);  /* append as a newfacet to end of qh facet_list */
  qh_appendfacet(newfacet);
  newfacet->newfacet= True;
  newfacet->simplicial= False;
  newfacet->newmerge= True;

  for (same= samecycle->f.samecycle; same; same= (same == samecycle ?  NULL : next)) {
    next= same->f.samecycle;  /* reused by willdelete */
    qh_willdelete(same, newfacet);
  }
  if (newfacet->center
      && qh_setsize(newfacet->vertices) <= qh hull_dim + qh_MAXnewcentrum) {
    qh_memfree(newfacet->center, qh normal_size);
    newfacet->center= NULL;
  }
  trace3((qh ferr, 3004, "qh_mergecycle_facets: merged facets from cycle f%d into f%d\n",
             samecycle->id, newfacet->id));
} /* mergecycle_facets */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="mergecycle_neighbors">-</a>

  qh_mergecycle_neighbors( samecycle, newfacet )
    add neighbors for samecycle facets to newfacet

  returns:
    newfacet with updated neighbors and vice-versa
    newfacet has ridges
    all neighbors of newfacet marked with qh.visit_id
    samecycle facets marked with qh.visit_id-1
    ridges updated for simplicial neighbors of samecycle with a ridge

  notes:
    assumes newfacet not in samecycle
    usually, samecycle facets are new, simplicial facets without internal ridges
      not so if horizon facet is coplanar to two different samecycles

  see:
    qh_mergeneighbors()

  design:
    check samecycle
    delete neighbors from newfacet that are also in samecycle
    for each neighbor of a facet in samecycle
      if neighbor is simplicial
        if first visit
          move the neighbor relation to newfacet
          update facet links for its ridges
        else
          make ridges for neighbor
          remove samecycle reference
      else
        update neighbor sets
*/
void qh_mergecycle_neighbors(facetT *samecycle, facetT *newfacet) {
  facetT *same, *neighbor, **neighborp;
  int delneighbors= 0, newneighbors= 0;
  unsigned int samevisitid;
  ridgeT *ridge, **ridgep;

  samevisitid= ++qh visit_id;
  FORALLsame_cycle_(samecycle) {
    if (same->visitid == samevisitid || same->visible)
      qh_infiniteloop(samecycle);
    same->visitid= samevisitid;
  }
  newfacet->visitid= ++qh visit_id;
  trace4((qh ferr, 4031, "qh_mergecycle_neighbors: delete shared neighbors from newfacet\n"));
  FOREACHneighbor_(newfacet) {
    if (neighbor->visitid == samevisitid) {
      SETref_(neighbor)= NULL;  /* samecycle neighbors deleted */
      delneighbors++;
    }else
      neighbor->visitid= qh visit_id;
  }
  qh_setcompact(newfacet->neighbors);

  trace4((qh ferr, 4032, "qh_mergecycle_neighbors: update neighbors\n"));
  FORALLsame_cycle_(samecycle) {
    FOREACHneighbor_(same) {
      if (neighbor->visitid == samevisitid)
        continue;
      if (neighbor->simplicial) {
        if (neighbor->visitid != qh visit_id) {
          qh_setappend(&newfacet->neighbors, neighbor);
          qh_setreplace(neighbor->neighbors, same, newfacet);
          newneighbors++;
          neighbor->visitid= qh visit_id;
          FOREACHridge_(neighbor->ridges) { /* update ridge in case of qh_makeridges */
            if (ridge->top == same) {
              ridge->top= newfacet;
              break;
            }else if (ridge->bottom == same) {
              ridge->bottom= newfacet;
              break;
            }
          }
        }else {
          qh_makeridges(neighbor);
          qh_setdel(neighbor->neighbors, same);
          /* same can't be horizon facet for neighbor */
        }
      }else { /* non-simplicial neighbor */
        qh_setdel(neighbor->neighbors, same);
        if (neighbor->visitid != qh visit_id) {
          qh_setappend(&neighbor->neighbors, newfacet);
          qh_setappend(&newfacet->neighbors, neighbor);
          neighbor->visitid= qh visit_id;
          newneighbors++;
        }
      }
    }
  }
  trace2((qh ferr, 2032, "qh_mergecycle_neighbors: deleted %d neighbors and added %d\n",
             delneighbors, newneighbors));
} /* mergecycle_neighbors */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="mergecycle_ridges">-</a>

  qh_mergecycle_ridges( samecycle, newfacet )
    add ridges/neighbors for facets in samecycle to newfacet
    all new/old neighbors of newfacet marked with qh.visit_id
    facets in samecycle marked with qh.visit_id-1
    newfacet marked with qh.visit_id

  returns:
    newfacet has merged ridges

  notes:
    ridge already updated for simplicial neighbors of samecycle with a ridge
    qh_checkdelridge called by qh_mergecycle

  see:
    qh_mergeridges()
    qh_makeridges()

  design:
    remove ridges between newfacet and samecycle
    for each facet in samecycle
      for each ridge in facet
        update facet pointers in ridge
        skip ridges processed in qh_mergecycle_neighors
        free ridges between newfacet and samecycle
        free ridges between facets of samecycle (on 2nd visit)
        append remaining ridges to newfacet
      if simplicial facet
        for each neighbor of facet
          if simplicial facet
          and not samecycle facet or newfacet
            make ridge between neighbor and newfacet
*/
void qh_mergecycle_ridges(facetT *samecycle, facetT *newfacet) {
  facetT *same, *neighbor= NULL;
  int numold=0, numnew=0;
  int neighbor_i, neighbor_n;
  unsigned int samevisitid;
  ridgeT *ridge, **ridgep;
  boolT toporient;
  void **freelistp; /* used if !qh_NOmem by qh_memfree_() */

  trace4((qh ferr, 4033, "qh_mergecycle_ridges: delete shared ridges from newfacet\n"));
  samevisitid= qh visit_id -1;
  FOREACHridge_(newfacet->ridges) {
    neighbor= otherfacet_(ridge, newfacet);
    if (neighbor->visitid == samevisitid)
      SETref_(ridge)= NULL; /* ridge free'd below */
  }
  qh_setcompact(newfacet->ridges);

  trace4((qh ferr, 4034, "qh_mergecycle_ridges: add ridges to newfacet\n"));
  FORALLsame_cycle_(samecycle) {
    FOREACHridge_(same->ridges) {
      if (ridge->top == same) {
        ridge->top= newfacet;
        neighbor= ridge->bottom;
      }else if (ridge->bottom == same) {
        ridge->bottom= newfacet;
        neighbor= ridge->top;
      }else if (ridge->top == newfacet || ridge->bottom == newfacet) {
        qh_setappend(&newfacet->ridges, ridge);
        numold++;  /* already set by qh_mergecycle_neighbors */
        continue;
      }else {
        qh_fprintf(qh ferr, 6098, "qhull internal error (qh_mergecycle_ridges): bad ridge r%d\n", ridge->id);
        qh_errexit(qh_ERRqhull, NULL, ridge);
      }
      if (neighbor == newfacet) {
        if (qh traceridge == ridge)
          qh traceridge= NULL;
        qh_setfree(&(ridge->vertices));
        qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
        numold++;
      }else if (neighbor->visitid == samevisitid) {
        qh_setdel(neighbor->ridges, ridge);
        if (qh traceridge == ridge)
          qh traceridge= NULL;
        qh_setfree(&(ridge->vertices));
        qh_memfree_(ridge, (int)sizeof(ridgeT), freelistp);
        numold++;
      }else {
        qh_setappend(&newfacet->ridges, ridge);
        numold++;
      }
    }
    if (same->ridges)
      qh_settruncate(same->ridges, 0);
    if (!same->simplicial)
      continue;
    FOREACHneighbor_i_(same) {       /* note: !newfact->simplicial */
      if (neighbor->visitid != samevisitid && neighbor->simplicial) {
        ridge= qh_newridge();
        ridge->vertices= qh_setnew_delnthsorted(same->vertices, qh hull_dim,
                                                          neighbor_i, 0);
        toporient= (boolT)(same->toporient ^ (neighbor_i & 0x1));
        if (toporient) {
          ridge->top= newfacet;
          ridge->bottom= neighbor;
          ridge->simplicialbot= True;
        }else {
          ridge->top= neighbor;
          ridge->bottom= newfacet;
          ridge->simplicialtop= True;
        }
        qh_setappend(&(newfacet->ridges), ridge);
        qh_setappend(&(neighbor->ridges), ridge);
        if (qh ridge_id == qh traceridge_id)
          qh traceridge= ridge;
        numnew++;
      }
    }
  }

  trace2((qh ferr, 2033, "qh_mergecycle_ridges: found %d old ridges and %d new ones\n",
             numold, numnew));
} /* mergecycle_ridges */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="mergecycle_vneighbors">-</a>

  qh_mergecycle_vneighbors( samecycle, newfacet )
    create vertex neighbors for newfacet from vertices of facets in samecycle
    samecycle marked with visitid == qh.visit_id - 1

  returns:
    newfacet vertices with updated neighbors
    marks newfacet with qh.visit_id-1
    deletes vertices that are merged away
    sets delridge on all vertices (faster here than in mergecycle_ridges)

  see:
    qh_mergevertex_neighbors()

  design:
    for each vertex of samecycle facet
      set vertex->delridge
      delete samecycle facets from vertex neighbors
      append newfacet to vertex neighbors
      if vertex only in newfacet
        delete it from newfacet
        add it to qh.del_vertices for later deletion
*/
void qh_mergecycle_vneighbors(facetT *samecycle, facetT *newfacet) {
  facetT *neighbor, **neighborp;
  unsigned int mergeid;
  vertexT *vertex, **vertexp, *apex;
  setT *vertices;

  trace4((qh ferr, 4035, "qh_mergecycle_vneighbors: update vertex neighbors for newfacet\n"));
  mergeid= qh visit_id - 1;
  newfacet->visitid= mergeid;
  vertices= qh_basevertices(samecycle); /* temp */
  apex= SETfirstt_(samecycle->vertices, vertexT);
  qh_setappend(&vertices, apex);
  FOREACHvertex_(vertices) {
    vertex->delridge= True;
    FOREACHneighbor_(vertex) {
      if (neighbor->visitid == mergeid)
        SETref_(neighbor)= NULL;
    }
    qh_setcompact(vertex->neighbors);
    qh_setappend(&vertex->neighbors, newfacet);
    if (!SETsecond_(vertex->neighbors)) {
      zinc_(Zcyclevertex);
      trace2((qh ferr, 2034, "qh_mergecycle_vneighbors: deleted v%d when merging cycle f%d into f%d\n",
        vertex->id, samecycle->id, newfacet->id));
      qh_setdelsorted(newfacet->vertices, vertex);
      vertex->deleted= True;
      qh_setappend(&qh del_vertices, vertex);
    }
  }
  qh_settempfree(&vertices);
  trace3((qh ferr, 3005, "qh_mergecycle_vneighbors: merged vertices from cycle f%d into f%d\n",
             samecycle->id, newfacet->id));
} /* mergecycle_vneighbors */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="mergefacet">-</a>

  qh_mergefacet( facet1, facet2, mergetype, mindist, maxdist, mergeapex )
    merges facet1 into facet2
    mergeapex==qh_MERGEapex if merging new facet into coplanar horizon (optimizes qh_mergesimplex)

  returns:
    qh.max_outside and qh.min_vertex updated
    initializes vertex neighbors on first merge

  note:
    mergetype only used for logging and error reporting

  returns:
    facet2 contains facet1's vertices, neighbors, and ridges
      facet2 moved to end of qh.facet_list
      makes facet2 a newfacet
      sets facet2->newmerge set
      clears facet2->center (unless merging into a large facet)
      clears facet2->tested and ridge->tested for facet1

    facet1 prepended to visible_list for later deletion and partitioning
      facet1->f.replace == facet2

    adds neighboring facets to facet_mergeset if redundant or degenerate

  notes:
    when done, tests facet1 and facet2 for degenerate or redundant neighbors and dupridges
    mindist/maxdist may be NULL (only if both NULL)
    traces merge if fmax_(maxdist,-mindist) > TRACEdist

  see:
    qh_mergecycle()

  design:
    trace merge and check for degenerate simplex
    make ridges for both facets
    update qh.max_outside, qh.max_vertex, qh.min_vertex
    update facet2->maxoutside and keepcentrum
    update facet2->nummerge
    update tested flags for facet2
    if facet1 is simplicial
      merge facet1 into facet2
    else
      merge facet1's neighbors into facet2
      merge facet1's ridges into facet2
      merge facet1's vertices into facet2
      merge facet1's vertex neighbors into facet2
      add facet2's vertices to qh.new_vertexlist
    move facet2 to end of qh.newfacet_list
    unless MRGcoplanarhorizon
      test facet2 for redundant neighbors
      test facet1 for degenerate neighbors
      test for redundant facet2
      maybe test for duplicate ridges ('Q15')
    move facet1 to qh.visible_list for later deletion
*/
void qh_mergefacet(facetT *facet1, facetT *facet2, mergeType mergetype, realT *mindist, realT *maxdist, boolT mergeapex) {
  boolT traceonce= False;
  vertexT *vertex, **vertexp;
  realT mintwisted, vertexdist;
  realT onemerge;
  int tracerestore=0, nummerge;
  const char *mergename;

  if(mergetype > 0 && mergetype < sizeof(mergetypes)/sizeof(char *))
    mergename= mergetypes[mergetype];
  else
    mergename= mergetypes[MRGnone];
  if (facet1->tricoplanar || facet2->tricoplanar) {
    if (!qh TRInormals) {
      qh_fprintf(qh ferr, 6226, "qhull internal error (qh_mergefacet): merge f%d into f%d for mergetype %d (%s) does not work for tricoplanar facets.  Use option 'Q11'\n",
        facet1->id, facet2->id, mergetype, mergename);
      qh_errexit2(qh_ERRqhull, facet1, facet2);
    }
    if (facet2->tricoplanar) {
      facet2->tricoplanar= False;
      facet2->keepcentrum= False;
    }
  }
  zzinc_(Ztotmerge);
  if (qh REPORTfreq2 && qh POSTmerging) {
    if (zzval_(Ztotmerge) > qh mergereport + qh REPORTfreq2)
      qh_tracemerging();
  }
#ifndef qh_NOtrace
  if (qh build_cnt >= qh RERUN) {
    if (mindist && (-*mindist > qh TRACEdist || *maxdist > qh TRACEdist)) {
      tracerestore= 0;
      qh IStracing= qh TRACElevel;
      traceonce= True;
      qh_fprintf(qh ferr, 8075, "qh_mergefacet: ========= trace wide merge #%d(%2.2g) for f%d into f%d for mergetype %d (%s), last point was p%d\n",
          zzval_(Ztotmerge), fmax_(-*mindist, *maxdist), facet1->id, facet2->id, mergetype, mergename, qh furthest_id);
    }else if (facet1 == qh tracefacet || facet2 == qh tracefacet) {
      tracerestore= qh IStracing;
      qh IStracing= 4;
      traceonce= True;
      qh_fprintf(qh ferr, 8076, "qh_mergefacet: ========= trace merge #%d for f%d into f%d for mergetype %d (%s), furthest is p%d\n",
                 zzval_(Ztotmerge), facet1->id, facet2->id, mergetype, mergename, qh furthest_id);
    }
  }
  if (qh IStracing >= 2) {
    realT mergemin= -2;
    realT mergemax= -2;

    if (mindist) {
      mergemin= *mindist;
      mergemax= *maxdist;
    }
    qh_fprintf(qh ferr, 2081, "qh_mergefacet: #%d merge f%d into f%d for merge for mergetype %d (%s), mindist= %2.2g, maxdist= %2.2g, max_outside %2.2g\n",
    zzval_(Ztotmerge), facet1->id, facet2->id, mergetype, mergename, mergemin, mergemax, qh max_outside);
  }
#endif /* !qh_NOtrace */
  if(!qh ALLOWwide && mindist) {
    mintwisted= qh_WIDEmaxoutside * qh ONEmerge;  /* same as qh_merge_twisted and qh_check_maxout (poly2) */
    maximize_(mintwisted, facet1->maxoutside);
    maximize_(mintwisted, facet2->maxoutside);
    if (*maxdist > mintwisted || -*mindist > mintwisted) {
      vertexdist= qh_vertex_bestdist(facet1->vertices);
      onemerge= qh ONEmerge + qh DISTround;
      if (vertexdist > mintwisted) {
        qh_fprintf(qh ferr, 6347, "qhull precision error (qh_mergefacet): wide merge for facet f%d into f%d for mergetype %d (%s).  maxdist %2.2g (%.1fx) mindist %2.2g (%.1fx) vertexdist %2.2g  Allow with 'Q12' (allow-wide)\n",
          facet1->id, facet2->id, mergetype, mergename, *maxdist, *maxdist/onemerge, *mindist, -*mindist/onemerge, vertexdist);
      }else {
        qh_fprintf(qh ferr, 6348, "qhull precision error (qh_mergefacet): wide merge for pinched facet f%d into f%d for mergetype %d (%s).  maxdist %2.2g (%.fx) mindist %2.2g (%.1fx) vertexdist %2.2g  Allow with 'Q12' (allow-wide)\n",
          facet1->id, facet2->id, mergetype, mergename, *maxdist, *maxdist/onemerge, *mindist, -*mindist/onemerge, vertexdist);
      }
      qh_errexit2(qh_ERRwide, facet1, facet2);
    }
  }
  if (facet1 == facet2 || facet1->visible || facet2->visible) {
    qh_fprintf(qh ferr, 6099, "qhull internal error (qh_mergefacet): either f%d and f%d are the same or one is a visible facet, mergetype %d (%s)\n",
             facet1->id, facet2->id, mergetype, mergename);
    qh_errexit2(qh_ERRqhull, facet1, facet2);
  }
  if (qh num_facets - qh num_visible <= qh hull_dim + 1) {
    qh_fprintf(qh ferr, 6227, "qhull topology error: Only %d facets remain.  The input is too degenerate or the convexity constraints are too strong.\n", 
          qh hull_dim+1);
    if (qh hull_dim >= 5 && !qh MERGEexact)
      qh_fprintf(qh ferr, 8079, "    Option 'Qx' may avoid this problem.\n");
    qh_errexit(qh_ERRtopology, NULL, NULL);
  }
  if (!qh VERTEXneighbors)
    qh_vertexneighbors();
  qh_makeridges(facet1);
  qh_makeridges(facet2);
  if (qh IStracing >=4)
    qh_errprint("MERGING", facet1, facet2, NULL, NULL);
  if (mindist) {
    maximize_(qh max_outside, *maxdist);
    maximize_(qh max_vertex, *maxdist);
#if qh_MAXoutside
    maximize_(facet2->maxoutside, *maxdist);
#endif
    minimize_(qh min_vertex, *mindist);
    if (!facet2->keepcentrum
    && (*maxdist > qh WIDEfacet || *mindist < -qh WIDEfacet)) {
      facet2->keepcentrum= True;
      zinc_(Zwidefacet);
    }
  }
  nummerge= facet1->nummerge + facet2->nummerge + 1;
  if (nummerge >= qh_MAXnummerge)
    facet2->nummerge= qh_MAXnummerge;
  else
    facet2->nummerge= (short unsigned int)nummerge; /* limited to 9 bits by qh_MAXnummerge, -Wconversion */
  facet2->newmerge= True;
  facet2->dupridge= False;
  qh_updatetested(facet1, facet2);
  if (qh hull_dim > 2 && qh_setsize(facet1->vertices) == qh hull_dim)
    qh_mergesimplex(facet1, facet2, mergeapex);
  else {
    qh vertex_visit++;
    FOREACHvertex_(facet2->vertices)
      vertex->visitid= qh vertex_visit;
    if (qh hull_dim == 2)
      qh_mergefacet2d(facet1, facet2);
    else {
      qh_mergeneighbors(facet1, facet2);
      qh_mergevertices(facet1->vertices, &facet2->vertices);
    }
    qh_mergeridges(facet1, facet2);
    qh_mergevertex_neighbors(facet1, facet2);
    if (!facet2->newfacet)
      qh_newvertices(facet2->vertices);
  }
  if (facet2->coplanarhorizon) {
    zinc_(Zmergeintocoplanar);
  }else if (!facet2->newfacet) {
    zinc_(Zmergeintohorizon);
  }else if (!facet1->newfacet && facet2->newfacet) {
    zinc_(Zmergehorizon);
  }else {
    zinc_(Zmergenew);
  }
  qh_removefacet(facet2);  /* append as a newfacet to end of qh facet_list */
  qh_appendfacet(facet2);
  facet2->newfacet= True;
  facet2->tested= False;
  qh_tracemerge(facet1, facet2, mergetype);
  if (traceonce) {
    qh_fprintf(qh ferr, 8080, "qh_mergefacet: end of wide tracing\n");
    qh IStracing= tracerestore;
  }
  if (mergetype != MRGcoplanarhorizon) {
    trace3((qh ferr, 3076, "qh_mergefacet: check f%d and f%d for redundant and degenerate neighbors\n",
        facet1->id, facet2->id));
    qh_test_redundant_neighbors(facet2);
    qh_test_degen_neighbors(facet1);  /* after qh_test_redundant_neighbors since MRGdegen more difficult than MRGredundant
                                             and before qh_willdelete which clears facet1.neighbors */
    qh_degen_redundant_facet(facet2); /* may occur in qh_merge_pinchedvertices, e.g., rbox 175 C3,2e-13 D4 t1545228104 | qhull d */
    qh_maybe_duplicateridges(facet2);
  }
  qh_willdelete(facet1, facet2);
} /* mergefacet */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="mergefacet2d">-</a>

  qh_mergefacet2d( facet1, facet2 )
    in 2d, merges neighbors and vertices of facet1 into facet2

  returns:
    build ridges for neighbors if necessary
    facet2 looks like a simplicial facet except for centrum, ridges
      neighbors are opposite the corresponding vertex
      maintains orientation of facet2

  notes:
    qh_mergefacet() retains non-simplicial structures
      they are not needed in 2d, but later routines may use them
    preserves qh.vertex_visit for qh_mergevertex_neighbors()

  design:
    get vertices and neighbors
    determine new vertices and neighbors
    set new vertices and neighbors and adjust orientation
    make ridges for new neighbor if needed
*/
void qh_mergefacet2d(facetT *facet1, facetT *facet2) {
  vertexT *vertex1A, *vertex1B, *vertex2A, *vertex2B, *vertexA, *vertexB;
  facetT *neighbor1A, *neighbor1B, *neighbor2A, *neighbor2B, *neighborA, *neighborB;

  vertex1A= SETfirstt_(facet1->vertices, vertexT);
  vertex1B= SETsecondt_(facet1->vertices, vertexT);
  vertex2A= SETfirstt_(facet2->vertices, vertexT);
  vertex2B= SETsecondt_(facet2->vertices, vertexT);
  neighbor1A= SETfirstt_(facet1->neighbors, facetT);
  neighbor1B= SETsecondt_(facet1->neighbors, facetT);
  neighbor2A= SETfirstt_(facet2->neighbors, facetT);
  neighbor2B= SETsecondt_(facet2->neighbors, facetT);
  if (vertex1A == vertex2A) {
    vertexA= vertex1B;
    vertexB= vertex2B;
    neighborA= neighbor2A;
    neighborB= neighbor1A;
  }else if (vertex1A == vertex2B) {
    vertexA= vertex1B;
    vertexB= vertex2A;
    neighborA= neighbor2B;
    neighborB= neighbor1A;
  }else if (vertex1B == vertex2A) {
    vertexA= vertex1A;
    vertexB= vertex2B;
    neighborA= neighbor2A;
    neighborB= neighbor1B;
  }else { /* 1B == 2B */
    vertexA= vertex1A;
    vertexB= vertex2A;
    neighborA= neighbor2B;
    neighborB= neighbor1B;
  }
  /* vertexB always from facet2, neighborB always from facet1 */
  if (vertexA->id > vertexB->id) {
    SETfirst_(facet2->vertices)= vertexA;
    SETsecond_(facet2->vertices)= vertexB;
    if (vertexB == vertex2A)
      facet2->toporient= !facet2->toporient;
    SETfirst_(facet2->neighbors)= neighborA;
    SETsecond_(facet2->neighbors)= neighborB;
  }else {
    SETfirst_(facet2->vertices)= vertexB;
    SETsecond_(facet2->vertices)= vertexA;
    if (vertexB == vertex2B)
      facet2->toporient= !facet2->toporient;
    SETfirst_(facet2->neighbors)= neighborB;
    SETsecond_(facet2->neighbors)= neighborA;
  }
  /* qh_makeridges not needed since neighborB is not degenerate */
  qh_setreplace(neighborB->neighbors, facet1, facet2);
  trace4((qh ferr, 4036, "qh_mergefacet2d: merged v%d and neighbor f%d of f%d into f%d\n",
       vertexA->id, neighborB->id, facet1->id, facet2->id));
} /* mergefacet2d */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="mergeneighbors">-</a>

  qh_mergeneighbors( facet1, facet2 )
    merges the neighbors of facet1 into facet2

  notes:
    only called by qh_mergefacet
    qh.hull_dim >= 3
    see qh_mergecycle_neighbors

  design:
    for each neighbor of facet1
      if neighbor is also a neighbor of facet2
        if neighbor is simplicial
          make ridges for later deletion as a degenerate facet
        update its neighbor set
      else
        move the neighbor relation to facet2
    remove the neighbor relation for facet1 and facet2
*/
void qh_mergeneighbors(facetT *facet1, facetT *facet2) {
  facetT *neighbor, **neighborp;

  trace4((qh ferr, 4037, "qh_mergeneighbors: merge neighbors of f%d and f%d\n",
          facet1->id, facet2->id));
  qh visit_id++;
  FOREACHneighbor_(facet2) {
    neighbor->visitid= qh visit_id;
  }
  FOREACHneighbor_(facet1) {
    if (neighbor->visitid == qh visit_id) {
      if (neighbor->simplicial)    /* is degen, needs ridges */
        qh_makeridges(neighbor);
      if (SETfirstt_(neighbor->neighbors, facetT) != facet1) /*keep newfacet->horizon*/
        qh_setdel(neighbor->neighbors, facet1);
      else {
        qh_setdel(neighbor->neighbors, facet2);
        qh_setreplace(neighbor->neighbors, facet1, facet2);
      }
    }else if (neighbor != facet2) {
      qh_setappend(&(facet2->neighbors), neighbor);
      qh_setreplace(neighbor->neighbors, facet1, facet2);
    }
  }
  qh_setdel(facet1->neighbors, facet2);  /* here for makeridges */
  qh_setdel(facet2->neighbors, facet1);
} /* mergeneighbors */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="mergeridges">-</a>

  qh_mergeridges( facet1, facet2 )
    merges the ridge set of facet1 into facet2

  returns:
    may delete all ridges for a vertex
    sets vertex->delridge on deleted ridges

  see:
    qh_mergecycle_ridges()

  design:
    delete ridges between facet1 and facet2
      mark (delridge) vertices on these ridges for later testing
    for each remaining ridge
      rename facet1 to facet2
*/
void qh_mergeridges(facetT *facet1, facetT *facet2) {
  ridgeT *ridge, **ridgep;

  trace4((qh ferr, 4038, "qh_mergeridges: merge ridges of f%d into f%d\n",
          facet1->id, facet2->id));
  FOREACHridge_(facet2->ridges) {
    if ((ridge->top == facet1) || (ridge->bottom == facet1)) {
      /* ridge.nonconvex is irrelevant due to merge */
      qh_delridge_merge(ridge);  /* expensive in high-d, could rebuild */
      ridgep--; /* deleted this ridge, repeat with next ridge*/
    }
  }
  FOREACHridge_(facet1->ridges) {
    if (ridge->top == facet1) {
      ridge->top= facet2;
      ridge->simplicialtop= False;
    }else { /* ridge.bottom is facet1 */
      ridge->bottom= facet2;
      ridge->simplicialbot= False;
    }
    qh_setappend(&(facet2->ridges), ridge);
  }
} /* mergeridges */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="mergesimplex">-</a>

  qh_mergesimplex( facet1, facet2, mergeapex )
    merge simplicial facet1 into facet2
    mergeapex==qh_MERGEapex if merging samecycle into horizon facet
      vertex id is latest (most recently created)
    facet1 may be contained in facet2
    ridges exist for both facets

  returns:
    facet2 with updated vertices, ridges, neighbors
    updated neighbors for facet1's vertices
    facet1 not deleted
    sets vertex->delridge on deleted ridges

  notes:
    special case code since this is the most common merge
    called from qh_mergefacet()

  design:
    if qh_MERGEapex
      add vertices of facet2 to qh.new_vertexlist if necessary
      add apex to facet2
    else
      for each ridge between facet1 and facet2
        set vertex->delridge
      determine the apex for facet1 (i.e., vertex to be merged)
      unless apex already in facet2
        insert apex into vertices for facet2
      add vertices of facet2 to qh.new_vertexlist if necessary
      add apex to qh.new_vertexlist if necessary
      for each vertex of facet1
        if apex
          rename facet1 to facet2 in its vertex neighbors
        else
          delete facet1 from vertex neighbors
          if only in facet2
            add vertex to qh.del_vertices for later deletion
      for each ridge of facet1
        delete ridges between facet1 and facet2
        append other ridges to facet2 after renaming facet to facet2
*/
void qh_mergesimplex(facetT *facet1, facetT *facet2, boolT mergeapex) {
  vertexT *vertex, **vertexp, *opposite;
  ridgeT *ridge, **ridgep;
  boolT isnew= False;
  facetT *neighbor, **neighborp, *otherfacet;

  if (mergeapex) {
    opposite= SETfirstt_(facet1->vertices, vertexT); /* apex is opposite facet2.  It has the last vertex id */
    trace4((qh ferr, 4086, "qh_mergesimplex: merge apex v%d of f%d into facet f%d\n",
      opposite->id, facet1->id, facet2->id));
    if (!facet2->newfacet)
      qh_newvertices(facet2->vertices);  /* apex, the first vertex, is already new */
    if (SETfirstt_(facet2->vertices, vertexT) != opposite) {
      qh_setaddnth(&facet2->vertices, 0, opposite);
      isnew= True;
    }
  }else {
    zinc_(Zmergesimplex);
    FOREACHvertex_(facet1->vertices)
      vertex->seen= False;
    FOREACHridge_(facet1->ridges) {
      if (otherfacet_(ridge, facet1) == facet2) {
        FOREACHvertex_(ridge->vertices) {
          vertex->seen= True;
          vertex->delridge= True;
        }
        break;
      }
    }
    FOREACHvertex_(facet1->vertices) {
      if (!vertex->seen)
        break;  /* must occur */
    }
    opposite= vertex;
    trace4((qh ferr, 4039, "qh_mergesimplex: merge opposite v%d of f%d into facet f%d\n",
          opposite->id, facet1->id, facet2->id));
    isnew= qh_addfacetvertex(facet2, opposite);
    if (!facet2->newfacet)
      qh_newvertices(facet2->vertices);
    else if (!opposite->newfacet) {
      qh_removevertex(opposite);
      qh_appendvertex(opposite);
    }
  }
  trace4((qh ferr, 4040, "qh_mergesimplex: update vertex neighbors of f%d\n",
          facet1->id));
  FOREACHvertex_(facet1->vertices) {
    if (vertex == opposite && isnew)
      qh_setreplace(vertex->neighbors, facet1, facet2);
    else {
      qh_setdel(vertex->neighbors, facet1);
      if (!SETsecond_(vertex->neighbors))
        qh_mergevertex_del(vertex, facet1, facet2);
    }
  }
  trace4((qh ferr, 4041, "qh_mergesimplex: merge ridges and neighbors of f%d into f%d\n",
          facet1->id, facet2->id));
  qh visit_id++;
  FOREACHneighbor_(facet2)
    neighbor->visitid= qh visit_id;
  FOREACHridge_(facet1->ridges) {
    otherfacet= otherfacet_(ridge, facet1);
    if (otherfacet == facet2) {
      /* ridge.nonconvex is irrelevant due to merge */
      qh_delridge_merge(ridge);  /* expensive in high-d, could rebuild */
      ridgep--; /* deleted this ridge, repeat with next ridge*/
      qh_setdel(facet2->neighbors, facet1); /* a simplicial facet may have duplicate neighbors, need to delete each one */
    }else if (otherfacet->dupridge && !qh_setin(otherfacet->neighbors, facet1)) {
      qh_fprintf(qh ferr, 6356, "qhull topology error (qh_mergesimplex): f%d is a dupridge of f%d, cannot merge f%d into f%d\n",
        facet1->id, otherfacet->id, facet1->id, facet2->id);
      qh_errexit2(qh_ERRqhull, facet1, otherfacet);
    }else {
      trace4((qh ferr, 4059, "qh_mergesimplex: move r%d with f%d to f%d, new neighbor? %d, maybe horizon? %d\n",
        ridge->id, otherfacet->id, facet2->id, (otherfacet->visitid != qh visit_id), (SETfirstt_(otherfacet->neighbors, facetT) == facet1)));
      qh_setappend(&facet2->ridges, ridge);
      if (otherfacet->visitid != qh visit_id) {
        qh_setappend(&facet2->neighbors, otherfacet);
        qh_setreplace(otherfacet->neighbors, facet1, facet2);
        otherfacet->visitid= qh visit_id;
      }else {
        if (otherfacet->simplicial)    /* is degen, needs ridges */
          qh_makeridges(otherfacet);
        if (SETfirstt_(otherfacet->neighbors, facetT) == facet1) {
          /* keep new, otherfacet->neighbors->horizon */
          qh_setdel(otherfacet->neighbors, facet2);
          qh_setreplace(otherfacet->neighbors, facet1, facet2);
        }else {
          /* facet2 is already a neighbor of otherfacet, by f.visitid */
          qh_setdel(otherfacet->neighbors, facet1);
        }
      }
      if (ridge->top == facet1) { /* wait until after qh_makeridges */
        ridge->top= facet2;
        ridge->simplicialtop= False;
      }else {
        ridge->bottom= facet2;
        ridge->simplicialbot= False;
      }
    }
  }
  trace3((qh ferr, 3006, "qh_mergesimplex: merged simplex f%d v%d into facet f%d\n",
          facet1->id, opposite->id, facet2->id));
} /* mergesimplex */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="mergevertex_del">-</a>

  qh_mergevertex_del( vertex, facet1, facet2 )
    delete a vertex because of merging facet1 into facet2

  returns:
    deletes vertex from facet2
    adds vertex to qh.del_vertices for later deletion
*/
void qh_mergevertex_del(vertexT *vertex, facetT *facet1, facetT *facet2) {

  zinc_(Zmergevertex);
  trace2((qh ferr, 2035, "qh_mergevertex_del: deleted v%d when merging f%d into f%d\n",
          vertex->id, facet1->id, facet2->id));
  qh_setdelsorted(facet2->vertices, vertex);
  vertex->deleted= True;
  qh_setappend(&qh del_vertices, vertex);
} /* mergevertex_del */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="mergevertex_neighbors">-</a>

  qh_mergevertex_neighbors( facet1, facet2 )
    merge the vertex neighbors of facet1 to facet2

  returns:
    if vertex is current qh.vertex_visit
      deletes facet1 from vertex->neighbors
    else
      renames facet1 to facet2 in vertex->neighbors
    deletes vertices if only one neighbor

  notes:
    assumes vertex neighbor sets are good
*/
void qh_mergevertex_neighbors(facetT *facet1, facetT *facet2) {
  vertexT *vertex, **vertexp;

  trace4((qh ferr, 4042, "qh_mergevertex_neighbors: merge vertex neighborset for f%d into f%d\n",
          facet1->id, facet2->id));
  if (qh tracevertex) {
    qh_fprintf(qh ferr, 8081, "qh_mergevertex_neighbors: of f%d into f%d at furthest p%d f0= %p\n",
             facet1->id, facet2->id, qh furthest_id, qh tracevertex->neighbors->e[0].p);
    qh_errprint("TRACE", NULL, NULL, NULL, qh tracevertex);
  }
  FOREACHvertex_(facet1->vertices) {
    if (vertex->visitid != qh vertex_visit)
      qh_setreplace(vertex->neighbors, facet1, facet2);
    else {
      qh_setdel(vertex->neighbors, facet1);
      if (!SETsecond_(vertex->neighbors))
        qh_mergevertex_del(vertex, facet1, facet2);
    }
  }
  if (qh tracevertex)
    qh_errprint("TRACE", NULL, NULL, NULL, qh tracevertex);
} /* mergevertex_neighbors */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="mergevertices">-</a>

  qh_mergevertices( vertices1, vertices2 )
    merges the vertex set of facet1 into facet2

  returns:
    replaces vertices2 with merged set
    preserves vertex_visit for qh_mergevertex_neighbors
    updates qh.newvertex_list

  design:
    create a merged set of both vertices (in inverse id order)
*/
void qh_mergevertices(setT *vertices1, setT **vertices2) {
  int newsize= qh_setsize(vertices1)+qh_setsize(*vertices2) - qh hull_dim + 1;
  setT *mergedvertices;
  vertexT *vertex, **vertexp, **vertex2= SETaddr_(*vertices2, vertexT);

  mergedvertices= qh_settemp(newsize);
  FOREACHvertex_(vertices1) {
    if (!*vertex2 || vertex->id > (*vertex2)->id)
      qh_setappend(&mergedvertices, vertex);
    else {
      while (*vertex2 && (*vertex2)->id > vertex->id)
        qh_setappend(&mergedvertices, *vertex2++);
      if (!*vertex2 || (*vertex2)->id < vertex->id)
        qh_setappend(&mergedvertices, vertex);
      else
        qh_setappend(&mergedvertices, *vertex2++);
    }
  }
  while (*vertex2)
    qh_setappend(&mergedvertices, *vertex2++);
  if (newsize < qh_setsize(mergedvertices)) {
    qh_fprintf(qh ferr, 6100, "qhull internal error (qh_mergevertices): facets did not share a ridge\n");
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  qh_setfree(vertices2);
  *vertices2= mergedvertices;
  qh_settemppop();
} /* mergevertices */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="neighbor_intersections">-</a>

  qh_neighbor_intersections( vertex )
    return intersection of all vertices in vertex->neighbors except for vertex

  returns:
    returns temporary set of vertices
    does not include vertex
    NULL if a neighbor is simplicial
    NULL if empty set

  notes:
    only called by qh_redundant_vertex for qh_reducevertices
      so f.vertices does not contain extraneous vertices that are not in f.ridges
    used for renaming vertices

  design:
    initialize the intersection set with vertices of the first two neighbors
    delete vertex from the intersection
    for each remaining neighbor
      intersect its vertex set with the intersection set
      return NULL if empty
    return the intersection set
*/
setT *qh_neighbor_intersections(vertexT *vertex) {
  facetT *neighbor, **neighborp, *neighborA, *neighborB;
  setT *intersect;
  int neighbor_i, neighbor_n;

  FOREACHneighbor_(vertex) {
    if (neighbor->simplicial)
      return NULL;
  }
  neighborA= SETfirstt_(vertex->neighbors, facetT);
  neighborB= SETsecondt_(vertex->neighbors, facetT);
  zinc_(Zintersectnum);
  if (!neighborA)
    return NULL;
  if (!neighborB)
    intersect= qh_setcopy(neighborA->vertices, 0);
  else
    intersect= qh_vertexintersect_new(neighborA->vertices, neighborB->vertices);
  qh_settemppush(intersect);
  qh_setdelsorted(intersect, vertex);
  FOREACHneighbor_i_(vertex) {
    if (neighbor_i >= 2) {
      zinc_(Zintersectnum);
      qh_vertexintersect(&intersect, neighbor->vertices);
      if (!SETfirst_(intersect)) {
        zinc_(Zintersectfail);
        qh_settempfree(&intersect);
        return NULL;
      }
    }
  }
  trace3((qh ferr, 3007, "qh_neighbor_intersections: %d vertices in neighbor intersection of v%d\n",
          qh_setsize(intersect), vertex->id));
  return intersect;
} /* neighbor_intersections */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="neighbor_vertices">-</a>

  qh_neighbor_vertices( vertex )
    return neighboring vertices for a vertex (not in subridge)
    assumes vertices have full vertex->neighbors

  returns:
    temporary set of vertices

  notes:
    updates qh.visit_id and qh.vertex_visit
    similar to qh_vertexridges

*/
setT *qh_neighbor_vertices(vertexT *vertexA, setT *subridge) {
  facetT *neighbor, **neighborp;
  vertexT *vertex, **vertexp;
  setT *vertices= qh_settemp(qh TEMPsize);

  qh visit_id++;
  FOREACHneighbor_(vertexA)
    neighbor->visitid= qh visit_id;
  qh vertex_visit++;
  vertexA->visitid= qh vertex_visit;
  FOREACHvertex_(subridge) {
    vertex->visitid= qh vertex_visit;
  }
  FOREACHneighbor_(vertexA) {
    if (*neighborp)   /* no new ridges in last neighbor */
      qh_neighbor_vertices_facet(vertexA, neighbor, &vertices);
  }
  trace3((qh ferr, 3035, "qh_neighbor_vertices: %d non-subridge, vertex neighbors for v%d\n",
    qh_setsize(vertices), vertexA->id));
  return vertices;
} /* neighbor_vertices */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="neighbor_vertices_facet">-</a>

  qh_neighbor_vertices_facet( vertex, facet, vertices )
    add neighboring vertices on ridges for vertex in facet
    neighbor->visitid==qh.visit_id if it hasn't been visited
    v.visitid==qh.vertex_visit if it is already in vertices

  returns:
    vertices updated
    sets facet->visitid to qh.visit_id-1

  notes:
    only called by qh_neighbor_vertices
    similar to qh_vertexridges_facet

  design:
    for each ridge of facet
      if ridge of visited neighbor (i.e., unprocessed)
        if vertex in ridge
          append unprocessed vertices of ridge
    mark facet processed
*/
void qh_neighbor_vertices_facet(vertexT *vertexA, facetT *facet, setT **vertices) {
  ridgeT *ridge, **ridgep;
  facetT *neighbor;
  vertexT *second, *last, *vertex, **vertexp;
  int last_i= qh hull_dim-2, count= 0;
  boolT isridge;

  if (facet->simplicial) {
    FOREACHvertex_(facet->vertices) {
      if (vertex->visitid != qh vertex_visit) {
        vertex->visitid= qh vertex_visit;
        qh_setappend(vertices, vertex);
        count++;
      }
    }
  }else {
    FOREACHridge_(facet->ridges) {
      neighbor= otherfacet_(ridge, facet);
      if (neighbor->visitid == qh visit_id) {
        isridge= False;
        if (SETfirst_(ridge->vertices) == vertexA) {
          isridge= True;
        }else if (last_i > 2) {
          second= SETsecondt_(ridge->vertices, vertexT);
          last= SETelemt_(ridge->vertices, last_i, vertexT);
          if (second->id >= vertexA->id && last->id <= vertexA->id) { /* vertices inverse sorted by id */
            if (second == vertexA || last == vertexA)
              isridge= True;
            else if (qh_setin(ridge->vertices, vertexA))
              isridge= True;
          }
        }else if (SETelem_(ridge->vertices, last_i) == vertexA) {
          isridge= True;
        }else if (last_i > 1 && SETsecond_(ridge->vertices) == vertexA) {
          isridge= True;
        }
        if (isridge) {
          FOREACHvertex_(ridge->vertices) {
            if (vertex->visitid != qh vertex_visit) {
              vertex->visitid= qh vertex_visit;
              qh_setappend(vertices, vertex);
              count++;
            }
          }
        }
      }
    }
  }
  facet->visitid= qh visit_id-1;
  if (count) {
    trace4((qh ferr, 4079, "qh_neighbor_vertices_facet: found %d vertex neighbors for v%d in f%d (simplicial? %d)\n",
      count, vertexA->id, facet->id, facet->simplicial));
  }
} /* neighbor_vertices_facet */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="newvertices">-</a>

  qh_newvertices( vertices )
    add vertices to end of qh.vertex_list (marks as new vertices)

  returns:
    vertices on qh.newvertex_list
    vertex->newfacet set
*/
void qh_newvertices(setT *vertices) {
  vertexT *vertex, **vertexp;

  FOREACHvertex_(vertices) {
    if (!vertex->newfacet) {
      qh_removevertex(vertex);
      qh_appendvertex(vertex);
    }
  }
} /* newvertices */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="next_vertexmerge">-</a>

  qh_next_vertexmerge( )
    return next vertex merge from qh.vertex_mergeset

  returns:
    vertex merge either MRGvertices or MRGsubridge
    drops merges of deleted vertices

  notes:
    called from qh_merge_pinchedvertices
*/
mergeT *qh_next_vertexmerge(void /* qh.vertex_mergeset */) {
  mergeT *merge;
  int merge_i, merge_n, best_i= -1;
  realT bestdist= REALmax;

  FOREACHmerge_i_(qh vertex_mergeset) {
    if (!merge->vertex1 || !merge->vertex2) {
      qh_fprintf(qh ferr, 6299, "qhull internal error (qh_next_vertexmerge): expecting two vertices for vertex merge.  Got v%d v%d and optional f%d\n",
        getid_(merge->vertex1), getid_(merge->vertex2), getid_(merge->facet1));
      qh_errexit(qh_ERRqhull, merge->facet1, NULL);
    }
    if (merge->vertex1->deleted || merge->vertex2->deleted) {
      trace3((qh ferr, 3030, "qh_next_vertexmerge: drop merge of v%d (del? %d) into v%d (del? %d) due to deleted vertex of r%d and r%d\n",
        merge->vertex1->id, merge->vertex1->deleted, merge->vertex2->id, merge->vertex2->deleted, getid_(merge->ridge1), getid_(merge->ridge2)));
      qh_drop_mergevertex(merge);
      qh_setdelnth(qh vertex_mergeset, merge_i);
      merge_i--; merge_n--;
      qh_memfree(merge, (int)sizeof(mergeT));
    }else if (merge->distance < bestdist) {
      bestdist= merge->distance;
      best_i= merge_i;
    }
  }
  merge= NULL;
  if (best_i >= 0) {
    merge= SETelemt_(qh vertex_mergeset, best_i, mergeT);
    if (bestdist/qh ONEmerge > qh_WIDEpinched) {
      if (merge->mergetype==MRGvertices) {
        if (merge->ridge1->top == merge->ridge2->bottom && merge->ridge1->bottom == merge->ridge2->top)
          qh_fprintf(qh ferr, 6391, "qhull topology error (qh_next_vertexmerge): no nearly adjacent vertices to resolve opposite oriented ridges r%d and r%d in f%d and f%d.  Nearest v%d and v%d dist %2.2g (%.1fx)\n",
            merge->ridge1->id, merge->ridge2->id, merge->ridge1->top->id, merge->ridge1->bottom->id, merge->vertex1->id, merge->vertex2->id, bestdist, bestdist/qh ONEmerge);
        else
          qh_fprintf(qh ferr, 6381, "qhull topology error (qh_next_vertexmerge): no nearly adjacent vertices to resolve duplicate ridges r%d and r%d.  Nearest v%d and v%d dist %2.2g (%.1fx)\n",
            merge->ridge1->id, merge->ridge2->id, merge->vertex1->id, merge->vertex2->id, bestdist, bestdist/qh ONEmerge);
      }else {
        qh_fprintf(qh ferr, 6208, "qhull topology error (qh_next_vertexmerge): no nearly adjacent vertices to resolve dupridge.  Nearest v%d and v%d dist %2.2g (%.1fx)\n",
          merge->vertex1->id, merge->vertex2->id, bestdist, bestdist/qh ONEmerge);
      }
      /* it may be possible to find a different vertex, after other vertex merges have occurred */
      qh_errexit(qh_ERRtopology, NULL, merge->ridge1);
    }
    qh_setdelnth(qh vertex_mergeset, best_i);
  }
  return merge;
} /* next_vertexmerge */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="opposite_horizonfacet">-</a>

  qh_opposite_horizonfacet( merge, opposite )
    return horizon facet for one of the merge facets, and its opposite vertex across the ridge
    assumes either facet1 or facet2 of merge is 'mergehorizon'
    assumes both facets are simplicial facets on qh.new_facetlist

  returns:
    horizon facet and opposite vertex

  notes:
    called by qh_getpinchedmerges
*/
facetT *qh_opposite_horizonfacet(mergeT *merge, vertexT **opposite) {
  facetT *facet, *horizon, *otherfacet;
  int neighbor_i;

  if (!merge->facet1->simplicial || !merge->facet2->simplicial || (!merge->facet1->mergehorizon && !merge->facet2->mergehorizon)) {
    qh_fprintf(qh ferr, 6273, "qhull internal error (qh_opposite_horizonfacet): expecting merge of simplicial facets, at least one of which is mergehorizon.  Either simplicial or mergehorizon is wrong\n");
    qh_errexit2(qh_ERRqhull, merge->facet1, merge->facet2);
  }
  if (merge->facet1->mergehorizon) {
    facet= merge->facet1;
    otherfacet= merge->facet2;
  }else {
    facet= merge->facet2;
    otherfacet= merge->facet1;
  }
  horizon= SETfirstt_(facet->neighbors, facetT);
  neighbor_i= qh_setindex(otherfacet->neighbors, facet);
  if (neighbor_i==-1)
    neighbor_i= qh_setindex(otherfacet->neighbors, qh_MERGEridge);
  if (neighbor_i==-1) {
    qh_fprintf(qh ferr, 6238, "qhull internal error (qh_opposite_horizonfacet): merge facet f%d not connected to mergehorizon f%d\n",
      otherfacet->id, facet->id);
    qh_errexit2(qh_ERRqhull, otherfacet, facet);
  }
  *opposite= SETelemt_(otherfacet->vertices, neighbor_i, vertexT);
  return horizon;
} /* opposite_horizonfacet */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="reducevertices">-</a>

  qh_reducevertices( )
    reduce extra vertices, shared vertices, and redundant vertices
    facet->newmerge is set if merged since last call
    vertex->delridge is set if vertex was on a deleted ridge
    if !qh.MERGEvertices, only removes extra vertices

  returns:
    True if also merged degen_redundant facets
    vertices are renamed if possible
    clears facet->newmerge and vertex->delridge

  notes:
    called by qh_all_merges and qh_postmerge
    ignored if 2-d

  design:
    merge any degenerate or redundant facets
    repeat until no more degenerate or redundant facets
      for each newly merged facet
        remove extra vertices
      if qh.MERGEvertices
        for each newly merged facet
          for each vertex
            if vertex was on a deleted ridge
              rename vertex if it is shared
        for each new, undeleted vertex
          remove delridge flag
          if vertex is redundant
            merge degenerate or redundant facets
*/
boolT qh_reducevertices(void) {
  int numshare=0, numrename= 0;
  boolT degenredun= False;
  facetT *newfacet;
  vertexT *vertex, **vertexp;

  if (qh hull_dim == 2)
    return False;
  trace2((qh ferr, 2101, "qh_reducevertices: reduce extra vertices, shared vertices, and redundant vertices\n"));
  if (qh_merge_degenredundant())
    degenredun= True;
LABELrestart:
  FORALLnew_facets {
    if (newfacet->newmerge) {
      if (!qh MERGEvertices)
        newfacet->newmerge= False;
      if (qh_remove_extravertices(newfacet)) {
        qh_degen_redundant_facet(newfacet);
        if (qh_merge_degenredundant()) {
          degenredun= True;
          goto LABELrestart;
        }
      }
    }
  }
  if (!qh MERGEvertices)
    return False;
  FORALLnew_facets {
    if (newfacet->newmerge) {
      newfacet->newmerge= False;
      FOREACHvertex_(newfacet->vertices) {
        if (vertex->delridge) {
          if (qh_rename_sharedvertex(vertex, newfacet)) {
            numshare++;
            if (qh_merge_degenredundant()) {
              degenredun= True;
              goto LABELrestart;
            }
            vertexp--; /* repeat since deleted vertex */
          }
        }
      }
    }
  }
  FORALLvertex_(qh newvertex_list) {
    if (vertex->delridge && !vertex->deleted) {
      vertex->delridge= False;
      if (qh hull_dim >= 4 && qh_redundant_vertex(vertex)) {
        numrename++;
        if (qh_merge_degenredundant()) {
          degenredun= True;
          goto LABELrestart;
        }
      }
    }
  }
  trace1((qh ferr, 1014, "qh_reducevertices: renamed %d shared vertices and %d redundant vertices. Degen? %d\n",
          numshare, numrename, degenredun));
  return degenredun;
} /* reducevertices */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="redundant_vertex">-</a>

  qh_redundant_vertex( vertex )
    rename a redundant vertex if qh_find_newvertex succeeds
    assumes vertices have full vertex->neighbors

  returns:
    if find a replacement vertex
      returns new vertex
      qh_renamevertex sets vertex->deleted for redundant vertex

  notes:
    only called by qh_reducevertices for vertex->delridge and hull_dim >= 4
    may add degenerate facets to qh.facet_mergeset
    doesn't change vertex->neighbors or create redundant facets

  design:
    intersect vertices of all facet neighbors of vertex
    determine ridges for these vertices
    if find a new vertex for vertex among these ridges and vertices
      rename vertex to the new vertex
*/
vertexT *qh_redundant_vertex(vertexT *vertex) {
  vertexT *newvertex= NULL;
  setT *vertices, *ridges;

  trace3((qh ferr, 3008, "qh_redundant_vertex: check if v%d from a deleted ridge can be renamed\n", vertex->id));
  if ((vertices= qh_neighbor_intersections(vertex))) {
    ridges= qh_vertexridges(vertex, !qh_ALL);
    if ((newvertex= qh_find_newvertex(vertex, vertices, ridges))) {
      zinc_(Zrenameall);
      qh_renamevertex(vertex, newvertex, ridges, NULL, NULL); /* ridges invalidated */
    }
    qh_settempfree(&ridges);
    qh_settempfree(&vertices);
  }
  return newvertex;
} /* redundant_vertex */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="remove_extravertices">-</a>

  qh_remove_extravertices( facet )
    remove extra vertices from non-simplicial facets

  returns:
    returns True if it finds them
      deletes facet from vertex neighbors
      facet may be redundant (test with qh_degen_redundant)

  notes:
    called by qh_renamevertex and qh_reducevertices
    a merge (qh_reducevertices) or qh_renamevertex may drop all ridges for a vertex in a facet

  design:
    for each vertex in facet
      if vertex not in a ridge (i.e., no longer used)
        delete vertex from facet
        delete facet from vertex's neighbors
        unless vertex in another facet
          add vertex to qh.del_vertices for later deletion
*/
boolT qh_remove_extravertices(facetT *facet) {
  ridgeT *ridge, **ridgep;
  vertexT *vertex, **vertexp;
  boolT foundrem= False;

  if (facet->simplicial) {
    return False;
  }
  trace4((qh ferr, 4043, "qh_remove_extravertices: test non-simplicial f%d for extra vertices\n",
          facet->id));
  FOREACHvertex_(facet->vertices)
    vertex->seen= False;
  FOREACHridge_(facet->ridges) {
    FOREACHvertex_(ridge->vertices)
      vertex->seen= True;
  }
  FOREACHvertex_(facet->vertices) {
    if (!vertex->seen) {
      foundrem= True;
      zinc_(Zremvertex);
      qh_setdelsorted(facet->vertices, vertex);
      qh_setdel(vertex->neighbors, facet);
      if (!qh_setsize(vertex->neighbors)) {
        vertex->deleted= True;
        qh_setappend(&qh del_vertices, vertex);
        zinc_(Zremvertexdel);
        trace2((qh ferr, 2036, "qh_remove_extravertices: v%d deleted because it's lost all ridges\n", vertex->id));
      }else
        trace3((qh ferr, 3009, "qh_remove_extravertices: v%d removed from f%d because it's lost all ridges\n", vertex->id, facet->id));
      vertexp--; /*repeat*/
    }
  }
  return foundrem;
} /* remove_extravertices */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="remove_mergetype">-</a>

  qh_remove_mergetype( mergeset, mergetype )
    Remove mergetype merges from mergeset

  notes:
    Does not preserve order
*/
void qh_remove_mergetype(setT *mergeset, mergeType type) {
  mergeT *merge;
  int merge_i, merge_n;

  FOREACHmerge_i_(mergeset) {
    if (merge->mergetype == type) {
        trace3((qh ferr, 3037, "qh_remove_mergetype: remove merge f%d f%d v%d v%d r%d r%d dist %2.2g type %d",
            getid_(merge->facet1), getid_(merge->facet2), getid_(merge->vertex1), getid_(merge->vertex2), getid_(merge->ridge1), getid_(merge->ridge2), merge->distance, type));
        qh_setdelnth(mergeset, merge_i);
        merge_i--; merge_n--;  /* repeat with next merge */
    }
  }
} /* remove_mergetype */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="rename_adjacentvertex">-</a>

  qh_rename_adjacentvertex( oldvertex, newvertex )
    renames oldvertex as newvertex.  Must be adjacent (i.e., in the same subridge)
    no-op if either vertex is deleted

  notes:
    called from qh_merge_pinchedvertices

  design:
    for all neighbors of oldvertex
      if simplicial, rename oldvertex to newvertex and drop if degenerate
      if needed, add oldvertex neighbor to newvertex
    determine ridges for oldvertex
    rename oldvertex as newvertex in ridges (qh_renamevertex)
*/
void qh_rename_adjacentvertex(vertexT *oldvertex, vertexT *newvertex, realT dist) {
  setT *ridges;
  facetT *neighbor, **neighborp, *maxfacet= NULL;
  ridgeT *ridge, **ridgep;
  boolT istrace= False;
  int oldsize= qh_setsize(oldvertex->neighbors);
  int newsize= qh_setsize(newvertex->neighbors);
  coordT maxdist2= -REALmax, dist2;

  if (qh IStracing >= 4 || oldvertex->id == qh tracevertex_id || newvertex->id == qh tracevertex_id) {
    istrace= True;
  }
  zzinc_(Ztotmerge);
  if (istrace) {
    qh_fprintf(qh ferr, 2071, "qh_rename_adjacentvertex: merge #%d rename v%d (%d neighbors) to v%d (%d neighbors) dist %2.2g\n",
      zzval_(Ztotmerge), oldvertex->id, oldsize, newvertex->id, newsize, dist);
  }
  if (oldvertex->deleted || newvertex->deleted) {
    if (istrace || qh IStracing >= 2) {
      qh_fprintf(qh ferr, 2072, "qh_rename_adjacentvertex: ignore rename.  Either v%d (%d) or v%d (%d) is deleted\n",
        oldvertex->id, oldvertex->deleted, newvertex->id, newvertex->deleted);
    }
    return;
  }
  if (oldsize == 0 || newsize == 0) {
    qh_fprintf(qh ferr, 2072, "qhull internal error (qh_rename_adjacentvertex): expecting neighbor facets for v%d and v%d.  Got %d and %d neighbors resp.\n",
      oldvertex->id, newvertex->id, oldsize, newsize);
      qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  FOREACHneighbor_(oldvertex) {
    if (neighbor->simplicial) {
      if (qh_setin(neighbor->vertices, newvertex)) {
        if (istrace || qh IStracing >= 2) {
          qh_fprintf(qh ferr, 2070, "qh_rename_adjacentvertex: simplicial f%d contains old v%d and new v%d.  Will be marked degenerate by qh_renamevertex\n",
            neighbor->id, oldvertex->id, newvertex->id);
        }
        qh_makeridges(neighbor); /* no longer simplicial, nummerge==0, skipped by qh_maybe_duplicateridge */
      }else {
        qh_replacefacetvertex(neighbor, oldvertex, newvertex);
        qh_setunique(&newvertex->neighbors, neighbor);
        qh_newvertices(neighbor->vertices);  /* for qh_update_vertexneighbors of vertex neighbors */
      }
    }
  }
  ridges= qh_vertexridges(oldvertex, qh_ALL);
  if (istrace) {
    FOREACHridge_(ridges) {
      qh_printridge(qh ferr, ridge);
    }
  }
  FOREACHneighbor_(oldvertex) {
    if (!neighbor->simplicial){
      qh_addfacetvertex(neighbor, newvertex);
      qh_setunique(&newvertex->neighbors, neighbor);
      qh_newvertices(neighbor->vertices);  /* for qh_update_vertexneighbors of vertex neighbors */
      if (qh newfacet_list == qh facet_tail) {
        qh_removefacet(neighbor);  /* add a neighbor to newfacet_list so that qh_partitionvisible has a newfacet */
        qh_appendfacet(neighbor);
        neighbor->newfacet= True;
      }
    }
  }
  qh_renamevertex(oldvertex, newvertex, ridges, NULL, NULL);  /* ridges invalidated */
  if (oldvertex->deleted && !oldvertex->partitioned) {
    FOREACHneighbor_(newvertex) {
      if (!neighbor->visible) {
        qh_distplane(oldvertex->point, neighbor, &dist2);
        if (dist2>maxdist2) {
          maxdist2= dist2;
          maxfacet= neighbor;
        }
      }
    }
    trace2((qh ferr, 2096, "qh_rename_adjacentvertex: partition old p%d(v%d) as a coplanar point for furthest f%d dist %2.2g.  Maybe repartition later (QH0031)\n",
      qh_pointid(oldvertex->point), oldvertex->id, maxfacet->id, maxdist2))
    qh_partitioncoplanar(oldvertex->point, maxfacet, NULL, !qh_ALL);  /* faster with maxdist2, otherwise duplicates distance tests from maxdist2/dist2 */
    oldvertex->partitioned= True;
  }
  qh_settempfree(&ridges);
} /* rename_adjacentvertex */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="rename_sharedvertex">-</a>

  qh_rename_sharedvertex( vertex, facet )
    detect and rename if shared vertex in facet
    vertices have full ->neighbors

  returns:
    newvertex or NULL
    the vertex may still exist in other facets (i.e., a neighbor was pinched)
    does not change facet->neighbors
    updates vertex->neighbors

  notes:
    only called by qh_reducevertices after qh_remove_extravertices
       so f.vertices does not contain extraneous vertices
    a shared vertex for a facet is only in ridges to one neighbor
    this may undo a pinched facet

    it does not catch pinches involving multiple facets.  These appear
      to be difficult to detect, since an exhaustive search is too expensive.

  design:
    if vertex only has two neighbors
      determine the ridges that contain the vertex
      determine the vertices shared by both neighbors
      if can find a new vertex in this set
        rename the vertex to the new vertex
*/
vertexT *qh_rename_sharedvertex(vertexT *vertex, facetT *facet) {
  facetT *neighbor, **neighborp, *neighborA= NULL;
  setT *vertices, *ridges;
  vertexT *newvertex= NULL;

  if (qh_setsize(vertex->neighbors) == 2) {
    neighborA= SETfirstt_(vertex->neighbors, facetT);
    if (neighborA == facet)
      neighborA= SETsecondt_(vertex->neighbors, facetT);
  }else if (qh hull_dim == 3)
    return NULL;
  else {
    qh visit_id++;
    FOREACHneighbor_(facet)
      neighbor->visitid= qh visit_id;
    FOREACHneighbor_(vertex) {
      if (neighbor->visitid == qh visit_id) {
        if (neighborA)
          return NULL;
        neighborA= neighbor;
      }
    }
  }
  if (!neighborA) {
    qh_fprintf(qh ferr, 6101, "qhull internal error (qh_rename_sharedvertex): v%d's neighbors not in f%d\n",
        vertex->id, facet->id);
    qh_errprint("ERRONEOUS", facet, NULL, NULL, vertex);
    qh_errexit(qh_ERRqhull, NULL, NULL);
  }
  if (neighborA) { /* avoid warning */
    /* the vertex is shared by facet and neighborA */
    ridges= qh_settemp(qh TEMPsize);
    neighborA->visitid= ++qh visit_id;
    qh_vertexridges_facet(vertex, facet, &ridges);
    trace2((qh ferr, 2037, "qh_rename_sharedvertex: p%d(v%d) is shared by f%d(%d ridges) and f%d\n",
      qh_pointid(vertex->point), vertex->id, facet->id, qh_setsize(ridges), neighborA->id));
    zinc_(Zintersectnum);
    vertices= qh_vertexintersect_new(facet->vertices, neighborA->vertices);
    qh_setdel(vertices, vertex);
    qh_settemppush(vertices);
    if ((newvertex= qh_find_newvertex(vertex, vertices, ridges)))
      qh_renamevertex(vertex, newvertex, ridges, facet, neighborA);  /* ridges invalidated */
    qh_settempfree(&vertices);
    qh_settempfree(&ridges);
  }
  return newvertex;
} /* rename_sharedvertex */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="renameridgevertex">-</a>

  qh_renameridgevertex( ridge, oldvertex, newvertex )
    renames oldvertex as newvertex in ridge

  returns:
    True if renames oldvertex
    False if deleted the ridge

  notes:
    called by qh_renamevertex
    caller sets newvertex->delridge for deleted ridges (qh_reducevertices)

  design:
    delete oldvertex from ridge
    if newvertex already in ridge
      copy ridge->noconvex to another ridge if possible
      delete the ridge
    else
      insert newvertex into the ridge
      adjust the ridge's orientation
*/
boolT qh_renameridgevertex(ridgeT *ridge, vertexT *oldvertex, vertexT *newvertex) {
  int nth= 0, oldnth;
  facetT *temp;
  vertexT *vertex, **vertexp;

  oldnth= qh_setindex(ridge->vertices, oldvertex);
  if (oldnth < 0) {
    qh_fprintf(qh ferr, 6424, "qhull internal error (qh_renameridgevertex): oldvertex v%d not found in r%d.  Cannot rename to v%d\n",
        oldvertex->id, ridge->id, newvertex->id);
    qh_errexit(qh_ERRqhull, NULL, ridge);
  }
  qh_setdelnthsorted(ridge->vertices, oldnth);
  FOREACHvertex_(ridge->vertices) {
    if (vertex == newvertex) {
      zinc_(Zdelridge);
      if (ridge->nonconvex) /* only one ridge has nonconvex set */
        qh_copynonconvex(ridge);
      trace2((qh ferr, 2038, "qh_renameridgevertex: ridge r%d deleted.  It contained both v%d and v%d\n",
        ridge->id, oldvertex->id, newvertex->id));
      qh_delridge_merge(ridge); /* ridge.vertices deleted */
      return False;
    }
    if (vertex->id < newvertex->id)
      break;
    nth++;
  }
  qh_setaddnth(&ridge->vertices, nth, newvertex);
  ridge->simplicialtop= False;
  ridge->simplicialbot= False;
  if (abs(oldnth - nth)%2) {
    trace3((qh ferr, 3010, "qh_renameridgevertex: swapped the top and bottom of ridge r%d\n",
            ridge->id));
    temp= ridge->top;
    ridge->top= ridge->bottom;
    ridge->bottom= temp;
  }
  return True;
} /* renameridgevertex */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="renamevertex">-</a>

  qh_renamevertex( oldvertex, newvertex, ridges, oldfacet, neighborA )
    renames oldvertex as newvertex in ridges of non-simplicial neighbors
    set oldfacet/neighborA if oldvertex is shared between two facets (qh_rename_sharedvertex)
    otherwise qh_redundant_vertex or qh_rename_adjacentvertex

  returns:
    if oldfacet and multiple neighbors, oldvertex may still exist afterwards
    otherwise sets oldvertex->deleted for later deletion
    one or more ridges maybe deleted
    ridges is invalidated
    merges may be added to degen_mergeset via qh_maydropneighbor or qh_degen_redundant_facet

  notes:
    qh_rename_sharedvertex can not change neighbors of newvertex (since it's a subset)
    qh_redundant_vertex due to vertex->delridge for qh_reducevertices
    qh_rename_adjacentvertex for complete renames

  design:
    for each ridge in ridges
      rename oldvertex to newvertex and delete degenerate ridges
    if oldfacet not defined
      for each non-simplicial neighbor of oldvertex
        delete oldvertex from neighbor's vertices
        remove extra vertices from neighbor
      add oldvertex to qh.del_vertices
    else if oldvertex only between oldfacet and neighborA
      delete oldvertex from oldfacet and neighborA
      add oldvertex to qh.del_vertices
    else oldvertex is in oldfacet and neighborA and other facets (i.e., pinched)
      delete oldvertex from oldfacet
      delete oldfacet from old vertex's neighbors
      remove extra vertices (e.g., oldvertex) from neighborA
*/
void qh_renamevertex(vertexT *oldvertex, vertexT *newvertex, setT *ridges, facetT *oldfacet, facetT *neighborA) {
  facetT *neighbor, **neighborp;
  ridgeT *ridge, **ridgep;
  int topsize, bottomsize;
  boolT istrace= False;

#ifndef qh_NOtrace
  if (qh IStracing >= 2 || oldvertex->id == qh tracevertex_id ||
        newvertex->id == qh tracevertex_id) {
    istrace= True;
    qh_fprintf(qh ferr, 2086, "qh_renamevertex: rename v%d to v%d in %d ridges with old f%d and neighbor f%d\n",
      oldvertex->id, newvertex->id, qh_setsize(ridges), getid_(oldfacet), getid_(neighborA));
  }
#endif
  FOREACHridge_(ridges) {
    if (qh_renameridgevertex(ridge, oldvertex, newvertex)) { /* ridge is deleted if False, invalidating ridges */
      topsize= qh_setsize(ridge->top->vertices);
      bottomsize= qh_setsize(ridge->bottom->vertices);
      if (topsize < qh hull_dim || (topsize == qh hull_dim && !ridge->top->simplicial && qh_setin(ridge->top->vertices, newvertex))) {
        trace4((qh ferr, 4070, "qh_renamevertex: ignore duplicate check for r%d.  top f%d (size %d) will be degenerate after rename v%d to v%d\n",
          ridge->id, ridge->top->id, topsize, oldvertex->id, newvertex->id));
      }else if (bottomsize < qh hull_dim || (bottomsize == qh hull_dim && !ridge->bottom->simplicial && qh_setin(ridge->bottom->vertices, newvertex))) {
        trace4((qh ferr, 4071, "qh_renamevertex: ignore duplicate check for r%d.  bottom f%d (size %d) will be degenerate after rename v%d to v%d\n",
          ridge->id, ridge->bottom->id, bottomsize, oldvertex->id, newvertex->id));
      }else
        qh_maybe_duplicateridge(ridge);
    }
  }
  if (!oldfacet) {
    /* stat Zrenameall or Zpinchduplicate */
    if (istrace)
      qh_fprintf(qh ferr, 2087, "qh_renamevertex: renaming v%d to v%d in several facets for qh_redundant_vertex or MRGsubridge\n",
               oldvertex->id, newvertex->id);
    FOREACHneighbor_(oldvertex) {
      if (neighbor->simplicial) {
        qh_degen_redundant_facet(neighbor); /* e.g., rbox 175 C3,2e-13 D4 t1545235541 | qhull d */
      }else {
        if (istrace)
          qh_fprintf(qh ferr, 4080, "qh_renamevertex: rename vertices in non-simplicial neighbor f%d of v%d\n", neighbor->id, oldvertex->id);
        qh_maydropneighbor(neighbor);
        qh_setdelsorted(neighbor->vertices, oldvertex); /* if degenerate, qh_degen_redundant_facet will add to mergeset */
        if (qh_remove_extravertices(neighbor))
          neighborp--; /* neighbor deleted from oldvertex neighborset */
        qh_degen_redundant_facet(neighbor); /* either direction may be redundant, faster if combine? */
        qh_test_redundant_neighbors(neighbor);
        qh_test_degen_neighbors(neighbor);
      }
    }
    if (!oldvertex->deleted) {
      oldvertex->deleted= True;
      qh_setappend(&qh del_vertices, oldvertex);
    }
  }else if (qh_setsize(oldvertex->neighbors) == 2) {
    zinc_(Zrenameshare);
    if (istrace)
      qh_fprintf(qh ferr, 3039, "qh_renamevertex: renaming v%d to v%d in oldfacet f%d for qh_rename_sharedvertex\n",
               oldvertex->id, newvertex->id, oldfacet->id);
    FOREACHneighbor_(oldvertex) {
      qh_setdelsorted(neighbor->vertices, oldvertex);
      qh_degen_redundant_facet(neighbor);
    }
    oldvertex->deleted= True;
    qh_setappend(&qh del_vertices, oldvertex);
  }else {
    zinc_(Zrenamepinch);
    if (istrace || qh IStracing >= 1)
      qh_fprintf(qh ferr, 3040, "qh_renamevertex: renaming pinched v%d to v%d between f%d and f%d\n",
               oldvertex->id, newvertex->id, oldfacet->id, neighborA->id);
    qh_setdelsorted(oldfacet->vertices, oldvertex);
    qh_setdel(oldvertex->neighbors, oldfacet);
    if (qh_remove_extravertices(neighborA))
      qh_degen_redundant_facet(neighborA);
  }
  if (oldfacet)
    qh_degen_redundant_facet(oldfacet);
} /* renamevertex */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="test_appendmerge">-</a>

  qh_test_appendmerge( facet, neighbor, simplicial )
    test convexity and append to qh.facet_mergeset if non-convex
    if pre-merging,
      no-op if qh.SKIPconvex, or qh.MERGEexact and coplanar
    if simplicial, assumes centrum test is valid (e.g., adjacent, simplicial new facets)

  returns:
    true if appends facet/neighbor to qh.facet_mergeset
    sets facet->center as needed
    does not change facet->seen

  notes:
    called from qh_getmergeset_initial, qh_getmergeset, and qh_test_vneighbors
    must be at least as strong as qh_checkconvex (poly2.c)
    assumes !f.flipped

  design:
    exit if qh.SKIPconvex ('Q0') and !qh.POSTmerging
    if qh.cos_max ('An') is defined and merging coplanars
      if the angle between facet normals is too shallow
        append an angle-coplanar merge to qh.mergeset
        return True
    test convexity of facet and neighbor
*/
boolT qh_test_appendmerge(facetT *facet, facetT *neighbor, boolT simplicial) {
  realT angle= -REALmax;
  boolT okangle= False;

  if (qh SKIPconvex && !qh POSTmerging)
    return False;
  if (qh cos_max < REALmax/2 && (!qh MERGEexact || qh POSTmerging)) {
    angle= qh_getangle(facet->normal, neighbor->normal);
    okangle= True;
    zinc_(Zangletests);
    if (angle > qh cos_max) {
      zinc_(Zcoplanarangle);
      qh_appendmergeset(facet, neighbor, MRGanglecoplanar, 0.0, angle);
      trace2((qh ferr, 2039, "qh_test_appendmerge: coplanar angle %4.4g between f%d and f%d\n",
         angle, facet->id, neighbor->id));
      return True;
    }
  }
  if (simplicial || qh hull_dim <= 3)
    return qh_test_centrum_merge(facet, neighbor, angle, okangle);
  else
    return qh_test_nonsimplicial_merge(facet, neighbor, angle, okangle);
} /* test_appendmerge */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="test_centrum_merge">-</a>

  qh_test_centrum_merge( facet, neighbor, angle, okangle )
    test centrum convexity and append non-convex facets to qh.facet_mergeset
    'angle' is angle between facets if okangle is true, otherwise use 0.0

  returns:
    true if append facet/neighbor to qh.facet_mergeset
    sets facet->center as needed
    does not change facet->seen

  notes:
    called from test_appendmerge if adjacent simplicial facets or 2-d/3-d
    at least as strict as qh_checkconvex, including qh.DISTround ('En' and 'Rn')

  design:
    make facet's centrum if needed
    if facet's centrum is above the neighbor (qh.centrum_radius)
      set isconcave

    if facet's centrum is not below the neighbor (-qh.centrum_radius)
      set iscoplanar
    make neighbor's centrum if needed
    if neighbor's centrum is above the facet
      set isconcave
    else if neighbor's centrum is not below the facet
      set iscoplanar
    if isconcave or iscoplanar and merging coplanars
      get angle if needed (qh.ANGLEmerge 'An')
      append concave-coplanar, concave ,or coplanar merge to qh.mergeset
*/
boolT qh_test_centrum_merge(facetT *facet, facetT *neighbor, realT angle, boolT okangle) {
  coordT dist, dist2, mergedist;
  boolT isconcave= False, iscoplanar= False;

  if (!facet->center)
    facet->center= qh_getcentrum(facet);
  zzinc_(Zcentrumtests);
  qh_distplane(facet->center, neighbor, &dist);
  if (dist > qh centrum_radius)
    isconcave= True;
  else if (dist >= -qh centrum_radius)
    iscoplanar= True;
  if (!neighbor->center)
    neighbor->center= qh_getcentrum(neighbor);
  zzinc_(Zcentrumtests);
  qh_distplane(neighbor->center, facet, &dist2);
  if (dist2 > qh centrum_radius)
    isconcave= True;
  else if (!iscoplanar && dist2 >= -qh centrum_radius)
    iscoplanar= True;
  if (!isconcave && (!iscoplanar || (qh MERGEexact && !qh POSTmerging)))
    return False;
  if (!okangle && qh ANGLEmerge) {
    angle= qh_getangle(facet->normal, neighbor->normal);
    zinc_(Zangletests);
  }
  if (isconcave && iscoplanar) {
    zinc_(Zconcavecoplanarridge);
    if (dist > dist2)
      qh_appendmergeset(facet, neighbor, MRGconcavecoplanar, dist, angle);
    else
      qh_appendmergeset(neighbor, facet, MRGconcavecoplanar, dist2, angle);
    trace0((qh ferr, 36, "qh_test_centrum_merge: concave f%d to coplanar f%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n",
           facet->id, neighbor->id, dist, dist2, angle, qh furthest_id));
  }else if (isconcave) {
    mergedist= fmax_(dist, dist2);
    zinc_(Zconcaveridge);
    qh_appendmergeset(facet, neighbor, MRGconcave, mergedist, angle);
    trace0((qh ferr, 37, "qh_test_centrum_merge: concave f%d to f%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n",
      facet->id, neighbor->id, dist, dist2, angle, qh furthest_id));
  }else /* iscoplanar */ {
    mergedist= fmin_(fabs_(dist), fabs_(dist2));
    zinc_(Zcoplanarcentrum);
    qh_appendmergeset(facet, neighbor, MRGcoplanar, mergedist, angle);
    trace2((qh ferr, 2097, "qh_test_centrum_merge: coplanar f%d to f%d dist %4.4g, reverse dist %4.4g angle %4.4g\n",
              facet->id, neighbor->id, dist, dist2, angle));
  }
  return True;
} /* test_centrum_merge */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="test_degen_neighbors">-</a>

  qh_test_degen_neighbors( facet )
    append degenerate neighbors to qh.degen_mergeset

  notes:
  called at end of qh_mergefacet() and qh_renamevertex()
  call after test_redundant_facet() since MRGredundant is less expensive then MRGdegen
    a degenerate facet has fewer than hull_dim neighbors
    see: qh_merge_degenredundant()

*/
void qh_test_degen_neighbors(facetT *facet) {
  facetT *neighbor, **neighborp;
  int size;

  trace4((qh ferr, 4073, "qh_test_degen_neighbors: test for degenerate neighbors of f%d\n", facet->id));
  FOREACHneighbor_(facet) {
    if (neighbor->visible) {
      qh_fprintf(qh ferr, 6359, "qhull internal error (qh_test_degen_neighbors): facet f%d has deleted neighbor f%d (qh.visible_list)\n",
        facet->id, neighbor->id);
      qh_errexit2(qh_ERRqhull, facet, neighbor);
    }
    if (neighbor->degenerate || neighbor->redundant || neighbor->dupridge) /* will merge or delete */
      continue;
    /* merge flipped-degenerate facet before flipped facets */
    if ((size= qh_setsize(neighbor->neighbors)) < qh hull_dim) {
      qh_appendmergeset(neighbor, neighbor, MRGdegen, 0.0, 1.0);
      trace2((qh ferr, 2019, "qh_test_degen_neighbors: f%d is degenerate with %d neighbors.  Neighbor of f%d.\n", neighbor->id, size, facet->id));
    }
  }
} /* test_degen_neighbors */


/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="test_nonsimplicial_merge">-</a>

  qh_test_nonsimplicial_merge( facet, neighbor, angle, okangle )
    test centrum and vertex convexity and append non-convex or redundant facets to qh.facet_mergeset
    'angle' is angle between facets if okangle is true, otherwise use 0.0
    skips coplanar merges if pre-merging with qh.MERGEexact ('Qx')

  returns:
    true if appends facet/neighbor to qh.facet_mergeset
    sets facet->center as needed
    does not change facet->seen

  notes:
    only called from test_appendmerge if a non-simplicial facet and at least 4-d
    at least as strict as qh_checkconvex, including qh.DISTround ('En' and 'Rn')
      centrums must be < -qh.centrum_radius
    tests vertices as well as centrums since a facet may be twisted relative to its neighbor

  design:
    set precision constants for maxoutside, clearlyconcave, minvertex, and coplanarcentrum
      use maxoutside for coplanarcentrum if premerging with 'Qx' and qh_MAXcoplanarcentrum merges
      otherwise use qh.centrum_radious for coplanarcentrum
    make facet and neighbor centrums if needed
    isconcave if a centrum is above neighbor (coplanarcentrum)
    iscoplanar if a centrum is not below neighbor (-qh.centrum_radius)
    maybeconvex if a centrum is clearly below neighbor (-clearyconvex)
    return False if both centrums clearly below neighbor (-clearyconvex)
    return MRGconcave if isconcave

    facets are neither clearly convex nor clearly concave
    test vertices as well as centrums
    if maybeconvex
      determine mindist and maxdist for vertices of the other facet
      maybe MRGredundant
    otherwise
      determine mindist and maxdist for vertices of either facet
      maybe MRGredundant
      maybeconvex if a vertex is clearly below neighbor (-clearconvex)

    vertices are concave if dist > clearlyconcave
    vertices are twisted if dist > maxoutside (isconcave and maybeconvex)
    return False if not concave and pre-merge of 'Qx' (qh.MERGEexact)
    vertices are coplanar if dist in -minvertex..maxoutside
    if !isconcave, vertices are coplanar if dist >= -qh.MAXcoplanar (n*qh.premerge_centrum)

    return False if neither concave nor coplanar
    return MRGtwisted if isconcave and maybeconvex
    return MRGconcavecoplanar if isconcave and isconvex
    return MRGconcave if isconcave
    return MRGcoplanar if iscoplanar
*/
boolT qh_test_nonsimplicial_merge(facetT *facet, facetT *neighbor, realT angle, boolT okangle) {
  coordT dist, mindist, maxdist, mindist2, maxdist2, dist2, maxoutside, clearlyconcave, minvertex, clearlyconvex, mergedist, coplanarcentrum;
  boolT isconcave= False, iscoplanar= False, maybeconvex= False, isredundant= False;
  vertexT *maxvertex= NULL, *maxvertex2= NULL;

  maxoutside= fmax_(neighbor->maxoutside, qh ONEmerge + qh DISTround);
  maxoutside= fmax_(maxoutside, facet->maxoutside);
  clearlyconcave= qh_RATIOconcavehorizon * maxoutside;
  minvertex= fmax_(-qh min_vertex, qh MAXcoplanar); /* non-negative, not available per facet, not used for iscoplanar */
  clearlyconvex= qh_RATIOconvexmerge * minvertex; /* must be convex for MRGtwisted */
  if (qh MERGEexact && !qh POSTmerging && (facet->nummerge > qh_MAXcoplanarcentrum || neighbor->nummerge > qh_MAXcoplanarcentrum))
    coplanarcentrum= maxoutside;
  else
    coplanarcentrum= qh centrum_radius;

  if (!facet->center)
    facet->center= qh_getcentrum(facet);
  zzinc_(Zcentrumtests);
  qh_distplane(facet->center, neighbor, &dist);
  if (dist > coplanarcentrum)
    isconcave= True;
  else if (dist >= -qh centrum_radius)
    iscoplanar= True;
  else if (dist < -clearlyconvex)
    maybeconvex= True;
  if (!neighbor->center)
    neighbor->center= qh_getcentrum(neighbor);
  zzinc_(Zcentrumtests);
  qh_distplane(neighbor->center, facet, &dist2);
  if (dist2 > coplanarcentrum)
    isconcave= True;
  else if (dist2 >= -qh centrum_radius)
    iscoplanar= True;
  else if (dist2 < -clearlyconvex) {
    if (maybeconvex)
      return False; /* both centrums clearly convex */
    maybeconvex= True;
  }
  if (isconcave) {
    if (!okangle && qh ANGLEmerge) {
      angle= qh_getangle(facet->normal, neighbor->normal);
      zinc_(Zangletests);
    }
    mergedist= fmax_(dist, dist2);
    zinc_(Zconcaveridge);
    qh_appendmergeset(facet, neighbor, MRGconcave, mergedist, angle);
    trace0((qh ferr, 18, "qh_test_nonsimplicial_merge: concave centrum for f%d or f%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n",
      facet->id, neighbor->id, dist, dist2, angle, qh furthest_id));
    return True;
  }
  /* neither clearly convex nor clearly concave, test vertices as well as centrums */
  if (maybeconvex) {
    if (dist < -clearlyconvex) {
      maxdist= dist;  /* facet centrum clearly convex, no need to test its vertex distance */
      mindist= dist;
      maxvertex2= qh_furthestvertex(neighbor, facet, &maxdist2, &mindist2);
      if (!maxvertex2) {
        qh_appendmergeset(neighbor, facet, MRGredundant, maxdist2, qh_ANGLEnone);
        isredundant= True;
      }
    }else { /* dist2 < -clearlyconvex */
      maxdist2= dist2;   /* neighbor centrum clearly convex, no need to test its vertex distance */
      mindist2= dist2;
      maxvertex= qh_furthestvertex(facet, neighbor, &maxdist, &mindist);
      if (!maxvertex) {
        qh_appendmergeset(facet, neighbor, MRGredundant, maxdist, qh_ANGLEnone);
        isredundant= True;
      }
    }
  }else {
    maxvertex= qh_furthestvertex(facet, neighbor, &maxdist, &mindist);
    if (maxvertex) {
      maxvertex2= qh_furthestvertex(neighbor, facet, &maxdist2, &mindist2);
      if (!maxvertex2) {
        qh_appendmergeset(neighbor, facet, MRGredundant, maxdist2, qh_ANGLEnone);
        isredundant= True;
      }else if (mindist < -clearlyconvex || mindist2 < -clearlyconvex)
        maybeconvex= True;
    }else { /* !maxvertex */
      qh_appendmergeset(facet, neighbor, MRGredundant, maxdist, qh_ANGLEnone);
      isredundant= True;
    }
  }
  if (isredundant) {
    zinc_(Zredundantmerge);
    return True;
  }

  if (maxdist > clearlyconcave || maxdist2 > clearlyconcave)
    isconcave= True;
  else if (maybeconvex) {
    if (maxdist > maxoutside || maxdist2 > maxoutside)
      isconcave= True;  /* MRGtwisted */
  }
  if (!isconcave && qh MERGEexact && !qh POSTmerging)
    return False;
  if (isconcave && !iscoplanar) {
    if (maxdist < maxoutside && (-qh MAXcoplanar || (maxdist2 < maxoutside && mindist2 >= -qh MAXcoplanar)))
      iscoplanar= True; /* MRGconcavecoplanar */
  }else if (!iscoplanar) {
    if (mindist >= -qh MAXcoplanar || mindist2 >= -qh MAXcoplanar)
      iscoplanar= True;  /* MRGcoplanar */
  }
  if (!isconcave && !iscoplanar)
    return False;
  if (!okangle && qh ANGLEmerge) {
    angle= qh_getangle(facet->normal, neighbor->normal);
    zinc_(Zangletests);
  }
  if (isconcave && maybeconvex) {
    zinc_(Ztwistedridge);
    if (maxdist > maxdist2)
      qh_appendmergeset(facet, neighbor, MRGtwisted, maxdist, angle);
    else
      qh_appendmergeset(neighbor, facet, MRGtwisted, maxdist2, angle);
    trace0((qh ferr, 27, "qh_test_nonsimplicial_merge: twisted concave f%d v%d to f%d v%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n",
           facet->id, getid_(maxvertex), neighbor->id, getid_(maxvertex2), maxdist, maxdist2, angle, qh furthest_id));
  }else if (isconcave && iscoplanar) {
    zinc_(Zconcavecoplanarridge);
    if (maxdist > maxdist2)
      qh_appendmergeset(facet, neighbor, MRGconcavecoplanar, maxdist, angle);
    else
      qh_appendmergeset(neighbor, facet, MRGconcavecoplanar, maxdist2, angle);
    trace0((qh ferr, 28, "qh_test_nonsimplicial_merge: concave coplanar f%d v%d to f%d v%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n",
      facet->id, getid_(maxvertex), neighbor->id, getid_(maxvertex2), maxdist, maxdist2, angle, qh furthest_id));
  }else if (isconcave) {
    mergedist= fmax_(maxdist, maxdist2);
    zinc_(Zconcaveridge);
    qh_appendmergeset(facet, neighbor, MRGconcave, mergedist, angle);
    trace0((qh ferr, 29, "qh_test_nonsimplicial_merge: concave f%d v%d to f%d v%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n",
      facet->id, getid_(maxvertex), neighbor->id, getid_(maxvertex2), maxdist, maxdist2, angle, qh furthest_id));
  }else /* iscoplanar */ {
    mergedist= fmax_(fmax_(maxdist, maxdist2), fmax_(-mindist, -mindist2));
    zinc_(Zcoplanarcentrum);
    qh_appendmergeset(facet, neighbor, MRGcoplanar, mergedist, angle);
    trace2((qh ferr, 2099, "qh_test_nonsimplicial_merge: coplanar f%d v%d to f%d v%d, dist %4.4g and reverse dist %4.4g, angle %4.4g during p%d\n",
      facet->id, getid_(maxvertex), neighbor->id, getid_(maxvertex2), maxdist, maxdist2, angle, qh furthest_id));
  }
  return True;
} /* test_nonsimplicial_merge */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="test_redundant_neighbors">-</a>

  qh_test_redundant_neighbors( facet )
    append degenerate facet or its redundant neighbors to qh.degen_mergeset

  returns:
    bumps vertex_visit

  notes:
    called at end of qh_mergefacet(), qh_mergecycle_all(), and qh_renamevertex
    call before qh_test_degen_neighbors (MRGdegen are more likely to cause problems)
    a redundant neighbor's vertices is a subset of the facet's vertices
    with pinched and flipped facets, a redundant neighbor may have a wildly different normal

    see qh_merge_degenredundant() and qh_-_facet()

  design:
    if facet is degenerate
       appends facet to degen_mergeset
    else
       appends redundant neighbors of facet to degen_mergeset
*/
void qh_test_redundant_neighbors(facetT *facet) {
  vertexT *vertex, **vertexp;
  facetT *neighbor, **neighborp;
  int size;

  trace4((qh ferr, 4022, "qh_test_redundant_neighbors: test neighbors of f%d vertex_visit %d\n",
          facet->id, qh vertex_visit+1));
  if ((size= qh_setsize(facet->neighbors)) < qh hull_dim) {
    qh_appendmergeset(facet, facet, MRGdegen, 0.0, 1.0);
    trace2((qh ferr, 2017, "qh_test_redundant_neighbors: f%d is degenerate with %d neighbors.\n", facet->id, size));
  }else {
    qh vertex_visit++;
    FOREACHvertex_(facet->vertices)
      vertex->visitid= qh vertex_visit;
    FOREACHneighbor_(facet) {
      if (neighbor->visible) {
        qh_fprintf(qh ferr, 6360, "qhull internal error (qh_test_redundant_neighbors): facet f%d has deleted neighbor f%d (qh.visible_list)\n",
          facet->id, neighbor->id);
        qh_errexit2(qh_ERRqhull, facet, neighbor);
      }
      if (neighbor->degenerate || neighbor->redundant || neighbor->dupridge) /* will merge or delete */
        continue;
      if (facet->flipped && !neighbor->flipped) /* do not merge non-flipped into flipped */
        continue;
      /* merge redundant-flipped facet first */
      /* uses early out instead of checking vertex count */
      FOREACHvertex_(neighbor->vertices) {
        if (vertex->visitid != qh vertex_visit)
          break;
      }
      if (!vertex) {
        qh_appendmergeset(neighbor, facet, MRGredundant, 0.0, 1.0);
        trace2((qh ferr, 2018, "qh_test_redundant_neighbors: f%d is contained in f%d.  merge\n", neighbor->id, facet->id));
      }
    }
  }
} /* test_redundant_neighbors */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="test_vneighbors">-</a>

  qh_test_vneighbors( )
    test vertex neighbors for convexity
    tests all facets on qh.newfacet_list

  returns:
    true if non-convex vneighbors appended to qh.facet_mergeset
    initializes vertex neighbors if needed

  notes:
    called by qh_all_merges from qh_postmerge if qh.TESTvneighbors ('Qv')
    assumes all facet neighbors have been tested
    this can be expensive
    this does not guarantee that a centrum is below all facets
      but it is unlikely
    uses qh.visit_id

  design:
    build vertex neighbors if necessary
    for all new facets
      for all vertices
        for each unvisited facet neighbor of the vertex
          test new facet and neighbor for convexity
*/
boolT qh_test_vneighbors(void /* qh.newfacet_list */) {
  facetT *newfacet, *neighbor, **neighborp;
  vertexT *vertex, **vertexp;
  int nummerges= 0;

  trace1((qh ferr, 1015, "qh_test_vneighbors: testing vertex neighbors for convexity\n"));
  if (!qh VERTEXneighbors)
    qh_vertexneighbors();
  FORALLnew_facets
    newfacet->seen= False;
  FORALLnew_facets {
    newfacet->seen= True;
    newfacet->visitid= qh visit_id++;
    FOREACHneighbor_(newfacet)
      newfacet->visitid= qh visit_id;
    FOREACHvertex_(newfacet->vertices) {
      FOREACHneighbor_(vertex) {
        if (neighbor->seen || neighbor->visitid == qh visit_id)
          continue;
        if (qh_test_appendmerge(newfacet, neighbor, False)) /* ignores optimization for simplicial ridges */
          nummerges++;
      }
    }
  }
  zadd_(Ztestvneighbor, nummerges);
  trace1((qh ferr, 1016, "qh_test_vneighbors: found %d non-convex, vertex neighbors\n",
           nummerges));
  return (nummerges > 0);
} /* test_vneighbors */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="tracemerge">-</a>

  qh_tracemerge( facet1, facet2 )
    print trace message after merge
*/
void qh_tracemerge(facetT *facet1, facetT *facet2, mergeType mergetype) {
  boolT waserror= False;
  const char *mergename;

#ifndef qh_NOtrace
  if(mergetype > 0 && mergetype < sizeof(mergetypes)/sizeof(char *))
    mergename= mergetypes[mergetype];
  else
    mergename= mergetypes[MRGnone];
  if (qh IStracing >= 4)
    qh_errprint("MERGED", facet2, NULL, NULL, NULL);
  if (facet2 == qh tracefacet || (qh tracevertex && qh tracevertex->newfacet)) {
    qh_fprintf(qh ferr, 8085, "qh_tracemerge: trace facet and vertex after merge of f%d into f%d type %d (%s), furthest p%d\n",
      facet1->id, facet2->id, mergetype, mergename, qh furthest_id);
    if (facet2 != qh tracefacet)
      qh_errprint("TRACE", qh tracefacet,
        (qh tracevertex && qh tracevertex->neighbors) ?
           SETfirstt_(qh tracevertex->neighbors, facetT) : NULL,
        NULL, qh tracevertex);
  }
  if (qh tracevertex) {
    if (qh tracevertex->deleted)
      qh_fprintf(qh ferr, 8086, "qh_tracemerge: trace vertex deleted at furthest p%d\n",
            qh furthest_id);
    else
      qh_checkvertex(qh tracevertex, qh_ALL, &waserror);
  }
  if (qh tracefacet && qh tracefacet->normal && !qh tracefacet->visible)
    qh_checkfacet(qh tracefacet, True /* newmerge */, &waserror);
#endif /* !qh_NOtrace */
  if (qh CHECKfrequently || qh IStracing >= 4) { /* can't check polygon here */
    if (qh IStracing >= 4 && qh num_facets < 500) {
      qh_printlists();
    }
    qh_checkfacet(facet2, True /* newmerge */, &waserror);
  }
  if (waserror)
    qh_errexit(qh_ERRqhull, NULL, NULL); /* erroneous facet logged by qh_checkfacet */
} /* tracemerge */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="tracemerging">-</a>

  qh_tracemerging( )
    print trace message during POSTmerging

  returns:
    updates qh.mergereport

  notes:
    called from qh_mergecycle() and qh_mergefacet()

  see:
    qh_buildtracing()
*/
void qh_tracemerging(void) {
  realT cpu;
  int total;
  time_t timedata;
  struct tm *tp;

  qh mergereport= zzval_(Ztotmerge);
  time(&timedata);
  tp= localtime(&timedata);
  cpu= qh_CPUclock;
  cpu /= qh_SECticks;
  total= zzval_(Ztotmerge) - zzval_(Zcyclehorizon) + zzval_(Zcyclefacettot);
  qh_fprintf(qh ferr, 8087, "\n\
At %d:%d:%d & %2.5g CPU secs, qhull has merged %d facets with max_outside %2.2g, min_vertex %2.2g.\n\
  The hull contains %d facets and %d vertices.\n",
      tp->tm_hour, tp->tm_min, tp->tm_sec, cpu, total, qh max_outside, qh min_vertex,
      qh num_facets - qh num_visible,
      qh num_vertices-qh_setsize(qh del_vertices));
} /* tracemerging */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="updatetested">-</a>

  qh_updatetested( facet1, facet2 )
    clear facet2->tested and facet1->ridge->tested for merge

  returns:
    deletes facet2->center unless it's already large
      if so, clears facet2->ridge->tested

  notes:
    only called by qh_mergefacet

  design:
    clear facet2->tested
    clear ridge->tested for facet1's ridges
    if facet2 has a centrum
      if facet2 is large
        set facet2->keepcentrum
      else if facet2 has 3 vertices due to many merges, or not large and post merging
        clear facet2->keepcentrum
      unless facet2->keepcentrum
        clear facet2->center to recompute centrum later
        clear ridge->tested for facet2's ridges
*/
void qh_updatetested(facetT *facet1, facetT *facet2) {
  ridgeT *ridge, **ridgep;
  int size;

  facet2->tested= False;
  FOREACHridge_(facet1->ridges)
    ridge->tested= False;
  if (!facet2->center)
    return;
  size= qh_setsize(facet2->vertices);
  if (!facet2->keepcentrum) {
    if (size > qh hull_dim + qh_MAXnewcentrum) {
      facet2->keepcentrum= True;
      zinc_(Zwidevertices);
    }
  }else if (size <= qh hull_dim + qh_MAXnewcentrum) {
    /* center and keepcentrum was set */
    if (size == qh hull_dim || qh POSTmerging)
      facet2->keepcentrum= False; /* if many merges need to recompute centrum */
  }
  if (!facet2->keepcentrum) {
    qh_memfree(facet2->center, qh normal_size);
    facet2->center= NULL;
    FOREACHridge_(facet2->ridges)
      ridge->tested= False;
  }
} /* updatetested */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="vertexridges">-</a>

  qh_vertexridges( vertex, allneighbors )
    return temporary set of ridges adjacent to a vertex
    vertex->neighbors defined (qh_vertexneighbors)

  notes:
    uses qh.visit_id
    does not include implicit ridges for simplicial facets
    skips last neighbor, unless allneighbors.  For new facets, the last neighbor shares ridges with adjacent neighbors
    if the last neighbor is not simplicial, it will have ridges for its simplicial neighbors
    Use allneighbors when a new cone is attached to an existing convex hull
    similar to qh_neighbor_vertices

  design:
    for each neighbor of vertex
      add ridges that include the vertex to ridges
*/
setT *qh_vertexridges(vertexT *vertex, boolT allneighbors) {
  facetT *neighbor, **neighborp;
  setT *ridges= qh_settemp(qh TEMPsize);
  int size;

  qh visit_id += 2;  /* visit_id for vertex neighbors, visit_id-1 for facets of visited ridges */
  FOREACHneighbor_(vertex)
    neighbor->visitid= qh visit_id;
  FOREACHneighbor_(vertex) {
    if (*neighborp || allneighbors)   /* no new ridges in last neighbor */
      qh_vertexridges_facet(vertex, neighbor, &ridges);
  }
  if (qh PRINTstatistics || qh IStracing) {
    size= qh_setsize(ridges);
    zinc_(Zvertexridge);
    zadd_(Zvertexridgetot, size);
    zmax_(Zvertexridgemax, size);
    trace3((qh ferr, 3011, "qh_vertexridges: found %d ridges for v%d\n",
             size, vertex->id));
  }
  return ridges;
} /* vertexridges */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="vertexridges_facet">-</a>

  qh_vertexridges_facet( vertex, facet, ridges )
    add adjacent ridges for vertex in facet
    neighbor->visitid==qh.visit_id if it hasn't been visited

  returns:
    ridges updated
    sets facet->visitid to qh.visit_id-1

  design:
    for each ridge of facet
      if ridge of visited neighbor (i.e., unprocessed)
        if vertex in ridge
          append ridge
    mark facet processed
*/
void qh_vertexridges_facet(vertexT *vertex, facetT *facet, setT **ridges) {
  ridgeT *ridge, **ridgep;
  facetT *neighbor;
  int last_i= qh hull_dim-2;
  vertexT *second, *last;

  FOREACHridge_(facet->ridges) {
    neighbor= otherfacet_(ridge, facet);
    if (neighbor->visitid == qh visit_id) {
      if (SETfirst_(ridge->vertices) == vertex) {
        qh_setappend(ridges, ridge);
      }else if (last_i > 2) {
        second= SETsecondt_(ridge->vertices, vertexT);
        last= SETelemt_(ridge->vertices, last_i, vertexT);
        if (second->id >= vertex->id && last->id <= vertex->id) { /* vertices inverse sorted by id */
          if (second == vertex || last == vertex)
            qh_setappend(ridges, ridge);
          else if (qh_setin(ridge->vertices, vertex))
            qh_setappend(ridges, ridge);
        }
      }else if (SETelem_(ridge->vertices, last_i) == vertex
          || (last_i > 1 && SETsecond_(ridge->vertices) == vertex)) {
        qh_setappend(ridges, ridge);
      }
    }
  }
  facet->visitid= qh visit_id-1;
} /* vertexridges_facet */

/*-<a                             href="qh-merge.htm#TOC"
  >-------------------------------</a><a name="willdelete">-</a>

  qh_willdelete( facet, replace )
    moves facet to visible list for qh_deletevisible
    sets facet->f.replace to replace (may be NULL)
    clears f.ridges and f.neighbors -- no longer valid

  returns:
    bumps qh.num_visible
*/
void qh_willdelete(facetT *facet, facetT *replace) {

  trace4((qh ferr, 4081, "qh_willdelete: move f%d to visible list, set its replacement as f%d, and clear f.neighbors and f.ridges\n", facet->id, getid_(replace)));
  if (!qh visible_list && qh newfacet_list) {
    qh_fprintf(qh ferr, 6378, "qhull internal error (qh_willdelete): expecting qh.visible_list at before qh.newfacet_list f%d.   Got NULL\n",
        qh newfacet_list->id);
    qh_errexit2(qh_ERRqhull, NULL, NULL);
  }
  qh_removefacet(facet);
  qh_prependfacet(facet, &qh visible_list);
  qh num_visible++;
  facet->visible= True;
  facet->f.replace= replace;
  if (facet->ridges)
    SETfirst_(facet->ridges)= NULL;
  if (facet->neighbors)
    SETfirst_(facet->neighbors)= NULL;
} /* willdelete */

#else /* qh_NOmerge */

void qh_all_vertexmerges(int apexpointid, facetT *facet, facetT **retryfacet) {
  QHULL_UNUSED(apexpointid)
  QHULL_UNUSED(facet)
  QHULL_UNUSED(retryfacet)
}
void qh_premerge(int apexpointid, realT maxcentrum, realT maxangle) {
  QHULL_UNUSED(apexpointid)
  QHULL_UNUSED(maxcentrum)
  QHULL_UNUSED(maxangle)
}
void qh_postmerge(const char *reason, realT maxcentrum, realT maxangle,
                      boolT vneighbors) {
  QHULL_UNUSED(reason)
  QHULL_UNUSED(maxcentrum)
  QHULL_UNUSED(maxangle)
  QHULL_UNUSED(vneighbors)
}
void qh_checkdelfacet(facetT *facet, setT *mergeset) {
  QHULL_UNUSED(facet)
  QHULL_UNUSED(mergeset)
}
void qh_checkdelridge(void /* qh.visible_facets, vertex_mergeset */) {
}
boolT qh_checkzero(boolT testall) {
  QHULL_UNUSED(testall)
    
  return True;
}
void qh_freemergesets(void) {
}
void qh_initmergesets(void) {
}
void qh_merge_pinchedvertices(int apexpointid /* qh.newfacet_list */) {
  QHULL_UNUSED(apexpointid)
}
#endif /* qh_NOmerge */



<|start_filename|>src/mc_control/samples/ExternalForces/src/mc_external_forces_controller.cpp<|end_filename|>
/*
 * Copyright 2021 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include "mc_external_forces_controller.h"

ExternalForcesController::ExternalForcesController(mc_rbdyn::RobotModulePtr rm,
                                                   double dt,
                                                   const mc_rtc::Configuration & config)
: mc_control::fsm::Controller(rm, dt, config)
{
  datastore().make_call("KinematicAnchorFrame::" + robot().name(), [](const mc_rbdyn::Robot & robot) {
    return sva::interpolate(robot.surfacePose("LeftFoot"), robot.surfacePose("RightFoot"), 0.5);
  });
}

void ExternalForcesController::reset(const mc_control::ControllerResetData & reset_data)
{
  Controller::reset(reset_data);

  auto handForceConfig = mc_rtc::gui::ForceConfig(mc_rtc::gui::Color::Red);
  handForceConfig.force_scale *= 10;
  gui()->addElement({"Forces"},
                    mc_rtc::gui::Force(
                        "LeftGripperForce", handForceConfig, [this]() { return robot().surfaceWrench("LeftGripper"); },
                        [this]() { return robot().surfacePose("LeftGripper"); }),
                    mc_rtc::gui::Force(
                        "RightGripperForce", handForceConfig,
                        [this]() { return robot().surfaceWrench("RightGripper"); },
                        [this]() { return robot().surfacePose("RightGripper"); }));

  gui()->addPlot("Surface wrenches", mc_rtc::gui::plot::X("t", [this]() { return t_; }),
                 mc_rtc::gui::plot::Y(
                     "LeftGripperForceZ", [this]() { return robot().surfaceWrench("LeftGripper").force().z(); },
                     mc_rtc::gui::Color::Red),
                 mc_rtc::gui::plot::Y(
                     "RightGripperForceZ", [this]() { return robot().surfaceWrench("RightGripper").force().z(); },
                     mc_rtc::gui::Color::Red));
}

bool ExternalForcesController::run()
{
  t_ += timeStep;
  return Controller::run();
}


<|start_filename|>tests/testCompletionCriteria.cpp<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include <mc_control/CompletionCriteria.h>
#include <mc_rbdyn/RobotLoader.h>
#include <mc_rtc/pragma.h>
#include <mc_tasks/CoMTask.h>

#include <boost/test/unit_test.hpp>

#include "utils.h"

const double dt = 0.005;

mc_rbdyn::Robots & get_robots()
{
  static std::shared_ptr<mc_rbdyn::Robots> robots_ptr = nullptr;
  if(robots_ptr)
  {
    return *robots_ptr;
  }
  configureRobotLoader();
  auto rm = mc_rbdyn::RobotLoader::get_robot_module("JVRC1");
  robots_ptr = mc_rbdyn::loadRobot(*rm);
  return *robots_ptr;
}

struct MockTask : public mc_tasks::CoMTask
{
  MockTask() : mc_tasks::CoMTask(get_robots(), 0) {}

  Eigen::VectorXd eval() const override
  {
    return eval_;
  }
  Eigen::VectorXd speed() const override
  {
    return speed_;
  }

  std::function<bool(const mc_tasks::MetaTask & t, std::string & out)> buildCompletionCriteria(
      double dt,
      const mc_rtc::Configuration & config) const override
  {
    if(config.has("MYCRITERIA"))
    {
      Eigen::Vector3d myCrit = config("MYCRITERIA");
      return [myCrit](const mc_tasks::MetaTask & t, std::string & out) {
        MC_RTC_diagnostic_push
        MC_RTC_diagnostic_ignored(GCC, "-Wunused-value")
        BOOST_REQUIRE_NO_THROW(dynamic_cast<const MockTask &>(t));
        MC_RTC_diagnostic_pop
        const auto & self = static_cast<const MockTask &>(t);
        const auto & eval_ = self.eval_;
        if(eval_.x() < myCrit.x() && eval_.y() < myCrit.y() && eval_.z() < myCrit.z())
        {
          out += "MYCRITERIA";
          return true;
        }
        return false;
      };
    }
    return MetaTask::buildCompletionCriteria(dt, config);
  }

  Eigen::Vector3d eval_;
  Eigen::Vector3d speed_;
};

BOOST_AUTO_TEST_CASE(TestDefault)
{
  MockTask task;
  mc_control::CompletionCriteria criteria;
  BOOST_REQUIRE(criteria.completed(task));
}

BOOST_AUTO_TEST_CASE(TestTimeout)
{
  MockTask task;
  double timeout = 5.0;
  mc_rtc::Configuration config;
  config.add("timeout", timeout);
  mc_control::CompletionCriteria criteria;
  criteria.configure(task, dt, config);
  unsigned int ticks = static_cast<unsigned int>(std::floor(timeout / dt));
  for(size_t i = 0; i < ticks; ++i)
  {
    task.incrementIterInSolver();
    BOOST_REQUIRE(!criteria.completed(task));
  }
  task.incrementIterInSolver();
  BOOST_REQUIRE(criteria.completed(task));
  BOOST_REQUIRE(criteria.output() == "timeout");
  // Reset the criteria, timeout should resume from the current iteration count
  criteria.configure(task, dt, config);
  for(size_t i = 0; i < ticks; ++i)
  {
    task.incrementIterInSolver();
    BOOST_REQUIRE(!criteria.completed(task));
  }
  task.incrementIterInSolver();
  BOOST_REQUIRE(criteria.completed(task));
  BOOST_REQUIRE(criteria.output() == "timeout");
}

BOOST_AUTO_TEST_CASE(TestEval)
{
  MockTask task;
  double norm = 1e-3;
  mc_rtc::Configuration config;
  config.add("eval", norm);
  mc_control::CompletionCriteria criteria;
  criteria.configure(task, dt, config);
  task.eval_ = Eigen::Vector3d::UnitZ();
  BOOST_REQUIRE(!criteria.completed(task));
  task.eval_.z() = norm; // task.eval().norm() == 1e-3 == norm
  BOOST_REQUIRE(!criteria.completed(task));
  task.eval_.z() = norm * 1e-1;
  BOOST_REQUIRE(criteria.completed(task));
  BOOST_REQUIRE(criteria.output() == "eval");
}

BOOST_AUTO_TEST_CASE(TestSpeed)
{
  MockTask task;
  double norm = 1e-3;
  mc_rtc::Configuration config;
  config.add("speed", norm);
  mc_control::CompletionCriteria criteria;
  criteria.configure(task, dt, config);
  task.speed_ = Eigen::Vector3d::UnitZ();
  BOOST_REQUIRE(!criteria.completed(task));
  task.speed_.z() = norm; // task.speed().norm() == 1e-3 == norm
  BOOST_REQUIRE(!criteria.completed(task));
  task.speed_.z() = norm * 1e-1;
  BOOST_REQUIRE(criteria.completed(task));
  BOOST_REQUIRE(criteria.output() == "speed");
}

BOOST_AUTO_TEST_CASE(TestEvalAndSpeed)
{
  MockTask task;
  double norm = 1e-3;
  mc_rtc::Configuration config;
  auto AND = config.array("AND", 2);
  AND.push([norm]() {
    mc_rtc::Configuration c;
    c.add("eval", norm);
    return c;
  }());
  AND.push([norm]() {
    mc_rtc::Configuration c;
    c.add("speed", norm);
    return c;
  }());
  mc_control::CompletionCriteria criteria;
  criteria.configure(task, dt, config);
  task.eval_ = Eigen::Vector3d::UnitZ();
  task.speed_ = Eigen::Vector3d::UnitZ();
  BOOST_REQUIRE(!criteria.completed(task));
  task.eval_.z() = norm * 1e-1;
  BOOST_REQUIRE(!criteria.completed(task));
  task.eval_ = Eigen::Vector3d::UnitZ();
  task.speed_.z() = norm * 1e-1;
  BOOST_REQUIRE(!criteria.completed(task));
  task.eval_.z() = norm * 1e-1;
  BOOST_REQUIRE(criteria.completed(task));
  BOOST_REQUIRE(criteria.output() == "eval AND speed");
}

BOOST_AUTO_TEST_CASE(TestEvalOrSpeed)
{
  MockTask task;
  double norm = 1e-3;
  mc_rtc::Configuration config;
  auto OR = config.array("OR", 2);
  OR.push([norm]() {
    mc_rtc::Configuration c;
    c.add("eval", norm);
    return c;
  }());
  OR.push([norm]() {
    mc_rtc::Configuration c;
    c.add("speed", norm);
    return c;
  }());
  mc_control::CompletionCriteria criteria;
  criteria.configure(task, dt, config);
  task.eval_ = Eigen::Vector3d::UnitZ();
  task.speed_ = Eigen::Vector3d::UnitZ();
  BOOST_REQUIRE(!criteria.completed(task));
  task.eval_.z() = norm * 1e-1;
  BOOST_REQUIRE(criteria.completed(task));
  BOOST_REQUIRE(criteria.output() == "eval");
  task.eval_ = Eigen::Vector3d::UnitZ();
  task.speed_.z() = norm * 1e-1;
  BOOST_REQUIRE(criteria.completed(task));
  BOOST_REQUIRE(criteria.output() == "speed");
  task.eval_.z() = norm * 1e-1;
  BOOST_REQUIRE(criteria.completed(task));
  BOOST_REQUIRE(criteria.output() == "eval");
}

BOOST_AUTO_TEST_CASE(TestEvalAndSpeedOrTimeout)
{
  MockTask task;
  double norm = 1e-3;
  double timeout = 5.0;
  mc_rtc::Configuration config;
  auto OR = config.array("OR", 2);
  OR.push([norm]() {
    mc_rtc::Configuration c;
    auto AND = c.array("AND", 2);
    AND.push([norm]() {
      mc_rtc::Configuration c;
      c.add("eval", norm);
      return c;
    }());
    AND.push([norm]() {
      mc_rtc::Configuration c;
      c.add("speed", norm);
      return c;
    }());
    return c;
  }());
  OR.push([timeout]() {
    mc_rtc::Configuration c;
    c.add("timeout", timeout);
    return c;
  }());
  mc_control::CompletionCriteria criteria;
  criteria.configure(task, dt, config);
  // criteria <=> (eval().norm() < norm && speed().norm() < 1e-3) || timeout)
  task.eval_ = Eigen::Vector3d::UnitZ();
  task.speed_ = Eigen::Vector3d::UnitZ();
  task.incrementIterInSolver();
  BOOST_REQUIRE(!criteria.completed(task)); // every condition false
  task.eval_.z() = norm * 1e-1;
  task.incrementIterInSolver();
  BOOST_REQUIRE(!criteria.completed(task)); // eval true, speed false, timeout false
  task.eval_.z() = 1.0;
  task.speed_.z() = norm * 1e-1;
  task.incrementIterInSolver();
  BOOST_REQUIRE(!criteria.completed(task)); // eval false, speed true, timeout false
  task.eval_.z() = norm * 1e-1;
  BOOST_REQUIRE(criteria.completed(task)); // eval true, speed true, timeout false
  BOOST_REQUIRE(criteria.output() == "eval AND speed");
  task.eval_.z() = 1.0; // from now, eval always false, speed always true
  unsigned int ticks = static_cast<unsigned int>(std::floor(timeout / dt));
  for(size_t i = 3; i < ticks; ++i)
  {
    task.incrementIterInSolver();
    BOOST_REQUIRE(!criteria.completed(task));
  }
  task.incrementIterInSolver();
  BOOST_REQUIRE(criteria.completed(task));
  BOOST_REQUIRE(criteria.output() == "timeout");
}

BOOST_AUTO_TEST_CASE(TestCustomCriteria)
{
  MockTask task;
  Eigen::Vector3d myCrit{1., 2., 3.};
  mc_rtc::Configuration config;
  config.add("MYCRITERIA", myCrit);
  mc_control::CompletionCriteria criteria;
  criteria.configure(task, dt, config);
  task.eval_ = Eigen::Vector3d::UnitZ() * 4.;
  BOOST_REQUIRE(!criteria.completed(task));
  task.eval_ = myCrit;
  BOOST_REQUIRE(!criteria.completed(task));
  task.eval_ = myCrit - Eigen::Vector3d::Ones();
  BOOST_REQUIRE(criteria.completed(task));
  BOOST_REQUIRE(criteria.output() == "MYCRITERIA");
}


<|start_filename|>include/mc_control/ControllerLoader.h<|end_filename|>
/*
 * Copyright 2015-2020 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

/*! Static interface used to load controllers, this is only used in static builds of mc_rtc */

#include <mc_control/MCController.h>

#include <mc_rtc/config.h>
#include <mc_rtc/loader.h>

#include <map>
#include <memory>
#include <mutex>

namespace mc_control
{

/*! \class ControllerLoader
 *
 * A wrapper around a singleton mc_rtc::ObjectLoader<MCController>
 */
struct MC_CONTROL_DLLAPI ControllerLoader
{
public:
  static mc_rtc::ObjectLoader<MCController> & loader();

private:
  static std::unique_ptr<mc_rtc::ObjectLoader<MCController>> loader_;
};

} // namespace mc_control


<|start_filename|>src/mc_tasks/ExactCubicTrajectoryTask.cpp<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include <mc_tasks/ExactCubicTrajectoryTask.h>

#include <mc_tasks/MetaTaskLoader.h>

#include <mc_trajectory/ExactCubic.h>
#include <mc_trajectory/InterpolatedRotation.h>

#include <mc_rtc/gui/Arrow.h>

namespace mc_tasks
{
ExactCubicTrajectoryTask::ExactCubicTrajectoryTask(const mc_rbdyn::Robots & robots,
                                                   unsigned int robotIndex,
                                                   const std::string & surfaceName,
                                                   double duration,
                                                   double stiffness,
                                                   double weight,
                                                   const sva::PTransformd & target,
                                                   const std::vector<std::pair<double, Eigen::Vector3d>> & posWp,
                                                   const Eigen::Vector3d & init_vel,
                                                   const Eigen::Vector3d & init_acc,
                                                   const Eigen::Vector3d & end_vel,
                                                   const Eigen::Vector3d & end_acc,
                                                   const std::vector<std::pair<double, Eigen::Matrix3d>> & oriWp)
: SplineTrajectoryTask<ExactCubicTrajectoryTask>(robots,
                                                 robotIndex,
                                                 surfaceName,
                                                 duration,
                                                 stiffness,
                                                 weight,
                                                 target.rotation(),
                                                 oriWp),
  bspline(duration,
          robots.robot(robotIndex).surface(surfaceName).X_0_s(robots.robot(robotIndex)).translation(),
          target.translation(),
          posWp,
          init_vel,
          init_acc,
          end_vel,
          end_acc)
{
  const mc_rbdyn::Robot & robot = robots.robot(robotIndex);
  type_ = "exact_cubic_trajectory";
  name_ = "exact_cubic_trajectory_" + robot.name() + "_" + surfaceName_;
  bspline.constraints(init_vel, init_acc, end_vel, end_acc);
  initialPose_ = robot.surface(surfaceName_).X_0_s(robot);
}

void ExactCubicTrajectoryTask::posWaypoints(const std::vector<std::pair<double, Eigen::Vector3d>> & posWp)
{
  bspline.waypoints(posWp);
}

void ExactCubicTrajectoryTask::constraints(const Eigen::Vector3d & init_vel,
                                           const Eigen::Vector3d & init_acc,
                                           const Eigen::Vector3d & end_vel,
                                           const Eigen::Vector3d & end_acc)
{
  bspline.constraints(init_vel, init_acc, end_vel, end_acc);
}

void ExactCubicTrajectoryTask::targetPos(const Eigen::Vector3d & target)
{
  bspline.target(target);
}

const Eigen::Vector3d & ExactCubicTrajectoryTask::targetPos() const
{
  return bspline.target();
}

void ExactCubicTrajectoryTask::addToGUI(mc_rtc::gui::StateBuilder & gui)
{
  SplineTrajectoryBase::addToGUI(gui);

  bspline.addToGUI(gui, {"Tasks", name_});

  gui.addElement(
      {"Tasks", name_, "Velocity Constraints"},
      mc_rtc::gui::Arrow(
          "Initial", mc_rtc::gui::ArrowConfig(mc_rtc::gui::Color(0., 1., 1.)),
          [this]() -> Eigen::Vector3d { return initialPose_.translation(); },
          [this]() -> Eigen::Vector3d { return initialPose_.translation() + bspline.init_vel(); }),
      mc_rtc::gui::Arrow(
          "Final", mc_rtc::gui::ArrowConfig(mc_rtc::gui::Color(0., 1., 1.)),
          [this]() -> Eigen::Vector3d { return SplineTrajectoryBase::target().translation(); },
          [this]() -> Eigen::Vector3d { return SplineTrajectoryBase::target().translation() + bspline.end_vel(); }));
}

} // namespace mc_tasks

namespace
{
static auto registered = mc_tasks::MetaTaskLoader::register_load_function(
    "exact_cubic_trajectory",
    [](mc_solver::QPSolver & solver, const mc_rtc::Configuration & config) {
      sva::PTransformd finalTarget_;
      std::vector<std::pair<double, Eigen::Vector3d>> waypoints;
      std::vector<std::pair<double, Eigen::Matrix3d>> oriWp;
      const auto robotIndex = robotIndexFromConfig(config, solver.robots(), "exact_cubic_trajectory");
      Eigen::Vector3d init_vel, end_vel, init_acc, end_acc;

      if(config.has("targetSurface"))
      { // Target defined from a target surface, with an offset defined
        // in the surface coordinates
        const auto & c = config("targetSurface");
        const auto & targetSurfaceName = c("surface");
        const auto & robot = robotFromConfig(c, solver.robots(), "exact_cubic_trajectory::targetSurface");

        const sva::PTransformd & targetSurface = robot.surface(targetSurfaceName).X_0_s(robot);
        const Eigen::Vector3d trans = c("translation", Eigen::Vector3d::Zero().eval());
        const Eigen::Matrix3d rot = c("rotation", Eigen::Matrix3d::Identity().eval());
        sva::PTransformd offset(rot, trans);
        finalTarget_ = offset * targetSurface;

        if(c.has("controlPoints"))
        {
          // Control points offsets defined wrt to the target surface frame
          std::vector<std::pair<double, Eigen::Vector3d>> controlPoints = c("controlPoints");
          waypoints.resize(controlPoints.size());
          for(unsigned int i = 0; i < controlPoints.size(); ++i)
          {
            const Eigen::Vector3d wp = controlPoints[i].second;
            sva::PTransformd X_offset(wp);
            waypoints[i].first = controlPoints[i].first;
            waypoints[i].second = (X_offset * targetSurface).translation();
          }
        }

        if(c.has("oriWaypoints"))
        {
          std::vector<std::pair<double, Eigen::Matrix3d>> oriWaypoints = c("oriWaypoints");
          for(const auto & wp : oriWaypoints)
          {
            const sva::PTransformd offset{wp.second};
            const sva::PTransformd ori = offset * targetSurface;
            oriWp.push_back(std::make_pair(wp.first, ori.rotation()));
          }
        }
        init_vel = targetSurface.rotation().inverse() * c("init_vel", Eigen::Vector3d::Zero().eval());
        init_acc = targetSurface.rotation().inverse() * c("init_acc", Eigen::Vector3d::Zero().eval());
        end_vel = targetSurface.rotation().inverse() * c("end_vel", Eigen::Vector3d::Zero().eval());
        end_acc = targetSurface.rotation().inverse() * c("end_acc", Eigen::Vector3d::Zero().eval());
      }
      else
      { // Absolute target pose
        finalTarget_ = config("target");

        if(config.has("controlPoints"))
        {
          // Control points defined in world coordinates
          std::vector<std::pair<double, Eigen::Vector3d>> controlPoints = config("controlPoints");
          waypoints.resize(controlPoints.size());
          for(unsigned int i = 0; i < controlPoints.size(); ++i)
          {
            waypoints[i] = controlPoints[i];
          }
        }
        init_vel = config("init_vel", Eigen::Vector3d::Zero().eval());
        init_acc = config("init_acc", Eigen::Vector3d::Zero().eval());
        end_vel = config("end_vel", Eigen::Vector3d::Zero().eval());
        end_acc = config("end_acc", Eigen::Vector3d::Zero().eval());
        oriWp = config("oriWaypoints", std::vector<std::pair<double, Eigen::Matrix3d>>{});
      }

      std::shared_ptr<mc_tasks::ExactCubicTrajectoryTask> t = std::make_shared<mc_tasks::ExactCubicTrajectoryTask>(
          solver.robots(), robotIndex, config("surface"), config("duration", 10.), config("stiffness", 100.),
          config("weight", 500.), finalTarget_, waypoints, init_vel, init_acc, end_vel, end_acc, oriWp);
      t->load(solver, config);
      const auto displaySamples = config("displaySamples", t->displaySamples());
      t->displaySamples(displaySamples);
      t->pause(config("paused", false));
      return t;
    });
}


<|start_filename|>include/mc_tasks/SplineTrajectoryTask.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#pragma once

#include <mc_tasks/TrajectoryTaskGeneric.h>
#include <mc_trajectory/InterpolatedRotation.h>
#include <mc_trajectory/SequenceInterpolator.h>

namespace mc_tasks
{

/*! \brief Generic CRTP implementation for a task tracking a curve in both position
 * and orientation. This class takes care of much of the logic behind tracking a
 * curve:
 * - Interpolates between orientation waypoints defined as pairs [time, orientation]
 * - Provides reference position/velocity/acceleration from underlying curve
 *   (defined in the Derived class)
 * - Implements GUI elements for controlling waypoints, targets, etc.
 * - Brings in all the functionalities from TrajectoryTaskGeneric
 *
 * To implement tracking of a new curve, simply derive from this class and
 * implement the functionalites specific to the curve. You need to at least
 * implement:
 * - spline() accessors
 * - targetPos() accessors/setters
 * The spline itself (as returned by spline()) should at least implement:
 * - update() triggers the curve computation if needed (if waypoints/constraints
 *   changed)
 * - target() accessors/setters
 * - splev(t, order) computes the position and its nth-order derivatives at time t along
 *   the curve
 */
template<typename Derived>
struct SplineTrajectoryTask : public TrajectoryTaskGeneric<tasks::qp::TransformTask>
{
  using SplineTrajectoryBase = SplineTrajectoryTask<Derived>;
  using TrajectoryTask = TrajectoryTaskGeneric<tasks::qp::TransformTask>;
  using SequenceInterpolator6d =
      mc_trajectory::SequenceInterpolator<Eigen::Vector6d, mc_trajectory::LinearInterpolation<Eigen::Vector6d>>;

  /*! \brief Constructor
   *
   * \param robots Robots controlled by the task
   * \param robotIndex Which robot is controlled
   * \param surfaceName Surface controlled by the task (should belong to the controlled robot)
   * \param duration Length of the movement
   * \param stiffness Task stiffness (position and orientation)
   * \param posW Task weight (position)
   * \param oriW Task weight (orientation)
   * \param oriWp Optional orientation waypoints (pairs of [time, orientation])
   */
  SplineTrajectoryTask(const mc_rbdyn::Robots & robots,
                       unsigned int robotIndex,
                       const std::string & surfaceName,
                       double duration,
                       double stiffness,
                       double weight,
                       const Eigen::Matrix3d & target,
                       const std::vector<std::pair<double, Eigen::Matrix3d>> & oriWp = {});

  /*! \brief Load parameters from a Configuration object */
  void load(mc_solver::QPSolver & solver, const mc_rtc::Configuration & config) override;

  /** Add support for the following entries
   *
   * - timeElapsed: True when the task duration has elapsed
   * - wrench: True when the force applied on the robot surface is higher than the provided threshold (6d vector, NaN
   * value ignores the reading, negative values invert the condition). Ignored if the surface has no force-sensor
   * attached.
   *   @throws if the surface does not have an associated force sensor
   */
  std::function<bool(const mc_tasks::MetaTask &, std::string &)> buildCompletionCriteria(
      double dt,
      const mc_rtc::Configuration & config) const override;

  /** \brief Sets the orientation waypoints
   *
   * \param oriWp Orientation waypoints defined as pairs of [time, orientation]
   */
  void oriWaypoints(const std::vector<std::pair<double, Eigen::Matrix3d>> & oriWp);

  /** @name Setters for the tasks gains and gain interpolation (dimWeight/Stiffnes/Damping)
   *
   * This task supports two different ways to define gains:
   * - the \ref dimWeight, \ref stiffness, \ref damping and \ref setGains setters inherited from TrajectoryTaskGeneric
   * - linear interpolation of gains values managed by \ref dimWeightInterpolation, \ref stiffnessInterpolation, \ref
   * dampingInterpolation
   *
   * If an interpolation method is present, calling the corresponding dimWeight/stiffness/damping/setGains setter will
   * disable interpolation and set a constant gain. The interpolation setters ensure that gains are interpolated
   * starting from the current (time, gain) values.
   *
   * @{
   */

  // Ensures that accessors from the base class are available
  using TrajectoryBase::damping;
  using TrajectoryBase::dimWeight;
  using TrajectoryBase::stiffness;

  /*! \brief Sets the dimensional weights (controls the importance of
   * orientation/translation).
   *
   * \note Removes the dimWeightInterpolator_ if it exists
   *
   * \throw if dimW is not a Vector6d
   *
   * \param dimW Weights expressed as a Vector6d [wx, wy, wz, tx, ty, tz]
   */
  void dimWeight(const Eigen::VectorXd & dimW) override;

  /*! \brief Gets the dimensional weights (orientation/translation)
   *
   * \returns Dimensional weights expressed as a Vector6d [wx, wy, wz, tx, ty, tz]
   */
  Eigen::VectorXd dimWeight() const override;

  /*! \brief Set the task stiffness/damping
   *
   * Damping is automatically set to 2*sqrt(stiffness)
   *
   * \note Removes the stiffnessInterpolator_ if it exists
   *
   * \param stiffness Task stiffness
   */
  void stiffness(double stiffness);

  /*!
   * \anchor stiffness
   * \brief Set dimensional stiffness
   *
   * The caller should be sure that the dimension of the vector fits the task dimension.
   *
   * Damping is automatically set to 2*sqrt(stiffness)
   *
   * \note Removes the stiffnessInterpolator_ if it exists
   *
   * \param stiffness Dimensional stiffness
   */
  void stiffness(const Eigen::VectorXd & stiffness);

  /*! \brief Set the task damping, leaving its stiffness unchanged
   *
   * \note Removes the dampingInterpolator_ if it exists
   *
   * \param damping Task stiffness
   */
  void damping(double damping);

  /*!
   * \anchor damping
   * \brief Set dimensional damping
   *
   * The caller should be sure that the dimension of the vector fits the task dimension.
   *
   * \note Removes the dampingInterpolator_ if it exists
   *
   * \param damping Dimensional damping
   */
  void damping(const Eigen::VectorXd & damping);

  /*! \brief Set both stiffness and damping
   *
   * \param stiffness Task stiffness
   *
   * \note Removes the stiffnessInterpolator_ and dampingInterpolator_ if they exist
   *
   * \param damping Task damping
   */
  void setGains(double stiffness, double damping);

  /*!
   * \anchor setGains
   *
   * \brief Set dimensional stiffness and damping
   *
   * The caller should be sure that the dimensions of the vectors fit the task dimension.
   *
   * \note Removes the stiffnessInterpolator_ and dampingInterpolator_ if they exist
   *
   * \param stiffness Dimensional stiffness
   *
   * \param damping Dimensional damping
   */
  void setGains(const Eigen::VectorXd & stiffness, const Eigen::VectorXd & damping);

  /**
   * \anchor dimWeightInterpolation
   *
   * Interpolate the dimensional weight between the specified values
   *
   * \param dimWeights Pairs of [time, dimensional gain], strictly ordered by ascending time.
   *
   * \note To remove interpolation, call \ref dimWeight
   * \note If you call this function after the task is started, and wish to keep the gains continuous, you should insert
   * the [currentTime(), dimWeight()] pair in the dimWeights argument.
   *
   * \throws std::runtime_error If dimWeights is not meeting the requirements
   */
  void dimWeightInterpolation(const std::vector<std::pair<double, Eigen::Vector6d>> & dimWeights);
  inline const std::vector<std::pair<double, Eigen::Vector6d>> & dimWeightInterpolation() const noexcept
  {
    return dimWeightInterpolator_.values();
  }

  /**
   * \anchor stiffnessInterpolation
   *
   * Interpolate the stiffness between the provided values
   *
   * \param stiffnessGains Pairs of [time, dimensional gain], strictly ordered by ascending time.
   *
   * \note If there is no \ref dampingInterpolation, damping will be set to 2*sqrt(stiffness)
   * \note To remove interpolation, call one of the stiffness setters (see \ref stiffness)
   * \note If you call this function after the task is started, and wish to keep the gains continuous, you should insert
   * the [currentTime(), dimWeight()] pair in the stiffnessGains argument.
   *
   * \throws std::runtime_error If stiffnessGains is not meeting the requirements
   */
  void stiffnessInterpolation(const std::vector<std::pair<double, Eigen::Vector6d>> & stiffnessGains);
  inline const std::vector<std::pair<double, Eigen::Vector6d>> & stiffnessInterpolation() const noexcept
  {
    return stiffnessInterpolator_.values();
  }

  /**
   * \anchor dampingInterpolation
   * Interpolate the dimensional damping between the provided values
   *
   * \param dampingGains Pairs of [time, dimensional gain]. Must be strictly
   * ordered by ascending time, and time > currentTime()
   *
   * \note To remove interpolation, call one of the damping setters (see \ref damping)
   * \note If you call this function after the task is started, and wish to keep the gains continuous, you should insert
   * the [currentTime(), dimWeight()] pair in the dampingGains argument.
   *
   * \throws std::runtime_error If dampingGains is not meeting the requirements
   */
  void dampingInterpolation(const std::vector<std::pair<double, Eigen::Vector6d>> & dampingGains);
  inline const std::vector<std::pair<double, Eigen::Vector6d>> & dampingInterpolation() const noexcept
  {
    return dampingInterpolator_.values();
  }

  /** @} */

  /*! \brief Whether the trajectory has finished
   *
   * \returns True if the trajectory has finished
   */
  bool timeElapsed() const;

  /*! \brief Returns the transformError between current robot surface pose and
   * its final target
   *
   * \returns The error w.r.t the final target
   */
  Eigen::VectorXd eval() const override;

  /**
   * \brief Returns the trajectory tracking error: transformError between the current robot surface pose
   * and its next desired pose along the trajectory error
   *
   * \return The trajectory tracking error
   */
  virtual Eigen::VectorXd evalTracking() const;

  /*! \brief Sets the curve target pose.
   * Translation target will be handled by the Derived curve, while orientation
   * target is interpolated here.
   *
   * \param target Target pose for the curve
   */
  void target(const sva::PTransformd & target);
  /*! \brief Gets the target pose (position/orientation)
   *
   * \returns target pose
   */
  const sva::PTransformd target() const;

  /*! \brief Get the control points of the trajectory's b-spline
   *
   * \returns The list of control points in the spline
   */
  std::vector<Eigen::Vector3d> controlPoints();

  /*! \brief Number of points to sample on the spline for the gui display
   *
   * \param s number of samples
   */
  void displaySamples(unsigned s);
  /*! \brief Number of samples for displaying the spline
   *
   * \return number of samples
   */
  unsigned displaySamples() const;

  /**
   * @brief Allows to pause the task
   *
   * This feature is mainly intended to allow starting the task in paused state
   * to allow adjusting the parameters of the trajectory before its execution.
   * Use with care in other contexts.
   *
   * @warning Pausing sets the task's desired velocity and acceleration to zero, which
   * will suddently stop the motion. Unpausing causes the motion to resume
   * at the current speed along the trajectory.
   * Avoid pausing/resuming during high-speed trajectories.
   *
   * @param paused True to pause the task, False to resume.
   */
  inline void pause(bool paused)
  {
    paused_ = paused;
  }

  /** True when the task is paused */
  inline bool pause() const noexcept
  {
    return paused_;
  }

  /** Returns the current time along the trajectory */
  inline double currentTime() const noexcept
  {
    return currTime_;
  }

  /** @brief Returns the trajectory's duration */
  inline double duration() const noexcept
  {
    return duration_;
  }

protected:
  /**
   * \brief Tracks a reference world pose
   *
   * \param pose Desired position (world)
   */
  void refPose(const sva::PTransformd & pose);
  /**
   * \brief Returns the trajectory reference world pose
   *
   * \return Desired pose (world)
   */
  const sva::PTransformd & refPose() const;

  /* Hide parent's refVel and refAccel implementation as the Spline tasks
   * are overriding these at every iteration according to the underlying curve */
  using TrajectoryTaskGeneric<tasks::qp::TransformTask>::refVel;
  using TrajectoryTaskGeneric<tasks::qp::TransformTask>::refAccel;

  /*! \brief Add task controls to the GUI.
   * Interactive controls for the trajectory waypoints and end-endpoints
   * automatically updates the curve
   */
  void addToGUI(mc_rtc::gui::StateBuilder & gui) override;

  /*! \brief Add information about the task to the logger
   *
   * @param logger
   */
  void addToLogger(mc_rtc::Logger & logger) override;

  /*! \brief Update trajectory target */
  void update(mc_solver::QPSolver &) override;

  /** Interpolate dimWeight, stiffness, damping */
  void interpolateGains();

protected:
  unsigned int rIndex_ = 0;
  std::string surfaceName_;
  double duration_ = 0;
  mc_trajectory::InterpolatedRotation oriSpline_;
  // Linear interpolation for gains
  SequenceInterpolator6d dimWeightInterpolator_;
  SequenceInterpolator6d stiffnessInterpolator_;
  SequenceInterpolator6d dampingInterpolator_;

  bool paused_ = false;

  double currTime_ = 0.;
  unsigned samples_ = 20;
  bool inSolver_ = false;
};
} // namespace mc_tasks

#include <mc_tasks/SplineTrajectoryTask.hpp>


<|start_filename|>3rd-party/qhull/src/libqhullcpp/QhullFacet.h<|end_filename|>
/****************************************************************************
**
** Copyright (c) 2008-2020 <NAME>. All rights reserved.
** $Id: //main/2019/qhull/src/libqhullcpp/QhullFacet.h#4 $$Change: 2963 $
** $DateTime: 2020/06/03 19:31:01 $$Author: bbarber $
**
****************************************************************************/

#ifndef QHULLFACET_H
#define QHULLFACET_H

#include "libqhull_r/qhull_ra.h"
#include "libqhullcpp/QhullHyperplane.h"
#include "libqhullcpp/QhullPoint.h"
#include "libqhullcpp/QhullSet.h"
#include "libqhullcpp/QhullPointSet.h"

#include <ostream>

namespace orgQhull {

#//!\name Used here
    class Coordinates;
    class Qhull;
    class QhullFacetSet;
    class QhullRidge;
    class QhullVertex;
    class QhullVertexSet;

#//!\name Defined here
    class QhullFacet;
    typedef QhullSet<QhullRidge>  QhullRidgeSet;

//! A QhullFacet is the C++ equivalent to Qhull's facetT*
class QhullFacet {

#//!\name Defined here
public:
    typedef facetT *   base_type;  // for QhullVertexSet

private:
#//!\name Fields -- no additions (QhullFacetSet of facetT*)
    facetT *            qh_facet;  //!< Corresponding facetT, may be 0 for corner cases (e.g., *facetSet.end()==0) and tricoplanarOwner()
    QhullQh *           qh_qh;     //!< QhullQh/qhT for facetT, may be 0

#//!\name Class objects
    static facetT       s_empty_facet; // needed for shallow copy

public:
#//!\name Constructors
                        QhullFacet() : qh_facet(&s_empty_facet), qh_qh(0) {}
    explicit            QhullFacet(const Qhull &q);
                        QhullFacet(const Qhull &q, facetT *f);
    explicit            QhullFacet(QhullQh *qqh) : qh_facet(&s_empty_facet), qh_qh(qqh) {}
                        QhullFacet(QhullQh *qqh, facetT *f) : qh_facet(f ? f : &s_empty_facet), qh_qh(qqh) {}
                        // Creates an alias.  Does not copy QhullFacet.  Needed for return by value and parameter passing
                        QhullFacet(const QhullFacet &other) : qh_facet(other.qh_facet ? other.qh_facet : &s_empty_facet), qh_qh(other.qh_qh) {}
                        // Creates an alias.  Does not copy QhullFacet.  Needed for vector<QhullFacet>
    QhullFacet &        operator=(const QhullFacet &other) { qh_facet= other.qh_facet ? other.qh_facet : &s_empty_facet; qh_qh= other.qh_qh; return *this; }
                        ~QhullFacet() {}


#//!\name GetSet
    int                 dimension() const { return (qh_qh ? qh_qh->hull_dim : 0); }
    QhullPoint          getCenter() { return getCenter(qh_PRINTpoints); }
    QhullPoint          getCenter(qh_PRINT printFormat);
    facetT *            getBaseT() const { return getFacetT(); } //!< For QhullSet<QhullFacet>
                        // Do not define facetT().  It conflicts with return type facetT*
    facetT *            getFacetT() const { return qh_facet; }
    bool                hasNext() const { return (qh_facet->next != NULL && qh_facet->next != qh_qh->facet_tail); }
    bool                hasPrevious() const { return (qh_facet->previous != NULL); }
    QhullHyperplane     hyperplane() const { return QhullHyperplane(qh_qh, dimension(), qh_facet->normal, qh_facet->offset); }
    countT              id() const { return (qh_facet ? qh_facet->id : static_cast<countT>(qh_IDunknown)); }
    QhullHyperplane     innerplane() const;
    bool                isValid() const { return qh_qh && qh_facet && qh_facet != &s_empty_facet; }
    bool                isGood() const { return qh_facet && qh_facet->good; }
    bool                isSimplicial() const { return qh_facet && qh_facet->simplicial; }
    bool                isTopOrient() const { return qh_facet && qh_facet->toporient; }
    bool                isTriCoplanar() const { return qh_facet && qh_facet->tricoplanar; }
    bool                isUpperDelaunay() const { return qh_facet && qh_facet->upperdelaunay; }
    QhullFacet          next() const { return QhullFacet(qh_qh, qh_facet->next); }
    QhullFacet          nextFacet2d(QhullVertex *nextVertex) const;
    bool                operator==(const QhullFacet &other) const { return qh_facet==other.qh_facet; }
    bool                operator!=(const QhullFacet &other) const { return !operator==(other); }
    QhullHyperplane     outerplane() const;
    QhullFacet          previous() const { return QhullFacet(qh_qh, qh_facet->previous); }
    QhullQh *           qh() const { return qh_qh; }
    void                setFacetT(QhullQh *qqh, facetT *facet) { qh_qh= qqh; qh_facet= facet; }
    QhullFacet          tricoplanarOwner() const;
    int                 visitId() const { return (qh_facet ? qh_facet->visitid : -1); }
    QhullPoint          voronoiVertex();

#//!\name value
    //! Undefined if c.size() != dimension()
    double              distance(const Coordinates &c) const { return distance(c.data()); }
    double              distance(const pointT *p) const { return distance(QhullPoint(qh_qh, const_cast<coordT *>(p))); }
    double              distance(const QhullPoint &p) const { return hyperplane().distance(p); }
    double              facetArea();

#//!\name foreach
    // Can not inline.  Otherwise circular reference
    QhullPointSet       coplanarPoints() const;
    QhullFacetSet       neighborFacets() const;
    QhullPointSet       outsidePoints() const;
    QhullRidgeSet       ridges() const;
    QhullVertexSet      vertices() const;

#//!\name IO
    struct PrintCenter{
        QhullFacet *    facet;  // non-const due to facet.center()
        const char *    message;
        qh_PRINT        print_format;
                        PrintCenter(QhullFacet &f, qh_PRINT printFormat, const char * s) : facet(&f), message(s), print_format(printFormat){}
    };//PrintCenter
    PrintCenter         printCenter(qh_PRINT printFormat, const char *message) { return PrintCenter(*this, printFormat, message); }

    struct PrintFacet{
        QhullFacet *    facet;  // non-const due to f->center()
        const char *    message;
        explicit        PrintFacet(QhullFacet &f, const char * s) : facet(&f), message(s) {}
    };//PrintFacet
    PrintFacet          print(const char *message) { return PrintFacet(*this, message); }

    struct PrintFlags{
        const QhullFacet *facet;
        const char *    message;
                        PrintFlags(const QhullFacet &f, const char *s) : facet(&f), message(s) {}
    };//PrintFlags
    PrintFlags          printFlags(const char *message) const { return PrintFlags(*this, message); }

    struct PrintHeader{
        QhullFacet *    facet;  // non-const due to f->center()
                        PrintHeader(QhullFacet &f) : facet(&f) {}
    };//PrintHeader
    PrintHeader         printHeader() { return PrintHeader(*this); }

    struct PrintRidges{
        const QhullFacet *facet;
                        PrintRidges(QhullFacet &f) : facet(&f) {}
    };//PrintRidges
    PrintRidges         printRidges() { return PrintRidges(*this); }

};//class QhullFacet

}//namespace orgQhull

#//!\name Global

std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacet::PrintFacet &pr);
std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacet::PrintCenter &pr);
std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacet::PrintFlags &pr);
std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacet::PrintHeader &pr);
std::ostream &operator<<(std::ostream &os, const orgQhull::QhullFacet::PrintRidges &pr);
std::ostream &operator<<(std::ostream &os, orgQhull::QhullFacet &f); // non-const due to qh_getcenter()

#endif // QHULLFACET_H


<|start_filename|>src/mc_robots/json.cpp<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include "json.h"

#include <mc_rbdyn/configuration_io.h>

#ifndef MC_RTC_BUILD_STATIC

extern "C"
{
  ROBOT_MODULE_API void MC_RTC_ROBOT_MODULE(std::vector<std::string> & names)
  {
    names = {"json"};
  }
  ROBOT_MODULE_API void destroy(mc_rbdyn::RobotModule * ptr)
  {
    delete ptr;
  }
  ROBOT_MODULE_API mc_rbdyn::RobotModule * create(const std::string &, const std::string & path)
  {
    mc_rtc::Configuration config(path);
    mc_rbdyn::RobotModule rm(config);
    return new mc_rbdyn::RobotModule(rm);
  }
}

#else

#  include <mc_rbdyn/RobotLoader.h>

namespace
{

static auto registered = []() {
  using fn_t = std::function<mc_rbdyn::RobotModule *(const std::string &)>;
  fn_t callback = [](const std::string & path) {
    mc_rtc::Configuration config(path);
    mc_rbdyn::RobotModule rm(config);
    return new mc_rbdyn::RobotModule(rm);
  };
  mc_rbdyn::RobotLoader::register_object("json", callback);
  return true;
}();
}

#endif


<|start_filename|>include/mc_control/GlobalPlugin.h<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL, BIT
 */

#pragma once

#include <mc_control/mc_global_controller.h>

namespace mc_control
{

/** \class Global plugin base-class
 *
 * This interface must be implemented by each global plugin.
 *
 * A global plugin is loaded by an instance of \ref
 * mc_control::MCGlobalController and performs operations before and/or after
 * \ref mc_control::MCGlobalController::run()
 *
 */
struct MC_CONTROL_DLLAPI GlobalPlugin
{
  virtual ~GlobalPlugin() = default;

  /** Holds detail regarding how the plugin runs
   *
   * The plugin should return a configuration via \ref configuration()
   *
   * The default constructor for this object implies that:
   * - the plugin uses before/after
   * - the plugin runs even when the globla controller is not running
   */
  struct MC_CONTROL_DLLAPI GlobalPluginConfiguration
  {
    /** True if this plugin should run before the global controller (i.e. implements \ref before) */
    bool should_run_before = true;
    /** True if this plugin should run after the global controller (i.e. implements \ref after) */
    bool should_run_after = true;
    /** True if this plugin should run regardless of the gc.running status, if false, this plugin only runs when
     * gc.running is true */
    bool should_always_run = true;
  };

  /** Returns the plugin running configuration
   *
   * This impacts which functions are called and when they are called
   *
   */
  virtual GlobalPluginConfiguration configuration()
  {
    return {};
  }

  /** Initialize the plugin
   *
   * This function is called when the plugin is created by the
   * MCGlobalController instance
   *
   * The plugin configuration is read from:
   * - $PLUGIN_DIR/etc/$PLUGIN_NAME.yaml
   * - $HOME/.config/mc_rtc/plugins/$PLUGIN_NAME.yaml on Linux/macOS
   * - %APPDATA%/mc_rtc/plugins/$PLUGIN_NAME.yaml on Windows
   *
   * \param controller MCGlobalController instance that owns this plugin
   *
   * \param config Plugin configuration
   *
   */
  virtual void init(mc_control::MCGlobalController & controller, const mc_rtc::Configuration & config) = 0;

  /** Reset the plugin
   *
   * This function is called when the controller is switched
   *
   * \param controller MCGlobalController instance that owns this plugin
   *
   */
  virtual void reset(mc_control::MCGlobalController & controller) = 0;

  /** Run before \ref mc_control::MCGlobalController::run()
   *
   * \param controller MCGlobalController instance that owns this plugin
   *
   */
  virtual void before(mc_control::MCGlobalController & controller) = 0;

  /** Run after \ref mc_control::MCGlobalController::run()
   *
   * \param controller MCGlobalController instance that owns this plugin
   *
   */
  virtual void after(mc_control::MCGlobalController & controller) = 0;
};

} // namespace mc_control

#ifdef WIN32
#  define GLOBAL_PLUGIN_API __declspec(dllexport)
#else
#  if __GNUC__ >= 4
#    define GLOBAL_PLUGIN_API __attribute__((visibility("default")))
#  else
#    define GLOBAL_PLUGIN_API
#  endif
#endif


<|start_filename|>doc/_data/schemas/common/dcm_tracking.json<|end_filename|>
{
  "type": "object",
  "title": "DCM Tracking Parameters",
  "description": "DCM Tracking parameters",
  "properties":
  {
    "gains":
    {
      "type": "object",
      "properties":
      {
        "prop": { "type": "number", "minimum": 0, "default": 1, "description": "Proportional gain on DCM error" },
        "integral": { "type": "number", "minimum": 0, "default": 5, "description": "Integral gain on DCM error" },
        "deriv": { "type": "number", "minimum": 0, "default": 0, "description": "Derivative gain on DCM error" },
        "comdError": { "type": "number", "minimum": 0, "default": 1, "description": "Gain on CoM derivative error"},
        "zmpd": { "type": "number", "minimum": 0, "default": 0, "description": "Gain on ZMP derivative" }
      }
    },
    "derivator_time_constant": { "type": "number", "minimum": 0, "default": 1, "description": "Time window for exponential moving average filter of the DCM integrator [s]" },
    "integrator_time_constant": { "type": "number", "minimum": 0, "default": 15, "description": "Time window for the stationary offset filter of the DCM derivator [s]" }
  }
}


<|start_filename|>doc/_data/schemas/common/ImpedanceGains.json<|end_filename|>
{
  "type": "object",
  "title": "mc_tasks::force::ImpedanceGains",
  "description": "Impedance gains configuration for mc_tasks::force::ImpedanceTask",
  "properties":
  {
    "mass": { "$ref": "/../../SpaceVecAlg/ImpedanceVecd.json", "description": "Impedance mass parameter represented in the controlled surface" },
    "damper": { "$ref": "/../../SpaceVecAlg/ImpedanceVecd.json", "description": "Impedance damper parameter represetnted in the controlled surface" },
    "spring": { "$ref": "/../../SpaceVecAlg/ImpedanceVecd.json", "description": "Impedance spring parameter repesented in the controlled surface" },
    "wrench": { "$ref": "/../../SpaceVecAlg/ImpedanceVecd.json", "description": "Coefficient-wise gain on the wrench error" }
  }
}


<|start_filename|>doc/_data/schemas/common/StabilizerConfiguration.json<|end_filename|>
{
  "type": "object",
  "properties":
  {
    "leftFootSurface": { "type": "string" },
    "rightFootSurface": { "type": "string" },
    "torsoBodyName": { "type": "string" },

    "friction": { "type": "number", "minimum": 0, "default": 0.7 },
    "tasks":
    {
      "type": "object",
      "title": "Tasks configuration",
      "description": "Configuration of the tasks managed by the stabilizer: CoM, left and right contact CoP tasks, and torso and pelvis regularization tasks",
      "properties":
      {
        "com": {"$ref": "/../../common/com.json"},
        "contact": {"$ref": "/../../common/contact.json"},
        "pelvis": {"$ref": "/../../common/pelvis.json"},
        "torso": {"$ref": "/../../common/torso.json"}
      }
    },
    "safety_thresholds": {"$ref": "/../../common/stabilizer_safety_thresholds.json"},
    "fdqp_weights": {"$ref": "/../../common/fdqp_weights.json"},
    "vdc": {"$ref": "/../../common/vdc.json"},
    "admittance": {"$ref": "/../../common/admittance.json"},
    "dcm_tracking": {"$ref": "/../../common/dcm_tracking.json"},
    "dcm_bias": {"$ref": "/../../common/dcm_bias.json"},
    "external_wrench": {"$ref": "/../../common/external_wrench.json"}
  }
}


<|start_filename|>src/mc_solver/BoundedSpeedConstr.cpp<|end_filename|>
/*
 * Copyright 2015-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#include <mc_rtc/logging.h>
#include <mc_solver/BoundedSpeedConstr.h>
#include <mc_solver/ConstraintSetLoader.h>

namespace mc_solver
{

BoundedSpeedConstr::BoundedSpeedConstr(const mc_rbdyn::Robots & robots, unsigned int robotIndex, double dt)
: constr(new tasks::qp::BoundedSpeedConstr(robots.mbs(), static_cast<int>(robotIndex), dt)), robotIndex(robotIndex)
{
}

void BoundedSpeedConstr::addToSolver(const std::vector<rbd::MultiBody> & mbs, tasks::qp::QPSolver & solver)
{
  constr->addToSolver(mbs, solver);
}

void BoundedSpeedConstr::removeFromSolver(tasks::qp::QPSolver & solver)
{
  constr->removeFromSolver(solver);
}

size_t BoundedSpeedConstr::nrBoundedSpeeds() const
{
  return constr->nrBoundedSpeeds();
}

void BoundedSpeedConstr::addBoundedSpeed(QPSolver & solver,
                                         const std::string & bodyName,
                                         const Eigen::Vector3d & bodyPoint,
                                         const Eigen::MatrixXd & dof,
                                         const Eigen::VectorXd & speed)
{
  addBoundedSpeed(solver, bodyName, bodyPoint, dof, speed, speed);
}

void BoundedSpeedConstr::addBoundedSpeed(QPSolver & solver,
                                         const std::string & bodyName,
                                         const Eigen::Vector3d & bodyPoint,
                                         const Eigen::MatrixXd & dof,
                                         const Eigen::VectorXd & lowerSpeed,
                                         const Eigen::VectorXd & upperSpeed)
{
  if(solver.robots().robot(robotIndex).hasBody(bodyName))
  {
    constr->addBoundedSpeed(solver.robots().mbs(), bodyName, bodyPoint, dof, lowerSpeed, upperSpeed);
    constr->updateBoundedSpeeds();
    solver.updateConstrSize();
  }
  else
  {
    mc_rtc::log::error("Could not add bounded speed constraint for body {} since it does not exist in robot {}",
                       bodyName, solver.robots().robot(robotIndex).name());
  }
}

bool BoundedSpeedConstr::removeBoundedSpeed(QPSolver & solver, const std::string & bodyName)
{
  bool r = false;
  if(solver.robots().robot(robotIndex).hasBody(bodyName))
  {
    r = constr->removeBoundedSpeed(bodyName);
    constr->updateBoundedSpeeds();
    solver.updateConstrSize();
  }
  else
  {
    mc_rtc::log::error("Could not remove bounded speed constraint for body {} since it does not exist in robot {}",
                       bodyName, solver.robots().robot(robotIndex).name());
  }
  return r;
}

void BoundedSpeedConstr::reset(QPSolver & solver)
{
  constr->resetBoundedSpeeds();
  constr->updateBoundedSpeeds();
  solver.updateConstrSize();
}

} // namespace mc_solver

namespace
{

static auto registered = mc_solver::ConstraintSetLoader::register_load_function(
    "boundedSpeed",
    [](mc_solver::QPSolver & solver, const mc_rtc::Configuration & config) {
      auto ret = std::make_shared<mc_solver::BoundedSpeedConstr>(
          solver.robots(), robotIndexFromConfig(config, solver.robots(), "boundedSpeed"), solver.dt());
      if(config.has("constraints"))
      {
        for(const auto & c : config("constraints"))
        {
          std::string bName = c("body");
          Eigen::Vector3d bPoint = c("bodyPoint", Eigen::Vector3d::Zero().eval());
          Eigen::Matrix6d dof = Eigen::Matrix6d::Identity();
          if(c.has("dof"))
          {
            const auto & c_dof = c("dof");
            if(c_dof.size() == 6)
            {
              Eigen::Vector6d v = c_dof;
              dof = v.asDiagonal();
            }
            else
            {
              dof = c_dof;
            }
          }
          if(c.has("speed"))
          {
            ret->addBoundedSpeed(solver, bName, bPoint, dof, c("speed"));
          }
          else if(c.has("lowerSpeed"))
          {
            assert(c.has("upperSpeed"));
            ret->addBoundedSpeed(solver, bName, bPoint, dof, c("lowerSpeed"), c("upperSpeed"));
          }
          else
          {
            mc_rtc::log::error("No speed or lowerSpeed/upperSpeed entry for bounded speed constraint on {}", bName);
          }
        }
      }
      return ret;
    });
}


<|start_filename|>doc/_data/schemas/common/stabilizer_safety_thresholds.json<|end_filename|>
{
  "type": "object",
  "title": "mc_rbyn::lipm_stabilizer::SafetyThresholds",
  "description": "These thresholds are intended to make the stabilizer behaviour safer against invalid parameter choices, and sensor noise. Change these values with caution. See {% doxygen mc_rbdyn::lipm_stabilizer::SafetyThresholds %}",
  "properties":
  {
    "MAX_AVERAGE_DCM_ERROR": { "type": "number", "minimum": 0, "default": 0.05, "description": "Maximum average (integral) DCM error in [m]"  },
    "MAX_COP_ADMITTANCE": { "type": "number", "minimum": 0, "default": 0.1, "description": "Maximum CoP admittance for foot damping control" },
    "MAX_DCM_D_GAIN": { "type": "number", "minimum": 0, "default": 2, "description": "Maximum DCM derivative gain (no unit)" },
    "MAX_DCM_I_GAIN": { "type": "number", "minimum": 0, "default": 100, "description": "Maximum DCM average integral gain in [Hz]" },
    "MAX_DCM_P_GAIN": { "type": "number", "minimum": 0, "default": 20, "description": "Maximum DCM proportional gain in [Hz]" },
    "MAX_COMD_GAIN": { "type": "number", "minimum": 0, "default": 10, "description": "Maximum CoMd gain in [Hz]" },
    "MAX_ZMPD_GAIN": { "type": "number", "minimum": 0, "default": 10, "description": "Maximum ZMPd gain in [Hz]" },
    "MAX_DFZ_ADMITTANCE": { "type": "number", "minimum": 0, "default": 5e-4 , "description": "Maximum admittance in [s] / [kg] for foot force difference control" },
    "MAX_DFZ_DAMPING": { "type": "number", "minimum": 0, "default": 10 , "description": "Maximum normalized damping in [Hz] for foot force difference control" },
    "MAX_FDC_RX_VEL": { "type": "number", "minimum": 0, "default": 0.2, "description": "Maximum x-axis angular velocity in [rad] / [s] for foot damping control" },
    "MAX_FDC_RY_VEL": { "type": "number", "minimum": 0, "default": 0.2, "description": "Maximum y-axis angular velocity in [rad] / [s] for foot damping control"  },
    "MAX_FDC_RZ_VEL": { "type": "number", "minimum": 0, "default": 0.2, "description": "Maximum z-axis angular velocity in [rad] / [s] for foot damping control"  },
    "MIN_DS_PRESSURE": { "type": "number", "minimum": 0, "default": 15, "description": "Minimum normal contact force in DSP, used to avoid low-pressure targets when close to contact switches" },
    "MIN_NET_TOTAL_FORCE_ZMP": { "type": "number", "minimum": 0, "default": 1 }
  }
}
